diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 10f96df..0f8fa8b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -60,6 +60,16 @@ --- +## 🖥 Entwicklungsumgebung + + +- **Default-Shell:** fish + +Der Agent nutzt diese Shell für **alle** Terminal-Kommandos und passt die Syntax an +(fish ≠ POSIX sh/bash – z.B. `set -x VAR wert` statt `export VAR=wert`, `(cmd)` statt +`$(cmd)`, kein `&&`/`||`-Verketten in fish-Skripten). Bei anderem System / neuem Repo +diesen Wert anpassen. + ## Project diff --git a/docs/MAINTAINER.md b/docs/MAINTAINER.md index c05fa24..4edeef9 100644 --- a/docs/MAINTAINER.md +++ b/docs/MAINTAINER.md @@ -149,6 +149,7 @@ Lokal vor dem Commit: `sh scripts/selftest.sh` ausführen. | Kein globales `init.templateDir`, Opt-in `git init-copilot` | Standard-`git init`/`git clone` bleiben unberührt; Quality Gate landet nur in bewusst gewählten Repos | | Remote-Quelle konfigurierbar (env/Config) + Offline-Modus | Keine hartkodierte Infrastruktur; `git copilot-update` läuft auch ohne Netz und endet graceful (exit 0) | | `detect_vscode_user_dir` in jedem Skript dupliziert | Skripte laufen standalone aus `~/.local/bin` → keine gemeinsame Lib sourcebar; bewusst kopiert statt geteilt | +| Default-Shell-Feld in der Projekt-Sektion (nach `---`) | Bootstrap erkennt `$SHELL` und schreibt es nach `.github/copilot-instructions.md`; liegt bewusst **nach** dem `---`, damit `copilot-update` die Framework-Sektion neu schreiben kann, ohne den repo-/maschinenspezifischen Shell-Wert zu überschreiben | ## Bekannte Fallstricke @@ -159,3 +160,4 @@ Lokal vor dem Commit: `sh scripts/selftest.sh` ausführen. | Leerzeile vor `---` in `copilot-instructions.md` wird entfernt | `$(awk ...)` strippt trailing newlines; `printf '%s\n%s\n'` setzt Blöcke direkt zusammen | `printf '%s\n\n%s\n'` verwenden | | `$USER` unbound unter `set -u` | `deploy.sh` nutzt `set -euo pipefail`; `$USER` fehlt in minimalen Envs (`env -i`, CI) | In `detect_vscode_user_dir` `${USER:-}` statt `$USER` | | fish: Funktion ändert Skript-Variable nicht | fish-Funktionen sehen Caller-Locals nicht | `set -g` für Zähler/Flags die eine Funktion schreibt (z. B. `_append_gitignore`) | +| Anpassbares Feld wird von `copilot-update` überschrieben | Inhalt stand **vor** dem ersten `---` (Framework-Sektion wird immer aus Template neu geschrieben) | Repo-/maschinenspezifische, anpassbare Felder immer **nach** `---` platzieren (Projekt-Sektion bleibt erhalten) – siehe Default-Shell-Feld | diff --git a/docs/history/prompts/2026-06-18_default-shell-erkennung_session.md b/docs/history/prompts/2026-06-18_default-shell-erkennung_session.md new file mode 100644 index 0000000..a90fa43 --- /dev/null +++ b/docs/history/prompts/2026-06-18_default-shell-erkennung_session.md @@ -0,0 +1,53 @@ +# Session History + +### Prompt 1 +nimm mal mit auf was die default shell auf dem system ist auf dem entwickelt wird, bei mir ist das fast immer fish und das sollte als default drin stehen aber auch änderbar sein bzw. bei einem neuen repo neu hinzugefügt werden muss. + +### Antwort 1 +Ich habe ein **Default-Shell-Feld** eingeführt, das (a) standardmäßig `fish` enthält, (b) jederzeit manuell änderbar ist und (c) beim Bootstrap eines neuen Repos automatisch aus `$SHELL` des Entwicklungssystems gesetzt wird. + +Zentrale Designentscheidung: Das Feld liegt in der **Projekt-Sektion** der `copilot-instructions.md` (nach dem ersten `---`). Grund: `copilot-update` schreibt die Framework-Sektion (vor `---`) bei jedem Update neu — nur hinter `---` überlebt ein repo-/maschinenspezifischer Wert. Diese Constraint habe ich zusätzlich im Repo-Memory festgehalten. + +Geänderte Dateien (7): + +```text +.github/copilot-instructions.md # neue Sektion "🖥 Entwicklungsumgebung" mit "- **Default-Shell:** fish" (nach ---) +git-templates/.github/copilot-instructions.md # gleiche Sektion im Template (Wert wird beim Bootstrap ersetzt) +scripts/copilot-bootstrap.sh # $SHELL-Erkennung (basename, Fallback fish, Whitelist ^[a-z0-9_-]+$) + _set_dev_shell() +scripts/copilot-bootstrap.fish # Parität zur sh-Version (string match -qr Validierung, functions -e Cleanup) +scripts/selftest.sh # neue Fälle: Default-Shell-Zeile vorhanden + SHELL=zsz/leer → korrekter Wert (sh & fish) +docs/MAINTAINER.md # Designentscheidung + Fallstrick "Feld nach --- platzieren" dokumentiert +git-templates/docs/MAINTAINER.md # Default-Shell-Feld für neue Repos erklärt +``` + +Implementierungsdetails der Erkennung (identisch in sh und fish): +- `$SHELL` → `basename` (z. B. `/usr/bin/fish` → `fish`); leeres `$SHELL` → Fallback `fish`. +- Wert gegen `^[a-z0-9_-]+$` geprüft (verhindert sed-Injection), sonst Fallback `fish`. +- `_set_dev_shell()` ersetzt die `- **Default-Shell:** …`-Zeile **nur in frisch erzeugten** `copilot-instructions.md` (bestehende Dateien bleiben unangetastet → User-Anpassung bleibt erhalten). + +### Prompt 2 +War das hier alles abgeschlossen und committed, dokumentiert und gepusht? + +### Antwort 2 +Nein — der Code war fertig und validiert, aber **nicht** committed, **nicht** in die History dokumentiert und **nicht** gepusht; zusätzlich lag der lokale Branch 1 Commit **hinter** `origin/master`. Ich habe den Zustand geprüft, das fehlende Remote-Commit integriert, alles validiert und anschließend History + Kontext geschrieben, committed und gepusht. + +Statusprüfung: + +```text +git status → 7 unstaged Dateien, "behind 'origin/master' by 1 commit" +git log --oneline @{u}.. → leer (nichts unpushed, weil nichts committed) +sh -n / fish -n → bootstrap.sh OK, selftest.sh OK, bootstrap.fish OK +sh scripts/selftest.sh → 6/6 PASS (inkl. neuer Shell-Erkennungsfälle) +``` + +Integration des Remote-Commits (`e83c333 docs: add rd13 infrastructure conventions`) trotz dirty tree — die 3 überlappenden Dateien hatten Hunks an unterschiedlichen Stellen, daher konfliktfrei: + +```text +git stash push -m dev-shell-feature-wip -- <7 Dateien> +git pull --ff-only # 5dc89f0 → e83c333, Fast-forward +git stash pop # Auto-merge: docs/MAINTAINER.md, git-templates/{.github/copilot-instructions,docs/MAINTAINER}.md → exit 0, keine Konflikte +``` + +Verifikation nach Merge: beide Änderungssätze (rd13_-Blöcke + Default-Shell) in allen überlappenden Dateien vorhanden, Selftest erneut 6/6 PASS. + +Danach: Session-History (diese Datei) angelegt, `PROJECT_CONTEXT.md` aktualisiert, committed (pre-commit Quality Gate bestanden) und nach `origin/master` gepusht. diff --git a/docs/history/summary/PROJECT_CONTEXT.md b/docs/history/summary/PROJECT_CONTEXT.md index e49a5c8..346dfac 100644 --- a/docs/history/summary/PROJECT_CONTEXT.md +++ b/docs/history/summary/PROJECT_CONTEXT.md @@ -8,7 +8,7 @@ ## Aktueller Projektstatus -**Letzte Aktualisierung:** 2026-06-13 – rd13_-Grundkonvention ergänzt: Runner heißt Docker, Services hinter Caddy, zentrales Proxy-Repo `rd13_system_proxy` +**Letzte Aktualisierung:** 2026-06-18 – Default-Shell-Feld eingeführt: Bootstrap erkennt `$SHELL` automatisch, schreibt es in die Projekt-Sektion der `copilot-instructions.md` (überlebt `copilot-update`), manuell änderbar; Default `fish` **Phase:** Produktion / stabil – wird bei Bedarf erweitert --- @@ -33,6 +33,7 @@ die zentrale Proxy-Schicht liegt im Repo `rd13_system_proxy`. | Datum | Aufgabe | Ergebnis | Entscheidungen | |---|---|---|---| +| 2026-06-18 | Default-Shell-Erkennung | Bootstrap setzt `$SHELL` (Fallback `fish`) automatisch in `copilot-instructions.md`; Feld nach `---` (überlebt update); selftest +3 Fälle; beide MAINTAINER.md dokumentiert | Feld in Projekt-Sektion, nicht Framework-Sektion; Whitelist `^[a-z0-9_-]+$` gegen sed-Injection | | 2026-06-13 | rd13_-Infrastrukturkonvention | Runner in Docker; alle Services hinter Caddy; zentrales Proxy-Repo `rd13_system_proxy` | this session | | 2026-06-10 | Agent-History → `docs/history/` + Auto-Migration | pre-commit Cutover, alle Skripte, settings/prompt/instructions/docs; `git mv` dieses Repos | `2b20a98` + Dogfood | | 2026-06-10 | WP12 Doku-Portabilität | README/USER/ADMIN/MAINTAINER: env/config, init-copilot, offline; tote IP raus | `afd5b38` | @@ -83,6 +84,7 @@ die zentrale Proxy-Schicht liegt im Repo `rd13_system_proxy`. | CI: shellcheck + fish -n + selftest | Verhindert Regressionen in den Shell-Skripten | `.github/workflows/ci.yml`, `scripts/selftest.sh` | | Agent-History unter `docs/history/` | Aufgeräumte Struktur (alles Doku-Artefakte unter docs/); Cutover statt Dual-Pfad | pre-commit, alle Skripte | | Auto-Migration `history/` → `docs/history/` in copilot-update | Bestehende Repos ziehen ohne manuellen Eingriff nach (git mv, Fallback mv) | `scripts/copilot-update.{sh,fish}` | +| Default-Shell-Feld in Projekt-Sektion (nach `---`) | Bootstrap erkennt `$SHELL`; Wert überlebt `copilot-update` (Framework-Sektion wird neu geschrieben); manuell pro Repo/Maschine änderbar | `scripts/copilot-bootstrap.{sh,fish}`, `git-templates/.github/copilot-instructions.md` | --- diff --git a/git-templates/.github/copilot-instructions.md b/git-templates/.github/copilot-instructions.md index 4b9e377..49dea6a 100644 --- a/git-templates/.github/copilot-instructions.md +++ b/git-templates/.github/copilot-instructions.md @@ -60,6 +60,16 @@ --- +## 🖥 Entwicklungsumgebung + + +- **Default-Shell:** fish + +Der Agent nutzt diese Shell für **alle** Terminal-Kommandos und passt die Syntax an +(fish ≠ POSIX sh/bash – z.B. `set -x VAR wert` statt `export VAR=wert`, `(cmd)` statt +`$(cmd)`, kein `&&`/`||`-Verketten in fish-Skripten). Bei anderem System / neuem Repo +diesen Wert anpassen. + ## Project diff --git a/git-templates/docs/MAINTAINER.md b/git-templates/docs/MAINTAINER.md index 7a77bd9..ed4ad29 100644 --- a/git-templates/docs/MAINTAINER.md +++ b/git-templates/docs/MAINTAINER.md @@ -59,6 +59,12 @@ cd # Dev-Server starten ``` +> **Default-Shell:** `.github/copilot-instructions.md` enthält im Abschnitt +> *Entwicklungsumgebung* das Feld `- **Default-Shell:**`. Es sagt dem Copilot-Agent, +> welche Shell-Syntax er für Terminal-Kommandos nutzen soll. Beim Bootstrap wird der +> Wert automatisch aus `$SHELL` erkannt; bei abweichender Entwicklungs-Shell den Wert +> dort anpassen. + --- ## Architekturentscheidungen (ADRs) diff --git a/scripts/copilot-bootstrap.fish b/scripts/copilot-bootstrap.fish index f90dbcc..112f253 100755 --- a/scripts/copilot-bootstrap.fish +++ b/scripts/copilot-bootstrap.fish @@ -30,11 +30,37 @@ end echo "Bootstrapping Copilot config in: $TARGET" +# ── Default-Shell des Entwicklungssystems erkennen ─────────────────────────── +# Wird in die frisch erzeugte copilot-instructions.md geschrieben, damit der +# Agent die richtige Shell-Syntax fuer Terminal-Kommandos waehlt. Fallback: fish. +if test -n "$SHELL" + set -g DEV_SHELL (basename "$SHELL") +else + set -g DEV_SHELL fish +end +if test -z "$DEV_SHELL"; or not string match -qr '^[a-z0-9_-]+$' -- "$DEV_SHELL" + set -g DEV_SHELL fish +end + +# Ersetzt die `- **Default-Shell:** …`-Zeile in $f durch den erkannten Wert. +# set -g DEV_SHELL oben, damit die Funktion den Wert im Skript-Scope sieht. +function _set_dev_shell --argument-names f + test -f "$f"; or return 0 + grep -q '^- \*\*Default-Shell:\*\*' "$f" 2>/dev/null; or return 0 + set tmp "$f.tmp.$fish_pid" + if sed "s|^- \*\*Default-Shell:\*\* .*|- **Default-Shell:** $DEV_SHELL|" "$f" >$tmp 2>/dev/null + mv $tmp "$f" + else + rm -f $tmp + end +end + # .github/copilot-instructions.md if not test -f "$TARGET/.github/copilot-instructions.md" mkdir -p "$TARGET/.github" cp "$TEMPLATE_DIR/.github/copilot-instructions.md" "$TARGET/.github/copilot-instructions.md" - echo " ✓ .github/copilot-instructions.md created (TODO-Felder ausfüllen)" + _set_dev_shell "$TARGET/.github/copilot-instructions.md" + echo " ✓ .github/copilot-instructions.md created (Default-Shell: $DEV_SHELL; TODO-Felder ausfüllen)" else echo " ─ .github/copilot-instructions.md already exists, skipping" end @@ -149,6 +175,7 @@ else end functions -e _append_gitignore +functions -e _set_dev_shell echo "" echo "Done. Nächste Schritte:" diff --git a/scripts/copilot-bootstrap.sh b/scripts/copilot-bootstrap.sh index 7e23845..966b05d 100644 --- a/scripts/copilot-bootstrap.sh +++ b/scripts/copilot-bootstrap.sh @@ -27,11 +27,37 @@ fi echo "Bootstrapping Copilot config in: $TARGET" +# ── Default-Shell des Entwicklungssystems erkennen ─────────────────────────── +# Wird in die frisch erzeugte copilot-instructions.md geschrieben, damit der +# Agent die richtige Shell-Syntax fuer Terminal-Kommandos waehlt. Fallback: fish. +if [ -n "${SHELL:-}" ]; then + DEV_SHELL=$(basename "$SHELL") +else + DEV_SHELL=fish +fi +case "$DEV_SHELL" in + ''|*[!a-z0-9_-]*) DEV_SHELL=fish ;; +esac + +# Ersetzt die `- **Default-Shell:** …`-Zeile in $1 durch den erkannten Wert. +_set_dev_shell() { + f="$1" + [ -f "$f" ] || return 0 + grep -q '^- \*\*Default-Shell:\*\*' "$f" 2>/dev/null || return 0 + tmp="$f.tmp.$$" + if sed "s|^- \*\*Default-Shell:\*\* .*|- **Default-Shell:** $DEV_SHELL|" "$f" > "$tmp" 2>/dev/null; then + mv "$tmp" "$f" + else + rm -f "$tmp" + fi +} + # .github/copilot-instructions.md if [ ! -f "$TARGET/.github/copilot-instructions.md" ]; then mkdir -p "$TARGET/.github" cp "$TEMPLATE_DIR/.github/copilot-instructions.md" "$TARGET/.github/copilot-instructions.md" - echo " ✓ .github/copilot-instructions.md created (TODO-Felder ausfüllen)" + _set_dev_shell "$TARGET/.github/copilot-instructions.md" + echo " ✓ .github/copilot-instructions.md created (Default-Shell: $DEV_SHELL; TODO-Felder ausfüllen)" else echo " ─ .github/copilot-instructions.md already exists, skipping" fi diff --git a/scripts/selftest.sh b/scripts/selftest.sh index 44fece4..ae24338 100755 --- a/scripts/selftest.sh +++ b/scripts/selftest.sh @@ -14,7 +14,9 @@ trap 'rm -rf "$WORK"' EXIT build_template() { tpl="$1" mkdir -p "$tpl/.github" "$tpl/.vscode" "$tpl/hooks" "$tpl/docs" "$tpl/docs/history/summary" - echo "framework" > "$tpl/.github/copilot-instructions.md" + # Realistische copilot-instructions.md inkl. Default-Shell-Zeile (Projekt-Sektion) + printf '# Instr\n\n---\n\n## Entwicklungsumgebung\n\n- **Default-Shell:** fish\n\n## Project\n' \ + > "$tpl/.github/copilot-instructions.md" echo '{}' > "$tpl/.vscode/settings.json" echo '{}' > "$tpl/.vscode/extensions.json" printf '#!/bin/sh\nexit 0\n' > "$tpl/hooks/pre-commit" @@ -58,13 +60,47 @@ run_case() { echo "FAIL: $label – .gitignore enthaelt kein Zone.Identifier-Muster" >&2 exit 1 fi + if ! grep -q '^- \*\*Default-Shell:\*\*' "$target/.github/copilot-instructions.md"; then + echo "FAIL: $label – Default-Shell-Zeile fehlt in copilot-instructions.md" >&2 + exit 1 + fi echo "PASS: $label ($script)" } +# Prueft, dass die erkannte $SHELL korrekt in die copilot-instructions.md +# geschrieben wird (deterministisch ueber explizit gesetztes SHELL). +run_shell_case() { + label="$1" + runner="$2" + script="$3" + shellval="$4" + expected="$5" + home="$WORK/home_$label" + target="$WORK/target_$label" + mkdir -p "$home/.git-templates" "$target" + build_template "$home/.git-templates" + git -C "$target" init -q + + HOME="$home" SHELL="$shellval" "$runner" "$REPO_DIR/scripts/$script" "$target" >/dev/null + + got=$(sed -n 's|^- \*\*Default-Shell:\*\* ||p' "$target/.github/copilot-instructions.md") + if [ "$got" != "$expected" ]; then + echo "FAIL: $label – Default-Shell '$got' != erwartet '$expected'" >&2 + exit 1 + fi + echo "PASS: $label (SHELL='$shellval' → $expected)" +} + run_case sh_bootstrap sh copilot-bootstrap.sh +# Shell-Erkennung: expliziter Wert wird uebernommen, leeres SHELL faellt auf fish zurueck. +run_shell_case sh_shell_zsh sh copilot-bootstrap.sh /usr/bin/zsh zsh +run_shell_case sh_shell_empty sh copilot-bootstrap.sh "" fish + if command -v fish >/dev/null 2>&1; then run_case fish_bootstrap fish copilot-bootstrap.fish + run_shell_case fish_shell_zsh fish copilot-bootstrap.fish /usr/bin/zsh zsh + run_shell_case fish_shell_empty fish copilot-bootstrap.fish "" fish else echo "SKIP: fish nicht installiert – fish-Bootstrap uebersprungen" fi