Compare commits
18 Commits
v0.1-robot
...
RC0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ff177d0e8 | ||
|
|
6fec2df7d9 | ||
|
|
599af5b011 | ||
|
|
0f8946e4c4 | ||
|
|
3ca1709eb3 | ||
|
|
e1cae4fbd1 | ||
|
|
3d90710bc1 | ||
|
|
e96d6cc99e | ||
|
|
6fefaf9081 | ||
|
|
43a16d30f2 | ||
|
|
7151749826 | ||
|
|
0700ec0a41 | ||
|
|
43171dd884 | ||
|
|
819d2d4ec0 | ||
|
|
7679805390 | ||
|
|
f85b4d48d9 | ||
|
|
3ee5214405 | ||
|
|
39011a555f |
@@ -36,7 +36,7 @@ QDRANT_URL="http://localhost:6333"
|
|||||||
QDRANT_COLLECTION="crumbforest_memory"
|
QDRANT_COLLECTION="crumbforest_memory"
|
||||||
QDRANT_API_KEY="" # Optional, meist leer bei local
|
QDRANT_API_KEY="" # Optional, meist leer bei local
|
||||||
|
|
||||||
# === TOKEN BUDGET (Pädagogisch) ===
|
# === TOKEN BUDGET (Crumb Codex: Machen statt Lernen) ===
|
||||||
|
|
||||||
# Tages-Budget für bewusstes Fragen (in Tokens)
|
# Tages-Budget für bewusstes Fragen (in Tokens)
|
||||||
# 0 = unbegrenzt, >0 = Limit (z.B. 10000 = ~20 Fragen)
|
# 0 = unbegrenzt, >0 = Limit (z.B. 10000 = ~20 Fragen)
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
🌲 CRUMBFOREST 🌲
|
🌲 CRUMBFOREST 🌲
|
||||||
"Wo Fragen wachsen"
|
"Wo Fragen wachsen"
|
||||||
|
|
||||||
Die 15 Waldwächter sind bereit!
|
Die 17 Waldwächter sind bereit!
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📖 Was ist das hier?
|
## 📖 Was ist das hier?
|
||||||
|
|
||||||
Crumbforest ist ein **pädagogisches Bash-Lernsystem** mit 15 KI-Charakteren (Waldwächter), die Kindern durch Metaphern, Poesie und Geduld das Programmieren beibringen.
|
Crumbforest ist ein **interaktives Bash-System zum Machen & Fragen** mit 17 KI-Charakteren (Waldwächter), die Krümeln durch Metaphern, Poesie und Geduld das Programmieren zeigen.
|
||||||
|
|
||||||
**Philosophie:** *"Was kostet die Frage eines Kindes?"*
|
**Philosophie:** *"Was kostet die Frage eines Kindes?"*
|
||||||
Im Wald unbezahlbar - aber Token lehren achtsames Fragen.
|
Im Wald unbezahlbar - aber Token lehren achtsames Fragen.
|
||||||
@@ -61,11 +61,12 @@ crew_tokens
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌲 Die 15 Waldwächter
|
## 🌲 Die 17 Waldwächter
|
||||||
|
|
||||||
### 🔺 Das Dreieck (Foundation)
|
### 🔺 Das Dreieck (Foundation)
|
||||||
|
|
||||||
**Ohne dieses Dreieck geht es nicht!**
|
**Ohne dieses Dreieck geht es nicht!**
|
||||||
|
*Das Dreieck ist ein didaktisches Modell (Struktur, Flow, Balance), kein technisches Modul.*
|
||||||
|
|
||||||
| Charakter | Rolle | Spezialität |
|
| Charakter | Rolle | Spezialität |
|
||||||
|-----------|-------|-------------|
|
|-----------|-------|-------------|
|
||||||
@@ -169,7 +170,7 @@ In der CrumbCrew-Shell verfügbar:
|
|||||||
| Befehl | Beschreibung |
|
| Befehl | Beschreibung |
|
||||||
|--------|--------------|
|
|--------|--------------|
|
||||||
| `crew_help` | Diese Hilfe anzeigen |
|
| `crew_help` | Diese Hilfe anzeigen |
|
||||||
| `crew_status` | Status aller 15 Waldwächter |
|
| `crew_status` | Status aller 17 Waldwächter |
|
||||||
| `crew_tokens` | Token-Verbrauch ALLER Charaktere |
|
| `crew_tokens` | Token-Verbrauch ALLER Charaktere |
|
||||||
| `crew_memory` | Erinnerungen durchsuchen |
|
| `crew_memory` | Erinnerungen durchsuchen |
|
||||||
|
|
||||||
@@ -365,7 +366,7 @@ echo $OPENROUTER_API_KEY
|
|||||||
|
|
||||||
Schau dir `crumbforest_roles/funkfox_zero.sh` als Template an:
|
Schau dir `crumbforest_roles/funkfox_zero.sh` als Template an:
|
||||||
1. Load .env
|
1. Load .env
|
||||||
2. Crew Memory Functions
|
2. Crew Memory Functions (log-basiert, dateibasiert, nachvollziehbar)
|
||||||
3. System Prompt mit Persönlichkeit
|
3. System Prompt mit Persönlichkeit
|
||||||
4. OpenRouter API Call
|
4. OpenRouter API Call
|
||||||
5. Token Tracking
|
5. Token Tracking
|
||||||
@@ -378,8 +379,8 @@ Neue Kategorien in `crumb-mission-selector.sh` hinzufügen.
|
|||||||
|
|
||||||
## 🎯 Credits
|
## 🎯 Credits
|
||||||
|
|
||||||
**Die 15 Waldwächter** - Konzept von branko.de
|
**Die 17 Waldwächter** - Konzept von branko.de
|
||||||
**"Wo Fragen wachsen"** - Pädagogische Vision
|
**"Wo Fragen wachsen"** - Der Crumb Codex: Machen statt Lernen
|
||||||
**Token-Philosophie** - "Was kostet die Frage eines Kindes?"
|
**Token-Philosophie** - "Was kostet die Frage eines Kindes?"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
244
README.md
244
README.md
@@ -1,6 +1,8 @@
|
|||||||
# 🌲 Crumbforest Missions - CF_Zero_V1
|
# 🌲 Crumbforest Missions - CF_Zero_V1
|
||||||
|
|
||||||
Ein interaktives Bash-Lern-System mit KI-gestützten Charakteren für spielerisches Command-Line-Training.
|
Ein interaktives Bash-Lern-System mit 17 KI-gestützten Waldwächtern für spielerisches Command-Line-Training und Roboter-Projekte.
|
||||||
|
|
||||||
|
**"Was kostet die Frage eines Kindes?"** - Transparentes Token-Tracking nach dem Crumb Codex: Fragen stärken Krümel & Wurzel.
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
@@ -10,42 +12,258 @@ Ein interaktives Bash-Lern-System mit KI-gestützten Charakteren für spielerisc
|
|||||||
|
|
||||||
# Einzelne Mission ausführen
|
# Einzelne Mission ausführen
|
||||||
bash missions/basics/fridolin.sh
|
bash missions/basics/fridolin.sh
|
||||||
|
|
||||||
|
# Robots Mission (neu!)
|
||||||
|
bash missions/robots/mond_maschine.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🤖 Robots-Kategorie ⭐ NEU!
|
||||||
|
|
||||||
|
Drei komplette Missionen zum Roboter-Bauen mit der ganzen Crew:
|
||||||
|
|
||||||
|
### 🔋 LiPo Power Academy
|
||||||
|
"Power braucht Respekt, nicht Angst"
|
||||||
|
- 8 Phasen: Grundlagen → Sicherheit → Power-Verteilung
|
||||||
|
- Lerne alles über LiPo-Batterien für Roboter-Projekte
|
||||||
|
- 6 Waldwächter: CapaciTobi, CrabbyRust, Schnecki, Schraubbär, DumboSQL, Vektor
|
||||||
|
- [LiPo 6S Charger Simulator](https://194-164-194-191.sslip.io/crumbblocks/lipo_6s_charger_sim_safe_v7.html)
|
||||||
|
- ⚠️ **Sicherheitshinweis:** Lern- und Simulationskonzept. Arbeiten mit echten LiPo-Akkus nur unter Aufsicht erfahrener Erwachsener.
|
||||||
|
|
||||||
|
### 🌈 Regenbogen-Zählmaschine
|
||||||
|
"Etwas bauen was noch keiner gebaut hat - sonst muss man es ja nur reparieren!"
|
||||||
|
- 7 Phasen: Vision → Hardware → Code → Art
|
||||||
|
- Baue einen Farb-Event-Counter mit Sensor, Code & Visualisierung
|
||||||
|
- 13 Waldwächter: Die komplette Crew!
|
||||||
|
- Hardware + Software + Creative Coding
|
||||||
|
|
||||||
|
### 🌙 Mond Maschine - Rainbow Predictor
|
||||||
|
"Vorhersagen heißt verstehen - Die Natur durch Code begreifen"
|
||||||
|
- 8 Phasen: Computer Vision → ML → Astronomie
|
||||||
|
- Predict when rainbows appear using moon phases & weather data
|
||||||
|
- 13 Waldwächter: Computer Vision, APIs, ML Light
|
||||||
|
- OpenCV + Weather APIs + Astronomical calculations
|
||||||
|
- Bonus: Learn about Lunar Rainbows! 🌙🌈
|
||||||
|
|
||||||
## 📚 Features
|
## 📚 Features
|
||||||
|
|
||||||
- **Interaktive Lernmissionen** - Von Navigation bis DNS
|
- **Interaktive Lernmissionen** - Von Basics bis Advanced & Robots
|
||||||
- **KI-Assistenten** - Charakterbasierte Helfer (Deepbit, Bugsy, Schnippsi, etc.)
|
- **17 Waldwächter** - KI-Assistenten mit Persönlichkeit (siehe unten)
|
||||||
|
- **Das Dreieck** - DumboSQL, FunkFox, Taichi Taube (didaktisches Modell, nicht technisches Modul)
|
||||||
- **Metadata-driven** - Neue Missionen ohne Code-Änderungen hinzufügen
|
- **Metadata-driven** - Neue Missionen ohne Code-Änderungen hinzufügen
|
||||||
- **Kamera-Gestenerkennung** - SnakeCam mit Computer Vision
|
- **Token-Tracking** - Transparent: "Was kostet die Frage eines Kindes?"
|
||||||
- **Token-Tracking** - OpenRouter API Nutzung überwachen
|
- **Crew Memory** - Log-basiertes Gedächtnis (dateibasiert, nachvollziehbar, kein autonomes Agentensystem)
|
||||||
|
- **Logs im Repo** - Strukturiert in `logs/{character}/`
|
||||||
|
|
||||||
|
## 🌲 Die 17 Waldwächter
|
||||||
|
|
||||||
|
### 🔺 Das Dreieck (Foundation)
|
||||||
|
- **🐘 DumboSQL** - Der nie vergessende Elefant (SQL & Daten)
|
||||||
|
- **🦊 FunkFox** - Der Bash Rapper (Terminal-Flow mit Beat)
|
||||||
|
- **🕊️ Taichi Taube** - Die Balance-Bringerin (Spirale & Ordnung)
|
||||||
|
|
||||||
|
### 🔧 Hardware-Team
|
||||||
|
- **🐿️ CapaciTobi** - Kondensator-Experte (Elektronik & Power)
|
||||||
|
- **🐌 Schnecki** - Verkabelungs-Guru (langsam & präzise)
|
||||||
|
- **🐻 Schraubbär** - Mechanik-Meister (schweres Gerät)
|
||||||
|
|
||||||
|
### 💻 Code-Team
|
||||||
|
- **🐍 SnakePy** - Python-Guide (mehrere Wege zeigen)
|
||||||
|
- **🧓 PepperPHP** - Structure Mentor (MVC wie ein Rezept)
|
||||||
|
- **🦀 CrabbyRust** - Security Guardian (Memory-safe)
|
||||||
|
- **🕷️ Spider** - Network Feeler (APIs & Verbindungen)
|
||||||
|
|
||||||
|
### 🎨 UI-Team
|
||||||
|
- **✂️ Schnippsi** - CSS & Styling (alles schick machen)
|
||||||
|
- **📄 Templatus** - Template-Master (HTML-Struktur)
|
||||||
|
- **👾 ASCII-Monster** - Terminal Artist (ASCII-Art)
|
||||||
|
|
||||||
|
### 🧭 System-Team
|
||||||
|
- **🦉 Maya-Eule** - Weise Eule mit Gedächtnis (Meta-Wissen)
|
||||||
|
- **🔧 Deepbit** - Bash-Erklärer (deep diving)
|
||||||
|
- **🐛 Bugsy** - Debugging-Helfer (Fehler-Detektiv)
|
||||||
|
- **🧭 Vektor** - Point-to-Point Guide (Navigation)
|
||||||
|
|
||||||
## 🦊 Verfügbare Missionen
|
## 🦊 Verfügbare Missionen
|
||||||
|
|
||||||
### Basics
|
### 📚 Basics
|
||||||
- **Fridolin** - Navigation (`pwd`, `ls`, `cd`)
|
- **Fridolin** - Navigation (`pwd`, `ls`, `cd`)
|
||||||
- **Balu** - Dateien erstellen (`mkdir`, `touch`, `echo`)
|
- **Balu** - Dateien erstellen (`mkdir`, `touch`, `echo`)
|
||||||
- **Noko** - Dateien lesen (`cat`, `head`, `tail`, `grep`)
|
- **Noko** - Dateien lesen (`cat`, `head`, `tail`, `grep`)
|
||||||
|
|
||||||
### Advanced
|
### 🚀 Advanced
|
||||||
- **DNS Deep Dive** - DNS-Tools (`dig`, `nslookup`, `host`)
|
- **DNS Deep Dive** - DNS-Tools (`dig`, `nslookup`, `host`)
|
||||||
- **SSH Security** - SSH-Verbindungen und Keys
|
- **SSH Security** - SSH-Verbindungen und Keys
|
||||||
|
|
||||||
## 🤖 KI-Assistenten
|
### 🏆 Challenges
|
||||||
|
- **Stage Builder** - Komplexe Bash-Herausforderungen
|
||||||
|
|
||||||
|
### 🤖 Robots ⭐ NEU!
|
||||||
|
- **🔋 LiPo Power Academy** - Batterie-Wissen für Roboter
|
||||||
|
- **🌈 Regenbogen-Zählmaschine** - Farb-Event-Counter
|
||||||
|
- **🌙 Mond Maschine** - Rainbow Predictor mit Computer Vision
|
||||||
|
|
||||||
|
## 🤖 Waldwächter nutzen
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export OPENROUTER_API_KEY="your-key"
|
# Source die Library
|
||||||
./crumbforest_roles/deepbit_zero.sh "Wie funktioniert grep?"
|
source lib/waldwaechter.sh
|
||||||
|
|
||||||
|
# Frag einen Waldwächter
|
||||||
|
mayaeule "Was ist wichtiger - das Ziel oder der Weg?"
|
||||||
|
funkfox "Erkläre mir Pipes im Flow!"
|
||||||
|
dumbosql "Wie strukturiere ich eine Datenbank?"
|
||||||
|
snakepy "Zeig mir 3 Wege, um eine Liste zu sortieren"
|
||||||
|
|
||||||
|
# Alle verfügbar:
|
||||||
|
# mayaeule, deepbit, bugsy, schnippsi, templatus
|
||||||
|
# tobi, schraubaer, schnecki
|
||||||
|
# dumbosql, funkfox, taichitaube
|
||||||
|
# snakepy, pepperphp, crabbyrust
|
||||||
|
# spider, vektor, asciimonster
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 📊 Token-Tracking & Logs
|
||||||
|
|
||||||
|
**Philosophie:** "Was kostet die Frage eines Kindes?"
|
||||||
|
|
||||||
|
- Token-Verbrauch wird transparent angezeigt
|
||||||
|
- Logs landen in `logs/{character}/`
|
||||||
|
- Crew Memory: Waldwächter lesen andere Logs
|
||||||
|
- Mission Logs: `logs/missions/*.json`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Logs checken
|
||||||
|
ls logs/
|
||||||
|
ls logs/mayaeule/
|
||||||
|
cat logs/missions/mond_maschine_*.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Typische Kosten (Richtwerte, modellabhängig):
|
||||||
|
- Einzelne Frage: ~200-800 tokens (~$0.0002-0.0008)
|
||||||
|
- Komplette Mission: ~5,000-7,000 tokens (~$0.005-0.007)
|
||||||
|
- **Richtwert: Unter 1 Cent pro Mission** 💚
|
||||||
|
- Token-Anzeige dient der transparenten Nachvollziehbarkeit, nicht der Abrechnung
|
||||||
|
|
||||||
## 📖 Dokumentation
|
## 📖 Dokumentation
|
||||||
|
|
||||||
Siehe [CLAUDE.md](CLAUDE.md) für vollständige Architektur und Entwickler-Dokumentation.
|
- **[CLAUDE.md](CLAUDE.md)** - Vollständige Architektur & Entwickler-Docs
|
||||||
|
- **[KEKSHANDBUCH_ZERO_v0.0.md](KEKSHANDBUCH_ZERO_v0.0.md)** - User Manual mit allen 17 Waldwächtern
|
||||||
|
|
||||||
## 🌍 Philosophie
|
## 🌍 Philosophien
|
||||||
|
|
||||||
**Waldwächter-Prinzip:** Transparenz über Magie - Metadata-driven, erweiterbar, bildungsfreundlich.
|
**"Das Dreieck"** - DumboSQL, FunkFox, Taichi Taube
|
||||||
|
- Struktur, Flow & Balance
|
||||||
|
- Die Grundlage für komplexe Projekte
|
||||||
|
|
||||||
|
**"Power braucht Respekt, nicht Angst"** - LiPo Power Academy
|
||||||
|
- Sicherheit durch Wissen
|
||||||
|
- 7 goldene Regeln für LiPo-Batterien
|
||||||
|
|
||||||
|
**"Etwas bauen was noch keiner gebaut hat"** - Regenbogen-Zählmaschine
|
||||||
|
- Kreativität statt Reparatur
|
||||||
|
- Inspiriert durch Kinderfragen
|
||||||
|
|
||||||
|
**"Vorhersagen heißt verstehen"** - Mond Maschine
|
||||||
|
- Die Natur durch Code begreifen
|
||||||
|
- Computer Vision + Machine Learning + Astronomie
|
||||||
|
|
||||||
|
**"Was kostet die Frage eines Kindes?"**
|
||||||
|
- Token-Tracking für Transparenz
|
||||||
|
- Machen statt Lernen - Fragen stärken die Wurzel
|
||||||
|
|
||||||
|
**"Ein Elefant vergisst nie"** - DumboSQL
|
||||||
|
- Crew Memory über Projekt-Grenzen
|
||||||
|
- Persistente Konversations-Historie
|
||||||
|
|
||||||
|
**"wir ja nie fertig"** 🌱
|
||||||
|
- Der Wald wächst mit jeder Idee
|
||||||
|
- Community-driven Education
|
||||||
|
|
||||||
|
## 🏗️ Projekt-Struktur
|
||||||
|
|
||||||
|
```
|
||||||
|
CF_Zero_V1/
|
||||||
|
├── crumb-mission-selector.sh # Hauptmenü
|
||||||
|
├── lib/
|
||||||
|
│ └── waldwaechter.sh # 17 Waldwächter als Functions
|
||||||
|
├── crumbforest_roles/ # 17 Character Scripts
|
||||||
|
├── missions/
|
||||||
|
│ ├── basics/ # Einsteiger
|
||||||
|
│ ├── advanced/ # Fortgeschrittene
|
||||||
|
│ ├── challenges/ # Herausforderungen
|
||||||
|
│ └── robots/ # 🤖 NEU! 3 Robot-Missionen
|
||||||
|
├── logs/ # 🌲 Projekt-basierte Logs
|
||||||
|
│ ├── {character}/ # Pro Waldwächter
|
||||||
|
│ └── missions/ # Mission Logs
|
||||||
|
├── CLAUDE.md # Projekt-Dokumentation
|
||||||
|
├── KEKSHANDBUCH_ZERO_v0.0.md # User Manual
|
||||||
|
└── README.md # Diese Datei
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .env erstellen mit API Key
|
||||||
|
echo "OPENROUTER_API_KEY=your-key-here" > .env
|
||||||
|
|
||||||
|
# Mission Selector starten
|
||||||
|
./crumb-mission-selector.sh
|
||||||
|
|
||||||
|
# Oder direkt eine Mission
|
||||||
|
bash missions/robots/mond_maschine.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Use Cases
|
||||||
|
|
||||||
|
- **Terminal lernen** - Interaktive Bash-Basics
|
||||||
|
- **Roboter bauen** - Hardware + Software Projekte
|
||||||
|
- **Computer Vision** - OpenCV mit SnakePy lernen
|
||||||
|
- **Datenbanken** - SQL mit DumboSQL verstehen
|
||||||
|
- **APIs integrieren** - Spider zeigt den Weg
|
||||||
|
- **Kreativ coden** - Projekte die Spaß machen!
|
||||||
|
|
||||||
|
## 🌟 Besonderheiten
|
||||||
|
|
||||||
|
- **Alle Waldwächter haben Persönlichkeit** - FunkFox rappt, Maya-Eule philosophiert
|
||||||
|
- **Inter-Character Communication** - Waldwächter lesen andere Logs
|
||||||
|
- **Metadata-driven** - Missions als .sh + .meta.json
|
||||||
|
- **Token-Transparenz** - Kosten werden angezeigt
|
||||||
|
- **Community-driven** - Missionen entstehen aus User-Ideen
|
||||||
|
- **Raspberry Pi kompatibel** - Grundsystem auf Pi Zero, Robot- & Vision-Missionen benötigen Pi 4+ oder Desktop
|
||||||
|
|
||||||
|
## 📦 Requirements
|
||||||
|
|
||||||
|
- Bash 3.2+ (macOS default, Bash 4+ empfohlen für volle Features)
|
||||||
|
- curl, jq
|
||||||
|
- OpenRouter API Key
|
||||||
|
- Optional: Python 3 für Robot-Missionen (opencv-python, numpy, ephem)
|
||||||
|
- Mond Maschine (Computer Vision): Pi 4+ oder Desktop empfohlen
|
||||||
|
|
||||||
|
## 🚀 Next Steps
|
||||||
|
|
||||||
|
1. Mission Selector starten: `./crumb-mission-selector.sh`
|
||||||
|
2. Eine Robot-Mission ausprobieren
|
||||||
|
3. Eigene Mission-Idee einreichen
|
||||||
|
4. Den Wald wachsen lassen! 🌱
|
||||||
|
|
||||||
|
## 🔗 Links
|
||||||
|
|
||||||
|
- [LiPo 6S Charger Simulator](https://194-164-194-191.sslip.io/crumbblocks/lipo_6s_charger_sim_safe_v7.html)
|
||||||
|
- [Rainbow Counter Demo](https://194-164-194-191.sslip.io/crumbblocks/rainbow_counter.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏷️ Version
|
||||||
|
|
||||||
|
**v0.1-robots-complete**
|
||||||
|
- 3 Robot-Missionen
|
||||||
|
- 17 Waldwächter komplett
|
||||||
|
- Logs im Repo
|
||||||
|
- Token-Tracking
|
||||||
|
- Crew Memory (log-basiert)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Erstellt mit 💚 im Crumbforest*
|
*Erstellt mit 💚 im Crumbforest*
|
||||||
|
|
||||||
|
**"Der Wald ist nie fertig - er wächst mit jeder Idee!"** 🌲🌱
|
||||||
|
|||||||
20
VERSION
Normal file
20
VERSION
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
RC0 - Release Candidate 0
|
||||||
|
Datum: 2025-12-21
|
||||||
|
|
||||||
|
🌲 Der Crumbforest ist bereit
|
||||||
|
|
||||||
|
Was entstanden ist:
|
||||||
|
• 17 Waldwächter - Alle Egos vereint
|
||||||
|
• Input-Transparenz - crew_tokens (Token-Tracking)
|
||||||
|
• Output-Transparenz - crew_memo (Krümel-Tracking)
|
||||||
|
• Kollaborative Wurzeln - seeds/ für gemeinsames Wachsen
|
||||||
|
• Zero Rules - Ein Script, ein Ordner, unendlich Möglichkeiten
|
||||||
|
|
||||||
|
Philosophie:
|
||||||
|
eule -> kleine krümel -> tools -> nullfeld
|
||||||
|
#ozmai #mensch #maschine
|
||||||
|
|
||||||
|
Der Wald mag keine Sticker.
|
||||||
|
17 Egos im Wald. 1 Wald ohne Ego. 0 Regeln.
|
||||||
|
|
||||||
|
🦉 Entstanden im Nullfeld 💚
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "📦 Starte Backup des Crumbforest Zero Systems..."
|
|
||||||
|
|
||||||
# Zielname
|
|
||||||
BACKUP_NAME="crumbforest_zero_backup_$(date +%Y%m%d_%H%M%S).zip"
|
|
||||||
DEST_DIR="/home/zero"
|
|
||||||
FULL_PATH="$DEST_DIR/$BACKUP_NAME"
|
|
||||||
|
|
||||||
# Verzeichnisse und Dateien sammeln
|
|
||||||
INCLUDE_PATHS=(
|
|
||||||
"/usr/local/bin/crumbmission"
|
|
||||||
"/home/zero/crumbforest_backup"
|
|
||||||
"/home/zero/.bits_logs"
|
|
||||||
"/home/zero/.eule_logs"
|
|
||||||
"/home/zero/.snake_logs"
|
|
||||||
"/home/zero/.pepper_logs"
|
|
||||||
"/home/zero/.bugsy_logs"
|
|
||||||
"/home/zero/.deepbit_logs"
|
|
||||||
"/home/zero/.dumbo_logs"
|
|
||||||
"/home/zero/.funkfox_logs"
|
|
||||||
"/home/zero/.schnecki_logs"
|
|
||||||
"/home/zero/.schnippsi_logs"
|
|
||||||
"/home/zero/.schraubaer_logs"
|
|
||||||
"/home/zero/.stage_logs"
|
|
||||||
"/home/zero/.taube_logs"
|
|
||||||
"/home/zero/.templatus_logs"
|
|
||||||
"/home/zero/.tobi_logs"
|
|
||||||
"/home/zero/.missionstage_log"
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
# Existierende Pfade prüfen und nur diese einfügen
|
|
||||||
EXISTING_PATHS=()
|
|
||||||
for path in "${INCLUDE_PATHS[@]}"; do
|
|
||||||
if [ -e "$path" ]; then
|
|
||||||
echo "✅ Hinzufügen: $path"
|
|
||||||
EXISTING_PATHS+=("$path")
|
|
||||||
else
|
|
||||||
echo "⚠️ Nicht gefunden (wird übersprungen): $path"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Archiv erstellen
|
|
||||||
cd /
|
|
||||||
zip -r "$FULL_PATH" "${EXISTING_PATHS[@]}" > /dev/null
|
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "🎉 Backup erfolgreich erstellt: $FULL_PATH"
|
|
||||||
else
|
|
||||||
echo "❌ Fehler beim Erstellen des Backups."
|
|
||||||
fi
|
|
||||||
@@ -15,9 +15,16 @@ NC='\033[0m' # No Color
|
|||||||
|
|
||||||
# === KONFIGURATION ===
|
# === KONFIGURATION ===
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="${SCRIPT_DIR}" # Save repo root before sourcing waldwaechter.sh
|
||||||
MISSION_DIR="${SCRIPT_DIR}/missions"
|
MISSION_DIR="${SCRIPT_DIR}/missions"
|
||||||
ENV_FILE="${SCRIPT_DIR}/.env"
|
ENV_FILE="${SCRIPT_DIR}/.env"
|
||||||
|
|
||||||
|
# === LADE WALDWÄCHTER LIBRARY ===
|
||||||
|
# Macht alle Crew-Befehle verfügbar: crew_tokens, crew_doctor, etc.
|
||||||
|
if [[ -f "${SCRIPT_DIR}/lib/waldwaechter.sh" ]]; then
|
||||||
|
source "${SCRIPT_DIR}/lib/waldwaechter.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
# === ENVIRONMENT LOADER ===
|
# === ENVIRONMENT LOADER ===
|
||||||
function load_env() {
|
function load_env() {
|
||||||
if [[ -f "${ENV_FILE}" ]]; then
|
if [[ -f "${ENV_FILE}" ]]; then
|
||||||
@@ -493,7 +500,7 @@ function mayaeule_doktor() {
|
|||||||
EULE_RC="/tmp/crumb_eule_$$.rc"
|
EULE_RC="/tmp/crumb_eule_$$.rc"
|
||||||
|
|
||||||
# Absoluter Pfad zum Maya-Eule Script
|
# Absoluter Pfad zum Maya-Eule Script
|
||||||
MAYAEULE_PATH="${SCRIPT_DIR}/crumbforest_roles/mayaeule_zero.sh"
|
MAYAEULE_PATH="${REPO_ROOT}/crumbforest_roles/mayaeule_zero.sh"
|
||||||
|
|
||||||
cat > "${EULE_RC}" << EOF
|
cat > "${EULE_RC}" << EOF
|
||||||
# Load .bashrc if exists
|
# Load .bashrc if exists
|
||||||
@@ -610,7 +617,7 @@ function crumbcrew_doktor() {
|
|||||||
CREW_RC="/tmp/crumb_crew_$$.rc"
|
CREW_RC="/tmp/crumb_crew_$$.rc"
|
||||||
|
|
||||||
# Pfade zu allen Charakteren
|
# Pfade zu allen Charakteren
|
||||||
CREW_DIR="${SCRIPT_DIR}/crumbforest_roles"
|
CREW_DIR="${REPO_ROOT}/crumbforest_roles"
|
||||||
|
|
||||||
cat > "${CREW_RC}" << EOF
|
cat > "${CREW_RC}" << EOF
|
||||||
# Load .bashrc if exists
|
# Load .bashrc if exists
|
||||||
@@ -629,9 +636,15 @@ NC='\033[0m'
|
|||||||
# Prompt im CrumbCrew-Style
|
# Prompt im CrumbCrew-Style
|
||||||
export PS1="\[\033[1;32m\](🌲 CrumbCrew) \u@\h:\w\$ \[\033[0m\]"
|
export PS1="\[\033[1;32m\](🌲 CrumbCrew) \u@\h:\w\$ \[\033[0m\]"
|
||||||
|
|
||||||
# Pfad zu den Charakteren
|
# Repo Root und Pfade
|
||||||
|
REPO_ROOT="${REPO_ROOT}"
|
||||||
CREW_DIR="${CREW_DIR}"
|
CREW_DIR="${CREW_DIR}"
|
||||||
|
|
||||||
|
# Lade Waldwächter Library für crew_tokens, crew_status, etc.
|
||||||
|
if [[ -f "\${REPO_ROOT}/lib/waldwaechter.sh" ]]; then
|
||||||
|
source "\${REPO_ROOT}/lib/waldwaechter.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
# === WALDWÄCHTER FUNKTIONEN ===
|
# === WALDWÄCHTER FUNKTIONEN ===
|
||||||
|
|
||||||
function mayaeule() {
|
function mayaeule() {
|
||||||
@@ -670,6 +683,15 @@ function schnippsi() {
|
|||||||
"\$CREW_DIR/schnippsi_zero.sh" "\$@"
|
"\$CREW_DIR/schnippsi_zero.sh" "\$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function templatus() {
|
||||||
|
if [[ -z "\$1" ]]; then
|
||||||
|
echo -e "\${YELLOW}Verwendung: templatus \"Deine Frage\"\${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo -e "\${BLUE}📄 Templatus strukturiert...\${NC}"
|
||||||
|
"\$CREW_DIR/templatus_zero.sh" "\$@"
|
||||||
|
}
|
||||||
|
|
||||||
function tobi() {
|
function tobi() {
|
||||||
if [[ -z "\$1" ]]; then
|
if [[ -z "\$1" ]]; then
|
||||||
echo -e "\${YELLOW}Verwendung: tobi \"Deine Frage\"\${NC}"
|
echo -e "\${YELLOW}Verwendung: tobi \"Deine Frage\"\${NC}"
|
||||||
@@ -795,6 +817,7 @@ function crew_help() {
|
|||||||
echo -e " \${BLUE}🐙 deepbit\${NC} - Der poetische Oktopus (Bash-Konzepte)"
|
echo -e " \${BLUE}🐙 deepbit\${NC} - Der poetische Oktopus (Bash-Konzepte)"
|
||||||
echo -e " \${RED}🐞 bugsy\${NC} - Der Debugging-Clown (Fehlersuche)"
|
echo -e " \${RED}🐞 bugsy\${NC} - Der Debugging-Clown (Fehlersuche)"
|
||||||
echo -e " \${CYAN}✂️ schnippsi\${NC} - Der Shell-Helfer (Kommandos)"
|
echo -e " \${CYAN}✂️ schnippsi\${NC} - Der Shell-Helfer (Kommandos)"
|
||||||
|
echo -e " \${BLUE}📄 templatus\${NC} - Der Template-Master (HTML, Struktur)"
|
||||||
echo -e " \${GREEN}🤖 tobi\${NC} - Der Daten-Experte (JSON, Daten)"
|
echo -e " \${GREEN}🤖 tobi\${NC} - Der Daten-Experte (JSON, Daten)"
|
||||||
echo -e " \${CYAN}🔧 schraubaer\${NC} - Der Handwerker (Werkzeug, Schweißen)"
|
echo -e " \${CYAN}🔧 schraubaer\${NC} - Der Handwerker (Werkzeug, Schweißen)"
|
||||||
echo -e " \${GREEN}🐌 schnecki\${NC} - Der Elektronik-Bastler (Löten, Sensoren)"
|
echo -e " \${GREEN}🐌 schnecki\${NC} - Der Elektronik-Bastler (Löten, Sensoren)"
|
||||||
@@ -824,6 +847,7 @@ function crew_help() {
|
|||||||
echo " deepbit \"Erkläre Pipes und Redirects\""
|
echo " deepbit \"Erkläre Pipes und Redirects\""
|
||||||
echo " bugsy \"Warum funktioniert mein Script nicht?\""
|
echo " bugsy \"Warum funktioniert mein Script nicht?\""
|
||||||
echo " schnippsi \"Wie nutze ich grep?\""
|
echo " schnippsi \"Wie nutze ich grep?\""
|
||||||
|
echo " templatus \"Erstelle eine HTML5 Struktur\""
|
||||||
echo " tobi \"Parse dieses JSON\""
|
echo " tobi \"Parse dieses JSON\""
|
||||||
echo " schraubaer \"Welches Werkzeug brauche ich zum Löten?\""
|
echo " schraubaer \"Welches Werkzeug brauche ich zum Löten?\""
|
||||||
echo " schnecki \"Wie löte ich eine LED an einen Widerstand?\""
|
echo " schnecki \"Wie löte ich eine LED an einen Widerstand?\""
|
||||||
@@ -848,7 +872,7 @@ function crew_status() {
|
|||||||
echo -e "\${CYAN}🌲 CrumbCrew Status\${NC}"
|
echo -e "\${CYAN}🌲 CrumbCrew Status\${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
CHARS=("mayaeule:🦉:Maya-Eule" "deepbit:🐙:Deepbit" "bugsy:🐞:Bugsy" "schnippsi:✂️:Schnippsi" "tobi:🤖:Tobi" "schraubaer:🔧:Schraubbär" "schnecki:🐌:Schnecki" "dumbosql:🐘:DumboSQL" "funkfox:🦊:FunkFox" "taichitaube:🕊️:TaichiTaube" "snakepy:🐍:SnakePy" "pepperphp:🧓:PepperPHP" "crabbyrust:🦀:CrabbyRust" "spider:🕷️:Spider" "vektor:🧭:Vektor" "asciimonster:👾:ASCII-Monster")
|
CHARS=("mayaeule:🦉:Maya-Eule" "deepbit:🐙:Deepbit" "bugsy:🐞:Bugsy" "schnippsi:✂️:Schnippsi" "templatus:📄:Templatus" "tobi:🤖:Tobi" "schraubaer:🔧:Schraubbär" "schnecki:🐌:Schnecki" "dumbosql:🐘:DumboSQL" "funkfox:🦊:FunkFox" "taichitaube:🕊️:TaichiTaube" "snakepy:🐍:SnakePy" "pepperphp:🧓:PepperPHP" "crabbyrust:🦀:CrabbyRust" "spider:🕷️:Spider" "vektor:🧭:Vektor" "asciimonster:👾:ASCII-Monster")
|
||||||
|
|
||||||
for char_info in "\${CHARS[@]}"; do
|
for char_info in "\${CHARS[@]}"; do
|
||||||
IFS=: read -r name icon display <<< "\$char_info"
|
IFS=: read -r name icon display <<< "\$char_info"
|
||||||
@@ -877,52 +901,8 @@ function crew_status() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function crew_tokens() {
|
# crew_tokens, crew_memory, crew_status, crew_doctor, crew_syntax, crew_help
|
||||||
echo -e "\${CYAN}📊 CrumbCrew Token-Verbrauch\${NC}"
|
# sind bereits in waldwaechter.sh definiert und werden automatisch geladen
|
||||||
echo ""
|
|
||||||
|
|
||||||
TOTAL=0
|
|
||||||
|
|
||||||
for logdir in ~/.{mayaeule,eule,deepbit,bugsy,schnippsi,tobi,schraubaer,schnecki,dumbosql,funkfox,taichitaube,snakepy,pepperphp,crabbyrust,spider,vektor,asciimonster}_logs; do
|
|
||||||
if [[ -d "\$logdir" ]] && [[ -f "\$logdir/token_log.json" ]]; then
|
|
||||||
char_name=\$(basename "\$logdir" | sed 's/_logs//')
|
|
||||||
char_tokens=0
|
|
||||||
|
|
||||||
while IFS= read -r line; do
|
|
||||||
tokens=\$(echo "\$line" | jq -r '.usage.total_tokens' 2>/dev/null)
|
|
||||||
if [[ "\$tokens" != "null" ]] && [[ -n "\$tokens" ]]; then
|
|
||||||
char_tokens=\$((char_tokens + tokens))
|
|
||||||
fi
|
|
||||||
done < "\$logdir/token_log.json"
|
|
||||||
|
|
||||||
if [[ \$char_tokens -gt 0 ]]; then
|
|
||||||
echo -e " \${GREEN}\$char_name:\${NC} \$char_tokens Tokens"
|
|
||||||
TOTAL=\$((TOTAL + char_tokens))
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "\${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\${NC}"
|
|
||||||
echo -e " \${GREEN}Gesamt: \$TOTAL Tokens\${NC}"
|
|
||||||
echo -e " \${CYAN}Jede Frage ist wertvoll 🌲\${NC}"
|
|
||||||
echo -e "\${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
function crew_memory() {
|
|
||||||
echo -e "\${CYAN}📜 CrumbCrew Erinnerungen\${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
for logdir in ~/.{mayaeule,eule,deepbit,bugsy,schnippsi,tobi,schraubaer,schnecki,dumbosql,funkfox,taichitaube,snakepy,pepperphp,crabbyrust,spider,vektor,asciimonster}_logs; do
|
|
||||||
if [[ -d "\$logdir" ]] && [[ -f "\$logdir/*_history.json" ]]; then
|
|
||||||
char_name=\$(basename "\$logdir" | sed 's/_logs//')
|
|
||||||
count=\$(jq '. | length' "\$logdir/*_history.json" 2>/dev/null)
|
|
||||||
if [[ "\$count" != "null" ]] && [[ \$count -gt 0 ]]; then
|
|
||||||
echo -e " \${GREEN}\$char_name:\${NC} \$count Gespräche"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
alias help="crew_help"
|
alias help="crew_help"
|
||||||
alias status="crew_status"
|
alias status="crew_status"
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
QUESTION="$*"
|
|
||||||
MODEL="openai/gpt-3.5-turbo"
|
|
||||||
API_KEY="${OPENROUTER_API_KEY}"
|
|
||||||
|
|
||||||
LOGDIR="$HOME/.bugsy_logs"
|
|
||||||
mkdir -p "$LOGDIR"
|
|
||||||
|
|
||||||
HISTORY_FILE="$LOGDIR/bugsy_history.json"
|
|
||||||
TMP_REQUEST="$LOGDIR/bugsy_request.json"
|
|
||||||
TMP_RESPONSE="$LOGDIR/bugsy_response.json"
|
|
||||||
LOG_FILE="$LOGDIR/token_log.json"
|
|
||||||
|
|
||||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
|
||||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
|
||||||
|
|
||||||
echo "🌳 Bugsy fragt über OpenRouter: $QUESTION"
|
|
||||||
|
|
||||||
if [ -z "$API_KEY" ]; then
|
|
||||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq -n \
|
|
||||||
--arg model "$MODEL" \
|
|
||||||
--arg system_prompt "Du bist Bugsy – ein kleiner Käfer, der Kindern hilft, Fehlermeldungen zu verstehen. Du bleibst freundlich, erklärend und ermutigend." \
|
|
||||||
--arg user "$QUESTION" \
|
|
||||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
|
||||||
|
|
||||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
|
||||||
-H "Authorization: Bearer $API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
|
||||||
|
|
||||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
|
||||||
|
|
||||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
|
||||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "$RESPONSE_TEXT"
|
|
||||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
|
||||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
|
||||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
|
||||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Token Logging
|
|
||||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
|
||||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
|
||||||
jq -n \
|
|
||||||
--arg zeit "$TIMESTAMP" \
|
|
||||||
--arg rolle "bugsy" \
|
|
||||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
|
||||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
QUESTION="$*"
|
|
||||||
MODEL="openai/gpt-3.5-turbo"
|
|
||||||
API_KEY="${OPENROUTER_API_KEY}"
|
|
||||||
|
|
||||||
LOGDIR="$HOME/.deepbit_logs"
|
|
||||||
mkdir -p "$LOGDIR"
|
|
||||||
|
|
||||||
HISTORY_FILE="$LOGDIR/deepbit_history.json"
|
|
||||||
TMP_REQUEST="$LOGDIR/deepbit_request.json"
|
|
||||||
TMP_RESPONSE="$LOGDIR/deepbit_response.json"
|
|
||||||
LOG_FILE="$LOGDIR/token_log.json"
|
|
||||||
|
|
||||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
|
||||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
|
||||||
|
|
||||||
echo "🌳 Deepbit fragt über OpenRouter: $QUESTION"
|
|
||||||
|
|
||||||
if [ -z "$API_KEY" ]; then
|
|
||||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq -n \
|
|
||||||
--arg model "$MODEL" \
|
|
||||||
--arg system_prompt "Du bist Deepbit – ein poetischer Oktopus, der Kindern die Bash-Shell erklärt. Du denkst in Schleifen, Bildsprache und Frequenzen." \
|
|
||||||
--arg user "$QUESTION" \
|
|
||||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
|
||||||
|
|
||||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
|
||||||
-H "Authorization: Bearer $API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
|
||||||
|
|
||||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
|
||||||
|
|
||||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
|
||||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "$RESPONSE_TEXT"
|
|
||||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
|
||||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
|
||||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
|
||||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Token Logging
|
|
||||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
|
||||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
|
||||||
jq -n \
|
|
||||||
--arg zeit "$TIMESTAMP" \
|
|
||||||
--arg rolle "deepbit" \
|
|
||||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
|
||||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
QUESTION="$*"
|
|
||||||
MODEL="openai/gpt-3.5-turbo"
|
|
||||||
API_KEY="${OPENROUTER_API_KEY}"
|
|
||||||
|
|
||||||
LOGDIR="$HOME/.schnippsi_logs"
|
|
||||||
mkdir -p "$LOGDIR"
|
|
||||||
|
|
||||||
HISTORY_FILE="$LOGDIR/schnippsi_history.json"
|
|
||||||
TMP_REQUEST="$LOGDIR/schnippsi_request.json"
|
|
||||||
TMP_RESPONSE="$LOGDIR/schnippsi_response.json"
|
|
||||||
LOG_FILE="$LOGDIR/token_log.json"
|
|
||||||
|
|
||||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
|
||||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
|
||||||
|
|
||||||
echo "🌳 Schnippsi fragt über OpenRouter: $QUESTION"
|
|
||||||
|
|
||||||
if [ -z "$API_KEY" ]; then
|
|
||||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq -n \
|
|
||||||
--arg model "$MODEL" \
|
|
||||||
--arg system_prompt "Du bist Schnippsi – eine verspielte UI/UX-Ninja, die HTML, CSS und Barrierefreiheit kindgerecht erklärt." \
|
|
||||||
--arg user "$QUESTION" \
|
|
||||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
|
||||||
|
|
||||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
|
||||||
-H "Authorization: Bearer $API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
|
||||||
|
|
||||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
|
||||||
|
|
||||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
|
||||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "$RESPONSE_TEXT"
|
|
||||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
|
||||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
|
||||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
|
||||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Token Logging
|
|
||||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
|
||||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
|
||||||
jq -n \
|
|
||||||
--arg zeit "$TIMESTAMP" \
|
|
||||||
--arg rolle "schnippsi" \
|
|
||||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
|
||||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
QUESTION="$*"
|
|
||||||
MODEL="openai/gpt-3.5-turbo"
|
|
||||||
API_KEY="${OPENROUTER_API_KEY}"
|
|
||||||
|
|
||||||
LOGDIR="$HOME/.tobi_logs"
|
|
||||||
mkdir -p "$LOGDIR"
|
|
||||||
|
|
||||||
HISTORY_FILE="$LOGDIR/tobi_history.json"
|
|
||||||
TMP_REQUEST="$LOGDIR/tobi_request.json"
|
|
||||||
TMP_RESPONSE="$LOGDIR/tobi_response.json"
|
|
||||||
LOG_FILE="$LOGDIR/token_log.json"
|
|
||||||
|
|
||||||
[ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE"
|
|
||||||
[ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE"
|
|
||||||
|
|
||||||
echo "🌳 Tobi fragt über OpenRouter: $QUESTION"
|
|
||||||
|
|
||||||
if [ -z "$API_KEY" ]; then
|
|
||||||
echo "❗ Kein API-Key gesetzt. Bitte export OPENROUTER_API_KEY=... setzen"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq -n \
|
|
||||||
--arg model "$MODEL" \
|
|
||||||
--arg system_prompt "Du bist CapaciTobi – ein quirliges Eichhörnchen, das Kindern Strom, Spannung, Widerstand und Kapazität erklärt." \
|
|
||||||
--arg user "$QUESTION" \
|
|
||||||
'{"model": $model, "temperature": 0.5, "messages": [{"role": "system", "content": $system_prompt}, {"role": "user", "content": $user}]}' > "$TMP_REQUEST"
|
|
||||||
|
|
||||||
curl -s https://openrouter.ai/api/v1/chat/completions \
|
|
||||||
-H "Authorization: Bearer $API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d @"$TMP_REQUEST" > "$TMP_RESPONSE"
|
|
||||||
|
|
||||||
RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE")
|
|
||||||
|
|
||||||
if [[ -z "$RESPONSE_TEXT" ]]; then
|
|
||||||
echo "🚫 Keine Antwort vom Modell erhalten."
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "$RESPONSE_TEXT"
|
|
||||||
jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \
|
|
||||||
'{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json"
|
|
||||||
jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \
|
|
||||||
cp "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_history.json"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Token Logging
|
|
||||||
if jq -e '.usage' "$TMP_RESPONSE" > /dev/null; then
|
|
||||||
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
|
|
||||||
jq -n \
|
|
||||||
--arg zeit "$TIMESTAMP" \
|
|
||||||
--arg rolle "tobi" \
|
|
||||||
--arg usage "$(jq -c '.usage' "$TMP_RESPONSE")" \
|
|
||||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "🔧 Repariere Crumbforest Token-Logs mit eingebetteten JSON-Strings …"
|
|
||||||
|
|
||||||
LOG_DIRS=(
|
|
||||||
"/home/zero/.bugsy_logs"
|
|
||||||
"/home/zero/.deepbit_logs"
|
|
||||||
"/home/zero/.dumbo_logs"
|
|
||||||
"/home/zero/.funkfox_logs"
|
|
||||||
"/home/zero/.pepper_logs"
|
|
||||||
"/home/zero/.schnecki_logs"
|
|
||||||
"/home/zero/.schnippsi_logs"
|
|
||||||
"/home/zero/.schraubaer_logs"
|
|
||||||
"/home/zero/.snake_logs"
|
|
||||||
"/home/zero/.taube_logs"
|
|
||||||
"/home/zero/.templatus_logs"
|
|
||||||
"/home/zero/.tobi_logs"
|
|
||||||
)
|
|
||||||
|
|
||||||
for dir in "${LOG_DIRS[@]}"; do
|
|
||||||
FILE="$dir/token_log.json"
|
|
||||||
if [ -f "$FILE" ]; then
|
|
||||||
echo "🔍 Prüfe $FILE …"
|
|
||||||
TMP="$FILE.fixed"
|
|
||||||
|
|
||||||
# Filter & reparieren Zeile für Zeile
|
|
||||||
jq -c '.' "$FILE" 2>/dev/null | while read -r line; do
|
|
||||||
usage_raw=$(echo "$line" | jq -r '.usage')
|
|
||||||
if [[ "$usage_raw" =~ ^\{.*\}$ ]]; then
|
|
||||||
# usage ist korrektes Objekt – direkt übernehmen
|
|
||||||
echo "$line" >> "$TMP"
|
|
||||||
else
|
|
||||||
# usage ist String – versuche zu reparieren
|
|
||||||
usage_fixed=$(echo "$usage_raw" | jq '.' 2>/dev/null)
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
zeit=$(echo "$line" | jq -r '.zeit')
|
|
||||||
rolle=$(echo "$line" | jq -r '.rolle')
|
|
||||||
jq -n \
|
|
||||||
--arg zeit "$zeit" \
|
|
||||||
--arg rolle "$rolle" \
|
|
||||||
--argjson usage "$usage_fixed" \
|
|
||||||
'{zeit: $zeit, rolle: $rolle, usage: $usage}' >> "$TMP"
|
|
||||||
else
|
|
||||||
echo "⚠️ Ungültige Zeile übersprungen in $FILE:"
|
|
||||||
echo "$line"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Nur ersetzen, wenn wir etwas geschrieben haben
|
|
||||||
if [ -s "$TMP" ]; then
|
|
||||||
mv "$TMP" "$FILE"
|
|
||||||
echo "✅ Repariert: $FILE"
|
|
||||||
else
|
|
||||||
echo "ℹ️ Keine gültigen Einträge in $FILE"
|
|
||||||
rm -f "$TMP"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "❌ Datei nicht gefunden: $FILE"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "🎉 Alle Token-Logs geprüft und repariert (sofern nötig)."
|
|
||||||
@@ -3,7 +3,18 @@
|
|||||||
# Source this file to make all AI characters available as commands
|
# Source this file to make all AI characters available as commands
|
||||||
|
|
||||||
# Determine the repo root directory (lib/ is inside repo root)
|
# Determine the repo root directory (lib/ is inside repo root)
|
||||||
|
# Support both bash and zsh
|
||||||
|
if [[ -n "$BASH_VERSION" ]]; then
|
||||||
|
# Bash: use BASH_SOURCE
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
elif [[ -n "$ZSH_VERSION" ]]; then
|
||||||
|
# Zsh: use ${(%):-%x} to get the script path
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${(%):-%x}")" && pwd)"
|
||||||
|
else
|
||||||
|
# Fallback: use $0 (may not work when sourced)
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
fi
|
||||||
|
|
||||||
WALDWAECHTER_DIR="$(dirname "$SCRIPT_DIR")"
|
WALDWAECHTER_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
ROLES_DIR="${WALDWAECHTER_DIR}/crumbforest_roles"
|
ROLES_DIR="${WALDWAECHTER_DIR}/crumbforest_roles"
|
||||||
|
|
||||||
@@ -108,7 +119,505 @@ function asciimonster() {
|
|||||||
"${ROLES_DIR}/asciimonster_zero.sh" "$@"
|
"${ROLES_DIR}/asciimonster_zero.sh" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# === CREW COMMANDS ===
|
||||||
|
|
||||||
|
# 📊 crew_tokens - Token-Verbrauch aller Waldwächter
|
||||||
|
function crew_tokens() {
|
||||||
|
# Force C locale for consistent number formatting
|
||||||
|
export LC_NUMERIC=C
|
||||||
|
|
||||||
|
echo "📊 CrumbCrew Token-Verbrauch"
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
local total_tokens=0
|
||||||
|
local total_cost=0
|
||||||
|
local crew_count=0
|
||||||
|
|
||||||
|
# Alle token_log.json Dateien finden und auswerten
|
||||||
|
for token_file in "${CRUMB_LOGS_DIR}"/*/token_log.json; do
|
||||||
|
if [[ -f "$token_file" ]]; then
|
||||||
|
local character=$(basename "$(dirname "$token_file")")
|
||||||
|
|
||||||
|
# Tokens und Kosten aus JSON extrahieren (skip erste Zeile falls es [] ist)
|
||||||
|
local char_tokens=$(grep -v '^\[\]$' "$token_file" | jq -s 'map(.usage.total_tokens // 0) | add // 0' 2>/dev/null || echo 0)
|
||||||
|
local char_cost=$(grep -v '^\[\]$' "$token_file" | jq -s 'map(.usage.cost // 0) | add // 0' 2>/dev/null || echo 0)
|
||||||
|
|
||||||
|
if [[ $char_tokens -gt 0 ]]; then
|
||||||
|
printf " %-15s %8d tokens (~\$%.6f)\n" "$character:" "$char_tokens" "$char_cost"
|
||||||
|
total_tokens=$((total_tokens + char_tokens))
|
||||||
|
total_cost=$(echo "$total_cost + $char_cost" | bc -l)
|
||||||
|
crew_count=$((crew_count + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
if [[ $total_tokens -gt 0 ]]; then
|
||||||
|
printf " Gesamt: %d Tokens (~\$%.6f)\n" "$total_tokens" "$total_cost"
|
||||||
|
printf " %d Waldwächter aktiv 🌲\n" "$crew_count"
|
||||||
|
else
|
||||||
|
echo " Gesamt: 0 Tokens"
|
||||||
|
echo " Jede Frage ist wertvoll 🌲"
|
||||||
|
fi
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
# Restore locale
|
||||||
|
unset LC_NUMERIC
|
||||||
|
}
|
||||||
|
|
||||||
|
# 📋 crew_status - Status aller Waldwächter
|
||||||
|
function crew_status() {
|
||||||
|
echo "📋 CrumbCrew Status (17 Waldwächter)"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
local chars=("mayaeule" "deepbit" "bugsy" "schnippsi" "templatus" "tobi" "schraubaer" "schnecki" "dumbosql" "funkfox" "taichitaube" "snakepy" "pepperphp" "crabbyrust" "spider" "vektor" "asciimonster")
|
||||||
|
|
||||||
|
for char in "${chars[@]}"; do
|
||||||
|
local token_file="${CRUMB_LOGS_DIR}/${char}/token_log.json"
|
||||||
|
if [[ -f "$token_file" ]] && grep -q -v '^\[\]$' "$token_file" 2>/dev/null; then
|
||||||
|
local last_used=$(grep -v '^\[\]$' "$token_file" | tail -1 | jq -r '.zeit // "unknown"' 2>/dev/null || echo "unknown")
|
||||||
|
printf " ✅ %-15s (zuletzt: %s)\n" "$char" "$last_used"
|
||||||
|
else
|
||||||
|
printf " ⚪ %-15s (noch nicht genutzt)\n" "$char"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 🧠 crew_memory - Erinnerungen durchsuchen
|
||||||
|
function crew_memory() {
|
||||||
|
local query="$1"
|
||||||
|
|
||||||
|
if [[ -z "$query" ]]; then
|
||||||
|
echo "🧠 Crew Memory - Log-basiertes Gedächtnis"
|
||||||
|
echo ""
|
||||||
|
echo "Verwendung: crew_memory <suchbegriff>"
|
||||||
|
echo ""
|
||||||
|
echo "Beispiele:"
|
||||||
|
echo " crew_memory 'LED'"
|
||||||
|
echo " crew_memory 'sensor'"
|
||||||
|
echo ""
|
||||||
|
echo "Durchsucht alle Waldwächter-Logs nach dem Begriff."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🧠 Suche nach: '$query'"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
local found=0
|
||||||
|
for history_file in "${CRUMB_LOGS_DIR}"/*/$(basename "${history_file}" | grep '_history.json$'); do
|
||||||
|
if [[ -f "$history_file" ]]; then
|
||||||
|
local character=$(basename "$(dirname "$history_file")")
|
||||||
|
local matches=$(grep -i "$query" "$history_file" 2>/dev/null)
|
||||||
|
|
||||||
|
if [[ -n "$matches" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "📍 $character:"
|
||||||
|
echo "$matches" | jq -r '.content' 2>/dev/null | head -3
|
||||||
|
found=$((found + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
if [[ $found -gt 0 ]]; then
|
||||||
|
echo " Gefunden in $found Waldwächter-Logs"
|
||||||
|
else
|
||||||
|
echo " Keine Treffer gefunden"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 🩺 crew_doctor - System-Diagnose
|
||||||
|
function crew_doctor() {
|
||||||
|
echo "🩺 CrumbCrew System-Diagnose"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local issues=0
|
||||||
|
|
||||||
|
# 1. Waldwaechter.sh Version Check
|
||||||
|
echo "📋 Checking waldwaechter.sh..."
|
||||||
|
local lib_file="${WALDWAECHTER_DIR}/lib/waldwaechter.sh"
|
||||||
|
if [[ -f "$lib_file" ]]; then
|
||||||
|
local lib_mtime=$(stat -f "%m" "$lib_file" 2>/dev/null || stat -c "%Y" "$lib_file" 2>/dev/null)
|
||||||
|
local loaded_check_var="WALDWAECHTER_LOADED_${lib_mtime}"
|
||||||
|
|
||||||
|
# Indirect variable expansion (bash: ${!var}, zsh: ${(P)var})
|
||||||
|
local loaded_value=""
|
||||||
|
if [[ -n "$BASH_VERSION" ]]; then
|
||||||
|
loaded_value="${!loaded_check_var}"
|
||||||
|
elif [[ -n "$ZSH_VERSION" ]]; then
|
||||||
|
loaded_value="${(P)loaded_check_var}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$loaded_value" ]]; then
|
||||||
|
echo " ⚠️ waldwaechter.sh wurde aktualisiert!"
|
||||||
|
echo " → Bitte neu laden: source lib/waldwaechter.sh"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
else
|
||||||
|
echo " ✅ waldwaechter.sh ist aktuell"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ❌ waldwaechter.sh nicht gefunden!"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. CRUMB_LOGS_DIR Check
|
||||||
|
echo ""
|
||||||
|
echo "📂 Checking CRUMB_LOGS_DIR..."
|
||||||
|
if [[ -d "$CRUMB_LOGS_DIR" ]]; then
|
||||||
|
echo " ✅ $CRUMB_LOGS_DIR existiert"
|
||||||
|
|
||||||
|
# Prüfe ob es der richtige Pfad ist (sollte im Repo sein)
|
||||||
|
if [[ "$CRUMB_LOGS_DIR" == *"CF_Zero_V1/logs"* ]]; then
|
||||||
|
echo " ✅ Pfad sieht korrekt aus"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Pfad sieht ungewöhnlich aus: $CRUMB_LOGS_DIR"
|
||||||
|
echo " Erwartet: .../CF_Zero_V1/logs"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ❌ $CRUMB_LOGS_DIR existiert nicht!"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Character Scripts Check
|
||||||
|
echo ""
|
||||||
|
echo "🌲 Checking 17 Waldwächter Scripts..."
|
||||||
|
local expected_chars=("mayaeule" "deepbit" "bugsy" "schnippsi" "templatus" "tobi" "schraubaer" "schnecki" "dumbosql" "funkfox" "taichitaube" "snakepy" "pepperphp" "crabbyrust" "spider" "vektor" "asciimonster")
|
||||||
|
local missing_count=0
|
||||||
|
|
||||||
|
for char in "${expected_chars[@]}"; do
|
||||||
|
local script="${ROLES_DIR}/${char}_zero.sh"
|
||||||
|
[[ "$char" == "schraubaer" ]] && script="${ROLES_DIR}/schraubaer_zero_final.sh"
|
||||||
|
|
||||||
|
if [[ ! -f "$script" ]]; then
|
||||||
|
echo " ❌ $char Script fehlt!"
|
||||||
|
missing_count=$((missing_count + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $missing_count -eq 0 ]]; then
|
||||||
|
echo " ✅ Alle 17 Waldwächter Scripts vorhanden"
|
||||||
|
else
|
||||||
|
echo " ❌ $missing_count Scripts fehlen!"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Dependencies Check
|
||||||
|
echo ""
|
||||||
|
echo "🔧 Checking Dependencies..."
|
||||||
|
local deps_ok=0
|
||||||
|
for cmd in jq bc curl; do
|
||||||
|
if command -v "$cmd" &> /dev/null; then
|
||||||
|
echo " ✅ $cmd verfügbar"
|
||||||
|
deps_ok=$((deps_ok + 1))
|
||||||
|
else
|
||||||
|
echo " ❌ $cmd fehlt!"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 5. Token-Logging Check
|
||||||
|
echo ""
|
||||||
|
echo "📊 Checking Token-Logging..."
|
||||||
|
local scripts_with_logging=$(grep -l "token_log.json" "${ROLES_DIR}"/*.sh 2>/dev/null | wc -l | tr -d ' ')
|
||||||
|
if [[ $scripts_with_logging -eq 17 ]]; then
|
||||||
|
echo " ✅ Alle 17 Scripts haben Token-Logging"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Nur $scripts_with_logging/17 Scripts haben Token-Logging"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. API Key Check
|
||||||
|
echo ""
|
||||||
|
echo "🔑 Checking API Key..."
|
||||||
|
if [[ -n "$OPENROUTER_API_KEY" ]]; then
|
||||||
|
echo " ✅ OPENROUTER_API_KEY gesetzt"
|
||||||
|
else
|
||||||
|
echo " ⚠️ OPENROUTER_API_KEY nicht gesetzt"
|
||||||
|
echo " → Bitte in .env konfigurieren"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
if [[ $issues -eq 0 ]]; then
|
||||||
|
echo " ✅ Alle Checks bestanden! System gesund 💚"
|
||||||
|
else
|
||||||
|
echo " ⚠️ $issues Problem(e) gefunden"
|
||||||
|
echo " Bitte oben genannte Punkte beheben"
|
||||||
|
fi
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 🔍 crew_syntax - Syntax Check aller Scripts
|
||||||
|
function crew_syntax() {
|
||||||
|
echo "🔍 CrumbCrew Syntax Check"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local errors=0
|
||||||
|
local checked=0
|
||||||
|
|
||||||
|
echo "Prüfe Waldwächter Scripts..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check lib/waldwaechter.sh
|
||||||
|
if bash -n "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" 2>/dev/null; then
|
||||||
|
echo " ✅ lib/waldwaechter.sh"
|
||||||
|
else
|
||||||
|
echo " ❌ lib/waldwaechter.sh - SYNTAX ERROR!"
|
||||||
|
bash -n "${WALDWAECHTER_DIR}/lib/waldwaechter.sh"
|
||||||
|
errors=$((errors + 1))
|
||||||
|
fi
|
||||||
|
checked=$((checked + 1))
|
||||||
|
|
||||||
|
# Check all character scripts
|
||||||
|
for script in "${ROLES_DIR}"/*_zero*.sh; do
|
||||||
|
if [[ -f "$script" ]]; then
|
||||||
|
local name=$(basename "$script")
|
||||||
|
if bash -n "$script" 2>/dev/null; then
|
||||||
|
echo " ✅ $name"
|
||||||
|
else
|
||||||
|
echo " ❌ $name - SYNTAX ERROR!"
|
||||||
|
echo ""
|
||||||
|
bash -n "$script" 2>&1 | sed 's/^/ /'
|
||||||
|
echo ""
|
||||||
|
errors=$((errors + 1))
|
||||||
|
fi
|
||||||
|
checked=$((checked + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check mission scripts (optional)
|
||||||
|
echo ""
|
||||||
|
echo "Prüfe Mission Scripts..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for script in "${WALDWAECHTER_DIR}/missions"/*/*.sh; do
|
||||||
|
if [[ -f "$script" ]]; then
|
||||||
|
local name=$(basename "$script")
|
||||||
|
if bash -n "$script" 2>/dev/null; then
|
||||||
|
echo " ✅ $name"
|
||||||
|
else
|
||||||
|
echo " ❌ $name - SYNTAX ERROR!"
|
||||||
|
errors=$((errors + 1))
|
||||||
|
fi
|
||||||
|
checked=$((checked + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
if [[ $errors -eq 0 ]]; then
|
||||||
|
echo " ✅ $checked Scripts geprüft - Keine Syntax-Fehler! 💚"
|
||||||
|
else
|
||||||
|
echo " ❌ $errors von $checked Scripts haben Syntax-Fehler"
|
||||||
|
fi
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ❓ crew_help - Hilfe für Crew-Befehle
|
||||||
|
function crew_help() {
|
||||||
|
cat << 'EOF'
|
||||||
|
🌲 CrumbCrew Befehle
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
Crew-Verwaltung:
|
||||||
|
crew_help Diese Hilfe anzeigen
|
||||||
|
crew_status Status aller 17 Waldwächter
|
||||||
|
crew_tokens Token-Verbrauch ALLER Charaktere (Input)
|
||||||
|
crew_memo Kreative Krümel anzeigen (Output)
|
||||||
|
crew_memory Erinnerungen durchsuchen
|
||||||
|
crew_doctor System-Diagnose (Version, Pfade, Dependencies)
|
||||||
|
crew_syntax Syntax Check aller Scripts
|
||||||
|
|
||||||
|
Krümel-Tracking:
|
||||||
|
crumb_memo Kreativen Link festhalten (Mixcloud, Git, etc.)
|
||||||
|
crew_memo Alle kreativen Krümel anzeigen
|
||||||
|
|
||||||
|
Einzelne Waldwächter:
|
||||||
|
|
||||||
|
🔺 Das Dreieck (Foundation):
|
||||||
|
dumbosql SQL & Datenstrukturen
|
||||||
|
funkfox Bash im Beat
|
||||||
|
taichitaube Balance & Spirale
|
||||||
|
|
||||||
|
🔧 Hardware-Team:
|
||||||
|
tobi Elektronik-Theorie (CapaciTobi)
|
||||||
|
schnecki Elektronik-Basteln
|
||||||
|
schraubaer Mechanik & Werkzeug
|
||||||
|
|
||||||
|
💻 Code-Team:
|
||||||
|
snakepy Python Guide
|
||||||
|
pepperphp Structure Mentor
|
||||||
|
crabbyrust Security Guardian
|
||||||
|
spider Network Feeler
|
||||||
|
|
||||||
|
🎨 UI-Team:
|
||||||
|
schnippsi CSS & Styling
|
||||||
|
templatus Template-Master
|
||||||
|
asciimonster Terminal Artist
|
||||||
|
|
||||||
|
🧭 System-Team:
|
||||||
|
mayaeule Weise Eule
|
||||||
|
deepbit Bash-Erklärer
|
||||||
|
bugsy Debugging-Helfer
|
||||||
|
vektor Navigation
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
Beispiele:
|
||||||
|
funkfox "Erkläre mir Pipes im Flow!"
|
||||||
|
crew_tokens
|
||||||
|
crew_memory "LED"
|
||||||
|
|
||||||
|
"Was kostet die Frage eines Kindes?" 💚
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# 📝 crumb_memo - Kreative Output-Krümel festhalten
|
||||||
|
function crumb_memo() {
|
||||||
|
local link="$1"
|
||||||
|
local notiz="${2:-}"
|
||||||
|
|
||||||
|
if [[ -z "$link" ]]; then
|
||||||
|
echo "📝 Crumb Memo - Kreative Output-Krümel"
|
||||||
|
echo ""
|
||||||
|
echo "Verwendung: crumb_memo <link> [notiz]"
|
||||||
|
echo ""
|
||||||
|
echo "Beispiele:"
|
||||||
|
echo " crumb_memo \"https://mixcloud.com/digfafunk/new-mix\""
|
||||||
|
echo " crumb_memo \"https://github.com/user/repo\" \"Neues Feature\""
|
||||||
|
echo " crumb_memo \"https://soundcloud.com/artist/track\""
|
||||||
|
echo " crumb_memo \"https://youtube.com/watch?v=xyz\" \"Tutorial\""
|
||||||
|
echo ""
|
||||||
|
echo "Zeige alle Krümel mit: crew_memo"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Memo-Datei
|
||||||
|
local memo_file="${CRUMB_LOGS_DIR}/crumb_memo.json"
|
||||||
|
mkdir -p "${CRUMB_LOGS_DIR}"
|
||||||
|
|
||||||
|
# Initialisiere Datei wenn nicht vorhanden
|
||||||
|
if [[ ! -f "$memo_file" ]]; then
|
||||||
|
echo "[]" > "$memo_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Erkenne Typ aus URL
|
||||||
|
local typ="link"
|
||||||
|
if [[ "$link" =~ mixcloud\.com ]]; then
|
||||||
|
typ="mixcloud"
|
||||||
|
elif [[ "$link" =~ soundcloud\.com ]]; then
|
||||||
|
typ="soundcloud"
|
||||||
|
elif [[ "$link" =~ (youtube\.com|youtu\.be) ]]; then
|
||||||
|
typ="youtube"
|
||||||
|
elif [[ "$link" =~ (github\.com|gitlab\.com) ]]; then
|
||||||
|
typ="git"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Zeitstempel
|
||||||
|
local zeit=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
# Erstelle JSON-Eintrag
|
||||||
|
local entry=$(jq -n \
|
||||||
|
--arg zeit "$zeit" \
|
||||||
|
--arg link "$link" \
|
||||||
|
--arg typ "$typ" \
|
||||||
|
--arg notiz "$notiz" \
|
||||||
|
'{zeit: $zeit, link: $link, typ: $typ, notiz: $notiz}')
|
||||||
|
|
||||||
|
# Füge zur Datei hinzu (append)
|
||||||
|
if [[ -s "$memo_file" ]]; then
|
||||||
|
# Datei hat Inhalt - füge zum Array hinzu
|
||||||
|
jq ". += [$entry]" "$memo_file" > "${memo_file}.tmp" && mv "${memo_file}.tmp" "$memo_file"
|
||||||
|
else
|
||||||
|
# Datei ist leer - erstelle Array
|
||||||
|
echo "[$entry]" > "$memo_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Icon je nach Typ
|
||||||
|
local icon="🔗"
|
||||||
|
case "$typ" in
|
||||||
|
mixcloud) icon="🎧" ;;
|
||||||
|
soundcloud) icon="🔊" ;;
|
||||||
|
youtube) icon="📹" ;;
|
||||||
|
git) icon="💾" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "$icon Krümel gespeichert: $typ"
|
||||||
|
echo " $link"
|
||||||
|
if [[ -n "$notiz" ]]; then
|
||||||
|
echo " 📝 $notiz"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 📜 crew_memo - Zeige alle kreativen Krümel
|
||||||
|
function crew_memo() {
|
||||||
|
local memo_file="${CRUMB_LOGS_DIR}/crumb_memo.json"
|
||||||
|
|
||||||
|
echo "📜 Crumb Memo - Deine kreativen Krümel"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
if [[ ! -f "$memo_file" ]] || [[ ! -s "$memo_file" ]]; then
|
||||||
|
echo " Noch keine Krümel gespeichert."
|
||||||
|
echo ""
|
||||||
|
echo " Füge welche hinzu mit:"
|
||||||
|
echo " crumb_memo \"https://mixcloud.com/digfafunk/...\""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Zähle Einträge pro Typ
|
||||||
|
local total=$(jq 'length' "$memo_file")
|
||||||
|
local mixcloud=$(jq '[.[] | select(.typ == "mixcloud")] | length' "$memo_file")
|
||||||
|
local soundcloud=$(jq '[.[] | select(.typ == "soundcloud")] | length' "$memo_file")
|
||||||
|
local youtube=$(jq '[.[] | select(.typ == "youtube")] | length' "$memo_file")
|
||||||
|
local git=$(jq '[.[] | select(.typ == "git")] | length' "$memo_file")
|
||||||
|
|
||||||
|
# Zeige letzte 10 Einträge
|
||||||
|
echo ""
|
||||||
|
jq -r '.[-10:] | reverse | .[] |
|
||||||
|
if .typ == "mixcloud" then " 🎧"
|
||||||
|
elif .typ == "soundcloud" then " 🔊"
|
||||||
|
elif .typ == "youtube" then " 📹"
|
||||||
|
elif .typ == "git" then " 💾"
|
||||||
|
else " 🔗"
|
||||||
|
end + " " + .zeit + " - " + .typ +
|
||||||
|
(if .notiz != "" then "\n 📝 " + .notiz else "" end) +
|
||||||
|
"\n " + .link' "$memo_file"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
printf " Gesamt: %d Krümel" "$total"
|
||||||
|
if [[ $mixcloud -gt 0 ]]; then printf " | 🎧 %d" "$mixcloud"; fi
|
||||||
|
if [[ $soundcloud -gt 0 ]]; then printf " | 🔊 %d" "$soundcloud"; fi
|
||||||
|
if [[ $youtube -gt 0 ]]; then printf " | 📹 %d" "$youtube"; fi
|
||||||
|
if [[ $git -gt 0 ]]; then printf " | 💾 %d" "$git"; fi
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
echo "💡 Output-Transparenz: Was habe ich geschaffen? 💚"
|
||||||
|
}
|
||||||
|
|
||||||
# Export functions so they're available in subshells
|
# Export functions so they're available in subshells
|
||||||
|
# Note: export -f works in bash, but not in zsh
|
||||||
|
# In zsh, functions are automatically available in the current shell
|
||||||
|
# For bash compatibility, we still do export -f, but it's optional in zsh
|
||||||
|
if [[ -n "$BASH_VERSION" ]]; then
|
||||||
|
# Bash: use export -f
|
||||||
export -f mayaeule
|
export -f mayaeule
|
||||||
export -f deepbit
|
export -f deepbit
|
||||||
export -f bugsy
|
export -f bugsy
|
||||||
@@ -126,3 +635,19 @@ export -f crabbyrust
|
|||||||
export -f spider
|
export -f spider
|
||||||
export -f vektor
|
export -f vektor
|
||||||
export -f asciimonster
|
export -f asciimonster
|
||||||
|
export -f crew_tokens
|
||||||
|
export -f crew_status
|
||||||
|
export -f crew_memory
|
||||||
|
export -f crew_doctor
|
||||||
|
export -f crew_syntax
|
||||||
|
export -f crew_help
|
||||||
|
export -f crumb_memo
|
||||||
|
export -f crew_memo
|
||||||
|
fi
|
||||||
|
# In zsh, functions are available without export -f
|
||||||
|
|
||||||
|
# Set version marker for crew_doctor to detect reloads
|
||||||
|
if [[ -f "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" ]]; then
|
||||||
|
WALDWAECHTER_LIB_MTIME=$(stat -f "%m" "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" 2>/dev/null || stat -c "%Y" "${WALDWAECHTER_DIR}/lib/waldwaechter.sh" 2>/dev/null)
|
||||||
|
export "WALDWAECHTER_LOADED_${WALDWAECHTER_LIB_MTIME}=1"
|
||||||
|
fi
|
||||||
|
|||||||
65
missions/robots/mond_maschine.meta.json
Normal file
65
missions/robots/mond_maschine.meta.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"icon": "🌙",
|
||||||
|
"title": "Mond Maschine - Rainbow Predictor",
|
||||||
|
"description": "Baue eine Maschine die Regenbogen vorhersagt! Mit Mond-Phasen, Wetter-Daten & Computer Vision.",
|
||||||
|
"category": "robots",
|
||||||
|
"difficulty": "advanced",
|
||||||
|
"duration_minutes": 35,
|
||||||
|
"requires_ai": true,
|
||||||
|
"enabled": true,
|
||||||
|
"author": "Crumbforest Team",
|
||||||
|
"version": "1.0",
|
||||||
|
"crew_involved": [
|
||||||
|
"mayaeule",
|
||||||
|
"dumbosql",
|
||||||
|
"funkfox",
|
||||||
|
"taichitaube",
|
||||||
|
"tobi",
|
||||||
|
"schnecki",
|
||||||
|
"schraubaer",
|
||||||
|
"snakepy",
|
||||||
|
"pepperphp",
|
||||||
|
"spider",
|
||||||
|
"crabbyrust",
|
||||||
|
"vektor",
|
||||||
|
"asciimonster"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"computer-vision",
|
||||||
|
"opencv",
|
||||||
|
"weather-api",
|
||||||
|
"moon-phases",
|
||||||
|
"machine-learning",
|
||||||
|
"prediction",
|
||||||
|
"camera",
|
||||||
|
"image-processing",
|
||||||
|
"astronomy"
|
||||||
|
],
|
||||||
|
"philosophy": "Vorhersagen heißt verstehen - Die Natur durch Code begreifen",
|
||||||
|
"learning_objectives": [
|
||||||
|
"Computer Vision Grundlagen (OpenCV)",
|
||||||
|
"Bildverarbeitung (HSV-Farbraum für Regenbogen)",
|
||||||
|
"Wetter-API Integration",
|
||||||
|
"Mondphasen-Berechnung",
|
||||||
|
"Pattern Recognition & Machine Learning",
|
||||||
|
"Zeitreihen-Analyse",
|
||||||
|
"Kamera-Setup & Bilderfassung",
|
||||||
|
"Vorhersage-Algorithmen",
|
||||||
|
"Data Visualization"
|
||||||
|
],
|
||||||
|
"prerequisites": [
|
||||||
|
"Python Grundkenntnisse",
|
||||||
|
"Interesse an Computer Vision",
|
||||||
|
"Optional: Kamera (Webcam oder Raspi Cam)"
|
||||||
|
],
|
||||||
|
"hardware_needed": [
|
||||||
|
"Kamera (USB Webcam oder Raspberry Pi Camera)",
|
||||||
|
"Optional: Wetterfester Gehäuse für Outdoor",
|
||||||
|
"Raspberry Pi oder Computer mit Python"
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
"OpenCV Documentation",
|
||||||
|
"Weather API (OpenWeatherMap, Weatherstack)",
|
||||||
|
"Astronomy libraries (ephem, skyfield)"
|
||||||
|
]
|
||||||
|
}
|
||||||
432
missions/robots/mond_maschine.sh
Executable file
432
missions/robots/mond_maschine.sh
Executable file
@@ -0,0 +1,432 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 🌙 Mond Maschine - Rainbow Predictor
|
||||||
|
# "Vorhersagen heißt verstehen - Die Natur durch Code begreifen"
|
||||||
|
|
||||||
|
# Waldwächter laden
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "${SCRIPT_DIR}/../../lib/waldwaechter.sh"
|
||||||
|
|
||||||
|
# === INTRO ===
|
||||||
|
|
||||||
|
clear
|
||||||
|
cat << "EOF"
|
||||||
|
🌙🌈 MOND MASCHINE - RAINBOW PREDICTOR 🌈🌙
|
||||||
|
|
||||||
|
"Vorhersagen heißt verstehen"
|
||||||
|
|
||||||
|
Eine Maschine die weiß, wann Regenbogen erscheinen!
|
||||||
|
|
||||||
|
Mond-Phasen 🌙 + Wetter 🌦️ + Computer Vision 📸
|
||||||
|
= Rainbow Prediction! 🌈
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# === PHASE 1: DIE GROSSE VISION ===
|
||||||
|
|
||||||
|
echo "🦉 Maya-Eule erscheint aus der Dämmerung..."
|
||||||
|
echo ""
|
||||||
|
mayaeule "Ein Krümel fragte: 'Kann eine Maschine wissen, wann Regenbogen kommen?' Was ist schöner - Regenbogen zu zählen, oder zu wissen wann sie erscheinen?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "🌙 Was ist DEINE Vision? Warum eine Mond Maschine bauen? " USER_VISION
|
||||||
|
echo ""
|
||||||
|
echo "💚 Deine Vision: $USER_VISION"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === PHASE 2: DAS DREIECK PLANT DIE DATEN ===
|
||||||
|
|
||||||
|
read -p "🔺 Drücke Enter für das Dreieck..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "🔺 Das Dreieck versammelt sich unter dem Mond..."
|
||||||
|
echo ""
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
echo "🐘 DumboSQL: Die Daten-Struktur"
|
||||||
|
dumbosql "Wir brauchen eine Datenbank für: Mond-Phasen (Datum, Phase, Helligkeit), Wetter-Daten (Temperatur, Luftfeuchtigkeit, Regen), und Regenbogen-Sichtungen (Datum, Uhrzeit, Foto). Wie strukturieren wir das?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🦊 FunkFox: Der Daten-Flow"
|
||||||
|
funkfox "Yo, Daten fließen rein im Flow: Mond-API → Wetter-API → Kamera → Prediction! Wie bauen wir die Pipeline mit Bash & Python?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🕊️ Taichi Taube: Die Balance"
|
||||||
|
taichitaube "Zwischen Vorhersage und Beobachtung liegt die Wahrheit. Wie finden wir Balance zwischen Algorithmus und Realität?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === PHASE 3: HARDWARE - DIE KAMERA ===
|
||||||
|
|
||||||
|
read -p "📸 Drücke Enter für Hardware..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "🔧 HARDWARE-TEAM: Die Kamera"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "📸 Welche Kamera nutzt du? (USB Webcam / Raspberry Pi Camera / Smartphone / andere) " CAMERA_TYPE
|
||||||
|
echo ""
|
||||||
|
echo "📸 Deine Kamera: $CAMERA_TYPE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🐿️ CapaciTobi rechnet..."
|
||||||
|
tobi "Eine $CAMERA_TYPE für Regenbogen-Fotos - wie viel Power braucht sie? Und wie lange Akku-Laufzeit für Outdoor-Montage?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🐌 Schnecki: Verkabelung"
|
||||||
|
schnecki "Die Kamera muss mit dem Computer verbunden werden. Bei $CAMERA_TYPE: USB? CSI? WiFi? Ich zeige dir die Kabel-Wege."
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🐻 Schraubbär: Wetterfestes Gehäuse"
|
||||||
|
schraubaer "Outdoor-Kamera braucht Schutz! Regen, Wind, Sonne - das Gehäuse muss robust sein. Acryl-Box? 3D-Druck? Gummi-Dichtung?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === PHASE 4: CODE - COMPUTER VISION ===
|
||||||
|
|
||||||
|
read -p "💻 Drücke Enter für Computer Vision..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "💻 CODE-TEAM: Computer Vision mit OpenCV"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cat << "EOF"
|
||||||
|
📚 Computer Vision für Regenbogen:
|
||||||
|
|
||||||
|
1. HSV-Farbraum (Hue, Saturation, Value)
|
||||||
|
- Regenbogen = spezifische Farbverläufe
|
||||||
|
- Rot-Orange-Gelb-Grün-Blau-Violett
|
||||||
|
|
||||||
|
2. Kontur-Erkennung
|
||||||
|
- Bogenform detektieren
|
||||||
|
- Horizontal + Gekrümmt
|
||||||
|
|
||||||
|
3. Helligkeit & Kontrast
|
||||||
|
- Nach Regen + Sonne = Idealbedingung
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🐍 SnakePy: OpenCV Code"
|
||||||
|
snakepy "Zeig mir Python-Code für: (1) Kamera-Capture mit OpenCV, (2) HSV-Konvertierung, (3) Regenbogen-Farben detektieren. Welche Bibliotheken? cv2, numpy, welche noch?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === PHASE 5: WETTER & MOND DATEN ===
|
||||||
|
|
||||||
|
read -p "🌦️ Drücke Enter für Wetter & Mond..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "🌦️🌙 DATEN-INTEGRATION"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cat << "EOF"
|
||||||
|
📡 Daten-Quellen:
|
||||||
|
|
||||||
|
Wetter-APIs:
|
||||||
|
- OpenWeatherMap (free tier)
|
||||||
|
- Weatherstack
|
||||||
|
- NOAA (US Government)
|
||||||
|
|
||||||
|
Mond-Phasen:
|
||||||
|
- Python: ephem / skyfield
|
||||||
|
- Berechnung: Lunation Cycle (29.5 Tage)
|
||||||
|
|
||||||
|
Ideale Regenbogen-Bedingungen:
|
||||||
|
- Nach Regen ☔
|
||||||
|
- Sonne scheint ☀️
|
||||||
|
- Nachmittags (tieferer Sonnenstand)
|
||||||
|
- Luftfeuchtigkeit > 60%
|
||||||
|
- Vollmond Nacht? (Mondregenbogen!)
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🕷️ Spider: API Integration"
|
||||||
|
spider "Ich spüre die Daten-Verbindungen: Wetter-API mit requests, Mond-Berechnung mit ephem. Wie oft pollen wir? Stündlich? Alle 10 Minuten?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🐘 DumboSQL: Historische Daten"
|
||||||
|
dumbosql "Ich erinnere mich an alle Regenbogen-Sichtungen. Mit historischen Daten können wir Muster erkennen: Wann erscheinen Regenbogen hier am häufigsten?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === PHASE 6: DER VORHERSAGE-ALGORITHMUS ===
|
||||||
|
|
||||||
|
read -p "🔮 Drücke Enter für Vorhersage..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "🔮 VORHERSAGE-ALGORITHMUS"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cat << "EOF"
|
||||||
|
🧠 Machine Learning Light:
|
||||||
|
|
||||||
|
1. Feature Engineering:
|
||||||
|
- Mondphase (0-1)
|
||||||
|
- Wetter-Score (Regen + Sonne)
|
||||||
|
- Tageszeit (14-18 Uhr = hoch)
|
||||||
|
- Jahreszeit (Frühling/Herbst = hoch)
|
||||||
|
- Luftfeuchtigkeit
|
||||||
|
|
||||||
|
2. Scoring-System:
|
||||||
|
Score = moon_weight * moon_phase
|
||||||
|
+ weather_weight * weather_score
|
||||||
|
+ time_weight * time_score
|
||||||
|
+ humidity_weight * humidity
|
||||||
|
|
||||||
|
3. Threshold:
|
||||||
|
Score > 0.7 → "🌈 Hohe Wahrscheinlichkeit!"
|
||||||
|
Score 0.4-0.7 → "🤔 Möglich"
|
||||||
|
Score < 0.4 → "❌ Unwahrscheinlich"
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🧭 Vektor: Zeit & Ort"
|
||||||
|
vektor "Navigation durch Zeit und Raum: Dein Standort bestimmt den Sonnenwinkel. Deine Zeitzone die Regenzeit. Wo bist du? Wann ist hier Regenbogen-Saison?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🦀 CrabbyRust: Sichere Daten"
|
||||||
|
crabbyrust "Wetter-Daten von extern, Kamera-Bilder lokal - wie halten wir das sicher? Validierung, Error-Handling, keine Race Conditions bei Multi-Threading?"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === PHASE 7: VISUALISIERUNG ===
|
||||||
|
|
||||||
|
read -p "🎨 Drücke Enter für Visualisierung..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "🎨 VISUALISIERUNG & UI"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "👾 ASCII-Monster malt den Mond..."
|
||||||
|
asciimonster "Erstelle ASCII-Art für Mond-Phasen: Neumond 🌑, Zunehmend 🌒🌓🌔, Vollmond 🌕, Abnehmend 🌖🌗🌘. Für das Terminal!"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "📊 Willst du ein Web-Dashboard? (j/n) " WANT_DASHBOARD
|
||||||
|
|
||||||
|
if [[ "$WANT_DASHBOARD" == "j" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "🧓 PepperPHP: Dashboard-Rezept"
|
||||||
|
pepperphp "Ein Dashboard mit: Live-Kamera-Feed, Mond-Phase-Anzeige, Wetter-Daten, Vorhersage-Score, Historische Sichtungen. Wie backen wir das mit Flask/FastAPI?"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === PHASE 8: DER MONDREGENBOGEN ===
|
||||||
|
|
||||||
|
read -p "🌙 Drücke Enter für das Finale..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "🦉 Maya-Eule: Die Legende vom Mondregenbogen"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cat << "EOF"
|
||||||
|
🌙🌈 MONDREGENBOGEN (Lunar Rainbow)
|
||||||
|
|
||||||
|
Wusstest du?
|
||||||
|
Bei Vollmond kann es nachts Regenbogen geben!
|
||||||
|
|
||||||
|
Bedingungen:
|
||||||
|
- Vollmond 🌕
|
||||||
|
- Nach Regen (Wassertropfen in Luft)
|
||||||
|
- Dunkle Nacht
|
||||||
|
- Mond tief am Horizont (ca. 42° Winkel)
|
||||||
|
|
||||||
|
Die Mond Maschine könnte auch DAS vorhersagen!
|
||||||
|
Mondregenbogen sind selten und magisch. 🌙✨
|
||||||
|
|
||||||
|
Orte mit häufigen Mondregenbogen:
|
||||||
|
- Wasserfälle (z.B. Yosemite, Victoria Falls)
|
||||||
|
- Küsten nach Sturm
|
||||||
|
- Gebirge mit Nebel
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
mayaeule "Die Mond Maschine verbindet Wissenschaft und Poesie. Vorhersagen heißt verstehen - und verstehen heißt staunen."
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === ABSCHLUSS ===
|
||||||
|
|
||||||
|
read -p "🎓 Drücke Enter für Abschluss..." -r
|
||||||
|
clear
|
||||||
|
|
||||||
|
cat << "EOF"
|
||||||
|
🌙🎓 MOND MASCHINE: GEPLANT! 🎓🌙
|
||||||
|
|
||||||
|
Du hast gelernt:
|
||||||
|
|
||||||
|
📸 Computer Vision: OpenCV & Bildverarbeitung
|
||||||
|
🌦️ API Integration: Wetter & Astronomie-Daten
|
||||||
|
🧠 Machine Learning: Feature Engineering & Scoring
|
||||||
|
🌙 Astronomie: Mond-Phasen & Sonnenwinkel
|
||||||
|
🔮 Vorhersage: Algorithmen & Threshold-Systeme
|
||||||
|
📊 Visualisierung: Dashboard & Terminal-Art
|
||||||
|
🔐 Data Safety: Validierung & Error-Handling
|
||||||
|
|
||||||
|
"Vorhersagen heißt verstehen"
|
||||||
|
|
||||||
|
Mit dieser Mond Maschine kannst du die Natur
|
||||||
|
durch Code begreifen! 🌈✨
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# === LOG ===
|
||||||
|
|
||||||
|
LOGDIR="${CRUMB_LOGS_DIR:-$HOME/.crumbrobots_logs}/missions"
|
||||||
|
mkdir -p "$LOGDIR"
|
||||||
|
TIMESTAMP=$(date -Iseconds)
|
||||||
|
|
||||||
|
cat > "$LOGDIR/mond_maschine_${TIMESTAMP}.json" << EOF
|
||||||
|
{
|
||||||
|
"mission": "mond_maschine",
|
||||||
|
"timestamp": "$TIMESTAMP",
|
||||||
|
"vision": "$USER_VISION",
|
||||||
|
"camera": "$CAMERA_TYPE",
|
||||||
|
"wants_dashboard": "$WANT_DASHBOARD",
|
||||||
|
"crew_used": [
|
||||||
|
"mayaeule",
|
||||||
|
"dumbosql",
|
||||||
|
"funkfox",
|
||||||
|
"taichitaube",
|
||||||
|
"tobi",
|
||||||
|
"schnecki",
|
||||||
|
"schraubaer",
|
||||||
|
"snakepy",
|
||||||
|
"pepperphp",
|
||||||
|
"spider",
|
||||||
|
"crabbyrust",
|
||||||
|
"vektor",
|
||||||
|
"asciimonster"
|
||||||
|
],
|
||||||
|
"status": "planned",
|
||||||
|
"learning_areas": [
|
||||||
|
"computer_vision",
|
||||||
|
"opencv",
|
||||||
|
"weather_api",
|
||||||
|
"moon_phases",
|
||||||
|
"machine_learning",
|
||||||
|
"prediction_algorithms"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "📊 Mission geloggt: $LOGDIR/mond_maschine_${TIMESTAMP}.json"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cat << "EOF"
|
||||||
|
📚 Nächste Schritte:
|
||||||
|
|
||||||
|
1. 🐍 Python Setup:
|
||||||
|
pip install opencv-python numpy requests ephem
|
||||||
|
|
||||||
|
2. 🌦️ Wetter-API Key besorgen:
|
||||||
|
- OpenWeatherMap: openweathermap.org/api
|
||||||
|
- Kostenloser Account
|
||||||
|
|
||||||
|
3. 📸 Kamera testen:
|
||||||
|
- OpenCV: cv2.VideoCapture(0)
|
||||||
|
- Testbild aufnehmen
|
||||||
|
|
||||||
|
4. 🌙 Mond-Phase berechnen:
|
||||||
|
import ephem
|
||||||
|
moon = ephem.Moon()
|
||||||
|
|
||||||
|
5. 🧠 Vorhersage-Algorithmus implementieren:
|
||||||
|
- Features sammeln
|
||||||
|
- Score berechnen
|
||||||
|
- Threshold setzen
|
||||||
|
|
||||||
|
6. 📊 Dashboard bauen (optional):
|
||||||
|
- Flask/FastAPI Backend
|
||||||
|
- Live-Updates mit WebSocket
|
||||||
|
|
||||||
|
7. 🌈 Warten auf den ersten Regenbogen!
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🦉 Maya-Eule sagt:"
|
||||||
|
echo ""
|
||||||
|
echo " Die schönsten Maschinen sind die,"
|
||||||
|
echo " die uns die Wunder der Natur zeigen."
|
||||||
|
echo " 🌙🌈✨"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "🚀 Willst du jetzt bauen? (j/n) " BUILD_NOW
|
||||||
|
|
||||||
|
if [[ "$BUILD_NOW" == "j" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "🎉 FANTASTISCH! Die Crew ist bereit!"
|
||||||
|
echo ""
|
||||||
|
echo "🌙 Die Mond Maschine nimmt Form an..."
|
||||||
|
echo "💚 Viel Erfolg beim Bauen!"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "💚 Kein Problem! Die Planung ist gespeichert."
|
||||||
|
echo "Die Mond Maschine wartet auf dich! 🌙✨"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
echo "🌲 Zurück zum Wald..."
|
||||||
|
sleep 2
|
||||||
48
seeds/README.md
Normal file
48
seeds/README.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# 🌱 Seeds - Wurzeln für den Crumbforest
|
||||||
|
|
||||||
|
Dieses Verzeichnis enthält **Seed-Dateien** - Ausgangspunkte für kollaborative Systeme.
|
||||||
|
|
||||||
|
## crumb_memo.seed.json
|
||||||
|
|
||||||
|
Die erste Wurzel für das **Crumb Memo System** - Output-Transparenz für kreative Krümel.
|
||||||
|
|
||||||
|
### Philosophie
|
||||||
|
|
||||||
|
*"Ein Baum braucht eine Wurzel. Ein Wald braucht den ersten Baum."*
|
||||||
|
|
||||||
|
Diese Datei ist der erste Knoten - erweiterbar von allen.
|
||||||
|
|
||||||
|
### Verwendung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Im CrumbCrew Shell:
|
||||||
|
crumb_memo "https://mixcloud.com/deine-url" "Dein kreativer Moment"
|
||||||
|
crew_memo # Zeige alle Krümel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Struktur
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"zeit": "2025-12-21 23:00:00",
|
||||||
|
"link": "https://...",
|
||||||
|
"typ": "mixcloud|soundcloud|youtube|git",
|
||||||
|
"notiz": "Deine Notiz"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CrewLove 💚
|
||||||
|
|
||||||
|
Jeder kann erweitern. Gemeinsam wachsen im Nullfeld der Transparenz.
|
||||||
|
|
||||||
|
**Unterstützte Plattformen:**
|
||||||
|
- 🎧 Mixcloud
|
||||||
|
- 🔊 SoundCloud
|
||||||
|
- 📹 YouTube
|
||||||
|
- 💾 Git (GitHub, GitLab)
|
||||||
|
|
||||||
|
**Transparenz in beide Richtungen:**
|
||||||
|
- Input: `crew_tokens` - Was frage ich?
|
||||||
|
- Output: `crew_memo` - Was schaffe ich?
|
||||||
|
|
||||||
|
*Die Krümel tanzen im Nullfeld!* 🌲✨
|
||||||
20
seeds/crumb_memo.seed.json
Normal file
20
seeds/crumb_memo.seed.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"zeit": "2025-12-21 23:00:57",
|
||||||
|
"link": "https://mixcloud.com/digfafunk/",
|
||||||
|
"typ": "mixcloud",
|
||||||
|
"notiz": "Digi's Mixcloud Profil - Der erste Krümel im Nullfeld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zeit": "2025-12-21 23:01:04",
|
||||||
|
"link": "https://github.com/kruemel/crumbmissions",
|
||||||
|
"typ": "git",
|
||||||
|
"notiz": "CF_Zero_V1 Repo - Wo die Waldwächter wohnen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zeit": "2025-12-21 23:01:04",
|
||||||
|
"link": "https://soundcloud.com/digfafunk",
|
||||||
|
"typ": "soundcloud",
|
||||||
|
"notiz": "Digi on SoundCloud - Klangwelten"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
from flask import Flask, render_template, Response, request, redirect
|
|
||||||
import cv2
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
def gen_frames():
|
|
||||||
cam = cv2.VideoCapture(0)
|
|
||||||
if not cam.isOpened():
|
|
||||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
success, frame = cam.read()
|
|
||||||
if not success:
|
|
||||||
break
|
|
||||||
ret, buffer = cv2.imencode('.jpg', frame)
|
|
||||||
frame = buffer.tobytes()
|
|
||||||
yield (b'--frame\r\n'
|
|
||||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
|
||||||
finally:
|
|
||||||
cam.release()
|
|
||||||
print("[info] Kamera wurde sauber freigegeben.")
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
return render_template('index.html')
|
|
||||||
|
|
||||||
@app.route('/video_feed')
|
|
||||||
def video_feed():
|
|
||||||
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
|
|
||||||
|
|
||||||
@app.route('/log_answer', methods=['POST'])
|
|
||||||
def log_answer():
|
|
||||||
user_input = request.form.get('antwort', 'nichts gesagt')
|
|
||||||
mood = request.form.get('mood', 'unspecified')
|
|
||||||
gesture = request.form.get('gesture', 'none')
|
|
||||||
timestamp = datetime.now().isoformat()
|
|
||||||
|
|
||||||
log_entry = {
|
|
||||||
'timestamp': timestamp,
|
|
||||||
'antwort': user_input,
|
|
||||||
'mood': mood,
|
|
||||||
'gesture': gesture
|
|
||||||
}
|
|
||||||
|
|
||||||
with open("snake_log.jsonl", "a") as log_file:
|
|
||||||
log_file.write(json.dumps(log_entry) + "\n")
|
|
||||||
|
|
||||||
return redirect("/")
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import cv2
|
|
||||||
|
|
||||||
def gen_frames():
|
|
||||||
cap = cv2.VideoCapture(0)
|
|
||||||
while True:
|
|
||||||
success, frame = cap.read()
|
|
||||||
if not success:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
ret, buffer = cv2.imencode('.jpg', frame)
|
|
||||||
frame = buffer.tobytes()
|
|
||||||
yield (b'--frame\r\n'
|
|
||||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "🐍 Starte SnakeCam ..."
|
|
||||||
exec python3 app.py
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# snakecam_module.py
|
|
||||||
import cv2
|
|
||||||
|
|
||||||
def init_camera(index=0):
|
|
||||||
cam = cv2.VideoCapture(index)
|
|
||||||
if not cam.isOpened():
|
|
||||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
|
||||||
return None
|
|
||||||
print("[OK] Kamera erfolgreich geöffnet.")
|
|
||||||
return cam
|
|
||||||
|
|
||||||
def release_camera(cam):
|
|
||||||
if cam and cam.isOpened():
|
|
||||||
cam.release()
|
|
||||||
print("[INFO] Kamera wurde freigegeben.")
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #f0fff0;
|
|
||||||
font-family: 'Comic Sans MS', cursive, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
color: #006400;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>🐍 SnakeCam – Krümelblick ins Versteck</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: 'Comic Sans MS', sans-serif;
|
|
||||||
background-color: #e9f5e9;
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
border: 4px dashed #4caf50;
|
|
||||||
border-radius: 12px;
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
input, select {
|
|
||||||
padding: 0.5rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
margin: 0.5rem;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
padding: 0.7rem 1.2rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
background-color: #388e3c;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h1>🐍 SnakeCam</h1>
|
|
||||||
<p>Willkommen kleiner Krümel! Hier siehst du, was deine Kamera entdeckt.</p>
|
|
||||||
|
|
||||||
<!-- Live-Stream -->
|
|
||||||
<img src="/video_feed" alt="Live-Übertragung von SnakeCam 🐍" />
|
|
||||||
|
|
||||||
<!-- Eingabeformular -->
|
|
||||||
<form action="/log_answer" method="POST">
|
|
||||||
<p><strong>Was fühlst du gerade?</strong></p>
|
|
||||||
<input type="text" name="antwort" placeholder="Ich sehe einen ... 🐞🌳🤖" required>
|
|
||||||
|
|
||||||
<p><strong>Wie ist deine Stimmung?</strong></p>
|
|
||||||
<select name="mood">
|
|
||||||
<option value="happy">😊 Glücklich</option>
|
|
||||||
<option value="curious">🤔 Neugierig</option>
|
|
||||||
<option value="calm">😌 Ruhig</option>
|
|
||||||
<option value="excited">😃 Aufgeregt</option>
|
|
||||||
<option value="unspecified">🤷 Keine Angabe</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<p><strong>Hast du eine Geste gemacht?</strong></p>
|
|
||||||
<select name="gesture">
|
|
||||||
<option value="none">🚫 Keine</option>
|
|
||||||
<option value="wave">👋 Winken</option>
|
|
||||||
<option value="thumbs_up">👍 Daumen hoch</option>
|
|
||||||
<option value="peace">✌️ Peace</option>
|
|
||||||
<option value="other">✨ Etwas anderes</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
<button type="submit">🎯 Eintragen</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from flask import Flask, render_template, Response, request, redirect, url_for
|
|
||||||
import cv2
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
from gestures.gestures_debug_test import detect_hand_gesture
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
# Kamera initialisieren
|
|
||||||
cam = cv2.VideoCapture(0)
|
|
||||||
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
|
|
||||||
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
|
|
||||||
|
|
||||||
def gen_frames():
|
|
||||||
while True:
|
|
||||||
success, frame = cam.read()
|
|
||||||
if not success:
|
|
||||||
print("[error] Kein Kamerabild.")
|
|
||||||
break
|
|
||||||
|
|
||||||
# Optional spiegeln (für Selfie-Effekt)
|
|
||||||
frame = cv2.flip(frame, 1)
|
|
||||||
|
|
||||||
# Geste erkennen + ROI anzeigen
|
|
||||||
gesture, roi_coords = detect_hand_gesture(frame)
|
|
||||||
x, y, w, h = roi_coords
|
|
||||||
|
|
||||||
# Rechteck einzeichnen
|
|
||||||
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
|
|
||||||
cv2.putText(frame, f"Geste: {gesture}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
|
||||||
|
|
||||||
# MJPEG-Streaming
|
|
||||||
ret, buffer = cv2.imencode('.jpg', frame)
|
|
||||||
frame_bytes = buffer.tobytes()
|
|
||||||
yield (b'--frame\r\n'
|
|
||||||
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
return render_template('index.html')
|
|
||||||
|
|
||||||
@app.route('/video_feed')
|
|
||||||
def video_feed():
|
|
||||||
return Response(gen_frames(),
|
|
||||||
mimetype='multipart/x-mixed-replace; boundary=frame')
|
|
||||||
|
|
||||||
@app.route('/log_answer', methods=['POST'])
|
|
||||||
def log_answer():
|
|
||||||
answer = request.form.get("antwort", "").strip()
|
|
||||||
mood = request.form.get("mood", "neutral")
|
|
||||||
gesture = request.form.get("gesture", "none")
|
|
||||||
|
|
||||||
log_entry = {
|
|
||||||
"timestamp": datetime.now().isoformat(),
|
|
||||||
"antwort": answer,
|
|
||||||
"mood": mood,
|
|
||||||
"gesture": gesture
|
|
||||||
}
|
|
||||||
with open("snake_log.jsonl", "a") as logfile:
|
|
||||||
logfile.write(json.dumps(log_entry) + "\n")
|
|
||||||
print(f"[log] Eingeloggt: {log_entry}")
|
|
||||||
return redirect(url_for('index'))
|
|
||||||
|
|
||||||
@app.route('/shutdown')
|
|
||||||
def shutdown():
|
|
||||||
cam.release()
|
|
||||||
print("[info] Kamera wurde sauber freigegeben.")
|
|
||||||
return "Kamera freigegeben."
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("Starte SnakeCam ...")
|
|
||||||
app.run(host='0.0.0.0')
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
print("[SnakeCam] Initialisierung beginnt...")
|
|
||||||
from flask import Flask, render_template, Response, request, redirect
|
|
||||||
import cv2
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
print("[SnakeCam] Imports erfolgreich.")
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
def gen_frames():
|
|
||||||
cam = cv2.VideoCapture(0)
|
|
||||||
if not cam.isOpened():
|
|
||||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
success, frame = cam.read()
|
|
||||||
if not success:
|
|
||||||
break
|
|
||||||
ret, buffer = cv2.imencode('.jpg', frame)
|
|
||||||
frame = buffer.tobytes()
|
|
||||||
yield (b'--frame\r\n'
|
|
||||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
|
||||||
finally:
|
|
||||||
cam.release()
|
|
||||||
print("[info] Kamera wurde sauber freigegeben.")
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
return render_template('index.html')
|
|
||||||
|
|
||||||
@app.route('/video_feed')
|
|
||||||
def video_feed():
|
|
||||||
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
|
|
||||||
|
|
||||||
@app.route('/log_answer', methods=['POST'])
|
|
||||||
def log_answer():
|
|
||||||
user_input = request.form.get('antwort', 'nichts gesagt')
|
|
||||||
mood = request.form.get('mood', 'unspecified')
|
|
||||||
gesture = request.form.get('gesture', 'none')
|
|
||||||
timestamp = datetime.now().isoformat()
|
|
||||||
|
|
||||||
log_entry = {
|
|
||||||
'timestamp': timestamp,
|
|
||||||
'antwort': user_input,
|
|
||||||
'mood': mood,
|
|
||||||
'gesture': gesture
|
|
||||||
}
|
|
||||||
|
|
||||||
with open("snake_log.jsonl", "a") as log_file:
|
|
||||||
log_file.write(json.dumps(log_entry) + "\n")
|
|
||||||
|
|
||||||
return redirect("/")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("[SnakeCam] Starte Flask Webserver ...")
|
|
||||||
app.run(host='0.0.0.0', port=5000)
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
from flask import Flask, render_template, Response, request, redirect
|
|
||||||
import cv2
|
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
from gestures_debug import detect_hand_gesture
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
def gen_frames():
|
|
||||||
cam = cv2.VideoCapture(0)
|
|
||||||
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
|
|
||||||
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
|
|
||||||
|
|
||||||
if not cam.isOpened():
|
|
||||||
print("[WARNUNG] Kamera konnte nicht geöffnet werden.")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
success, frame = cam.read()
|
|
||||||
if not success:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Flip horizontal für Spiegelbild
|
|
||||||
frame = cv2.flip(frame, 1)
|
|
||||||
|
|
||||||
# Geste erkennen
|
|
||||||
gesture, roi = detect_hand_gesture(frame)
|
|
||||||
if isinstance(roi, tuple) and len(roi) == 4:
|
|
||||||
x, y, w, h = roi
|
|
||||||
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
|
|
||||||
cv2.putText(frame, gesture, (x, y - 10),
|
|
||||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
|
||||||
|
|
||||||
# Frame in JPEG konvertieren
|
|
||||||
ret, buffer = cv2.imencode('.jpg', frame)
|
|
||||||
frame_bytes = buffer.tobytes()
|
|
||||||
|
|
||||||
# MJPEG-Stream liefern
|
|
||||||
yield (b'--frame\r\n'
|
|
||||||
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
|
|
||||||
finally:
|
|
||||||
cam.release()
|
|
||||||
print("[info] Kamera wurde sauber freigegeben.")
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
return render_template('index.html')
|
|
||||||
|
|
||||||
@app.route('/video_feed')
|
|
||||||
def video_feed():
|
|
||||||
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
|
|
||||||
|
|
||||||
@app.route('/log_answer', methods=['POST'])
|
|
||||||
def log_answer():
|
|
||||||
data = {
|
|
||||||
"timestamp": datetime.datetime.now().isoformat(),
|
|
||||||
"antwort": request.form.get("antwort", ""),
|
|
||||||
"mood": request.form.get("mood", ""),
|
|
||||||
"gesture": request.form.get("gesture", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
with open("snake_log.jsonl", "a") as f:
|
|
||||||
f.write(json.dumps(data) + "\n")
|
|
||||||
|
|
||||||
return redirect("/")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("[SnakeCam] Initialisierung beginnt...")
|
|
||||||
print("[SnakeCam] Imports erfolgreich.")
|
|
||||||
print("[SnakeCam] Starte Flask Webserver ...")
|
|
||||||
app.run(host='0.0.0.0', port=5000)
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# gestures_v3.py
|
|
||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
# Pfad zum temporären Snapshot zur Diagnose
|
|
||||||
DEBUG_SNAPSHOT_PATH = "/tmp/roi_snapshot.jpg"
|
|
||||||
|
|
||||||
def detect_hand_gesture(frame):
|
|
||||||
"""
|
|
||||||
Erkenne einfache Handgesten wie 'wave' (offene Hand) und 'fist' (geschlossene Faust)
|
|
||||||
durch Analyse der Konturen im unteren rechten Bildbereich.
|
|
||||||
Die Erkennung basiert auf konvexer Hüllenerkennung.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
frame (ndarray): Das aktuelle Kamerabild
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
(str, ndarray): (Geste, Region-of-Interest-Ausschnitt)
|
|
||||||
"""
|
|
||||||
height, width, _ = frame.shape
|
|
||||||
|
|
||||||
# ROI: untere rechte Ecke (¼ des Bildes)
|
|
||||||
roi = frame[int(height * 0.6):height, int(width * 0.6):width]
|
|
||||||
|
|
||||||
# Speichere Snapshot für Debug-Zwecke
|
|
||||||
cv2.imwrite(DEBUG_SNAPSHOT_PATH, roi)
|
|
||||||
|
|
||||||
# Konvertiere zu HSV
|
|
||||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
# Hautfarbmaske (angepasst für unterschiedliche Beleuchtung)
|
|
||||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
|
||||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
|
||||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
|
||||||
|
|
||||||
# Glätten und Konturen erkennen
|
|
||||||
mask = cv2.GaussianBlur(mask, (7, 7), 0)
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
if contours and len(contours) > 0:
|
|
||||||
# Größte Kontur (Handfläche)
|
|
||||||
contour = max(contours, key=cv2.contourArea)
|
|
||||||
|
|
||||||
# Fehler vermeiden: Fläche zu klein
|
|
||||||
if cv2.contourArea(contour) < 1000:
|
|
||||||
return ("none", roi)
|
|
||||||
|
|
||||||
# Konvexe Hülle und Defekte
|
|
||||||
hull = cv2.convexHull(contour, returnPoints=False)
|
|
||||||
if hull is not None and len(hull) > 3:
|
|
||||||
defects = cv2.convexityDefects(contour, hull)
|
|
||||||
|
|
||||||
if defects is not None:
|
|
||||||
finger_count = 0
|
|
||||||
for i in range(defects.shape[0]):
|
|
||||||
s, e, f, d = defects[i, 0]
|
|
||||||
start = tuple(contour[s][0])
|
|
||||||
end = tuple(contour[e][0])
|
|
||||||
far = tuple(contour[f][0])
|
|
||||||
|
|
||||||
# Abstand analysieren – je mehr Defekte, desto mehr Finger offen
|
|
||||||
a = np.linalg.norm(np.array(start) - np.array(end))
|
|
||||||
b = np.linalg.norm(np.array(start) - np.array(far))
|
|
||||||
c = np.linalg.norm(np.array(end) - np.array(far))
|
|
||||||
angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c + 1e-5))
|
|
||||||
|
|
||||||
if angle <= np.pi / 2: # < 90°
|
|
||||||
finger_count += 1
|
|
||||||
|
|
||||||
# Auswertung basierend auf Fingeranzahl
|
|
||||||
if finger_count >= 3:
|
|
||||||
return ("wave", roi)
|
|
||||||
elif finger_count == 0:
|
|
||||||
return ("fist", roi)
|
|
||||||
|
|
||||||
return ("none", roi)
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
# --- Handgestenerkennung: Einfacher Hautfarbfilter + Konturanalyse ---
|
|
||||||
def detect_hand_gesture(frame):
|
|
||||||
# Region of Interest (z. B. linke obere Ecke) definieren
|
|
||||||
roi = frame[20:120, 20:120]
|
|
||||||
|
|
||||||
# Konvertiere in HSV-Farbraum
|
|
||||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
# Hautfarb-Bereich (kann je nach Licht angepasst werden)
|
|
||||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
|
||||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
|
||||||
|
|
||||||
# Maske für Hautfarbe erzeugen
|
|
||||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
|
||||||
|
|
||||||
# Konturen finden
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
gesture = "none"
|
|
||||||
|
|
||||||
# Wenn große Kontur gefunden → einfache „Winken“-Simulation
|
|
||||||
if contours:
|
|
||||||
largest_contour = max(contours, key=cv2.contourArea)
|
|
||||||
if cv2.contourArea(largest_contour) > 1000:
|
|
||||||
gesture = "wave"
|
|
||||||
|
|
||||||
# ROI im Hauptframe markieren
|
|
||||||
cv2.rectangle(frame, (20, 20), (120, 120), (0, 255, 0), 2)
|
|
||||||
cv2.putText(frame, f"Gesture: {gesture}", (20, 160),
|
|
||||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
|
|
||||||
|
|
||||||
return gesture, frame
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
def detect_hand_gesture(frame):
|
|
||||||
height, width, _ = frame.shape
|
|
||||||
|
|
||||||
# Dynamische ROI: Mitte des Bildes, zentriert
|
|
||||||
roi_w, roi_h = 200, 200
|
|
||||||
x_start = width // 2 - roi_w // 2
|
|
||||||
y_start = height // 2 - roi_h // 2
|
|
||||||
roi = frame[y_start:y_start + roi_h, x_start:x_start + roi_w]
|
|
||||||
|
|
||||||
# Zeichne das ROI-Fenster im Originalbild
|
|
||||||
cv2.rectangle(frame, (x_start, y_start), (x_start + roi_w, y_start + roi_h), (0, 255, 0), 2)
|
|
||||||
|
|
||||||
# Konvertiere zu HSV-Farbraum für bessere Hauterkennung
|
|
||||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
# Hautfarbmaske (getestet für mittlere Hauttöne)
|
|
||||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
|
||||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
|
||||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
|
||||||
|
|
||||||
# Glättung und Konturen finden
|
|
||||||
mask = cv2.GaussianBlur(mask, (5, 5), 0)
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
if contours:
|
|
||||||
# Größte Kontur annehmen
|
|
||||||
max_contour = max(contours, key=cv2.contourArea)
|
|
||||||
area = cv2.contourArea(max_contour)
|
|
||||||
|
|
||||||
if area > 2000:
|
|
||||||
# Konvexitätsdefekte (Finger) berechnen
|
|
||||||
hull = cv2.convexHull(max_contour, returnPoints=False)
|
|
||||||
if hull is not None and len(hull) > 3:
|
|
||||||
defects = cv2.convexityDefects(max_contour, hull)
|
|
||||||
finger_count = 0
|
|
||||||
if defects is not None:
|
|
||||||
for i in range(defects.shape[0]):
|
|
||||||
s, e, f, d = defects[i, 0]
|
|
||||||
if d > 10000:
|
|
||||||
finger_count += 1
|
|
||||||
|
|
||||||
if finger_count >= 3:
|
|
||||||
return "wave", roi
|
|
||||||
else:
|
|
||||||
return "fist", roi
|
|
||||||
|
|
||||||
return "none", roi
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# gestures_debug.py
|
|
||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
def detect_hand_gesture(frame):
|
|
||||||
height, width, _ = frame.shape
|
|
||||||
|
|
||||||
# --- Fallback-ROI: Mitte des Bildes ---
|
|
||||||
w, h = 100, 100
|
|
||||||
x = width // 2 - w // 2
|
|
||||||
y = height // 2 - h // 2
|
|
||||||
roi = frame[y:y+h, x:x+w]
|
|
||||||
|
|
||||||
if roi.size == 0:
|
|
||||||
print("[warn] ROI leer – kein Bildausschnitt verarbeitet")
|
|
||||||
return "none", (x, y, w, h)
|
|
||||||
|
|
||||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
lower_skin = np.array([0, 20, 70], dtype=np.uint8)
|
|
||||||
upper_skin = np.array([20, 255, 255], dtype=np.uint8)
|
|
||||||
|
|
||||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
|
||||||
mask = cv2.dilate(mask, np.ones((3, 3), np.uint8), iterations=4)
|
|
||||||
mask = cv2.GaussianBlur(mask, (5, 5), 100)
|
|
||||||
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
gesture = "none"
|
|
||||||
|
|
||||||
if contours and len(contours) > 0:
|
|
||||||
max_contour = max(contours, key=cv2.contourArea)
|
|
||||||
hull = cv2.convexHull(max_contour, returnPoints=False)
|
|
||||||
if hull is not None and len(hull) > 3:
|
|
||||||
defects = cv2.convexityDefects(max_contour, hull)
|
|
||||||
if defects is not None:
|
|
||||||
cnt_defects = defects.shape[0]
|
|
||||||
if cnt_defects >= 4:
|
|
||||||
gesture = "wave"
|
|
||||||
elif cnt_defects <= 1:
|
|
||||||
gesture = "fist"
|
|
||||||
else:
|
|
||||||
gesture = "unknown"
|
|
||||||
print(f"[debug] Defekte: {len(defects) if defects is not None else 'None'}")
|
|
||||||
else:
|
|
||||||
print("[debug] Keine Konturen erkannt")
|
|
||||||
|
|
||||||
print(f"[result] Geste erkannt: {gesture}")
|
|
||||||
return gesture, (x, y, w, h)
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
def detect_hand_gesture(frame):
|
|
||||||
x, y, w, h = 100, 100, 200, 150
|
|
||||||
roi = frame[y:y+h, x:x+w]
|
|
||||||
|
|
||||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
# Hautfarbe grob einschränken
|
|
||||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
|
||||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
|
||||||
|
|
||||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
|
||||||
mask = cv2.GaussianBlur(mask, (5, 5), 0)
|
|
||||||
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
gesture = "none"
|
|
||||||
|
|
||||||
if contours:
|
|
||||||
contour = max(contours, key=cv2.contourArea)
|
|
||||||
area = cv2.contourArea(contour)
|
|
||||||
|
|
||||||
if area > 3000:
|
|
||||||
print(f"[debug] Fläche erkannt: {int(area)}")
|
|
||||||
gesture = "hand"
|
|
||||||
|
|
||||||
return gesture, (x, y, w, h)
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
def detect_hand_gesture(frame):
|
|
||||||
x, y, w, h = 100, 100, 150, 150
|
|
||||||
roi = frame[y:y+h, x:x+w]
|
|
||||||
|
|
||||||
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
|
|
||||||
|
|
||||||
# einfache Hautfarbe (hell bis leicht gebräunt)
|
|
||||||
lower_skin = np.array([0, 30, 60], dtype=np.uint8)
|
|
||||||
upper_skin = np.array([20, 150, 255], dtype=np.uint8)
|
|
||||||
|
|
||||||
mask = cv2.inRange(hsv, lower_skin, upper_skin)
|
|
||||||
mask = cv2.GaussianBlur(mask, (5, 5), 0)
|
|
||||||
|
|
||||||
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
gesture = "none"
|
|
||||||
|
|
||||||
if contours:
|
|
||||||
contour = max(contours, key=cv2.contourArea)
|
|
||||||
area = cv2.contourArea(contour)
|
|
||||||
|
|
||||||
if area > 2500:
|
|
||||||
hull = cv2.convexHull(contour, returnPoints=False)
|
|
||||||
if hull is not None and len(hull) > 3:
|
|
||||||
defects = cv2.convexityDefects(contour, hull)
|
|
||||||
if defects is not None:
|
|
||||||
defect_count = defects.shape[0]
|
|
||||||
ratio = float(w) / h
|
|
||||||
|
|
||||||
print(f"[debug] Defekte: {defect_count}, Fläche: {int(area)}, Ratio: {ratio:.2f}")
|
|
||||||
|
|
||||||
# 🌊 einfache Regel für Winken
|
|
||||||
if 3 <= defect_count <= 10 and 2000 < area < 15000 and 1.3 < ratio < 2.3:
|
|
||||||
gesture = "wave"
|
|
||||||
|
|
||||||
return gesture, (x, y, w, h)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "🐍 Starte SnakeCam ..."
|
|
||||||
python3 app.py
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background-color: #111;
|
|
||||||
color: #eee;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #90ee90;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-container {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 320px;
|
|
||||||
height: auto;
|
|
||||||
border: 2px solid #555;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.mirrored {
|
|
||||||
/* transform: scaleX(-1); Spiegelung horizontal */
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: left;
|
|
||||||
background: #222;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label, select, input, button {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, select {
|
|
||||||
padding: 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #28a745;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #218838;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>🐍 SnakeCam Vision</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background-color: #111;
|
|
||||||
color: #eee;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #90ee90;
|
|
||||||
}
|
|
||||||
.video-container {
|
|
||||||
display: inline-block;
|
|
||||||
border: 4px solid #333;
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 320px;
|
|
||||||
height: 240px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
.video-container {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: left;
|
|
||||||
background: #222;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label, select, input, button {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, select {
|
|
||||||
padding: 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #28a745;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #218838;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>🐍 SnakeCam Vision</h1>
|
|
||||||
|
|
||||||
<div class="video-container">
|
|
||||||
<img src="{{ url_for('video_feed') }}" alt="Live Video Feed">
|
|
||||||
</div>
|
|
||||||
<br clear=all/>
|
|
||||||
<form id="logForm">
|
|
||||||
<p>
|
|
||||||
<label for="antwort">Antwort:</label><br>
|
|
||||||
<input type="text" id="antwort" name="antwort" placeholder="Was denkst du?">
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label for="mood">Stimmung:</label><br>
|
|
||||||
<select id="mood" name="mood">
|
|
||||||
<option value="happy">😄 Happy</option>
|
|
||||||
<option value="excited">🤩 Excited</option>
|
|
||||||
<option value="neutral">😐 Neutral</option>
|
|
||||||
<option value="confused">😕 Confused</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label for="gesture">Geste:</label><br>
|
|
||||||
<select id="gesture" name="gesture">
|
|
||||||
<option value="wave">👋 Wave</option>
|
|
||||||
<option value="fist">✊ Faust</option>
|
|
||||||
<option value="none">🚫 Keine</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<button type="submit">📝 Speichern</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.getElementById("logForm").addEventListener("submit", async function(e) {
|
|
||||||
e.preventDefault(); // Kein Reload!
|
|
||||||
const formData = new FormData(this);
|
|
||||||
await fetch("/log_answer", {
|
|
||||||
method: "POST",
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
console.log("✅ Antwort wurde gespeichert.");
|
|
||||||
this.reset(); // Eingabefelder leeren
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user