Was macht dieses Tool?
sema-claude ist eine Zsh-Shell-Funktion, die drei Werkzeuge zu einer interaktiven Pipeline verbindet:
Beispiel: Du tippst sema-claude "Wardley Maps". sema findet 20 Dateien. Du wählst 3 davon aus. Dann wählst du extract_wisdom. Claude Code liest alle 3 Dateien und extrahiert Ideen, Zitate, Erkenntnisse — alles auf Deutsch.
Pipeline-Übersicht
flowchart TD
A["Suchbegriff eingeben<br/><i>z.B. 'Wardley Maps'</i>"] --> B["sema CLI<br/>Semantische Suche"]
B --> C["fzf Dateiauswahl<br/>Tab = markieren, Enter = fertig"]
C --> D{"Welcher Modus?"}
D -->|"Kuratiertes Pattern"| E["Fabric Pattern waehlen<br/><i>28 Patterns in 7 Kategorien</i>"]
D -->|"Freie Frage"| F["Eigene Frage eingeben"]
D -->|"Alle durchsuchen"| G["Alle 239 Patterns<br/>mit fzf durchsuchen"]
E --> H["system.md laden<br/><i>~/.config/fabric/patterns/</i>"]
G --> H
F --> I["Prompt zusammenbauen"]
H --> I
I --> J["Claude Code<br/>Dateien lesen + Framework abarbeiten"]
J --> K["Ausgabe auf Deutsch"]
Abb. 1: Pipeline-Übersicht — Vom Suchbegriff zum Ergebnis
Voraussetzungen
Komponentenarchitektur
graph TB
subgraph Terminal["Terminal (Zsh)"]
SC["sema-claude()<br/><i>Shell-Funktion</i>"]
end
subgraph SemaSystem["sema System"]
CLI["sema CLI<br/>bun cli.ts"]
DB["sema.db<br/><i>sqlite-vec</i><br/>38.425 Chunks"]
EMB["HuggingFace<br/>Embeddings"]
CLI --> DB
DB --> EMB
end
subgraph FabricSystem["Fabric AI"]
PDIR["~/.config/fabric/patterns/"]
P1["extract_wisdom/<br/>system.md"]
P2["summarize/<br/>system.md"]
P3["... 237 weitere ..."]
PDIR --> P1
PDIR --> P2
PDIR --> P3
end
subgraph Tools["Interaktive Tools"]
FZF["fzf<br/><i>Fuzzy Finder</i>"]
end
subgraph Claude["Claude Code"]
CC["claude CLI<br/><i>Anthropic API</i>"]
end
SC --> CLI
SC --> FZF
SC --> PDIR
SC --> CC
Abb. 2: Komponentenarchitektur — Alle beteiligten Systeme
5 Komponenten werden benötigt
| # | Komponente | Beschreibung |
|---|---|---|
| 1 | sema (Semantische Suche) | Lokale semantische Suche mit Vektoren. Indexiert dein Obsidian-Vault oder beliebige Markdown-Dateien und findet inhaltlich ähnliche Dokumente — nicht nur nach Stichwort, sondern nach Bedeutung. |
| 2 | Fabric AI (Pattern-Bibliothek) | 239 vorgefertigte „Patterns“ von Daniel Miessler. Jedes Pattern ist ein system.md-File mit einem präzisen Analyse-Framework, z.B. „extrahiere Weisheit“ oder „finde Logikfehler“. |
| 3 | fzf (Fuzzy Finder) | Interaktiver Filter fürs Terminal. Ermöglicht dir, aus langen Listen schnell etwas auszuwählen — mit Vorschau, Mehrfachauswahl und Fuzzy-Suche. |
| 4 | Claude Code (CLI) | Anthropics CLI-Tool. Nimmt einen Prompt entgegen, liest die angegebenen Dateien und liefert eine KI-Analyse. |
| 5 | Zsh + Bun | Zsh als Shell (macOS-Standard), Bun als JavaScript-Runtime für sema CLI. |
Datenfluss
flowchart LR
subgraph Stufe1["Stufe 1: Semantische Suche"]
Q["query<br/>'Wardley Maps'"] --> SEMA["sema CLI"]
SEMA --> RAW["raw_output<br/>1. [0.712] /pfad/datei.md<br/>2. [0.823] /pfad/andere.md"]
RAW --> SED["sed Parser"]
SED --> LINES["file_lines<br/>[0.712] /pfad/datei.md<br/>[0.823] /pfad/andere.md"]
end
subgraph Stufe1b["Stufe 1b: Dateiauswahl"]
LINES --> FZF1["fzf --multi"]
FZF1 --> SELECTED["selected<br/>[0.712] /pfad/datei.md"]
SELECTED --> SED2["sed Score entfernen"]
SED2 --> PATHS["paths<br/>/pfad/datei.md"]
end
subgraph Stufe2["Stufe 2: Pattern + Prompt"]
PATHS --> LIST["file_list<br/>- /pfad/datei.md"]
PATTERN["system.md<br/><i>Fabric Pattern</i>"] --> PROMPT["prompt"]
LIST --> PROMPT
end
PROMPT --> CLAUDE["claude '$prompt'"]
Abb. 3: Datenfluss — Vom Suchbegriff über sed-Parsing zum fertigen Prompt
Was ist fzf?
fzf (fuzzy finder) ist ein interaktiver Kommandozeilen-Filter. Stell dir vor, du hast eine lange Liste — z.B. 20 Suchergebnisse — und willst schnell die richtigen auswählen. fzf zeigt dir die Liste im Terminal, du tippst ein paar Buchstaben und die Liste filtert sich in Echtzeit.
| fzf → du wählst interaktiv aus → fzf gibt die Auswahl zurück.
Wichtige fzf-Parameter
| Parameter | Bedeutung |
|---|---|
--multi |
Mehrere Einträge auswählen erlauben (mit Tab) |
--no-multi |
Nur einen Eintrag auswählen |
--prompt="Text > " |
Text links neben dem Cursor |
--header="Text" |
Überschrift über der Liste |
--preview='befehl' |
Vorschau rechts, {} = aktueller Eintrag |
--preview-window=right:50%:wrap |
Position, Breite, Zeilenumbruch der Vorschau |
--height=80% |
80% der Terminal-Höhe nutzen |
--border=rounded |
Runder Rahmen um die Auswahl |
Shell-Grundlagen (für Anfänger)
Wenn du noch nie ein Shell-Script geschrieben hast, erklärt dieser Abschnitt alle Konzepte, die im sema-claude-Code vorkommen. Jedes Konzept wird mit einem eigenen Beispiel gezeigt.
Variablen
Eine Variable speichert einen Wert. In Zsh schreibst du den Namen ohne Leerzeichen um das =:
# Einfache Variable
name="Holger"
echo "$name" # Gibt: Holger
# Lokale Variable (nur in der Funktion sichtbar)
local query="Wardley Maps"
# Lokales Array
local -a SEMA_CLI=(bun /pfad/cli.ts)
local bedeutet: Diese Variable existiert nur innerhalb der Funktion. Ohne local wäre sie global und könnte andere Funktionen stören.local -a erstellt ein lokales Array (eine Liste von Werten).
Funktions-Definition
Eine Funktion ist ein wiederverwendbarer Codeblock mit einem Namen:
# Funktion definieren
sema-claude() {
echo "Hallo von sema-claude"
# ... hier kommt der Code
}
# Funktion aufrufen
sema-claude
Der Code zwischen { und } wird erst ausgeführt, wenn du den Funktionsnamen tippst. Die Klammern () nach dem Namen sind Pflicht — sie sagen der Shell: „Das ist eine Funktion.“
Parameter und Argumente
Wenn du einer Funktion Werte mitgibst, landen sie in $1, $2, etc.:
greet() {
echo "Hallo, $1!" # $1 = erstes Argument
}
greet "Welt" # Gibt: Hallo, Welt!
greet "Claude" # Gibt: Hallo, Claude!
${1:-} ist ein sicherer Zugriff: Wenn $1 leer ist, wird ein leerer String zurückgegeben statt eines Fehlers. Variante mit Default-Wert:
local query="${1:-}" # Leer, wenn nichts uebergeben
local top="${2:-20}" # Default: 20, wenn $2 fehlt
Bedingte Ausführung
Die doppelten eckigen Klammern [[ ]] prüfen Bedingungen:
# -z = "ist leer?" (zero length)
[[ -z "$query" ]] # wahr, wenn $query leer ist
# -n = "ist NICHT leer?" (non-zero)
[[ -n "$query" ]] # wahr, wenn $query einen Wert hat
# -f = "existiert die Datei?"
[[ -f "$pattern_file" ]] # wahr, wenn Datei existiert
# Vergleiche
[[ "$name" == "Holger" ]] # String-Vergleich
[[ "$count" -gt 10 ]] # Zahl groesser als 10
if / then / fi
Die klassische Verzweigung:
if [[ -z "$query" ]]; then
echo "Kein Suchbegriff!"
return 1
fi
# Mit else:
if [[ -f "$file" ]]; then
echo "Datei gefunden"
else
echo "Datei nicht vorhanden"
fi
return 1 beendet die Funktion mit einem Fehlercode. return 0 = Erfolg, alles andere = Fehler.
Kurzform mit &&
&& bedeutet „UND — führe den rechten Befehl nur aus, wenn der linke erfolgreich war“:
# Kurzform statt if/then/fi:
[[ -z "$selected" ]] && { echo "Abgebrochen."; return 1; }
# Das ist identisch mit:
if [[ -z "$selected" ]]; then
echo "Abgebrochen."
return 1
fi
Die geschweiften Klammern { } gruppieren mehrere Befehle. Wichtig: Das Semikolon vor } ist Pflicht!
Kurzform mit $DEBUG
local DEBUG=false
# $DEBUG wird "ausgefuehrt" - false ist ein gueltiger Befehl (gibt immer Fehler)
# && fuehrt printf nur aus, wenn $DEBUG=true
$DEBUG && printf "[DEBUG] Wert: %s\n" "$var"
# Aequivalent zu:
if $DEBUG; then
printf "[DEBUG] Wert: %s\n" "$var"
fi
true und false sind echte Programme in Unix. true gibt Exit-Code 0 (Erfolg), false gibt Exit-Code 1 (Fehler). Deshalb funktioniert $DEBUG && ... als bedingter Befehl.
Command Substitution $( )
Fängt die Ausgabe eines Befehls in einer Variable auf:
# Die Ausgabe von sema CLI wird in raw_output gespeichert
local raw_output
raw_output=$("${SEMA_CLI[@]}" search "$query" --top "$TOP" 2>&1)
# Einfacheres Beispiel:
local today=$(date +%Y-%m-%d) # z.B. "2026-02-18"
local files=$(ls *.md) # Alle .md-Dateien
Alles zwischen $( und ) wird ausgeführt und das Ergebnis ersetzt den gesamten Ausdruck.
Arrays mit [@]
# Array definieren
local -a SEMA_CLI=(bun /pfad/cli.ts)
# Alle Elemente des Arrays einsetzen
"${SEMA_CLI[@]}"
# wird zu: bun /pfad/cli.ts
# Warum Array statt String?
local SEMA_CLI_STRING="bun /pfad/cli.ts"
$SEMA_CLI_STRING # PROBLEM: Zsh zerlegt am Leerzeichen falsch
# Mit Array:
"${SEMA_CLI[@]}" # KORREKT: Jedes Element wird einzeln uebergeben
Pipe |
Die Pipe leitet die Ausgabe eines Befehls als Eingabe an den nächsten:
# Ausgabe von echo geht an sed:
echo "Hallo Welt" | sed 's/Welt/Claude/'
# Ergebnis: Hallo Claude
# Mehrere Pipes hintereinander:
echo "$raw_output" | sed -n 's/^pattern/\1/p' | wc -l | tr -d ' '
# Ausgabe → parsen → Zeilen zaehlen → Leerzeichen weg
Jeder | nimmt die Standardausgabe (stdout) des linken Befehls und füttert sie als Standardeingabe (stdin) in den rechten.
sed (Stream Editor)
sed verarbeitet Text zeilenweise mit Regulären Ausdrücken. Zentral für sema-claude, weil wir damit die sema-Ausgabe parsen:
# Der wichtigste sed-Befehl im Script:
sed -n 's/^[[:space:]]*[0-9]*\.[[:space:]]*\(\[.*\]\)/\1/p'
Das sieht auf den ersten Blick verwirrend aus. Zerlegen wir es:
| Teil | Bedeutung |
|---|---|
sed -n |
Unterdrückt Standard-Ausgabe. Nur Zeilen mit /p werden gedruckt. |
's/MUSTER/ERSETZUNG/p' |
Suchen und Ersetzen. /p = Ergebnis ausgeben. |
^ |
Zeilenanfang |
[[:space:]]* |
Beliebig viele Leerzeichen/Tabs |
[0-9]* |
Beliebig viele Ziffern (die Nummerierung: 1, 2, 3...) |
\. |
Wortwörtlicher Punkt (nach der Nummer) |
\(\[.*\]\) |
Capture Group: Fängt alles ab [Score] /pfad |
\1 |
Gibt die Capture Group aus (den eingefangenen Teil) |
Vorher → Nachher:
# Eingabe (sema-Ausgabe):
" 1. [0.712] /pfad/wardley-maps.md"
" 2. [0.823] /pfad/strategie-als-hypothese.md"
# Nach sed:
"[0.712] /pfad/wardley-maps.md"
"[0.823] /pfad/strategie-als-hypothese.md"
Ein zweiter, einfacherer sed-Befehl entfernt später den Score:
sed 's/^\[.*\] //'
# Vorher: [0.712] /pfad/wardley-maps.md
# Nachher: /pfad/wardley-maps.md
awk
awk ist ein Textverarbeitungs-Tool, das Zeilen in Spalten zerlegt:
# Erste Spalte extrahieren (Trennzeichen: Leerzeichen)
echo "extract_wisdom │ Weisheit extrahieren" | awk '{print $1}'
# Ergebnis: extract_wisdom
# Im Script: Pattern-Name aus der fzf-Auswahl extrahieren
local pattern
pattern=$(echo "$choice" | awk '{print $1}')
$1 bei awk bedeutet „erste Spalte“, $2 die zweite, etc. Standardmäßig trennt awk an Leerzeichen.
printf
printf gibt formatierten Text aus — zuverlässiger als echo:
# Formatierter Text mit Platzhaltern
printf "Suche nach \"%s\" ...\n" "$query"
# Gibt: Suche nach "Wardley Maps" ...
# Farbige Ausgabe mit ANSI-Codes
printf "\033[1;36m🔍 Semantic Search\033[0m\n"
printf "\033[0;33m⏳ Suche ...\033[0m\n"
printf "\033[0;32m✅ %s Treffer\033[0m\n" "$count"
ANSI-Farbcodes
| Code | Farbe | Verwendung im Script |
|---|---|---|
\033[0m |
Reset (Normal) | Farbe zurücksetzen |
\033[1;36m |
Fett + Cyan | Überschriften, wichtige Info |
\033[0;33m |
Gelb | Warnungen, Status-Infos |
\033[0;32m |
Grün | Erfolg, Bestätigungen |
\033[1;32m |
Fett + Grün | „Enter = Starten“ |
\033[1;31m |
Fett + Rot | „Ctrl+C = Abbrechen“ |
\033[0;90m |
Grau | Nebensächliche Infos |
Das Format: \033[ startet eine Escape-Sequenz, die Zahl bestimmt die Farbe, m beendet sie.
read (Benutzereingabe)
# Eingabe vom Benutzer lesen
printf "Suchbegriff: "
read -r query
# -r = "raw mode" - verhindert, dass Backslash als Escape interpretiert wird
# $query enthaelt jetzt die Eingabe
Im Script wird read -r an drei Stellen verwendet:
- Suchbegriff abfragen (wenn keiner als Argument übergeben)
- Freie Frage eingeben (bei FREIE_FRAGE Pattern)
- Bestätigung vor dem Start (Enter drücken)
Here-String <<<
# Here-String: Text als stdin uebergeben
while IFS= read -r p; do
file_list+="- $p"$'\n'
done <<< "$paths"
# <<< "$paths" fuettert den Inhalt von $paths Zeile fuer Zeile
# in die while-Schleife
<<< (Here-String) übergibt eine Variable als Eingabe, als wäre es eine Datei. Alternative wäre echo "$paths" | while ..., aber Here-Strings sind in Zsh effizienter.
2>&1 (stderr umleiten)
# Normalerweise gibt es zwei Ausgabe-Kanaele:
# 1 = stdout (normale Ausgabe)
# 2 = stderr (Fehlermeldungen)
# 2>&1 bedeutet: stderr (2) nach stdout (1) umleiten
raw_output=$("${SEMA_CLI[@]}" search "$query" --top "$TOP" 2>&1)
# Ohne 2>&1: Fehlermeldungen erscheinen im Terminal statt in der Variable
# Mit 2>&1: ALLES landet in $raw_output
Wichtig für sema-claude, weil manche CLI-Tools Warnungen auf stderr schreiben, die sonst verloren gehen.
$? (Exit-Code)
# Jeder Befehl gibt einen Exit-Code zurueck:
# 0 = Erfolg
# 1-255 = Fehler
ls /existiert/nicht
echo $? # Gibt: 1 (Fehler)
ls /tmp
echo $? # Gibt: 0 (Erfolg)
# Im Script:
local exit_code=$?
# Speichert den Exit-Code des vorherigen Befehls
String-Länge und Substring
# Laenge eines Strings:
echo ${#prompt} # z.B. 2847 (Zeichen)
# Substring (erste 300 Zeichen):
echo "${prompt:0:300}" # Ab Position 0, 300 Zeichen
# Letzte 150 Zeichen:
echo "${prompt: -150}" # Leerzeichen vor dem Minus ist Pflicht!
# Im Script: Langen Prompt abkuerzen
if [[ ${#prompt} -gt 500 ]]; then
echo "${prompt:0:300}"
printf "\n... (%s Zeichen) ...\n" "${#prompt}"
echo "${prompt: -150}"
fi
wc -l | tr -d ' '
# wc -l = Zeilen zaehlen (word count, lines)
echo "$file_lines" | wc -l
# Gibt z.B.: " 20" (mit fuehrenden Leerzeichen!)
# tr -d ' ' = Leerzeichen loeschen (translate, delete)
echo "$file_lines" | wc -l | tr -d ' '
# Gibt: "20" (sauber)
# Im Script:
local count=$(echo "$file_lines" | wc -l | tr -d ' ')
cat (Datei lesen)
# cat gibt den Inhalt einer Datei aus:
cat system.md
# Im Script: Pattern-Datei einlesen
local system_prompt
system_prompt=$(cat "$pattern_file")
# $system_prompt enthaelt jetzt den gesamten Dateiinhalt
cat steht für „concatenate“ (verknüpfen) und kann auch mehrere Dateien zusammenfügen: cat file1.txt file2.txt.
Installation
Schritt 1: Datei anlegen
mkdir -p ~/.zsh/functions
# Kopiere den Code (siehe Anhang) in diese Datei:
# ~/.zsh/functions/sema-claude.zsh
Schritt 2: In .zshrc laden
Füge diese Zeile am Ende deiner ~/.zshrc hinzu:
source ~/.zsh/functions/sema-claude.zsh
Schritt 3: Pfade anpassen
In der Datei sema-claude.zsh, passe diese zwei Variablen an:
# Pfad zu deiner sema CLI (Zeile 11):
local -a SEMA_CLI=(bun /dein/pfad/zu/sema/cli.ts)
# Pfad zu deinen Fabric Patterns (Zeile 14):
local PATTERNS_DIR="$HOME/.config/fabric/patterns"
Schritt 4: Shell neu laden und testen
source ~/.zshrc
# Test:
sema-claude "suchbegriff"
# Mit Debug-Ausgabe:
sema-claude --debug "suchbegriff"
Der Code Block für Block erklärt
Die Funktion besteht aus 13 logischen Blöcken. Jeder Block wird hier einzeln erklärt.
Block 1: Konfiguration (Zeile 10–15)
sema-claude() {
local -a SEMA_CLI=(bun /Users/holgergelhausen/PAI/PAI_DIRECTORY/tools/sema/cli.ts)
local TOP=20
local DEBUG=false
local PATTERNS_DIR="$HOME/.config/fabric/patterns"
[[ "$1" == "--debug" ]] && { DEBUG=true; shift; }
Was passiert hier:
SEMA_CLI— Array mit dem sema-Aufruf. Array statt String, damit Zsh die Argumente korrekt trennt.TOP=20— Maximale Anzahl Suchergebnisse von sema.DEBUG=false— Debug-Modus standardmäßig aus.PATTERNS_DIR— Wo Fabric seinesystem.md-Dateien speichert.[[ "$1" == "--debug" ]]— Prüft, ob--debugals erstes Argument übergeben wurde.shiftentfernt es, damit$1danach der Suchbegriff ist.
Block 2: Suchbegriff abfragen (Zeile 17–24)
local query="${1:-}"
if [[ -z "$query" ]]; then
printf "\033[1;36m🔍 Semantic Search → Claude Code (Fabric AI)\033[0m\n"
printf "Suchbegriff: "
read -r query
[[ -z "$query" ]] && { echo "Abgebrochen."; return 1; }
fi
Was passiert hier:
${1:-}— Nimmt den ersten Parameter oder leeren String.- Wenn kein Suchbegriff übergeben: Interaktive Eingabe mit
read. - Wenn auch dann nichts eingegeben wird: Abbruch mit
return 1.
Block 3: sema-Suche ausführen (Zeile 26–40)
printf "\033[0;33m⏳ Suche nach \"%s\" ...\033[0m\n" "$query"
local raw_output
raw_output=$("${SEMA_CLI[@]}" search "$query" --top "$TOP" 2>&1)
local exit_code=$?
$DEBUG && printf "[DEBUG] exit_code=%s, output_length=%s\n" "$exit_code" "${#raw_output}"
$DEBUG && echo "[DEBUG] First 3 lines:" && echo "$raw_output" | head -3
if [[ -z "$raw_output" ]] || [[ ${#raw_output} -lt 10 ]]; then
echo "❌ Keine Ausgabe von sema CLI (exit: $exit_code)"
return 1
fi
Was passiert hier:
"${SEMA_CLI[@]}"— Führtbun /pfad/cli.ts search "query" --top 20aus.2>&1— Fehlermeldungen ebenfalls auffangen.$?— Exit-Code speichern für Debug-Ausgabe.- Prüfung: Wenn die Ausgabe leer oder kürzer als 10 Zeichen ist, bricht die Funktion ab.
Block 4: Ergebnisse parsen mit sed (Zeile 42–57)
local file_lines
file_lines=$(echo "$raw_output" | sed -n 's/^[[:space:]]*[0-9]*\.[[:space:]]*\(\[.*\]\)/\1/p')
if [[ -z "$file_lines" ]]; then
echo "❌ Konnte Ergebnisse nicht parsen."
return 1
fi
local count=$(echo "$file_lines" | wc -l | tr -d ' ')
printf "\033[0;32m✅ %s Treffer\033[0m\n\n" "$count"
Was passiert hier:
- Die sema-Ausgabe hat das Format
1. [0.712] /pfad/datei.md. sedentfernt die Nummerierung und behält[Score] Pfad.wc -l | tr -d ' 'zählt die Treffer (sauber ohne Leerzeichen).- Wenn sed nichts findet (unerwartetes Format): Abbruch.
Block 5: fzf Dateiauswahl (Zeile 62–71)
local selected
selected=$(echo "$file_lines" | fzf \
--multi \
--prompt="Tab=markieren, Enter=fertig > " \
--header="🔍 \"$query\" — $count Treffer" \
--preview='f=$(echo {} | sed "s/^\[.*\] //"); head -40 "$f" 2>/dev/null || echo "Keine Preview"' \
--preview-window=right:50%:wrap \
--height=80% \
--border=rounded)
Was passiert hier:
--multi— Mehrere Dateien mit Tab markieren.--preview— Zeigt die ersten 40 Zeilen der aktuellen Datei rechts an. Dersedentfernt den Score aus dem Pfad.2>/dev/null— Fehlermeldungen bei nicht lesbaren Dateien unterdrücken.- Wenn der Benutzer Escape drückt, ist
$selectedleer → Abbruch.
Block 6: Score entfernen (Zeile 75–79)
local paths
paths=$(echo "$selected" | sed 's/^\[.*\] //')
local selected_count=$(echo "$paths" | wc -l | tr -d ' ')
printf "\n\033[0;32m📄 %s Datei(en) gewählt\033[0m\n\n" "$selected_count"
Was passiert hier:
sed 's/^\[.*\] //'— Entfernt den Score[0.712]vom Anfang jeder Zeile.- Übrig bleiben nur die reinen Dateipfade.
- Zählt und zeigt an, wie viele Dateien gewählt wurden.
Block 7: Pattern-Liste definieren (Zeile 82–120)
local -a CURATED=(
"── ANALYSIEREN & ERKLÄREN ───│"
"extract_wisdom │ Weisheit, Ideen, Zitate, Gewohnheiten extrahieren"
"extract_insights │ Die 10 überraschendsten Kernerkenntnisse"
...
"── CUSTOM ──────────────────-│"
"FREIE_FRAGE │ Eigene Frage an Claude Code"
"ALLE_PATTERNS │ Alle 239 Fabric Patterns durchsuchen"
)
Was passiert hier:
- Ein Array
CURATEDenthält die 28 handverlesenen Patterns plus Kategorie-Header. - Jeder Eintrag hat das Format:
pattern_name │ Beschreibung. - Die Kategorie-Header beginnen mit
──und dienen als visuelle Trenner in fzf. - Zwei Sondereinträge:
FREIE_FRAGEundALLE_PATTERNS.
Block 8: fzf Pattern-Auswahl mit Preview (Zeile 122–137)
local choice
choice=$(printf '%s\n' "${CURATED[@]}" | fzf \
--prompt="Pattern wählen > " \
--header="🧠 Fabric AI Pattern für Claude Code" \
--preview='p=$(echo {} | awk "{print \$1}");
f="'"$PATTERNS_DIR"'/$p/system.md";
if [[ -f "$f" ]]; then
printf "\033[1;33m── %s ──\033[0m\n\n" "$p"
head -30 "$f"
else
echo "📂 Kategorie-Header"
fi' \
--preview-window=right:50%:wrap \
--height=80% \
--border=rounded \
--no-multi)
Was passiert hier:
printf '%s\n' "${CURATED[@]}"— Jedes Array-Element als eigene Zeile.--preview— Zeigt diesystem.mddes gerade fokussierten Patterns rechts an.awk '{print $1}'— Extrahiert den Pattern-Namen (erste Spalte).--no-multi— Nur ein Pattern auswählen (keine Mehrfachauswahl).
Block 9: Pattern-Name extrahieren (Zeile 141–151)
local pattern
pattern=$(echo "$choice" | awk '{print $1}')
if [[ "$pattern" == "──" ]]; then
echo "⚠️ Kategorie-Header, kein Pattern."
return 1
fi
Was passiert hier:
awk '{print $1}'— Nimmt nur das erste Wort der Auswahl, z.B.extract_wisdom.- Sicherheitsprüfung: Wenn jemand einen Kategorie-Header auswählt (beginnt mit
──), wird abgebrochen.
Block 10: Sonderfälle (Zeile 153–178)
local action=""
if [[ "$pattern" == "FREIE_FRAGE" ]]; then
printf "\033[1;36mDeine Frage:\033[0m "
read -r action
[[ -z "$action" ]] && { echo "Abgebrochen."; return 1; }
fi
if [[ "$pattern" == "ALLE_PATTERNS" ]]; then
pattern=$(ls "$PATTERNS_DIR" | fzf \
--prompt="Pattern suchen > " \
--header="🔍 Alle Fabric Patterns durchsuchen" \
--preview='f="'"$PATTERNS_DIR"'/{}/system.md";
if [[ -f "$f" ]]; then
printf "\033[1;33m── {} ──\033[0m\n\n"
head -30 "$f"
else
echo "Keine system.md gefunden"
fi' \
--preview-window=right:50%:wrap \
--height=80% \
--border=rounded \
--no-multi)
[[ -z "$pattern" ]] && { echo "Abgebrochen."; return 1; }
fi
Was passiert hier:
- FREIE_FRAGE: Statt eines Fabric Patterns gibt der Benutzer eine eigene Frage ein. Diese wird später direkt als Prompt verwendet.
- ALLE_PATTERNS: Öffnet ein zweites fzf-Menü, das alle 239 Fabric-Verzeichnisse auflistet. So kann man auch Patterns außerhalb der kuratierten 28 nutzen.
Block 11: Datei-Liste aufbauen (Zeile 180–184)
local file_list=""
while IFS= read -r p; do
file_list+="- $p"$'\n'
done <<< "$paths"
Was passiert hier:
- Jeder Dateipfad wird mit einem
-präfixiert (Markdown-Listenformat). IFS=— Verhindert, dass führende/nachfolgende Leerzeichen abgeschnitten werden.<<< "$paths"— Here-String füttert die Pfade in die Schleife.$'\n'— Literal Newline (Zsh-Syntax).- Ergebnis z.B.:
- /pfad/wardley.md\n- /pfad/strategie.md\n
Block 12: Prompt zusammenbauen (Zeile 186–221)
local prompt=""
if [[ -n "$action" ]]; then
# Freie Frage: Einfacher Prompt
prompt="$action
Basierend auf diesen Dateien (semantische Suche: \"$query\"):
$file_list
Lies bitte zuerst alle Dateien und arbeite dann die Aufgabe ab. Antworte auf Deutsch."
else
# Fabric Pattern: system.md laden
local pattern_file="$PATTERNS_DIR/$pattern/system.md"
if [[ ! -f "$pattern_file" ]]; then
echo "❌ Pattern-Datei nicht gefunden: $pattern_file"
return 1
fi
local system_prompt
system_prompt=$(cat "$pattern_file")
prompt="SPRACHE: Deine gesamte Ausgabe MUSS auf Deutsch sein. ...
Du folgst diesem Analyse-Framework:
---
$system_prompt
---
Wende dieses Framework auf folgende Dateien an (semantische Suche: \"$query\"):
$file_list
Lies bitte zuerst alle Dateien vollstaendig und arbeite dann das Framework systematisch auf Deutsch ab."
fi
Was passiert hier:
- Zwei Pfade: Freie Frage (einfacher Text-Prompt) vs. Fabric Pattern (system.md wird eingebettet).
- Bei Fabric: Die gesamte
system.mdwird in den Prompt eingebaut, eingeklammert von---. - Die Sprach-Anweisung steht ganz oben — damit Claude auf Deutsch antwortet.
- Die Dateiliste sagt Claude, welche Dateien gelesen werden sollen.
Block 13: Bestätigung und Start (Zeile 223–251)
printf "\n\033[1;36m━━━ Claude Code Prompt ━━━\033[0m\n"
if [[ -n "$action" ]]; then
printf "\033[0;33mModus:\033[0m Freie Frage\n"
printf "\033[0;33mFrage:\033[0m %s\n" "$action"
else
printf "\033[0;33mPattern:\033[0m %s\n" "$pattern"
printf "\033[0;33mQuelle:\033[0m %s\n" "$PATTERNS_DIR/$pattern/system.md"
fi
printf "\033[0;33mDateien:\033[0m %s\n" "$selected_count"
printf "\033[0;33mSuche:\033[0m \"%s\"\n" "$query"
printf "\033[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n\n"
if [[ ${#prompt} -gt 500 ]]; then
echo "${prompt:0:300}"
printf "\n\033[0;90m... (%s Zeichen, inkl. system.md) ...\033[0m\n\n" "${#prompt}"
echo "${prompt: -150}"
else
echo "$prompt"
fi
printf "\n\033[1;32mEnter\033[0m = Starten, \033[1;31mCtrl+C\033[0m = Abbrechen "
read -r
printf "\n\033[0;33m🚀 Starte Claude Code mit Fabric Pattern \"%s\"...\033[0m\n" "${pattern:-custom}"
claude "$prompt"
}
Was passiert hier:
- Eine Übersicht wird angezeigt: Pattern-Name, Dateien, Suchbegriff.
- Bei langen Prompts (>500 Zeichen): Nur Anfang und Ende zeigen, Mitte abkürzen.
read -r— Wartet auf Enter. Ctrl+C bricht ab.claude "$prompt"— Der finale Aufruf! Claude Code erhält den kompletten Prompt mit Framework + Dateiliste.
Alle 28 kuratierten Patterns
Die 28 handverlesenen Fabric Patterns sind in 7 Kategorien organisiert. Jedes Pattern hat eine system.md-Datei mit einem präzisen Analyse-Framework.
Analysieren & Erklären (11 Patterns)
| Pattern | Beschreibung |
|---|---|
extract_wisdom |
Weisheit, Ideen, Zitate, Gewohnheiten extrahieren |
extract_insights |
Die 10 überraschendsten Kernerkenntnisse |
analyze_prose |
Schreibqualität bewerten + Verbesserungsvorschläge |
analyze_claims |
Behauptungen auf Wahrheit prüfen (A–F Rating) |
analyze_paper |
Wissenschaftliche Rigor-Analyse |
analyze_presentation |
Präsentation reviewen und kritisch bewerten |
analyze_tech_impact |
Technologie-Impact auf Gesellschaft analysieren |
find_logical_fallacies |
Logikfehler und Fehlschlüsse finden |
explain_docs |
Dokumentation verständlich erklären |
explain_terms |
Glossar aller wichtigen Begriffe erstellen |
rate_content |
Inhalt bewerten + Qualitäts-Rating (1–10) |
Zusammenfassen (4 Patterns)
| Pattern | Beschreibung |
|---|---|
summarize |
1-Satz-Summary + 10 Hauptpunkte + 5 Takeaways |
create_summary |
Strukturierte Markdown-Zusammenfassung |
create_5_sentence_summary |
5-Satz-Zusammenfassung |
extract_core_message |
Kernbotschaft auf den Punkt |
Vergleichen (1 Pattern)
| Pattern | Beschreibung |
|---|---|
compare_and_contrast |
Markdown-Tabelle: Gemeinsamkeiten & Unterschiede |
Content Erstellen (6 Patterns)
| Pattern | Beschreibung |
|---|---|
write_essay |
Essay im Paul-Graham-Stil |
create_keynote |
Keynote/Präsentation erstellen |
write_micro_essay |
Micro-Essay (< 300 Wörter) |
improve_writing |
Text überarbeiten: Klarheit, Kohärenz, Stil |
enrich_blog_post |
Blog-Post anreichern und verbessern |
create_newsletter_entry |
Newsletter-Abschnitt erstellen |
Extrahieren (4 Patterns)
| Pattern | Beschreibung |
|---|---|
extract_ideas |
20–50 überraschende Ideen |
extract_recommendations |
Handlungsempfehlungen |
extract_questions |
Offene Fragen identifizieren |
create_tags |
Tags und Kategorien generieren |
Lernen (2 Patterns)
| Pattern | Beschreibung |
|---|---|
create_flash_cards |
Lernkarten für Schlüsselkonzepte |
create_quiz |
Quiz-Fragen zum Inhalt generieren |
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
sema-claude: command not found |
source in .zshrc fehlt |
source ~/.zsh/functions/sema-claude.zsh hinzufügen und Shell neu laden |
| „Keine Ausgabe von sema CLI“ | sema nicht installiert oder falscher Pfad | SEMA_CLI-Pfad in der Funktion prüfen. Mit --debug testen. |
| „Konnte Ergebnisse nicht parsen“ | sema-Ausgabeformat hat sich geändert | sema manuell aufrufen (bun cli.ts search "test") und Format prüfen |
| fzf zeigt keine Preview | Dateipfade stimmen nicht / Dateien nicht lesbar | Pfade in der fzf-Ausgabe manuell prüfen |
| „Pattern-Datei nicht gefunden“ | Fabric nicht installiert oder PATTERNS_DIR falsch |
ls ~/.config/fabric/patterns/ prüfen. fabric --update ausführen. |
| Claude gibt englische Ausgabe | Selten bei kurzen Texten | Prompt beginnt mit SPRACHE: Deine gesamte Ausgabe MUSS auf Deutsch sein. — normalerweise reicht das. |
claude: command not found |
Claude Code CLI nicht installiert | npm install -g @anthropic-ai/claude-code |
| Word-Splitting-Fehler | SEMA_CLI als String statt Array |
Muss local -a SEMA_CLI=(...) sein (Array!) |
sema-claude --debug "test" starten. Die Debug-Ausgabe zeigt Exit-Code, Output-Länge und die ersten 3 Zeilen der sema-Antwort.
Claude Code Skill: obsidian-vault-query
Neben der Shell-Funktion sema-claude gibt es einen zweiten Weg, sema zu nutzen: als Claude Code Skill. Dieser Skill wird innerhalb von Claude Code aktiviert, nicht im Terminal.
Was ist ein Claude Code Skill?
Ein Skill ist eine SKILL.md-Datei im Verzeichnis .claude/skills/. Sie enthält Anweisungen, die Claude Code automatisch befolgt, wenn bestimmte Trigger-Wörter erkannt werden. Der Skill obsidian-vault-query bringt Claude Code bei, sema direkt aufzurufen.
Skill-Dateien und Pfade
| Datei | Pfad |
|---|---|
| Skill-Definition | .claude/skills/obsidian-vault-query/SKILL.md |
| sema CLI | tools/sema/cli.ts |
| sema Datenbank | ~/.local/share/sema/sema.db |
Trigger-Wörter
Der Skill wird automatisch aktiviert, wenn du in Claude Code sagst:
- „Was haben wir zu...“
- „Finde alles über...“
- „sema search...“
- „Durchsuche den Vault nach...“
- „Welche Notizen gibt es zu...“
Verfügbare Befehle
| Befehl | Beschreibung |
|---|---|
sema search "query" |
Semantische Suche (Standard) |
sema search "query" --hybrid |
Hybride Suche (Vektor + FTS5). Achtung: Bindestriche in Suchbegriffen können FTS5-Fehler verursachen. |
sema related /pfad/datei.md |
Findet ähnliche Dateien zu einer gegebenen Datei |
sema status |
Zeigt Index-Status (Dateien, Chunks, DB-Größe) |
Score-Logik
< 0.85 = sehr relevant, 0.85–1.0 = relevant, > 1.0 = möglicherweise nicht relevant.
Workflow
- Du fragst Claude Code: „Was haben wir zu Wardley Maps?“
- Claude erkennt den Trigger und ruft
sema search "Wardley Maps"auf - sema liefert Ergebnisse mit Scores und Pfaden
- Claude liest die relevantesten Dateien und fasst zusammen
Shell-Funktion vs. Claude Code Skill
flowchart TD
USER["Du"] --> CHOICE{"Wie willst du<br/>sema nutzen?"}
CHOICE -->|"Interaktiv im Terminal<br/>mit Dateiauswahl + Fabric"| SHELL["sema-claude<br/><i>Shell-Funktion</i>"]
CHOICE -->|"Direkt in Claude Code<br/>per Sprache"| SKILL["obsidian-vault-query<br/><i>Claude Code Skill</i>"]
SHELL --> FZF["fzf: Dateien waehlen"]
FZF --> FABRIC["Fabric Pattern waehlen"]
FABRIC --> CLAUDE1["Claude Code<br/>mit vollem Prompt"]
SKILL --> TRIGGER["Trigger: 'was haben wir zu...'"]
TRIGGER --> SEMA2["Claude ruft sema CLI auf"]
SEMA2 --> CLAUDE2["Claude zeigt Ergebnisse<br/>+ analysiert direkt"]
Abb. 4: Shell-Funktion vs. Claude Code Skill — Zwei Wege, ein Werkzeug
| Aspekt | Shell-Funktion (sema-claude) |
Claude Code Skill |
|---|---|---|
| Aufruf | Im Terminal: sema-claude "query" |
In Claude Code: „Was haben wir zu...“ |
| Dateiauswahl | Interaktiv mit fzf (Tab + Enter) | Claude wählt automatisch |
| Fabric Patterns | 28 kuratierte + alle 239 durchsuchbar | Nicht verfügbar (direkte Analyse) |
| Kontrolle | Volle Kontrolle über Dateien + Pattern | Claude entscheidet automatisch |
| Geschwindigkeit | 3–4 Interaktionsschritte | Ein Satz genügt |
| Ideal für | Gezielte Analyse mit bestimmtem Framework | Schnelle Recherche, Überblick |
Anhang: Vollständiger Quellcode
Der komplette Quellcode der sema-claude-Funktion (Version 2.0.0, 251 Zeilen).
sema-claude.zsh — Vollständiger Quellcode (251 Zeilen)
# sema-claude - Semantic Search → Claude Code Pipeline (Fabric-Powered)
# Version: 2.0.0
# Changelog:
# v2.0.0 (18.02.2026) - Fabric AI Patterns statt simple Aktionen, 28 kuratierte Patterns in 7 Kategorien, system.md als Claude-Prompt
# v1.3.0 (18.02.2026) - Fix: SEMA_CLI als Array statt String (zsh word-splitting)
# v1.2.0 (18.02.2026) - Debug-Modus, stderr→stdout, robustere Erkennung
# v1.1.0 (18.02.2026) - Fix: robusteres Parsing, bessere Preview
# v1.0.0 (18.02.2026) - Initial: 2-stufiger Workflow sema → fzf → claude
sema-claude() {
local -a SEMA_CLI=(bun /Users/holgergelhausen/PAI/PAI_DIRECTORY/tools/sema/cli.ts)
local TOP=20
local DEBUG=false
local PATTERNS_DIR="$HOME/.config/fabric/patterns"
[[ "$1" == "--debug" ]] && { DEBUG=true; shift; }
# Stufe 1: Suchbegriff
local query="${1:-}"
if [[ -z "$query" ]]; then
printf "\033[1;36m🔍 Semantic Search → Claude Code (Fabric AI)\033[0m\n"
printf "Suchbegriff: "
read -r query
[[ -z "$query" ]] && { echo "Abgebrochen."; return 1; }
fi
# Suche ausfuehren
printf "\033[0;33m⏳ Suche nach \"%s\" ...\033[0m\n" "$query"
local raw_output
raw_output=$("${SEMA_CLI[@]}" search "$query" --top "$TOP" 2>&1)
local exit_code=$?
$DEBUG && printf "[DEBUG] exit_code=%s, output_length=%s\n" "$exit_code" "${#raw_output}"
$DEBUG && echo "[DEBUG] First 3 lines:" && echo "$raw_output" | head -3
if [[ -z "$raw_output" ]] || [[ ${#raw_output} -lt 10 ]]; then
echo "❌ Keine Ausgabe von sema CLI (exit: $exit_code)"
return 1
fi
local file_lines
file_lines=$(echo "$raw_output" | sed -n 's/^[[:space:]]*[0-9]*\.[[:space:]]*\(\[.*\]\)/\1/p')
if [[ -z "$file_lines" ]]; then
echo "❌ Konnte Ergebnisse nicht parsen."
return 1
fi
local count=$(echo "$file_lines" | wc -l | tr -d ' ')
printf "\033[0;32m✅ %s Treffer\033[0m\n\n" "$count"
# Stufe 1b: Dateien waehlen mit fzf
local selected
selected=$(echo "$file_lines" | fzf \
--multi \
--prompt="Tab=markieren, Enter=fertig > " \
--header="🔍 \"$query\" — $count Treffer" \
--preview='f=$(echo {} | sed "s/^\[.*\] //"); head -40 "$f" 2>/dev/null || echo "Keine Preview"' \
--preview-window=right:50%:wrap \
--height=80% \
--border=rounded)
[[ -z "$selected" ]] && { echo "Abgebrochen."; return 1; }
local paths
paths=$(echo "$selected" | sed 's/^\[.*\] //')
local selected_count=$(echo "$paths" | wc -l | tr -d ' ')
printf "\n\033[0;32m📄 %s Datei(en) gewählt\033[0m\n\n" "$selected_count"
# Stufe 2: Fabric Pattern waehlen
local -a CURATED=(
"── ANALYSIEREN & ERKLÄREN ───│"
"extract_wisdom │ Weisheit, Ideen, Zitate, Gewohnheiten extrahieren"
"extract_insights │ Die 10 überraschendsten Kernerkenntnisse"
"analyze_prose │ Schreibqualität bewerten + Verbesserungsvorschläge"
"analyze_claims │ Behauptungen auf Wahrheit prüfen (A-F Rating)"
"analyze_paper │ Wissenschaftliche Rigor-Analyse"
"analyze_presentation │ Präsentation reviewen und kritisch bewerten"
"analyze_tech_impact │ Technologie-Impact auf Gesellschaft analysieren"
"find_logical_fallacies │ Logikfehler und Fehlschlüsse finden"
"explain_docs │ Dokumentation verständlich erklären"
"explain_terms │ Glossar aller wichtigen Begriffe erstellen"
"rate_content │ Inhalt bewerten + Qualitäts-Rating (1-10)"
"── ZUSAMMENFASSEN ───────────│"
"summarize │ 1-Satz-Summary + 10 Hauptpunkte + 5 Takeaways"
"create_summary │ Strukturierte Markdown-Zusammenfassung"
"create_5_sentence_summary │ 5-Satz-Zusammenfassung"
"extract_core_message │ Kernbotschaft auf den Punkt"
"── VERGLEICHEN ──────────────│"
"compare_and_contrast │ Markdown-Tabelle: Gemeinsamkeiten & Unterschiede"
"── CONTENT ERSTELLEN ────────│"
"write_essay │ Essay im Paul-Graham-Stil"
"create_keynote │ Keynote/Präsentation erstellen"
"write_micro_essay │ Micro-Essay (< 300 Wörter)"
"improve_writing │ Text überarbeiten: Klarheit, Kohärenz, Stil"
"enrich_blog_post │ Blog-Post anreichern und verbessern"
"create_newsletter_entry │ Newsletter-Abschnitt erstellen"
"── EXTRAHIEREN ──────────────│"
"extract_ideas │ 20-50 überraschende Ideen"
"extract_recommendations │ Handlungsempfehlungen"
"extract_questions │ Offene Fragen identifizieren"
"create_tags │ Tags und Kategorien generieren"
"── LERNEN ───────────────────│"
"create_flash_cards │ Lernkarten für Schlüsselkonzepte"
"create_quiz │ Quiz-Fragen zum Inhalt generieren"
"── CUSTOM ──────────────────-│"
"FREIE_FRAGE │ Eigene Frage an Claude Code"
"ALLE_PATTERNS │ Alle 239 Fabric Patterns durchsuchen"
)
local choice
choice=$(printf '%s\n' "${CURATED[@]}" | fzf \
--prompt="Pattern wählen > " \
--header="🧠 Fabric AI Pattern für Claude Code" \
--preview='p=$(echo {} | awk "{print \$1}");
f="'"$PATTERNS_DIR"'/$p/system.md";
if [[ -f "$f" ]]; then
printf "\033[1;33m── %s ──\033[0m\n\n" "$p"
head -30 "$f"
else
echo "📂 Kategorie-Header"
fi' \
--preview-window=right:50%:wrap \
--height=80% \
--border=rounded \
--no-multi)
[[ -z "$choice" ]] && { echo "Abgebrochen."; return 1; }
local pattern
pattern=$(echo "$choice" | awk '{print $1}')
if [[ "$pattern" == "──" ]]; then
echo "⚠️ Kategorie-Header, kein Pattern."
return 1
fi
local action=""
if [[ "$pattern" == "FREIE_FRAGE" ]]; then
printf "\033[1;36mDeine Frage:\033[0m "
read -r action
[[ -z "$action" ]] && { echo "Abgebrochen."; return 1; }
fi
if [[ "$pattern" == "ALLE_PATTERNS" ]]; then
pattern=$(ls "$PATTERNS_DIR" | fzf \
--prompt="Pattern suchen > " \
--header="🔍 Alle Fabric Patterns durchsuchen" \
--preview='f="'"$PATTERNS_DIR"'/{}/system.md";
if [[ -f "$f" ]]; then
printf "\033[1;33m── {} ──\033[0m\n\n"
head -30 "$f"
else
echo "Keine system.md gefunden"
fi' \
--preview-window=right:50%:wrap \
--height=80% \
--border=rounded \
--no-multi)
[[ -z "$pattern" ]] && { echo "Abgebrochen."; return 1; }
fi
local file_list=""
while IFS= read -r p; do
file_list+="- $p"$'\n'
done <<< "$paths"
local prompt=""
if [[ -n "$action" ]]; then
prompt="$action
Basierend auf diesen Dateien (semantische Suche: \"$query\"):
$file_list
Lies bitte zuerst alle Dateien und arbeite dann die Aufgabe ab. Antworte auf Deutsch."
else
local pattern_file="$PATTERNS_DIR/$pattern/system.md"
if [[ ! -f "$pattern_file" ]]; then
echo "❌ Pattern-Datei nicht gefunden: $pattern_file"
return 1
fi
local system_prompt
system_prompt=$(cat "$pattern_file")
prompt="SPRACHE: Deine gesamte Ausgabe MUSS auf Deutsch sein. Uebersetze ALLE englischen Section-Header ins Deutsche.
Du folgst diesem Analyse-Framework:
---
$system_prompt
---
Wende dieses Framework auf folgende Dateien an (semantische Suche: \"$query\"):
$file_list
Lies bitte zuerst alle Dateien vollstaendig und arbeite dann das Framework systematisch auf Deutsch ab."
fi
printf "\n\033[1;36m━━━ Claude Code Prompt ━━━\033[0m\n"
if [[ -n "$action" ]]; then
printf "\033[0;33mModus:\033[0m Freie Frage\n"
printf "\033[0;33mFrage:\033[0m %s\n" "$action"
else
printf "\033[0;33mPattern:\033[0m %s\n" "$pattern"
printf "\033[0;33mQuelle:\033[0m %s\n" "$PATTERNS_DIR/$pattern/system.md"
fi
printf "\033[0;33mDateien:\033[0m %s\n" "$selected_count"
printf "\033[0;33mSuche:\033[0m \"%s\"\n" "$query"
printf "\033[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n\n"
if [[ ${#prompt} -gt 500 ]]; then
echo "${prompt:0:300}"
printf "\n\033[0;90m... (%s Zeichen, inkl. system.md) ...\033[0m\n\n" "${#prompt}"
echo "${prompt: -150}"
else
echo "$prompt"
fi
printf "\n\033[1;32mEnter\033[0m = Starten, \033[1;31mCtrl+C\033[0m = Abbrechen "
read -r
printf "\n\033[0;33m🚀 Starte Claude Code mit Fabric Pattern \"%s\"...\033[0m\n" "${pattern:-custom}"
claude "$prompt"
}