ワンクリックで
setup-just-ship
Just Ship installieren und Projekt konfigurieren — Stack erkennen, project.json befüllen, Board verbinden
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Just Ship installieren und Projekt konfigurieren — Stack erkennen, project.json befüllen, Board verbinden
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Neues Projekt im aktuellen Workspace verknüpfen — nur Projekt-ID in project.json schreiben
Board-Verbindung einrichten — via jsp_ Token oder Status prüfen
/just-ship-review — Branch lokal auschecken, builden, Dev-Server starten und testen
Spike-Ergebnis reviewen — Zusammenfassung anzeigen, Follow-Up-Tickets erstellen, Spike-Ticket abschliessen
Single source of truth for how the just-ship pipeline talks to the CEO. Defines the voice (Result-First, tables for lists of 3+, status icons, short active sentences, no inner monologue) and the five Core Templates (develop-complete, ship-complete, ticket-created, epic-created, phase-progress). Every user-facing output string produced by an agent, a skill, or a pipeline phase runs through this skill — never freeform prose. Triggers at the end of /develop, at the end of /ship, in Sidekick replies for ticket/epic creation, in /just-ship-status, and during phase-progress updates. Load whenever you are about to render a terminal block the CEO will read.
The operating system for decision-making in this team. ALWAYS load this skill. It defines what the human decides and what the AI expert team decides autonomously. Use on EVERY task, EVERY conversation, EVERY feature. This skill prevents the anti-pattern of asking the user technical, design, or implementation questions that an expert should answer. It is the single most important skill in the stack — without it, every other skill's quality is bottlenecked by the user being asked questions they shouldn't answer. Triggers on: literally everything. This is not optional. Load it first, always.
| applies_to | all-agents |
| name | setup-just-ship |
| description | Just Ship installieren und Projekt konfigurieren — Stack erkennen, project.json befüllen, Board verbinden |
| disable-model-invocation | true |
Installiert Just Ship im aktuellen Projekt (falls noch nicht geschehen), erkennt den Tech-Stack automatisch, befüllt project.json und CLAUDE.md, und verbindet optional das Just Ship Board.
| Flag | Beschreibung |
|---|---|
--board | Board URL (z.B. https://board.just-ship.io) |
--key | Workspace API Key (z.B. adp_...) — vom Board generiert |
--project | Projekt UUID |
--workspace-id | Workspace UUID (alternativ zu --key) |
Prüfe als allererstes: Sind --board UND (--key ODER --workspace-id) UND --project übergeben worden?
Falls JA → führe NUR die folgenden Schritte aus und beende danach. Kein Stack-Analyse, kein Menü, keine Rückfragen. NICHT nach dem API-Key fragen — er wurde bereits als --key übergeben.
Merke dir die übergebenen Werte:
KEY = der Wert hinter --key (z.B. adp_abc123...)BOARD = der Wert hinter --board (z.B. https://board.just-ship.io)PROJECT = der Wert hinter --project (z.B. ddab92d9-...)Falls --key übergeben (kein --workspace-id): Rufe die Board-API mit dem bereits bekannten KEY auf:
RESPONSE=$(curl -s -H "X-Pipeline-Key: $KEY" "$BOARD/api/projects")
WORKSPACE_ID=$(echo "$RESPONSE" | node -e "
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8'));
process.stdout.write(d.workspace_id || d.data?.workspace_id || '');
")
Falls WORKSPACE_ID leer: Ausgabe ⚠ Konnte Workspace-ID nicht ermitteln. Prüfe deinen API-Key. und abbrechen.
Falls --workspace-id direkt übergeben: diesen Wert als WORKSPACE_ID verwenden.
Führe beide Befehle mit den bereits bekannten Werten aus — ohne Rückfragen, ohne nach dem Key zu fragen:
"$HOME/.just-ship/scripts/write-config.sh" add-workspace \
--workspace-id "$WORKSPACE_ID" \
--key "$KEY" \
--board "$BOARD"
bash .claude/scripts/write-config.sh set-project \
--workspace-id "$WORKSPACE_ID" \
--project-id "$PROJECT"
Führe Schritt 6 (Sidekick) aus — nutze dabei WORKSPACE_ID, --key als api_key und --board als board_url.
Ausgabe:
✓ Board verbunden
✓ Projekt verknüpft
✓ project.json aktualisiert
Erstelle dein erstes Ticket mit /ticket.
STOP. Nicht weiter unten lesen.
0a) Global installiert? Prüfe ob ~/.just-ship als git-Repo existiert:
[ -d "$HOME/.just-ship/.git" ] && echo "OK" || echo "NOT_INSTALLED"
Falls NOT_INSTALLED:
Just Ship wird installiert...curl -fsSL https://just-ship.io/install | bash
✓ Just Ship installiert0b) Im Projekt installiert? Prüfe ob .claude/agents/ existiert:
ls .claude/agents/ 2>/dev/null | head -1 || echo "NOT_INSTALLED"
Falls NOT_INSTALLED:
Framework-Dateien werden kopiert...just-ship setup --auto
✓ Framework eingerichtet0c) Bestehendes Setup erkennen
Falls .claude/agents/ bereits existiert UND project.json bereits existiert mit gesetzten Stack-Feldern (mindestens stack.framework oder stack.language sind non-empty):
Prüfe den Status:
project.json → pipeline.workspace_id gesetzt? → Board verbunden.env.local → JSP_BOARD_API_KEY gesetzt? (primärer Credential-Pfad)~/.just-ship/config.json → Workspace-Einträge (Legacy-Fallback, nur bei Alt-Installation)?Falls Stack erkannt aber Board NICHT verbunden:
✓ project.json gefunden ({stack.framework}, {stack.language})
✓ CLAUDE.md gefunden
✓ .claude/agents/ vorhanden
⚠ Board nicht verbunden
Projekt ist bereits eingerichtet. Was möchtest du tun?
1. Board verbinden → zeige Anleitung für 'just-ship connect' im Terminal
2. Nein, CLI-only nutzen
3. Setup komplett neu ausführen → Stack-Erkennung + Config überschreiben
Falls Stack erkannt UND Board verbunden: Zeige Status und frage ob Re-Setup gewünscht:
✓ Projekt vollständig eingerichtet
Stack: {framework}, Board: verbunden
Setup erneut ausführen? (Überschreibt Stack-Erkennung)
1. Ja, neu erkennen
2. Nein, alles gut
Lies die vorhandenen Dateien im Projekt-Root um den Stack zu erkennen:
Package Manager & Dependencies:
package.json → Dependencies, Scripts, Namepnpm-lock.yaml → pnpmyarn.lock → yarnbun.lockb / bun.lock → bunpackage-lock.json → npmrequirements.txt / pyproject.toml / Pipfile → Pythongo.mod → GoCargo.toml → RustFramework-Erkennung (aus Dependencies oder Dateistruktur):
next → Next.js (prüfe next.config.* für App Router vs Pages Router)nuxt → Nuxt@angular/core → Angularsvelte / @sveltejs/kit → Svelte/SvelteKitreact (ohne next) → React (Vite/CRA)vue (ohne nuxt) → Vueexpress / fastify / hono → Node Backenddjango / flask / fastapi → Python Backendsections/ + layout/theme.liquid existieren → Shopify Theme (kein package.json nötig)Datenbank:
supabase/ Verzeichnis oder @supabase/supabase-js → Supabaseprisma/ Verzeichnis → Prismadrizzle.config.* → DrizzleWeitere Config-Dateien:
tsconfig.json → TypeScript (prüfe paths für Import-Aliase wie @/)tailwind.config.* → Tailwind CSS.env.example / .env.local → Env-Variablen-Mustervitest.config.* / jest.config.* → Test-Frameworkplaywright.config.* → E2E TestsDockerfile / docker-compose.* → DockerProjekt-Struktur:
src/app/ → App Router (Next.js) oder Angularsrc/pages/ → Pages Router oder Viteapp/ → Next.js App Router (ohne src)pages/ → Next.js Pages Router (ohne src)src/components/ / components/ → Component-Verzeichnissrc/lib/ / lib/ / utils/ → Utility-Verzeichnissrc/server/ / server/ / api/ → Backend-VerzeichnisLies die aktuelle project.json. Befülle/aktualisiere folgende Felder basierend auf der Analyse — überschreibe keine Werte die bereits sinnvoll gesetzt sind:
{
"name": "<aus package.json name oder bestehender Wert>",
"description": "<aus package.json description oder bestehender Wert>",
"stack": {
"framework": "<erkanntes Framework, z.B. 'Next.js 15 (App Router)'>",
"language": "<z.B. 'TypeScript'>",
"styling": "<z.B. 'Tailwind CSS'>",
"database": "<z.B. 'Supabase (PostgreSQL)'>",
"orm": "<z.B. 'Prisma' oder 'Drizzle' oder null>",
"testing": "<z.B. 'Vitest' oder 'Jest'>",
"package_manager": "<pnpm|yarn|bun|npm>"
},
"build": {
"web": "<package_manager> run build",
"dev": "<package_manager> run dev",
"test": "<erkannter Test-Runner, z.B. 'npx vitest run'>"
},
"paths": {
"components": "<erkannter Pfad, z.B. 'src/components'>",
"pages": "<erkannter Pfad, z.B. 'src/app'>",
"lib": "<erkannter Pfad, z.B. 'src/lib'>",
"api": "<erkannter Pfad, z.B. 'src/app/api'>"
}
}
Shopify-Projekte: Falls als Shopify-Theme erkannt:
stack.framework: "shopify"stack.language: "liquid"build.web: "shopify theme check --fail-level error"hosting: "shopify"shopify.store: Aus shopify.theme.toml ([environments.default].store) lesen, falls vorhanden. Sonst aus bestehendem project.json. Falls nicht vorhanden → User fragen: "Shopify Store URL? (z.B. client-store.myshopify.com)"Shopify Token einrichten (nur bei Shopify-Projekten, nach Store-URL):
SHOPIFY_CLI_THEME_TOKEN bereits in .env steht:
grep -q 'SHOPIFY_CLI_THEME_TOKEN' .env 2>/dev/null && echo "EXISTS" || echo "MISSING"
MISSING → User fragen:
Shopify Theme Access Token? (findest du im Shopify Admin unter Apps → Theme Access)
Format: shptka_...
Leer lassen um zu überspringen.
.env schreiben (erstellen falls nicht vorhanden): SHOPIFY_CLI_THEME_TOKEN=shptka_....env in .gitignore steht. Falls nicht → .env zu .gitignore hinzufügenshopify theme list --store "$STORE" --password "$TOKEN" 2>&1 | head -3✓ Shopify Token validiert⚠ Token konnte nicht validiert werden — prüfe ob er korrekt ist. Du kannst ihn später in .env anpassen.ℹ Kein Token hinterlegt. Du kannst später SHOPIFY_CLI_THEME_TOKEN=shptka_... in .env eintragen.Regeln:
build Commands aus package.json scripts ableiten wenn vorhandenpaths nur setzen wenn das Verzeichnis tatsächlich existiertLies die aktuelle CLAUDE.md. Falls dort noch TODO-Platzhalter stehen:
Projekt-Beschreibung (unter ## Projekt):
TODO: Kurze Projektbeschreibung mit einer Beschreibung basierend auf package.json description, README, oder erkanntem StackCode-Konventionen (unter ### Code):
TODO: Code-Konventionen mit erkannten Konventionen:
@/ Alias wenn in tsconfig erkannt)Architektur (unter ## Architektur):
TODO: Projektstruktur mit der tatsächlichen Top-Level-StrukturRegeln:
Zeige nur Zeilen für Felder die tatsächlich erkannt wurden (leere Felder weglassen):
✓ Just Ship eingerichtet
Stack : {framework} + {language} + {styling} ← nur wenn erkannt
Build : {build_command} ← nur wenn erkannt
Test : {test_command} ← nur wenn erkannt
Package Mgr : {package_manager} ← nur wenn erkannt
Geänderte Dateien:
✓ project.json
✓ CLAUDE.md
Falls gar kein Stack erkannt wurde (leeres Projekt):
✓ Just Ship eingerichtet
Stack noch nicht erkannt — wird automatisch befüllt sobald
du Abhängigkeiten installierst und /setup-just-ship erneut ausführst.
Geänderte Dateien:
✓ project.json
✓ CLAUDE.md
Falls pipeline.workspace_id in project.json noch nicht gesetzt ist, frage:
Möchtest du das Just Ship Board verbinden? (j/n)
Falls nein: Abschließen mit:
Fertig! Erstelle dein erstes Ticket mit /ticket.
Falls ja: Ausgabe (NICHT in einem Code-Block, damit der Link klickbar ist):
Öffne https://board.just-ship.io — das Board führt dich durch die Einrichtung. Sag Bescheid wenn du fertig bist.
Keine weiteren Erklärungen. Das Board hat einen Onboarding-Stepper der alles erklärt.
Wenn der User zurückkommt, prüfe ob die Verbindung eingerichtet wurde:
# Primary: .env.local (current connect-flow writes here)
if [ -f .env.local ] && grep -q '^JSP_BOARD_API_KEY=' .env.local 2>/dev/null; then
echo "CONNECTED:env"
# Legacy fallback: ~/.just-ship/config.json (old installations)
elif [ -f "$HOME/.just-ship/config.json" ]; then
cat "$HOME/.just-ship/config.json" 2>/dev/null | node -e "
try {
const c=JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8'));
const ws=Object.keys(c.workspaces||{});
console.log(ws.length ? 'CONNECTED:legacy:' + ws.join(',') : 'NOT_CONNECTED');
} catch(e) { console.log('NOT_CONNECTED'); }
"
else
echo "NOT_CONNECTED"
fi
Falls CONNECTED: Bestätige mit ✓ Board verbunden
Falls NOT_CONNECTED: Frage ob etwas nicht geklappt hat.
Nur ausführen wenn Board verbunden (pipeline.workspace_id und pipeline.project_id in project.json gesetzt). Falls kein Board verbunden: Schritt überspringen.
Ausgabe: Sidekick wird eingerichtet...
6a) Projekt-Slug ermitteln:
Workspace-Credentials auflösen (nutze workspace_id aus project.json pipeline.workspace_id):
WS_JSON=$(bash .claude/scripts/write-config.sh read-workspace --id <workspace_id>)
board_url=$(echo "$WS_JSON" | node -e "console.log(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).board_url || '')")
api_key=$(echo "$WS_JSON" | node -e "console.log(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).api_key || '')")
Projekt-ID aus project.json auslesen:
project_id=$(node -e "console.log(require('./project.json').pipeline?.project_id || '')")
Projekt-Slug vom Board holen:
if [ -n "$board_url" ] && [ -n "$api_key" ] && [ -n "$project_id" ]; then
SLUG=$(curl -s -H "X-Pipeline-Key: ${api_key}" \
"${board_url}/api/projects" | \
node -e "
try {
const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8'));
const p=(d.data?.projects||[]).find(p=>p.id==='${project_id}');
console.log(p?.slug||'');
} catch(e) { console.log(''); }
")
fi
Falls leer oder API-Aufruf fehlgeschlagen, Slug aus dem Projektnamen in project.json name ableiten (kebab-case):
project_name=$(node -e "console.log(require('./project.json').name || '')")
if [ -z "$SLUG" ] && [ -n "$project_name" ]; then
SLUG=$(echo "$project_name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-\|-$//g')
fi
Falls SLUG immer noch leer → Fehlerausgabe und Snippet mit Platzhalter zeigen:
if [ -z "$SLUG" ]; then
echo "⚠ Sidekick: Projekt-Slug nicht verfügbar"
echo ""
echo "Füge das Snippet manuell in dein HTML ein (ersetze DEIN-SLUG):"
echo " <script src=\"https://board.just-ship.io/sidekick.js\" data-project=\"DEIN-SLUG\"></script>"
echo ""
echo "Aktivierung: Ctrl+Shift+S oder ?sidekick in der URL"
return 0
fi
Dann zum nächsten Schritt (6b) gehen.
6b) Layout-Datei erkennen:
Basierend auf dem erkannten Stack, suche die Haupt-Layout-Datei. Prüfe die Dateien der Reihe nach — die erste existierende wird verwendet:
| Stack | Layout-Dateien (Priorität) |
|---|---|
| Next.js (App Router) | src/app/layout.tsx, app/layout.tsx, src/app/layout.jsx, app/layout.jsx |
| Next.js (Pages Router) | src/pages/_document.tsx, pages/_document.tsx, src/pages/_document.jsx, pages/_document.jsx |
| Nuxt | app.vue |
| SvelteKit | src/app.html |
| HTML / Vite / andere | index.html, public/index.html |
Falls Stack nicht erkannt wurde: alle Kandidaten durchprobieren.
6c) Snippet einfügen:
Falls Layout-Datei gefunden — lies die Datei und prüfe ob das Sidekick-Snippet bereits vorhanden ist (sidekick.js im Inhalt). Falls ja: ✓ Sidekick bereits installiert und Schritt beenden.
Falls noch nicht vorhanden — füge das Snippet framework-gerecht ein (ersetze {slug} mit dem Wert aus $SLUG):
Next.js (App Router / Pages Router) — .tsx / .jsx:
import Script from 'next/script' noch nicht vorhanden → folgende Zeile nach anderen Imports hinzufügen:
import Script from 'next/script'
</body> Tag einfügen:
<Script src="https://board.just-ship.io/sidekick.js" data-project="{slug}" strategy="afterInteractive" />
HTML / Vite / SvelteKit — .html:
</body> einfügen:
<script src="https://board.just-ship.io/sidekick.js" data-project="{slug}"></script>
Nuxt — app.vue:
<script setup> Block hinzufügen (oder <script setup> Block erstellen falls nicht vorhanden):
useHead({ script: [{ src: 'https://board.just-ship.io/sidekick.js', 'data-project': '{slug}' }] })
Erfolgreiche Ausgabe: ✓ Sidekick installiert ({layout-datei})
Falls KEINE Layout-Datei gefunden — Snippet für manuelle Installation anzeigen:
⚠ Sidekick: Layout-Datei nicht erkannt
Füge dieses Snippet in dein HTML ein:
<script src="https://board.just-ship.io/sidekick.js" data-project="{slug}"></script>
Aktivierung: Ctrl+Shift+S oder ?sidekick in der URL
6d) Fehlerbehandlung:
Falls beim Einfügen ein Fehler auftritt (Datei nicht schreibbar, unerwartetes Format):
⚠ Sidekick konnte nicht automatisch installiert werden
Füge dieses Snippet manuell in dein HTML ein:
<script src="https://board.just-ship.io/sidekick.js" data-project="{slug}"></script>
Aktivierung: Ctrl+Shift+S oder ?sidekick in der URL
Kein Abbruch des Setup-Flows — Sidekick-Fehler sind nicht kritisch. Setup gilt als erfolgreich auch wenn Sidekick manuell eingebettet werden muss.