Help us improve
Share bugs, ideas, or general feedback.
From just-ship
Fetches next ready_to_develop ticket via Board API or Supabase, creates feature/fix/chore branch, implements autonomously through build, code review, QA, docs checks, and ships without stopping.
npx claudepluginhub yves-s/just-ship --plugin just-shipHow this skill is triggered — by the user, by Claude, or both
Slash command
/just-ship:developThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Hole das nächste Ticket mit Status `ready_to_develop` und starte den autonomen Entwicklungsflow.
Drives a ticket from analysis through investigation, planning, TDD implementation, committing, and PR creation. Adapts to your stack via an interactive supplement on first run.
Guides developers through the full development pipeline: discover, brainstorm, plan, execute, review, and ship. Invoke when starting work on a bug, feature, improvement, or task.
Share bugs, ideas, or general feedback.
Hole das nächste Ticket mit Status ready_to_develop und starte den autonomen Entwicklungsflow.
Lies project.json für Konventionen.
Branch-Prefix: Wird aus dem Ticket-Inhalt abgeleitet — NICHT aus der Config:
fix/chore/docs/feature/Pipeline (optional): Lies project.json und bestimme den Pipeline-Modus:
pipeline.workspace_id gesetzt → board-api.sh verwenden:
bash .claude/scripts/board-api.sh get "tickets/$TICKET_NUMBER"
bash .claude/scripts/board-api.sh patch "tickets/$TICKET_NUMBER" '{"status": "in_progress"}'
Credentials werden intern aufgelöst. pipeline.project_id aus project.json.project_id gesetzt (ohne workspace_id), und project_id hat keine Bindestriche → execute_sql verwenden, Warnung ausgeben: "Kein Board API konfiguriert. Nutze Legacy Supabase MCP. Fuehre /setup-just-ship aus um zu upgraden."workspace_id noch project_id konfiguriert → Alle Pipeline-Schritte überspringen. Ticket-Infos werden per $ARGUMENTS übergeben.project_id Format-Check: Falls pipeline.project_id gesetzt ist und KEINE Bindestriche enthält (kurzer alphanumerischer String wie wsmnutkobalfrceavpxs), ist es eine alte Supabase-Projekt-ID. Warnung ausgeben: "pipeline.project_id sieht nach einer alten Supabase-ID aus. Fuehre /setup-just-ship aus um auf Board-UUID zu migrieren."
STOPPE NICHT ZWISCHEN DEN SCHRITTEN. Nach Build-Check (Schritt 6) kommt Code Review (Schritt 6.5), dann QA Review (Schritt 7), dann Docs-Check (Schritt 8), dann Ship (Schritt 9). Du darfst NICHT nach dem Build dem User die Ergebnisse zeigen und auf Antwort warten. ALLES durchlaufen bis Schritt 9 fertig ist.
Standalone-Modus (kein Pipeline): Nutze
$ARGUMENTSdirekt als Ticket-Beschreibung. Springe zu Schritt 3 (nur Feature-Branch).
Falls $ARGUMENTS übergeben: Nutze als Ticket-ID oder Suchbegriff.
Falls kein Argument: Suche nach dem nächsten Ticket mit Status "ready_to_develop".
Bei übergebener Ticket-ID (z.B. T-162):
T-162 → 162bash .claude/scripts/board-api.sh get "tickets/162"
Bei fehlendem Argument (Suche nach "ready_to_develop"):
bash .claude/scripts/board-api.sh get "tickets?status=ready_to_develop&project={pipeline.project_id}"
Nimm das erste Ticket aus der Response (data[0] oder data.tickets[0]).
Falls nur pipeline.project_id gesetzt (ohne workspace_id), nutze mcp__claude_ai_Supabase__execute_sql:
Bei übergebener Ticket-ID:
SELECT * FROM public.tickets
WHERE number = 162
AND workspace_id = '{pipeline.workspace_id}';
Bei fehlendem Argument:
SELECT number, title, body, priority, tags
FROM public.tickets
WHERE status = 'ready_to_develop'
AND workspace_id = '{pipeline.workspace_id}'
AND (
project_id = (SELECT id FROM public.projects WHERE name = '{pipeline.project_name}' AND workspace_id = '{pipeline.workspace_id}')
OR project_id IS NULL
)
ORDER BY
CASE priority WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
created_at ASC
LIMIT 1;
Kein Ticket gefunden: User informieren und stoppen.
Zeige kurz an: ▶ Ticket T-{N}: {title} — dann direkt weiter, NICHT auf Bestätigung warten.
KRITISCH — Shell-Variable für alle folgenden Board-API-Calls setzen:
Extrahiere die Ticket-Nummer aus dem JSON-Response von Schritt 1 und setze sie als Shell-Variable:
# TICKET_NUMBER = die Zahl aus dem `number`-Feld des Ticket-JSON, z.B. 162
TICKET_NUMBER=$(echo "$TICKET_JSON" | node -e "const json = JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')); process.stdout.write(String(json.number || (json.data && json.data.number) || ''))" 2>/dev/null)
# Fallback: falls $ARGUMENTS eine Ticket-ID enthält (z.B. "T-162" oder "162")
if [ -z "$TICKET_NUMBER" ]; then
TICKET_NUMBER=$(echo "$ARGUMENTS" | grep -oE '[0-9]+' | head -1)
fi
if [ -z "$TICKET_NUMBER" ]; then
echo "ERROR: Konnte keine Ticket-Nummer ermitteln."
exit 1
fi
echo "TICKET_NUMBER=$TICKET_NUMBER"
ALLE folgenden board-api.sh Calls MÜSSEN $TICKET_NUMBER verwenden. Niemals {N} als Platzhalter in board-api.sh Calls — das wird nicht aufgelöst und erzeugt einen 404.
Falls Pipeline konfiguriert — PFLICHT, NICHT ÜBERSPRINGEN. Alle Aktionen ausführen:
3a) Status updaten + Projekt zuordnen:
Board API (bevorzugt): Via board-api.sh:
if bash .claude/scripts/board-api.sh patch "tickets/$TICKET_NUMBER" '{"status": "in_progress", "branch": "{branch}", "project_id": "{pipeline.project_id}"}'; then
echo "✓ Status in_progress für T-$TICKET_NUMBER gesetzt"
else
echo "⚠ Status-Update fehlgeschlagen für T-$TICKET_NUMBER — weiter ohne Blockade"
fi
Hinweis: branch wird mitgesendet damit das Board anzeigt welcher Branch aktiv ist. project_id ordnet das Ticket dem Projekt zu falls noch nicht geschehen.
Legacy Supabase MCP (Fallback): Via mcp__claude_ai_Supabase__execute_sql:
UPDATE public.tickets
SET status = 'in_progress',
branch = '{branch}',
project_id = COALESCE(project_id, (
SELECT id FROM public.projects
WHERE name = '{pipeline.project_name}'
AND workspace_id = '{pipeline.workspace_id}'
))
WHERE number = $TICKET_NUMBER
AND workspace_id = '{pipeline.workspace_id}'
RETURNING number, title, status;
Warte auf die Bestätigung, dass das Update erfolgreich war, bevor du weitermachst.
3b) Shopify Environment Check (nur wenn stack.platform === "shopify"):
PLATFORM=$(node -e "process.stdout.write(require('./project.json').stack?.platform || '')" 2>/dev/null)
if [ "$PLATFORM" = "shopify" ]; then
bash .claude/scripts/shopify-env-check.sh
if [ $? -ne 0 ]; then
echo "ERROR: Shopify environment check failed. Fix the issues above before continuing." >&2
exit 1
fi
fi
Falls Exit-Code 1: STOP und User über fehlende Requirements informieren. Nicht fortfahren.
3c) Feature-Branch in Worktree erstellen (parallelsicher):
Prüfe zuerst ob das aktuelle Verzeichnis bereits ein Worktree ist:
git rev-parse --git-dir 2>/dev/null
Falls die Ausgabe .git enthält (kein Worktree) UND das Projekt pipeline.max_workers > 1 hat (oder nicht gesetzt):
# Worktree erstellen für parallele Ausführung
git fetch origin main
BRANCH="{abgeleiteter-prefix}/{ticket-nummer}-{kurzbeschreibung}"
WORKTREE_DIR=".worktrees/T-$TICKET_NUMBER"
git worktree add "$WORKTREE_DIR" -b "$BRANCH" origin/main
# Bootstrap: Pipeline-Tools aus Parent-.claude/ in den Worktree kopieren.
# Konsumer-Repos (z.B. just-ship-board) haben .claude/ in .gitignore — ohne
# diesen Schritt fehlen send-event.sh, board-api.sh, Skills, Agents, Rules,
# Hooks und settings.json im Worktree. cp -RnL ist idempotent (no-overwrite)
# und dereferenziert Symlinks (Engine-Repo hat .claude/agents → ../agents als
# Symlink; Konsumer-Repos haben reale Dirs — beides funktioniert mit -L).
# User-Edits im Worktree werden durch -n nicht überschrieben.
REPO_ROOT=$(git rev-parse --show-toplevel)
if [ ! -d "$REPO_ROOT/.claude" ]; then
echo "✗ worktree-bootstrap — Parent-.claude/ unter $REPO_ROOT nicht gefunden" >&2
exit 1
fi
mkdir -p "$WORKTREE_DIR/.claude"
for sub in scripts skills agents rules hooks; do
if [ -d "$REPO_ROOT/.claude/$sub" ]; then
cp -RnL "$REPO_ROOT/.claude/$sub" "$WORKTREE_DIR/.claude/" 2>/dev/null || true
fi
done
if [ -f "$REPO_ROOT/.claude/settings.json" ] && [ ! -f "$WORKTREE_DIR/.claude/settings.json" ]; then
cp "$REPO_ROOT/.claude/settings.json" "$WORKTREE_DIR/.claude/settings.json"
fi
echo "▶ worktree-bootstrap — .claude/ tools synchronisiert"
# Symlink .env.local from repo root into worktree (credentials for board-api.sh etc.)
ln -sf "$REPO_ROOT/.env.local" "$WORKTREE_DIR/.env.local" 2>/dev/null || true
Was der Bootstrap NICHT kopiert: Session-/Local-State-Files unter .claude/ (settings.local.json, .agent-map/, .active-ticket, .token-snapshot-*.json, .quality-gate-cache, .pipeline-version, .template-hash). Die liegen direkt unter .claude/ (nicht in den 5 Subdirs), werden also automatisch nicht mit-kopiert. Die .token-snapshot-T-*.json-Files leben im Main-Repo-Root unter .claude/ und gehören dort hin — nicht ins Worktree.
Danach: Alle weiteren Schritte (4-11) im Worktree-Verzeichnis ausführen. Nutze $WORKTREE_DIR als Arbeitsverzeichnis für alle Bash-Befehle (cwd), Read, Edit, Glob, Grep.
Ausgabe: ▶ worktree — .worktrees/T-$TICKET_NUMBER erstellt
Falls bereits in einem Worktree (z.B. bei Resume): einfach den Branch erstellen wie bisher:
git checkout main && git pull origin main
git checkout -b {abgeleiteter-prefix}/{ticket-nummer}-{kurzbeschreibung}
3d) Pipeline-Event senden (Board zeigt aktiven Orchestrator):
Note:
.active-ticketwird automatisch vom PostToolUse-Hook (detect-ticket-post.sh) gesetzt, sobald der erste Bash-Befehl im Worktree läuft. Kein manuelles Schreiben nötig.
bash .claude/scripts/send-event.sh $TICKET_NUMBER orchestrator agent_started
3e) Token-Snapshot schreiben (für per-Ticket-Kosten bei /ship):
# Resolve main repo root (in worktrees, git-common-dir is absolute → go up one level)
GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null) || true
if [ -n "$GIT_COMMON" ] && [ "$GIT_COMMON" != ".git" ]; then
MAIN_ROOT=$(cd "$GIT_COMMON/.." && pwd)
else
MAIN_ROOT="$PWD"
fi
SAFE_CWD=$(echo "$MAIN_ROOT" | sed 's|^/||' | sed 's|/|-|g' | sed 's| |-|g' | sed 's|\.|-|g')
SESSION_DIR="$HOME/.claude/projects/-${SAFE_CWD}"
SESSION_FILE=$(ls -t "$SESSION_DIR"/*.jsonl 2>/dev/null | head -1)
if [ -n "$SESSION_FILE" ]; then
bash .claude/scripts/calculate-session-cost.sh "$(basename "$SESSION_FILE" .jsonl)" "$MAIN_ROOT" > "$MAIN_ROOT/.claude/.token-snapshot-T-$TICKET_NUMBER.json" 2>/dev/null || true
fi
Der Snapshot wird immer im Main-Repo-Root geschrieben (nicht im Worktree), da ship-token-tracking.sh den Snapshot dort sucht.
bash .claude/scripts/send-event.sh $TICKET_NUMBER triage agent_started
Ausgabe: ▶ triage — Ticket-Qualität prüfen
Spawne einen Triage-Agent mit model: "haiku" und subagent_type: "triage":
Prompt:
Lies .claude/agents/triage.md für deine Rolle.
Analysiere folgendes Ticket:
Ticket-ID: T-$TICKET_NUMBER
Titel: {title}
Beschreibung:
{body}
Labels: {labels}
Verarbeite das JSON-Ergebnis des Agents:
enriched_body als verbesserte Beschreibung für alle weiteren Schritte (Planung, Agent-Prompts).qa_tier (full/light/skip), qa_pages und qa_flows für Schritt 10 (Automated QA).bash .claude/scripts/send-event.sh $TICKET_NUMBER triage completed '{"verdict": "{verdict}"}'
Ausgabe:
✓ triage — Ticket ausreichend klar (bei sufficient)✓ triage — Ticket angereichert (bei enriched, zeige 1-Zeiler Analysis)NICHT STOPPEN. SOFORT weiter zu Schritt 4.
Lies nur die 5-10 betroffenen Dateien direkt mit Read/Glob/Grep.
Lies CLAUDE.md für Architektur und Konventionen.
Lies project.json für Pfade und Stack-Details.
Dann: Instruktionen für Agents formulieren — mit exakten Code-Änderungen und neuen Dateien direkt im Prompt.
Die Skill-Auswahl folgt dem Inhalt des Tickets. Diese Tabelle ist Heuristik, kein Automat: mehrere Skills können parallel gelten, der Orchestrator entscheidet welche tatsächlich Mehrwert bringen.
| Ticket-Typ | Skills |
|---|---|
| UI/Frontend-Änderung an bestehendem Design-System | frontend-design |
| Neue Seite / neues Feature mit eigener UX | design-lead (frame first) + ux-planning + ggf. creative-design (Greenfield) |
| Produkt-Struktur / Interaction-Philosophie / Design-System-Richtung | design-lead |
| Cross-Feature-Konsistenz-Review | design-lead |
| Architektur / Performance / Ops / Security-Strategie | product-cto |
| API- oder Hook-Änderung | backend |
| Schema- oder Migration-Arbeit | data-engineer |
| Test-Strategie, neue Test-Suite | webapp-testing + test-driven-development → QA-Agent |
Peer-Regel: design-lead und product-cto sind gleichberechtigt. Bei Cross-Cutting-Entscheidungen (Technik + UX) laufen beide. Bei reinen Design-/Produkt-Struktur-Fragen entscheidet design-lead allein. Bei reinen Architektur-/Ops-Fragen entscheidet product-cto allein.
Tickets, die pipeline/, commands/develop.md, commands/ship.md oder .claude/scripts/ ändern, dürfen NICHT als done markiert werden ohne:
bash scripts/pipeline-smoke-test.sh — verifiziert Board-API-Round-Trip und Stuck-Recovery-Pfad. FAIL = Fix nicht verifiziert, nicht mergen.bash scripts/pipeline-vps-test.sh --host <vps-host> — erstellt ein echtes Ticket, schickt es durch die VPS-Pipeline, verifiziert Agents liefen, PR erstellt, Status in_review. FAIL = Pipeline funktioniert nicht end-to-end auf VPS.Für spezifische Bereiche gilt zusätzlich manuelle Verifikation:
| Änderung | VPS-Verifikation |
|---|---|
| Worker-Logik (lifecycle, recovery, retry) | Worker-Log zeigt erwartetes Verhalten; Ticket-State manuell manipulieren und Recovery beobachten |
| Pipeline-Phase (triage, orchestrator, qa) | Ein echtes Ticket autonom durchlaufen lassen und Phasen-Output prüfen |
| Status-Updates / Board-Events | Board-UI zeigt korrekten Status ohne Hänger nach Ticket-Durchlauf |
| Scripts / Config | Script auf VPS ausführen, Output verifizieren |
Warum: Code kann lokal korrekt aussehen und den Smoke-Test bestehen, aber auf der VPS trotzdem brechen — unterschiedliche Node-Version, fehlende Env-Vars, echte Supabase-Latenz, systemd-Eigenheiten.
Für JEDEN Agent-Spawn — Events senden UND Ausgabe anzeigen:
Vor Agent-Start:
bash .claude/scripts/send-event.sh $TICKET_NUMBER {agent-type} agent_started
Ausgabe: ▶ [{agent-type}] — {was der Agent macht}
Nach Agent-Ende — Token-Verbrauch aus dem Agent-Ergebnis extrahieren:
bash .claude/scripts/send-event.sh $TICKET_NUMBER {agent-type} completed '{"tokens_used": {total_tokens}}'
Dabei {total_tokens} aus dem <usage>total_tokens: X</usage> Block des Agent-Ergebnisses lesen. Falls kein Usage-Block vorhanden, 0 senden.
Ausgabe: ✓ [{agent-type}] abgeschlossen ({formatted_tokens} tokens)
Spawne Agents via Agent-Tool mit konkreten Instruktionen:
| Agent | model | Wann |
|---|---|---|
data-engineer | haiku | Bei Schema-Änderungen |
backend | sonnet | Bei API/Hook-Änderungen |
frontend | sonnet | Bei UI-Änderungen |
Prompt-Muster: Exakte Dateiliste + Code-Snippets, NICHT "lies die Spec".
Ausgabe: ▶ build-check — {build command}
Lies Build-Commands aus project.json und führe sie aus.
Nur bei Build-Fehlern:
bash .claude/scripts/send-event.sh $TICKET_NUMBER devops agent_started
Ausgabe: ▶ devops — Build-Fehler beheben und DevOps-Agent mit model: "haiku" spawnen.
Nach DevOps-Agent:
bash .claude/scripts/send-event.sh $TICKET_NUMBER devops completed
NICHT STOPPEN. Zeige dem User NICHT die Build-Ergebnisse und warte NICHT auf Antwort. SOFORT weiter zu Schritt 6.5.
bash .claude/scripts/send-event.sh $TICKET_NUMBER code-review agent_started
Ausgabe: ▶ code-review — Diff gegen main reviewen
Ein Code-Review-Agent mit model: "sonnet" und subagent_type: "code-reviewer":
git diff main..HEAD auf Code-Qualität, Patterns, Edge Cases, Error Handling, Performance, Securitychore(code-review): ...)Prompt-Muster:
Du bist der Code Review Agent. Lies agents/code-review.md für deine Rolle.
Arbeitsverzeichnis: {WORKTREE_DIR oder CWD}
Branch: {aktueller Branch}
Ticket: T-$TICKET_NUMBER
Reviewe den Diff auf diesem Branch gegen main. Lies agents/code-review.md für Review-Kriterien und Workflow.
Lies CLAUDE.md für Projekt-Konventionen.
Lies project.json für Stack und Build-Commands.
Fixe gefundene Issues direkt. Jeder Fix als eigener Commit: chore(code-review): {beschreibung}
Verarbeite das Ergebnis:
bash .claude/scripts/send-event.sh $TICKET_NUMBER code-review completed
Ausgabe nach Abschluss:
✓ code-review abgeschlossen ({N} issues gefixt) (bei Findings)✓ code-review abgeschlossen (clean) (bei keinen Findings)Falls Review-Fixes den Build brechen: DevOps-Agent spawnen (wie in Schritt 6), dann weiterlaufen.
NICHT STOPPEN. SOFORT weiter zu Schritt 7.
bash .claude/scripts/send-event.sh $TICKET_NUMBER qa agent_started
Ausgabe: ▶ qa — Acceptance Criteria & Security prüfen
Ein QA-Agent mit model: "haiku":
bash .claude/scripts/send-event.sh $TICKET_NUMBER qa completed
Ausgabe nach Abschluss: ✓ qa abgeschlossen
NICHT STOPPEN. SOFORT weiter zu Schritt 8.
Ausgabe: ▶ docs — Dokumentation prüfen
Ermittle alle geänderten Dateien auf diesem Branch:
git diff --name-only $(git merge-base main HEAD) HEAD
git status --porcelain
Der Docs-Check hat zwei Teile: einen universellen Teil (läuft in jedem Projekt) und einen projektspezifischen Teil (nur wenn die jeweiligen Dateien existieren).
CHANGELOG.md wird bei JEDER Änderung aktualisiert — egal welches Projekt, egal welche Dateien sich geändert haben.
Falls CHANGELOG.md nicht existiert, erstelle sie mit diesem Header:
# Changelog
All notable changes to this project will be documented in this file.
Format: [Keep a Changelog](https://keepachangelog.com/)
## [Unreleased]
Falls CHANGELOG.md existiert aber keine [Unreleased]-Sektion hat, füge sie als erste Sektion nach dem Header ein.
Format: Keep-a-Changelog mit Gruppen ### Added, ### Changed, ### Fixed, ### Removed. Beschreibung auf Englisch, 1 Zeile pro Änderung. Nur Gruppen verwenden, die auch Einträge haben.
Prüfe ob die jeweilige Zieldatei existiert. Nur bestehende Dateien aktualisieren — keine neuen Docs anlegen. Falls eine Zieldatei nicht existiert, diesen Eintrag überspringen.
| Geänderte Dateien | Zu prüfende Docs | Aktion |
|---|---|---|
commands/*.md | README.md | Commands-Tabelle + Architecture-Abschnitt |
agents/*.md | README.md | Agents-Tabelle |
skills/*.md | README.md | Skills-Tabelle |
pipeline/**, agents/*.md, commands/*.md | README.md | Workflow-Diagramm |
pipeline/**, agents/*.md, .claude/** | docs/ARCHITECTURE.md | Betroffene Sektionen (Agent System, Slash Commands, Pipeline SDK, etc.) |
| Pipeline/Architektur-Strukturen | CLAUDE.md | Architektur-Abschnitt |
commands/*.md, agents/*.md, skills/*.md | templates/CLAUDE.md | Template aktualisieren falls Commands/Agents/Skills-Referenzen enthalten |
pipeline/worker.ts, pipeline/server.ts | docs/ARCHITECTURE.md | Pipeline-Server Abschnitt |
| Workflow, Conventions, Dev-Setup | CONTRIBUTING.md | Contributing Guidelines |
| Keine der obigen Trigger-Dateien | — | Teil 2 überspringen |
Falls Anpassung nötig: direkt mit Edit-Tool ändern.
Ausgabe pro geprüfter Datei:
✓ docs — CHANGELOG.md aktualisiert✓ docs — README.md aktualisiert✓ docs — docs/ARCHITECTURE.md aktualisiert✓ docs — templates/CLAUDE.md aktualisiert✓ docs — docs/ARCHITECTURE.md aktualisiert (Pipeline-Server)✓ docs — CONTRIBUTING.md aktualisiert✓ docs — keine Änderungen nötig (falls nur CHANGELOG und sonst nichts zu tun war)NICHT STOPPEN. SOFORT weiter zu Schritt 9.
/ship, KEIN Merge)Pipeline-Event senden (Orchestrator abgeschlossen):
bash .claude/scripts/send-event.sh $TICKET_NUMBER orchestrator completed
WICHTIG: /ship wird NICHT aufgerufen. /ship mergt automatisch — das darf nur der User auslösen.
Stattdessen: Commit, Push und PR manuell durchführen.
NICHT den Skill finishing-a-development-branch aufrufen.
NICHT dem User Optionen präsentieren.
NICHT fragen ob committed/gepusht werden soll.
NICHT mergen. NICHT auf main wechseln. NICHT Status auf "done" setzen.
9a. Commit:
git add <betroffene-dateien>
git commit -m "feat(T-$TICKET_NUMBER): {englische Beschreibung}
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
9b. Push:
git push -u origin $(git branch --show-current)
9c. PR erstellen:
gh pr view 2>/dev/null || gh pr create --title "feat(T-$TICKET_NUMBER): {Beschreibung}" --body "$(cat <<'EOF'
## Summary
- {Bullet Points}
## Test plan
- {Was wurde getestet}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
9d. Change Summary ins Ticket schreiben:
Generiere eine Zusammenfassung der Änderungen auf diesem Branch und schreibe sie ins Ticket:
MERGE_BASE=$(git merge-base main HEAD)
CHANGED_FILES=$(git diff --name-status $MERGE_BASE..HEAD)
COMMIT_LOG=$(git log --oneline $MERGE_BASE..HEAD)
DIFF_STAT=$(git diff --stat $MERGE_BASE..HEAD | tail -1)
REVIEW_URL=$(gh pr view --json url -q .url 2>/dev/null || echo "")
Baue daraus einen Markdown-String für das summary-Feld:
## Changes Summary als Überschriftgit diff --stat)Board API (bevorzugt):
if bash .claude/scripts/board-api.sh patch "tickets/$TICKET_NUMBER" '{"summary": "{summary_markdown}"}'; then
echo "✓ summary auf T-$TICKET_NUMBER gesetzt"
else
echo "⚠ summary Patch fehlgeschlagen für T-$TICKET_NUMBER — weiter ohne Blockade"
fi
Legacy Supabase MCP (Fallback):
UPDATE public.tickets SET summary = '{summary_markdown}' WHERE number = $TICKET_NUMBER AND workspace_id = '{pipeline.workspace_id}';
Ausgabe: ✓ summary — Änderungszusammenfassung ins Ticket geschrieben
9e. Status auf "in_review" + Review-URL:
PR-URL extrahieren (falls nicht schon in 9d geschehen):
REVIEW_URL=$(gh pr view --json url -q .url 2>/dev/null || echo "")
Board API (bevorzugt):
if bash .claude/scripts/board-api.sh patch "tickets/$TICKET_NUMBER" '{"status": "in_review", "review_url": "'"$REVIEW_URL"'"}'; then
echo "✓ Status in_review + review_url auf T-$TICKET_NUMBER gesetzt"
else
echo "⚠ in_review Patch fehlgeschlagen für T-$TICKET_NUMBER — weiter ohne Blockade"
fi
Legacy Supabase MCP (Fallback):
UPDATE public.tickets SET status = 'in_review', review_url = '$REVIEW_URL' WHERE number = $TICKET_NUMBER AND workspace_id = '{pipeline.workspace_id}' RETURNING number, title, status;
Der PR bleibt offen bis der User ihn freigibt (via /ship oder "passt").
Nur ausführen wenn hosting.provider gesetzt ist. Die Scripts prüfen selbst ob ein Hosting-Provider konfiguriert ist und exiten graceful wenn nicht. Bei nicht gesetztem hosting-Feld wird dieser gesamte Schritt übersprungen — kein API-Call, kein Warten.
WICHTIG: Die Preview-URL MUSS eine Deployment-URL sein (z.B. https://<project>-<hash>.vercel.app, https://<store>.myshopify.com/?preview_theme_id=... oder https://<app>.coolify-domain.tld). NIEMALS einen GitHub-Link, PR-URL oder Repository-URL als preview_url setzen. Das preview_url-Feld ist ausschließlich für die live deployete Vorschau.
# Read hosting provider from project.json (supports object and legacy string format)
HOSTING_PROVIDER=$(node -e "
const c = require('./project.json');
const h = c.hosting;
if (typeof h === 'object' && h !== null) {
process.stdout.write(h.provider || '');
} else if (typeof h === 'string') {
process.stdout.write(h);
}
")
# Also check stack.framework for legacy Shopify detection
if [ -z "$HOSTING_PROVIDER" ]; then
HOSTING_PROVIDER=$(node -e "
const c = require('./project.json');
if (c.stack?.framework === 'shopify') process.stdout.write('shopify');
")
fi
if [ "$HOSTING_PROVIDER" = "shopify" ]; then
PREVIEW_URL=$(bash .claude/scripts/shopify-dev.sh start "T-${TICKET_NUMBER}" "${TITLE}")
elif [ "$HOSTING_PROVIDER" = "vercel" ]; then
PREVIEW_URL=$(bash .claude/scripts/get-preview-url.sh 30)
elif [ "$HOSTING_PROVIDER" = "coolify" ]; then
PREVIEW_URL=$(bash .claude/scripts/get-preview-url.sh 60)
else
# No hosting provider configured — skip preview URL entirely
PREVIEW_URL=""
fi
Falls kein Hosting-Provider konfiguriert ist ($HOSTING_PROVIDER leer), direkt ausgeben:
Ausgabe: ✓ preview — übersprungen (kein Preview-Deployment konfiguriert)
Dann den Rest von Schritt 9f überspringen und zu Schritt 10 weitergehen.
Falls eine URL gefunden wurde ($PREVIEW_URL nicht leer), ins Ticket schreiben:
Board API:
if [ -n "$PREVIEW_URL" ]; then
if bash .claude/scripts/board-api.sh patch "tickets/$TICKET_NUMBER" '{"preview_url": "'"$PREVIEW_URL"'"}'; then
echo "✓ preview_url auf T-$TICKET_NUMBER gesetzt"
else
echo "⚠ preview_url Patch fehlgeschlagen für T-$TICKET_NUMBER — weiter ohne Blockade"
fi
fi
Preview-Comment posten (non-blocking):
if [ -n "$PREVIEW_URL" ]; then
bash .claude/scripts/post-comment.sh $TICKET_NUMBER "Preview: $PREVIEW_URL" preview
fi
Legacy Supabase MCP (Fallback):
if [ -n "$PREVIEW_URL" ]; then
mcp__claude_ai_Supabase__execute_sql "UPDATE public.tickets SET preview_url = '$PREVIEW_URL' WHERE number = $TICKET_NUMBER AND workspace_id = '{pipeline.workspace_id}' RETURNING number, preview_url;"
fi
Ausgabe:
✓ preview — {PREVIEW_URL} (falls URL gefunden)✓ preview — kein Deployment gefunden, übersprungen (falls Deployment-Script keine URL lieferte)✓ preview — übersprungen (kein Preview-Deployment konfiguriert) (falls kein Hosting-Provider gesetzt)Kein Fehler wenn keine URL gefunden wird. Die Scripts exiten immer mit Code 0. Projekte ohne Vercel-, Shopify- oder Coolify-Integration überspringen diesen Schritt automatisch. Keine internen Details (Provider-Namen, Script-Pfade, Implementierungshinweise) in der Ausgabe.
Nutze das qa_tier aus der Triage (Schritt 3.5). Falls die Triage kein qa_tier lieferte, default auf light.
bash .claude/scripts/send-event.sh $TICKET_NUMBER qa-auto agent_started
Ausgabe: ▶ qa-auto — Automatisierte QA ({qa_tier} tier)
| Tier | Was passiert |
|---|---|
| full | Build + Tests + Playwright Smoke Tests gegen Preview URL (falls $PREVIEW_URL aus Schritt 9f vorhanden — egal ob Vercel, Shopify oder Coolify) |
| light | Build + Tests (falls konfiguriert) |
| skip | Nur Build-Check |
Prüfe ob Pipeline-relevante Dateien auf diesem Branch geändert wurden:
PIPELINE_CHANGED=$(git diff --name-only $(git merge-base main HEAD) HEAD | grep -E '^(pipeline/|commands/(develop|ship)\.md|\.claude/scripts/)' | head -1)
Falls $PIPELINE_CHANGED nicht leer: Pipeline E2E Smoke Test ausführen:
bash scripts/pipeline-smoke-test.sh
Bei FAIL: Dies ist ein blocking Fehler. Der PR darf NICHT als ready markiert werden. Label qa:needs-review setzen und im QA-Report dokumentieren.
Lies Build- und Test-Commands aus project.json und führe sie aus (identisch zu Schritt 6, aber als erneute Verification nach PR-Push).
full tier + Preview URL)Voraussetzung: qa_tier === "full" UND $PREVIEW_URL aus Schritt 9f ist nicht leer.
Falls eine dieser Bedingungen nicht erfüllt: Playwright komplett überspringen.
Shopify-spezifisch: Falls der Store passwortgeschützt ist (Env-Variable SHOPIFY_STORE_PASSWORD gesetzt), muss Playwright zuerst das Storefront-Passwort eingeben:
const storePassword = process.env.SHOPIFY_STORE_PASSWORD || '';
if (storePassword) {
await page.fill('input[type="password"]', storePassword);
await page.click('button[type="submit"]');
await page.waitForNavigation();
}
Playwright installieren (falls nötig):
npx playwright install chromium 2>/dev/null || true
Für jede Seite aus qa_pages (oder ["/"] falls leer) ein Smoke-Test-Script ausführen.
Generiere safeName als filename-sicherer String aus qa_page (z.B. "/" → "index", "/about" → "about", "/" und "." durch "-" ersetzen):
node -e "
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
const errors = [];
page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });
const resp = await page.goto('${PREVIEW_URL}{qa_page}', { waitUntil: 'networkidle', timeout: 60000 });
const status = resp?.status() ?? 0;
await page.screenshot({ path: '/tmp/qa-screenshot-{safeName}.png' });
await browser.close();
console.log(JSON.stringify({ status, errors, ok: status >= 200 && status < 400 }));
})();
"
Ergebnis auswerten:
Screenshots per Read-Tool inspizieren und im QA-Report referenzieren.
Bei Fehlern: Automatisch fixen (max 3 Versuche). Jeder Fix als eigener Commit: fix(qa): address QA failures (attempt {N}).
Nach 3 gescheiterten Versuchen: trotzdem weitermachen, Fehler im PR-Kommentar dokumentieren.
gh pr comment --body-file /tmp/qa-report-$TICKET_NUMBER.md
gh pr edit --add-label "qa:passed" # alles grün
gh pr edit --add-label "qa:needs-review" # nach 3 Fix-Versuchen nicht grün
gh pr edit --add-label "qa:skipped" # skip tier
bash .claude/scripts/send-event.sh $TICKET_NUMBER qa-auto completed '{"tier": "{qa_tier}", "status": "{passed|failed}"}'
Ausgabe: ✓ qa-auto — {qa_tier} tier {passed|needs-review|skipped}
Zeige am Ende eine strukturierte Zusammenfassung der gesamten Session im Terminal.
Daten sammeln:
MERGE_BASE=$(git merge-base main HEAD)
PR_URL=$(gh pr view --json url -q .url 2>/dev/null || echo "")
$PREVIEW_URL ist aus Schritt 9f verfügbar (leer wenn kein Hosting-Provider).
$QA_RESULT ist aus Schritt 10e verfügbar (passed, needs-review, oder skipped).
Summary ausgeben:
bash .claude/scripts/session-summary.sh \
"{N}" \
"{ticket_title}" \
"{1-2 Sätze Zusammenfassung was das Ticket adressiert}" \
"{qa_result}" \
"$PR_URL" \
"$PREVIEW_URL"
Das Script sammelt automatisch:
calculate-session-cost.shFalls Token-Daten nicht verfügbar sind, werden Token- und Cost-Blöcke automatisch weggelassen. Falls keine Preview-URL vorhanden ist, wird die Zeile automatisch weggelassen.
Keine Ausgabe außer dem Script-Output. Das Script ersetzt die bisherige "fertig"-Meldung.
Bevor du den Workflow als fertig meldest, prüfe:
qa:* Label (Schritt 10)Hinweis: Worktree wird NICHT hier aufgeräumt — das passiert in /ship nach dem Merge, damit Nachbesserungen nach Code Review im Worktree möglich bleiben.