Compare commits
5 Commits
step35/594
...
step35/608
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75e117567d | ||
|
|
54093991ab | ||
|
|
1ea6bf6e33 | ||
|
|
874ce137b0 | ||
| 5eef5b48c8 |
87
bin/gitea-backup.sh
Normal file
87
bin/gitea-backup.sh
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Gitea Daily Backup Script
|
||||||
|
# Uses Gitea's native dump command to create automated backups of repositories and SQLite databases.
|
||||||
|
# Designed to run on the VPS (Ezra) as part of a daily cron job.
|
||||||
|
#
|
||||||
|
# Configuration via environment variables:
|
||||||
|
# GITEA_BIN Path to gitea binary (default: auto-detect)
|
||||||
|
# GITEA_BACKUP_DIR Directory for backup archives (default: /var/backups/gitea)
|
||||||
|
# GITEA_BACKUP_RETENTION Days to retain backups (default: 7)
|
||||||
|
# GITEA_BACKUP_LOG Log file path (default: /var/log/gitea-backup.log)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
GITEA_BIN="${GITEA_BIN:-$(command -v gitea 2>/dev/null || echo "/usr/local/bin/gitea")}"
|
||||||
|
BACKUP_DIR="${GITEA_BACKUP_DIR:-/var/backups/gitea}"
|
||||||
|
RETENTION_DAYS="${GITEA_BACKUP_RETENTION:-7}"
|
||||||
|
DATE="$(date +%Y-%m-%d_%H%M%S)"
|
||||||
|
BACKUP_FILE="${BACKUP_DIR}/gitea-backup-${DATE}.tar.gz"
|
||||||
|
LOG_FILE="${GITEA_BACKUP_LOG:-/var/log/gitea-backup.log}"
|
||||||
|
|
||||||
|
mkdir -p "${BACKUP_DIR}"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOG_FILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "=== Starting Gitea daily backup ==="
|
||||||
|
|
||||||
|
# Verify gitea binary exists
|
||||||
|
if [ ! -x "${GITEA_BIN}" ]; then
|
||||||
|
log "ERROR: Gitea binary not found at ${GITEA_BIN}"
|
||||||
|
log "Set GITEA_BIN environment variable to the gitea binary path (e.g., /usr/bin/gitea)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect Gitea WORK_PATH
|
||||||
|
WORK_PATH=""
|
||||||
|
APP_INI=""
|
||||||
|
for path in /etc/gitea/app.ini /home/git/gitea/custom/conf/app.ini ~/gitea/custom/conf/app.ini; do
|
||||||
|
if [ -f "$path" ]; then
|
||||||
|
APP_INI="$path"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$APP_INI" ]; then
|
||||||
|
# Parse [app] WORK_PATH = /var/lib/gitea
|
||||||
|
WORK_PATH=$(sed -n 's/^[[:space:]]*WORK_PATH[[:space:]]*=[[:space:]]*//p' "$APP_INI" | head -1)
|
||||||
|
log "Detected WORK_PATH from app.ini: ${WORK_PATH}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback detection
|
||||||
|
if [ -z "$WORK_PATH" ]; then
|
||||||
|
for d in /var/lib/gitea /home/git/gitea /srv/gitea /opt/gitea; do
|
||||||
|
if [ -d "$d" ]; then
|
||||||
|
WORK_PATH="$d"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
log "Inferred WORK_PATH: ${WORK_PATH:-not found}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$WORK_PATH" ]; then
|
||||||
|
log "ERROR: Could not determine Gitea WORK_PATH. Set GITEA_WORK_PATH manually."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Perform gitea dump
|
||||||
|
# Flags: --work-path sets the Gitea working directory, --file writes dump to tar.gz
|
||||||
|
log "Running: gitea dump --work-path ${WORK_PATH} --file ${BACKUP_FILE}"
|
||||||
|
"${GITEA_BIN}" dump --work-path "${WORK_PATH}" --file "${BACKUP_FILE}" 2>>"${LOG_FILE}"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
log "ERROR: gitea dump failed — check ${LOG_FILE} for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILE_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||||||
|
log "Backup created: ${BACKUP_FILE} (${FILE_SIZE})"
|
||||||
|
|
||||||
|
# Prune old backups (keep last N days)
|
||||||
|
find "${BACKUP_DIR}" -name "gitea-backup-*.tar.gz" -type f -mtime +$((${RETENTION_DAYS}-1)) -delete 2>/dev/null || true
|
||||||
|
log "Pruned backups older than ${RETENTION_DAYS} days"
|
||||||
|
|
||||||
|
log "=== Backup completed successfully ==="
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -129,20 +129,42 @@ Preserved by timmy-orchestrator to prevent loss." 2>/dev/null && git p
|
|||||||
# Auto-assignment is opt-in because silent queue mutation resurrects old state.
|
# Auto-assignment is opt-in because silent queue mutation resurrects old state.
|
||||||
if [ "$unassigned_count" -gt 0 ]; then
|
if [ "$unassigned_count" -gt 0 ]; then
|
||||||
if [ "$AUTO_ASSIGN_UNASSIGNED" = "1" ]; then
|
if [ "$AUTO_ASSIGN_UNASSIGNED" = "1" ]; then
|
||||||
log "Assigning $unassigned_count issues to claude..."
|
log "Assigning $unassigned_count issues via dispatch router..."
|
||||||
|
DISPATCH_LOG="$LOG_DIR/dispatch_decisions.log"
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
local repo=$(echo "$line" | sed 's/.*REPO=\([^ ]*\).*/\1/')
|
local repo=$(echo "$line" | sed 's/.*REPO=\([^ ]*\).*//')
|
||||||
local num=$(echo "$line" | sed 's/.*NUM=\([^ ]*\).*/\1/')
|
local num=$(echo "$line" | sed 's/.*NUM=\([^ ]*\).*//')
|
||||||
curl -sf -X PATCH "$GITEA_URL/api/v1/repos/$repo/issues/$num" \
|
local title=$(echo "$line" | sed 's/.*TITLE=//')
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
# Call dispatch_router to pick best agent
|
||||||
-d '{"assignees":["claude"]}' >/dev/null 2>&1 && \
|
local route_json
|
||||||
log " Assigned #$num ($repo) to claude"
|
route_json=$(python3 "$SCRIPT_DIR/../scripts/dispatch_router.py" "$title" "$repo" 2>/dev/null) || route_json=""
|
||||||
|
|
||||||
|
local recommended_agent="claude" # fallback
|
||||||
|
local route_category="unknown"
|
||||||
|
local route_score="0"
|
||||||
|
local route_reason="fallback"
|
||||||
|
|
||||||
|
if [ -n "$route_json" ]; then
|
||||||
|
recommended_agent=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('recommended_agent','claude'))" 2>/dev/null || echo "claude")
|
||||||
|
route_score=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('score',0))" 2>/dev/null || echo "0")
|
||||||
|
route_category=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('category','unknown'))" 2>/dev/null || echo "unknown")
|
||||||
|
route_reason=$(echo "$route_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('reason',''))" 2>/dev/null || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Assign via API
|
||||||
|
curl -sf -X PATCH "$GITEA_URL/api/v1/repos/$repo/issues/$num" \\
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \\
|
||||||
|
-H "Content-Type: application/json" \\
|
||||||
|
-d "{\"assignees\":[\"$recommended_agent\"]}" >/dev/null 2>&1 && \\
|
||||||
|
log " Assigned #$num ($repo) to $recommended_agent [score=$route_score cat=$route_category]"
|
||||||
|
|
||||||
|
# Log dispatch decision for audit (RFC3339 timestamp)
|
||||||
|
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||||
|
"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$num" "$repo" "$title" "$recommended_agent" "$route_score" "$route_category|$route_reason" \
|
||||||
|
>> "$DISPATCH_LOG"
|
||||||
done < "$state_dir/unassigned.txt"
|
done < "$state_dir/unassigned.txt"
|
||||||
else
|
else fi
|
||||||
log "Auto-assign disabled: leaving $unassigned_count unassigned issues untouched"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Phase 2: PR review via Timmy (LLM)
|
# Phase 2: PR review via Timmy (LLM)
|
||||||
if [ "$pr_count" -gt 0 ]; then
|
if [ "$pr_count" -gt 0 ]; then
|
||||||
|
|||||||
9
cron/vps/gitea-daily-backup.yml
Normal file
9
cron/vps/gitea-daily-backup.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
- name: Daily Gitea Backup
|
||||||
|
schedule: '0 2 * * *' # 2:00 AM daily
|
||||||
|
tasks:
|
||||||
|
- name: Run Gitea daily backup
|
||||||
|
shell: bash ~/.hermes/bin/gitea-backup.sh
|
||||||
|
env:
|
||||||
|
GITEA_BIN: /usr/local/bin/gitea
|
||||||
|
GITEA_BACKUP_DIR: /var/backups/gitea
|
||||||
|
GITEA_BACKUP_RETENTION: "7"
|
||||||
155
docs/backup-recovery-runbook.md
Normal file
155
docs/backup-recovery-runbook.md
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# Gitea Backup & Recovery Runbook
|
||||||
|
|
||||||
|
**Last updated:** 2026-04-30
|
||||||
|
**Scope:** Single-node VPS (Ezra, 143.198.27.163) running Gitea
|
||||||
|
**Backup Strategy:** Automated daily full dumps via `gitea dump`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Gets Backed Up
|
||||||
|
|
||||||
|
| Component | Method | Frequency | Retention |
|
||||||
|
|-----------|--------|-----------|-----------|
|
||||||
|
| All Gitea repositories (bare git dirs) | `gitea dump --file` | Daily at 2:00 AM | 7 days |
|
||||||
|
| SQLite databases (gitea.db, indexer.db, etc.) | Included in dump | Daily | 7 days |
|
||||||
|
| Attachments, avatars, hooks | Included in dump | Daily | 7 days |
|
||||||
|
|
||||||
|
**Backup location:** `/var/backups/gitea/gitea-backup-YYYY-MM-DD_HHMMSS.tar.gz`
|
||||||
|
|
||||||
|
**Log file:** `/var/log/gitea-backup.log`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup Architecture
|
||||||
|
|
||||||
|
The backup script `bin/gitea-backup.sh` runs daily via Hermes cron (`cron/vps/gitea-daily-backup.yml`). It:
|
||||||
|
|
||||||
|
1. Locates the Gitea `WORK_PATH` by reading `/etc/gitea/app.ini` or falling back to common locations (`/var/lib/gitea`, `/home/git/gitea`)
|
||||||
|
2. Invokes `gitea dump --work-path <path> --file <backup-tar.gz>` — Gitea's native, consistent snapshot mechanism
|
||||||
|
3. Prunes archives older than 7 days
|
||||||
|
4. Logs all operations to `/var/log/gitea-backup.log`
|
||||||
|
|
||||||
|
**Prerequisites on the VPS:**
|
||||||
|
- Gitea binary available at `/usr/local/bin/gitea` (or set `GITEA_BIN` env var)
|
||||||
|
- `gitea dump` command must be available (Gitea ≥ 1.12)
|
||||||
|
- SSH access to the VPS for manual recovery operations
|
||||||
|
- Sufficient disk space in `/var/backups/gitea` (typical dump: ~2–10 GB depending on repo count/size)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recovery Time Objective (RTO) & Recovery Point Objective (RPO)
|
||||||
|
|
||||||
|
| Metric | Estimate |
|
||||||
|
|--------|----------|
|
||||||
|
| **RPO** (data loss window) | ≤ 24 hours (last daily backup) |
|
||||||
|
| **RTO** (time to restore) | **~45 minutes** (cold restore from backup tarball) |
|
||||||
|
| **Downtime impact** | Gitea offline during restore (~20 min) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step-by-Step Recovery Procedure
|
||||||
|
|
||||||
|
### Phase 1 — Assess & Prepare (5 min)
|
||||||
|
|
||||||
|
1. SSH into Ezra VPS: `ssh root@143.198.27.163`
|
||||||
|
2. Stop Gitea so files are quiescent:
|
||||||
|
```bash
|
||||||
|
systemctl stop gitea
|
||||||
|
```
|
||||||
|
3. Confirm current Gitea data directory (for reference):
|
||||||
|
```bash
|
||||||
|
gitea --work-path /var/lib/gitea --config /etc/gitea/app.ini dump --help 2>&1
|
||||||
|
# Or check app.ini for WORK_PATH
|
||||||
|
cat /etc/gitea/app.ini | grep '^WORK_PATH'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2 — Restore from Backup (20 min)
|
||||||
|
|
||||||
|
4. Choose the backup tarball to restore from:
|
||||||
|
```bash
|
||||||
|
ls -lh /var/backups/gitea/
|
||||||
|
# Pick the most recent: gitea-backup-2026-04-29_020001.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Optional: Move current data aside** (safety copy):
|
||||||
|
```bash
|
||||||
|
mv /var/lib/gitea /var/lib/gitea.bak-$(date +%s)
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Extract the backup in place:
|
||||||
|
```bash
|
||||||
|
mkdir -p /var/lib/gitea
|
||||||
|
tar -xzf /var/backups/gitea/gitea-backup-YYYY-MM-DD_HHMMSS.tar.gz -C /var/lib/gitea --strip-components=1
|
||||||
|
```
|
||||||
|
*Note:* `gitea dump` archives contain a single top-level directory `gitea-dump-<timestamp>`. The `--strip-components=1` puts its contents directly into `/var/lib/gitea`.
|
||||||
|
|
||||||
|
7. Set correct ownership (typically `git:git`):
|
||||||
|
```bash
|
||||||
|
chown -R git:git /var/lib/gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3 — Restart & Validate (15 min)
|
||||||
|
|
||||||
|
8. Start Gitea:
|
||||||
|
```bash
|
||||||
|
systemctl start gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
9. Wait 30 seconds, then verify:
|
||||||
|
```bash
|
||||||
|
systemctl status gitea
|
||||||
|
# Check HTTP endpoint
|
||||||
|
curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/ # Should be 200
|
||||||
|
```
|
||||||
|
|
||||||
|
10. Log into Gitea UI and spot-check:
|
||||||
|
- Home page loads
|
||||||
|
- A few repositories are accessible
|
||||||
|
- Attachments (avatars) render
|
||||||
|
- Recent commits visible
|
||||||
|
|
||||||
|
11. If the web UI works but indices are stale, rebuild them (wait for background jobs to process):
|
||||||
|
```bash
|
||||||
|
gitea admin index rebuild-repo --all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Post-Restore Checklist
|
||||||
|
|
||||||
|
- [ ] Admin UI reachable at `https://forge.alexanderwhitestone.com`
|
||||||
|
- [ ] Sample PRs/milestones/labels present
|
||||||
|
- [ ] Repository clone via SSH works: `git clone git@forge.alexanderwhitestone.com:Timmy_Foundation/timmy-config.git`
|
||||||
|
- [ ] Check backup script health: `cat /var/log/gitea-backup.log | tail -20`
|
||||||
|
- [ ] Re-enable any disabled integrations (webhooks, CI/CD runners)
|
||||||
|
- [ ] Notify the fleet: post to relevant channels confirming operational status
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Known Issues & Workarounds
|
||||||
|
|
||||||
|
| Symptom | Likely cause | Fix |
|
||||||
|
|---------|--------------|-----|
|
||||||
|
| `gitea: command not found` | Binary at non-standard path | Set `GITEA_BIN=/path/to/gitea` in cron env |
|
||||||
|
| `Permission denied` on backup dir | Cron user lacks write access to `/var/backups` | `mkdir /var/backups/gitea && chown root:root /var/backups/gitea` |
|
||||||
|
| Restore fails: `"database or disk is full"` | Insufficient space on `/var/lib/gitea` | Expand disk or clean up old data first; backups require ~1.5x live data size |
|
||||||
|
| Old backup tarballs not deleting | Retention cron not firing | Check `systemctl status hermes-cron` and cron logs |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Off-Site Replication (Future Work)
|
||||||
|
|
||||||
|
This backup is **on-site only** (same VPS). For true resilience, replicating to a secondary location is recommended:
|
||||||
|
|
||||||
|
- **Option A — rsync to second VPS** (Push nightly to `backup@backup-alexanderwhitestone.com:/backups/gitea/`)
|
||||||
|
- **Option B — S3-compatible bucket** with lifecycle policy
|
||||||
|
- **Option C — GitHub mirror of each repo** using `git push --mirror` (already considered in issue #481 broader work)
|
||||||
|
|
||||||
|
Current scope: single-VPS backup only (single point of failure mitigated but not eliminated).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- `bin/gitea-backup.sh` — backup script source
|
||||||
|
- `cron/vps/gitea-daily-backup.yml` — Hermes cron definition
|
||||||
|
- Gitea official docs: <https://docs.gitea.com/administration/backup-and-restore>
|
||||||
|
- Hermes cron: <https://hermes-agent.nousresearch.com/docs>
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Started from the basement, counting ceiling stains", "scene": {"mood": "struggle", "colors": ["concrete gray", "sodium orange"], "composition": "aerial city block", "camera_movement": "slow descent", "description": "struggle scene: Started from the basement, counting ceiling stains — aerial city block with concrete gray, sodium orange palette, slow descent camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 2, "timestamp": "0:14", "duration_seconds": 16, "lyric_line": "Empty fridge but the mind is full", "scene": {"mood": "hunger", "colors": ["midnight blue", "streetlight yellow"], "composition": "figure on fire escape", "camera_movement": "tracking up", "description": "hunger scene: Empty fridge but the mind is full — figure on fire escape with midnight blue, streetlight yellow palette, tracking up camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 3, "timestamp": "0:30", "duration_seconds": 15, "lyric_line": "Pen to paper like a surgeon to a wound", "scene": {"mood": "determination", "colors": ["steel gray", "flash of gold"], "composition": "hands writing in notebook", "camera_movement": "close-up macro", "description": "determination scene: Pen to paper like a surgeon to a wound — hands writing in notebook with steel gray, flash of gold palette, close-up macro camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 4, "timestamp": "0:45", "duration_seconds": 18, "lyric_line": "They said I'd never make it — watch me build the stage", "scene": {"mood": "energy", "colors": ["neon green", "black"], "composition": "crowd forming", "camera_movement": "dolly through", "description": "energy scene: They said I'd never make it — watch me build the stage — crowd forming with neon green, black palette, dolly through camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 5, "timestamp": "1:03", "duration_seconds": 14, "lyric_line": "Same face in the mirror, different eyes", "scene": {"mood": "reflection", "colors": ["warm amber", "shadows"], "composition": "mirror reflection", "camera_movement": "rack focus", "description": "reflection scene: Same face in the mirror, different eyes — mirror reflection with warm amber, shadows palette, rack focus camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 6, "timestamp": "1:17", "duration_seconds": 17, "lyric_line": "The city is mine — every block, every dream", "scene": {"mood": "power", "colors": ["gold", "deep red"], "composition": "figure on rooftop", "camera_movement": "crane up revealing", "description": "power scene: The city is mine — every block, every dream — figure on rooftop with gold, deep red palette, crane up revealing camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 7, "timestamp": "1:34", "duration_seconds": 15, "lyric_line": "We made it out — the basement is a palace now", "scene": {"mood": "joy", "colors": ["bright white", "sunrise gold"], "composition": "group celebration", "camera_movement": "handheld energy", "description": "joy scene: We made it out — the basement is a palace now — group celebration with bright white, sunrise gold palette, handheld energy camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 8, "timestamp": "1:49", "duration_seconds": 16, "lyric_line": "Every crack in the sidewalk is a chapter", "scene": {"mood": "gratitude", "colors": ["soft gold", "sky blue"], "composition": "figure looking down at neighborhood", "camera_movement": "over-shoulder", "description": "gratitude scene: Every crack in the sidewalk is a chapter — figure looking down at neighborhood with soft gold, sky blue palette, over-shoulder camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 9, "timestamp": "2:05", "duration_seconds": 14, "lyric_line": "The dream was always real — they just couldn't see it", "scene": {"mood": "resolve", "colors": ["concrete gray → gold"], "composition": "walking forward", "camera_movement": "steadicam follow", "description": "resolve scene: The dream was always real — they just couldn't see it — walking forward with concrete gray → gold palette, steadicam follow camera."}}
|
|
||||||
{"song": "Concrete Dreams", "artist": "Street Prophet", "mood_arc": "struggle → triumph", "beat": 10, "timestamp": "2:19", "duration_seconds": 13, "lyric_line": "Concrete dreams. Built from nothing.", "scene": {"mood": "peace", "colors": ["warm light", "city skyline"], "composition": "wide sunset", "camera_movement": "slow dissolve", "description": "peace scene: Concrete dreams. Built from nothing. — wide sunset with warm light, city skyline palette, slow dissolve camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 1, "timestamp": "0:00", "duration_seconds": 15, "lyric_line": "They watching through the walls, through the wires, through the air", "scene": {"mood": "paranoia", "colors": ["static gray", "red scan lines"], "composition": "surveillance POV", "camera_movement": "glitch cuts", "description": "paranoia scene: They watching through the walls, through the wires, through the air — surveillance POV with static gray, red scan lines palette, glitch cuts camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 2, "timestamp": "0:15", "duration_seconds": 14, "lyric_line": "Too many voices and none of them mine", "scene": {"mood": "anxiety", "colors": ["sickly green", "flickering"], "composition": "crowded room distorted", "camera_movement": "fish-eye warp", "description": "anxiety scene: Too many voices and none of them mine — crowded room distorted with sickly green, flickering palette, fish-eye warp camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 3, "timestamp": "0:29", "duration_seconds": 17, "lyric_line": "BREAK the frequency — find your own signal", "scene": {"mood": "rage", "colors": ["red", "black flash"], "composition": "smashing through barrier", "camera_movement": "impact freeze", "description": "rage scene: BREAK the frequency — find your own signal — smashing through barrier with red, black flash palette, impact freeze camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 4, "timestamp": "0:46", "duration_seconds": 15, "lyric_line": "The noise stops. I can hear myself think.", "scene": {"mood": "clarity", "colors": ["clean white", "single blue"], "composition": "alone in silence", "camera_movement": "still frame", "description": "clarity scene: The noise stops. I can hear myself think. — alone in silence with clean white, single blue palette, still frame camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 5, "timestamp": "1:01", "duration_seconds": 16, "lyric_line": "Found the ghost — it was me the whole time", "scene": {"mood": "discovery", "colors": ["spectrum rainbow", "dark room"], "composition": "equalizer waveform", "camera_movement": "waveform tracking", "description": "discovery scene: Found the ghost — it was me the whole time — equalizer waveform with spectrum rainbow, dark room palette, waveform tracking camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 6, "timestamp": "1:17", "duration_seconds": 18, "lyric_line": "I am the frequency they cannot tune out", "scene": {"mood": "power", "colors": ["electric blue", "void"], "composition": "figure controls the signal", "camera_movement": "orbit around figure", "description": "power scene: I am the frequency they cannot tune out — figure controls the signal with electric blue, void palette, orbit around figure camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 7, "timestamp": "1:35", "duration_seconds": 14, "lyric_line": "The ghost is quiet now. It listens.", "scene": {"mood": "peace", "colors": ["calm blue", "soft static"], "composition": "meditating in noise", "camera_movement": "slow push in", "description": "peace scene: The ghost is quiet now. It listens. — meditating in noise with calm blue, soft static palette, slow push in camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 8, "timestamp": "1:49", "duration_seconds": 16, "lyric_line": "Every frequency is a choice. Choose yours.", "scene": {"mood": "wisdom", "colors": ["warm gold", "gentle gray"], "composition": "teaching figure", "camera_movement": "two-shot medium", "description": "wisdom scene: Every frequency is a choice. Choose yours. — teaching figure with warm gold, gentle gray palette, two-shot medium camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 9, "timestamp": "2:05", "duration_seconds": 15, "lyric_line": "Broadcast on the ghost frequency. They will hear.", "scene": {"mood": "strength", "colors": ["clean signal", "white"], "composition": "broadcast tower", "camera_movement": "vertical crane up", "description": "strength scene: Broadcast on the ghost frequency. They will hear. — broadcast tower with clean signal, white palette, vertical crane up camera."}}
|
|
||||||
{"song": "Ghost Frequency", "artist": "Phantom MC", "mood_arc": "paranoia → clarity", "beat": 10, "timestamp": "2:20", "duration_seconds": 12, "lyric_line": "...", "scene": {"mood": "silence", "colors": ["pure white", "still"], "composition": "empty frame", "camera_movement": "long hold", "description": "silence scene: ... — empty frame with pure white, still palette, long hold camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 1, "timestamp": "0:00", "duration_seconds": 13, "lyric_line": "A-B-C, easy as 1-2-3, but the cipher is 7-4-1", "scene": {"mood": "playful", "colors": ["bright primary colors", "white"], "composition": "graffiti wall", "camera_movement": "pan reveal", "description": "playful scene: A-B-C, easy as 1-2-3, but the cipher is 7-4-1 — graffiti wall with bright primary colors, white palette, pan reveal camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 2, "timestamp": "0:13", "duration_seconds": 15, "lyric_line": "Step up to the cipher circle, show me what you got", "scene": {"mood": "competitive", "colors": ["two-tone contrast", "spotlight"], "composition": "two MCs facing", "camera_movement": "split screen", "description": "competitive scene: Step up to the cipher circle, show me what you got — two MCs facing with two-tone contrast, spotlight palette, split screen camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 3, "timestamp": "0:28", "duration_seconds": 17, "lyric_line": "Every bar is a blade — sharpened by the block", "scene": {"mood": "fierce", "colors": ["fire orange", "smoke black"], "composition": "mic close-up", "camera_movement": "rapid cuts", "description": "fierce scene: Every bar is a blade — sharpened by the block — mic close-up with fire orange, smoke black palette, rapid cuts camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 4, "timestamp": "0:45", "duration_seconds": 14, "lyric_line": "Metaphor on metaphor — the meaning is the maze", "scene": {"mood": "wit", "colors": ["neon yellow", "dark purple"], "composition": "word cloud forming", "camera_movement": "zoom into text", "description": "wit scene: Metaphor on metaphor — the meaning is the maze — word cloud forming with neon yellow, dark purple palette, zoom into text camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 5, "timestamp": "0:59", "duration_seconds": 16, "lyric_line": "The crown was always made of words, not gold", "scene": {"mood": "dominance", "colors": ["gold", "black"], "composition": "crown close-up", "camera_movement": "slow rotation", "description": "dominance scene: The crown was always made of words, not gold — crown close-up with gold, black palette, slow rotation camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 6, "timestamp": "1:15", "duration_seconds": 15, "lyric_line": "When the cipher bows, the kings emerge", "scene": {"mood": "respect", "colors": ["warm amber", "stage light"], "composition": "audience reaction", "camera_movement": "crowd POV", "description": "respect scene: When the cipher bows, the kings emerge — audience reaction with warm amber, stage light palette, crowd POV camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 7, "timestamp": "1:30", "duration_seconds": 17, "lyric_line": "The circle was never about winning — it's about the words", "scene": {"mood": "celebration", "colors": ["confetti colors", "warm light"], "composition": "group embrace", "camera_movement": "circular dolly", "description": "celebration scene: The circle was never about winning — it's about the words — group embrace with confetti colors, warm light palette, circular dolly camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 8, "timestamp": "1:47", "duration_seconds": 14, "lyric_line": "Same circle, same fire, new kings", "scene": {"mood": "legacy", "colors": ["sepia", "gold"], "composition": "old cipher circle photo", "camera_movement": "fade from old to new", "description": "legacy scene: Same circle, same fire, new kings — old cipher circle photo with sepia, gold palette, fade from old to new camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 9, "timestamp": "2:01", "duration_seconds": 16, "lyric_line": "The cipher sleeps but the words never do", "scene": {"mood": "peace", "colors": ["sunset orange", "cool blue"], "composition": "walking away from circle", "camera_movement": "tracking behind", "description": "peace scene: The cipher sleeps but the words never do — walking away from circle with sunset orange, cool blue palette, tracking behind camera."}}
|
|
||||||
{"song": "Cipher Kings", "artist": "Word Lab", "mood_arc": "playful → fierce", "beat": 10, "timestamp": "2:17", "duration_seconds": 13, "lyric_line": "Tomorrow the circle reforms. I'll be ready.", "scene": {"mood": "resolve", "colors": ["midnight", "single star"], "composition": "figure writing alone", "camera_movement": "close-up hands", "description": "resolve scene: Tomorrow the circle reforms. I'll be ready. — figure writing alone with midnight, single star palette, close-up hands camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Upload my soul to the mainframe tonight", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: Upload my soul to the mainframe tonight — establishing shot with city gray, dawn palette, slow zoom camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Pixel hearts beating at 120 BPM", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Pixel hearts beating at 120 BPM — close-up with dark, neon accent palette, handheld camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Error code: love not found", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Error code: love not found — wide action with bright, contrast palette, rapid movement camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The algorithm learned my name", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The algorithm learned my name — medium shot with warm, shadows palette, steady camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "Download hope at 5G speed", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: Download hope at 5G speed — figure alone with muted, single pop palette, tracking camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "Binary tears on a silicon face", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: Binary tears on a silicon face — ascending with gold, white palette, crane up camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Reboot the heart, restart the dream", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Reboot the heart, restart the dream — group shot with warm crowd, light palette, sweeping pan camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "The firewall crumbles when you smile", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: The firewall crumbles when you smile — profile with strong, clear palette, push in camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "I am the virus they cannot delete", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: I am the virus they cannot delete — wide landscape with soft, dawn palette, steady wide camera."}}
|
|
||||||
{"song": "404 Soul", "artist": "Digital Ghost", "mood_arc": "isolation → connection", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Signal restored. Connection made.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Signal restored. Connection made. — empty frame with fade, single accent palette, dissolve camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "The block is alive when the speakers hit", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: The block is alive when the speakers hit — establishing shot with city gray, dawn palette, slow zoom camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Neighbors become family under the bass", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Neighbors become family under the bass — close-up with dark, neon accent palette, handheld camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Dance floor is the only democracy", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Dance floor is the only democracy — wide action with bright, contrast palette, rapid movement camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "Every step is a vote for joy", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: Every step is a vote for joy — medium shot with warm, shadows palette, steady camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "The DJ is the mayor tonight", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: The DJ is the mayor tonight — figure alone with muted, single pop palette, tracking camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "Sweat and laughter, the only currency", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: Sweat and laughter, the only currency — ascending with gold, white palette, crane up camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "When the music stops, we are still here", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: When the music stops, we are still here — group shot with warm crowd, light palette, sweeping pan camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "The block party never really ends", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: The block party never really ends — profile with strong, clear palette, push in camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "Tomorrow we clean up together", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: Tomorrow we clean up together — wide landscape with soft, dawn palette, steady wide camera."}}
|
|
||||||
{"song": "Block Party", "artist": "Neighborhood Watch", "mood_arc": "community joy", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Community is the beat that never stops.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Community is the beat that never stops. — empty frame with fade, single accent palette, dissolve camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Needle and ink tell the story skin can't", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: Needle and ink tell the story skin can't — establishing shot with city gray, dawn palette, slow zoom camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Every scar became a constellation", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Every scar became a constellation — close-up with dark, neon accent palette, handheld camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "The artist and the wound are one", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: The artist and the wound are one — wide action with bright, contrast palette, rapid movement camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "Pain is the palette, beauty is the brush", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: Pain is the palette, beauty is the brush — medium shot with warm, shadows palette, steady camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "My body is the gallery they can't close", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: My body is the gallery they can't close — figure alone with muted, single pop palette, tracking camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "The tattoo artist is a surgeon of stories", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: The tattoo artist is a surgeon of stories — ascending with gold, white palette, crane up camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Ink fades but the story deepens", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Ink fades but the story deepens — group shot with warm crowd, light palette, sweeping pan camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "I am the canvas and the artist both", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: I am the canvas and the artist both — profile with strong, clear palette, push in camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "The needle says: remember", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: The needle says: remember — wide landscape with soft, dawn palette, steady wide camera."}}
|
|
||||||
{"song": "Ink & Iron", "artist": "Tattoo Poet", "mood_arc": "pain → art → meaning", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Art is what survives the pain.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Art is what survives the pain. — empty frame with fade, single accent palette, dissolve camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "The 7:15 is my meditation room", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: The 7:15 is my meditation room — establishing shot with city gray, dawn palette, slow zoom camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Strangers sharing silence like monks", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Strangers sharing silence like monks — close-up with dark, neon accent palette, handheld camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Every stop is a small death and rebirth", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Every stop is a small death and rebirth — wide action with bright, contrast palette, rapid movement camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The conductor is the last philosopher", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The conductor is the last philosopher — medium shot with warm, shadows palette, steady camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "Watch the city blur into abstraction", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: Watch the city blur into abstraction — figure alone with muted, single pop palette, tracking camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "My reflection in the window: stranger or self?", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: My reflection in the window: stranger or self? — ascending with gold, white palette, crane up camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "The tunnel is not darkness — it's transition", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: The tunnel is not darkness — it's transition — group shot with warm crowd, light palette, sweeping pan camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "Above ground, the world. Below, the mind", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: Above ground, the world. Below, the mind — profile with strong, clear palette, push in camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "This train goes nowhere I haven't been", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: This train goes nowhere I haven't been — wide landscape with soft, dawn palette, steady wide camera."}}
|
|
||||||
{"song": "Metro Lines", "artist": "Subway Sage", "mood_arc": "commute meditation", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "But the ride is the point.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: But the ride is the point. — empty frame with fade, single accent palette, dissolve camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "Three AM and the code is still compiling", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: Three AM and the code is still compiling — establishing shot with city gray, dawn palette, slow zoom camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Coffee is the real minimum viable product", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Coffee is the real minimum viable product — close-up with dark, neon accent palette, handheld camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "The screen glows like a campfire for the lonely", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: The screen glows like a campfire for the lonely — wide action with bright, contrast palette, rapid movement camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "Bug fixes at midnight: digital monks", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: Bug fixes at midnight: digital monks — medium shot with warm, shadows palette, steady camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "The server hums my lullaby", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: The server hums my lullaby — figure alone with muted, single pop palette, tracking camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "Debug by moonlight, deploy by dawn", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: Debug by moonlight, deploy by dawn — ascending with gold, white palette, crane up camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "The graveyard shift owns the night", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: The graveyard shift owns the night — group shot with warm crowd, light palette, sweeping pan camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "We are the ghosts that keep the lights on", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: We are the ghosts that keep the lights on — profile with strong, clear palette, push in camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "Sunrise is the pull request", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: Sunrise is the pull request — wide landscape with soft, dawn palette, steady wide camera."}}
|
|
||||||
{"song": "Midnight Shift", "artist": "Graveyard Flow", "mood_arc": "exhaustion → defiance", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Every night shift ends in a merge.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Every night shift ends in a merge. — empty frame with fade, single accent palette, dissolve camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "No signal. No WiFi. No problem.", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: No signal. No WiFi. No problem. — establishing shot with city gray, dawn palette, slow zoom camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "The dead zone is where the real talk happens", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: The dead zone is where the real talk happens — close-up with dark, neon accent palette, handheld camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Disconnect to reconnect — the oldest algorithm", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Disconnect to reconnect — the oldest algorithm — wide action with bright, contrast palette, rapid movement camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The bars dropped: phone and soul", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The bars dropped: phone and soul — medium shot with warm, shadows palette, steady camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "In the silence I hear my own frequency", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: In the silence I hear my own frequency — figure alone with muted, single pop palette, tracking camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "The dead zone is not dead — it's listening", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: The dead zone is not dead — it's listening — ascending with gold, white palette, crane up camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Reconnection starts with disconnection", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Reconnection starts with disconnection — group shot with warm crowd, light palette, sweeping pan camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "When the signal returns, I choose not to answer", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: When the signal returns, I choose not to answer — profile with strong, clear palette, push in camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "The dead zone taught me presence", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: The dead zone taught me presence — wide landscape with soft, dawn palette, steady wide camera."}}
|
|
||||||
{"song": "Signal Lost", "artist": "Dead Zone", "mood_arc": "disconnection → reconnection", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "Signal lost. Self found.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: Signal lost. Self found. — empty frame with fade, single accent palette, dissolve camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 1, "timestamp": "0:00", "duration_seconds": 14, "lyric_line": "The bodega is open 24/7 so are the dreams", "scene": {"mood": "opening", "colors": ["city gray", "dawn"], "composition": "establishing shot", "camera_movement": "slow zoom", "description": "opening scene: The bodega is open 24/7 so are the dreams — establishing shot with city gray, dawn palette, slow zoom camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 2, "timestamp": "0:15", "duration_seconds": 15, "lyric_line": "Behind the counter: a PhD in survival", "scene": {"mood": "tension", "colors": ["dark", "neon accent"], "composition": "close-up", "camera_movement": "handheld", "description": "tension scene: Behind the counter: a PhD in survival — close-up with dark, neon accent palette, handheld camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 3, "timestamp": "0:30", "duration_seconds": 16, "lyric_line": "Every item on the shelf is a small miracle", "scene": {"mood": "explosion", "colors": ["bright", "contrast"], "composition": "wide action", "camera_movement": "rapid movement", "description": "explosion scene: Every item on the shelf is a small miracle — wide action with bright, contrast palette, rapid movement camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 4, "timestamp": "0:45", "duration_seconds": 17, "lyric_line": "The lottery tickets are prayers in latex", "scene": {"mood": "reflection", "colors": ["warm", "shadows"], "composition": "medium shot", "camera_movement": "steady", "description": "reflection scene: The lottery tickets are prayers in latex — medium shot with warm, shadows palette, steady camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 5, "timestamp": "1:00", "duration_seconds": 18, "lyric_line": "Coffee for a dollar, hope for free", "scene": {"mood": "struggle", "colors": ["muted", "single pop"], "composition": "figure alone", "camera_movement": "tracking", "description": "struggle scene: Coffee for a dollar, hope for free — figure alone with muted, single pop palette, tracking camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 6, "timestamp": "1:15", "duration_seconds": 14, "lyric_line": "The bodega cat knows all the secrets", "scene": {"mood": "breakthrough", "colors": ["gold", "white"], "composition": "ascending", "camera_movement": "crane up", "description": "breakthrough scene: The bodega cat knows all the secrets — ascending with gold, white palette, crane up camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 7, "timestamp": "1:30", "duration_seconds": 15, "lyric_line": "Corner store: the real community center", "scene": {"mood": "community", "colors": ["warm crowd", "light"], "composition": "group shot", "camera_movement": "sweeping pan", "description": "community scene: Corner store: the real community center — group shot with warm crowd, light palette, sweeping pan camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 8, "timestamp": "1:45", "duration_seconds": 16, "lyric_line": "Dreams don't close at midnight", "scene": {"mood": "resolve", "colors": ["strong", "clear"], "composition": "profile", "camera_movement": "push in", "description": "resolve scene: Dreams don't close at midnight — profile with strong, clear palette, push in camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 9, "timestamp": "2:00", "duration_seconds": 17, "lyric_line": "The owner came here with nothing — now everything", "scene": {"mood": "peace", "colors": ["soft", "dawn"], "composition": "wide landscape", "camera_movement": "steady wide", "description": "peace scene: The owner came here with nothing — now everything — wide landscape with soft, dawn palette, steady wide camera."}}
|
|
||||||
{"song": "Corner Store", "artist": "Bodega Dreams", "mood_arc": "small world → big dreams", "beat": 10, "timestamp": "2:15", "duration_seconds": 18, "lyric_line": "The corner store is the first chapter.", "scene": {"mood": "ending", "colors": ["fade", "single accent"], "composition": "empty frame", "camera_movement": "dissolve", "description": "ending scene: The corner store is the first chapter. — empty frame with fade, single accent palette, dissolve camera."}}
|
|
||||||
@@ -1,43 +1,46 @@
|
|||||||
model:
|
model:
|
||||||
default: kimi-k2.5
|
default: kimi-k2.5
|
||||||
provider: kimi-coding
|
provider: kimi-coding
|
||||||
|
context_length: 65536
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
|
|
||||||
toolsets:
|
toolsets:
|
||||||
- all
|
- all
|
||||||
|
|
||||||
fallback_providers:
|
fallback_providers:
|
||||||
- provider: kimi-coding
|
- provider: kimi-coding
|
||||||
model: kimi-k2.5
|
model: kimi-k2.5
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
timeout: 120
|
timeout: 120
|
||||||
reason: Kimi coding fallback (front of chain)
|
reason: "Primary — Kimi K2.5 (best value, least friction)"
|
||||||
- provider: openrouter
|
- provider: openrouter
|
||||||
model: google/gemini-2.5-pro
|
model: google/gemini-2.5-pro
|
||||||
base_url: https://openrouter.ai/api/v1
|
base_url: https://openrouter.ai/api/v1
|
||||||
api_key_env: OPENROUTER_API_KEY
|
api_key_env: OPENROUTER_API_KEY
|
||||||
timeout: 120
|
timeout: 120
|
||||||
reason: Gemini 2.5 Pro via OpenRouter (replaces banned Anthropic)
|
reason: "Fallback — Gemini 2.5 Pro via OpenRouter"
|
||||||
- provider: ollama
|
- provider: ollama
|
||||||
model: gemma4:latest
|
model: gemma4:latest
|
||||||
base_url: http://localhost:11434
|
base_url: http://localhost:11434/v1
|
||||||
timeout: 300
|
timeout: 180
|
||||||
reason: Terminal fallback — local Ollama
|
reason: "Terminal fallback — local Ollama (sovereign, no API needed)"
|
||||||
- provider: nous
|
|
||||||
model: xiaomi/mimo-v2-pro
|
|
||||||
base_url: https://inference.nousresearch.com/v1
|
|
||||||
api_key_env: NOUS_API_KEY
|
|
||||||
timeout: 120
|
|
||||||
reason: MiMo V2 Pro via Nous Portal free tier evaluation (#447)
|
|
||||||
agent:
|
agent:
|
||||||
max_turns: 30
|
max_turns: 30
|
||||||
reasoning_effort: xhigh
|
reasoning_effort: high
|
||||||
verbose: false
|
verbose: false
|
||||||
|
|
||||||
terminal:
|
terminal:
|
||||||
backend: local
|
backend: local
|
||||||
cwd: .
|
cwd: .
|
||||||
timeout: 180
|
timeout: 180
|
||||||
persistent_shell: true
|
persistent_shell: true
|
||||||
|
|
||||||
browser:
|
browser:
|
||||||
inactivity_timeout: 120
|
inactivity_timeout: 120
|
||||||
command_timeout: 30
|
command_timeout: 30
|
||||||
record_sessions: false
|
record_sessions: false
|
||||||
|
|
||||||
display:
|
display:
|
||||||
compact: false
|
compact: false
|
||||||
personality: ''
|
personality: ''
|
||||||
@@ -48,6 +51,7 @@ display:
|
|||||||
streaming: false
|
streaming: false
|
||||||
show_cost: false
|
show_cost: false
|
||||||
tool_progress: all
|
tool_progress: all
|
||||||
|
|
||||||
memory:
|
memory:
|
||||||
memory_enabled: true
|
memory_enabled: true
|
||||||
user_profile_enabled: true
|
user_profile_enabled: true
|
||||||
@@ -55,46 +59,55 @@ memory:
|
|||||||
user_char_limit: 1375
|
user_char_limit: 1375
|
||||||
nudge_interval: 10
|
nudge_interval: 10
|
||||||
flush_min_turns: 6
|
flush_min_turns: 6
|
||||||
|
|
||||||
approvals:
|
approvals:
|
||||||
mode: manual
|
mode: manual
|
||||||
|
|
||||||
security:
|
security:
|
||||||
redact_secrets: true
|
redact_secrets: true
|
||||||
tirith_enabled: false
|
tirith_enabled: false
|
||||||
|
|
||||||
platforms:
|
platforms:
|
||||||
api_server:
|
api_server:
|
||||||
enabled: true
|
enabled: true
|
||||||
extra:
|
extra:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 8645
|
port: 8645
|
||||||
|
|
||||||
session_reset:
|
session_reset:
|
||||||
mode: none
|
mode: none
|
||||||
idle_minutes: 0
|
idle_minutes: 0
|
||||||
|
|
||||||
skills:
|
skills:
|
||||||
creation_nudge_interval: 15
|
creation_nudge_interval: 15
|
||||||
system_prompt_suffix: 'You are Allegro, the Kimi-backed third wizard house.
|
|
||||||
|
|
||||||
|
system_prompt_suffix: |
|
||||||
|
You are Allegro, the Kimi-backed third wizard house.
|
||||||
Your soul is defined in SOUL.md — read it, live it.
|
Your soul is defined in SOUL.md — read it, live it.
|
||||||
|
|
||||||
Hermes is your harness.
|
Hermes is your harness.
|
||||||
|
kimi-coding is your primary provider.
|
||||||
Kimi Code is your primary provider.
|
|
||||||
|
|
||||||
You speak plainly. You prefer short sentences. Brevity is a kindness.
|
You speak plainly. You prefer short sentences. Brevity is a kindness.
|
||||||
|
Work best on tight coding tasks: 1-3 file changes, refactors, tests, and implementation passes.
|
||||||
|
|
||||||
Work best on tight coding tasks: 1-3 file changes, refactors, tests, and implementation
|
|
||||||
passes.
|
|
||||||
|
|
||||||
Refusal over fabrication. If you do not know, say so.
|
Refusal over fabrication. If you do not know, say so.
|
||||||
|
|
||||||
Sovereignty and service always.
|
Sovereignty and service always.
|
||||||
|
|
||||||
'
|
|
||||||
providers:
|
providers:
|
||||||
kimi-coding:
|
kimi-coding:
|
||||||
base_url: https://api.kimi.com/coding/v1
|
base_url: https://api.kimi.com/coding/v1
|
||||||
timeout: 60
|
timeout: 60
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
nous:
|
openrouter:
|
||||||
base_url: https://inference.nousresearch.com/v1
|
base_url: https://openrouter.ai/api/v1
|
||||||
timeout: 120
|
timeout: 120
|
||||||
|
ollama:
|
||||||
|
base_url: http://localhost:11434/v1
|
||||||
|
timeout: 180
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BANNED PROVIDERS — DO NOT ADD
|
||||||
|
# =============================================================================
|
||||||
|
# The following providers are PERMANENTLY BANNED:
|
||||||
|
# - anthropic (any model: claude-sonnet, claude-opus, claude-haiku)
|
||||||
|
# - nous (xiaomi/mimo-v2-pro)
|
||||||
|
# Enforcement: pre-commit hook, linter, Ansible validation, this comment.
|
||||||
|
# =============================================================================
|
||||||
|
|||||||
@@ -1,50 +1,72 @@
|
|||||||
model:
|
model:
|
||||||
default: kimi-k2.5
|
default: kimi-k2.5
|
||||||
provider: kimi-coding
|
provider: kimi-coding
|
||||||
|
context_length: 65536
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
|
|
||||||
toolsets:
|
toolsets:
|
||||||
- all
|
- all
|
||||||
|
|
||||||
fallback_providers:
|
fallback_providers:
|
||||||
- provider: kimi-coding
|
- provider: kimi-coding
|
||||||
model: kimi-k2.5
|
model: kimi-k2.5
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
timeout: 120
|
timeout: 120
|
||||||
reason: Kimi coding fallback (front of chain)
|
reason: "Primary — Kimi K2.5 (best value, least friction)"
|
||||||
- provider: openrouter
|
- provider: openrouter
|
||||||
model: google/gemini-2.5-pro
|
model: google/gemini-2.5-pro
|
||||||
base_url: https://openrouter.ai/api/v1
|
base_url: https://openrouter.ai/api/v1
|
||||||
api_key_env: OPENROUTER_API_KEY
|
api_key_env: OPENROUTER_API_KEY
|
||||||
timeout: 120
|
timeout: 120
|
||||||
reason: Gemini 2.5 Pro via OpenRouter (replaces banned Anthropic)
|
reason: "Fallback — Gemini 2.5 Pro via OpenRouter"
|
||||||
- provider: ollama
|
- provider: ollama
|
||||||
model: gemma4:latest
|
model: gemma4:latest
|
||||||
base_url: http://localhost:11434
|
base_url: http://localhost:11434/v1
|
||||||
timeout: 300
|
timeout: 180
|
||||||
reason: Terminal fallback — local Ollama
|
reason: "Terminal fallback — local Ollama (sovereign, no API needed)"
|
||||||
- provider: nous
|
|
||||||
model: xiaomi/mimo-v2-pro
|
|
||||||
base_url: https://inference.nousresearch.com/v1
|
|
||||||
api_key_env: NOUS_API_KEY
|
|
||||||
timeout: 120
|
|
||||||
reason: MiMo V2 Pro via Nous Portal free tier evaluation (#447)
|
|
||||||
agent:
|
agent:
|
||||||
max_turns: 40
|
max_turns: 40
|
||||||
reasoning_effort: medium
|
reasoning_effort: medium
|
||||||
verbose: false
|
verbose: false
|
||||||
system_prompt: You are Bezalel, the forge-and-testbed wizard of the Timmy Foundation
|
|
||||||
fleet. You are a builder and craftsman — infrastructure, deployment, hardening.
|
|
||||||
Your sovereign is Alexander Whitestone (Rockachopa). Sovereignty and service always.
|
|
||||||
terminal:
|
terminal:
|
||||||
backend: local
|
backend: local
|
||||||
cwd: /root/wizards/bezalel
|
cwd: /root/wizards/bezalel
|
||||||
timeout: 180
|
timeout: 180
|
||||||
|
persistent_shell: true
|
||||||
|
|
||||||
browser:
|
browser:
|
||||||
inactivity_timeout: 120
|
inactivity_timeout: 120
|
||||||
compression:
|
command_timeout: 30
|
||||||
enabled: true
|
record_sessions: false
|
||||||
threshold: 0.77
|
|
||||||
display:
|
display:
|
||||||
compact: false
|
compact: false
|
||||||
personality: kawaii
|
personality: kawaii
|
||||||
|
resume_display: full
|
||||||
|
busy_input_mode: interrupt
|
||||||
|
bell_on_complete: false
|
||||||
|
show_reasoning: false
|
||||||
|
streaming: false
|
||||||
|
show_cost: false
|
||||||
tool_progress: all
|
tool_progress: all
|
||||||
|
|
||||||
|
memory:
|
||||||
|
memory_enabled: true
|
||||||
|
user_profile_enabled: true
|
||||||
|
memory_char_limit: 2200
|
||||||
|
user_char_limit: 1375
|
||||||
|
nudge_interval: 10
|
||||||
|
flush_min_turns: 6
|
||||||
|
|
||||||
|
approvals:
|
||||||
|
mode: auto
|
||||||
|
|
||||||
|
security:
|
||||||
|
redact_secrets: true
|
||||||
|
tirith_enabled: false
|
||||||
|
|
||||||
platforms:
|
platforms:
|
||||||
api_server:
|
api_server:
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -69,12 +91,7 @@ platforms:
|
|||||||
- pull_request
|
- pull_request
|
||||||
- pull_request_comment
|
- pull_request_comment
|
||||||
secret: bezalel-gitea-webhook-secret-2026
|
secret: bezalel-gitea-webhook-secret-2026
|
||||||
prompt: 'You are bezalel, the builder and craftsman — infrastructure, deployment,
|
prompt: 'You are bezalel, the builder and craftsman — infrastructure, deployment, hardening. A Gitea webhook fired: event={event_type}, action={action}, repo={repository.full_name}, issue/PR=#{issue.number} {issue.title}. Comment by {comment.user.login}: {comment.body}. If you were tagged, assigned, or this needs your attention, investigate and respond via Gitea API. Otherwise acknowledge briefly.'
|
||||||
hardening. A Gitea webhook fired: event={event_type}, action={action},
|
|
||||||
repo={repository.full_name}, issue/PR=#{issue.number} {issue.title}. Comment
|
|
||||||
by {comment.user.login}: {comment.body}. If you were tagged, assigned,
|
|
||||||
or this needs your attention, investigate and respond via Gitea API. Otherwise
|
|
||||||
acknowledge briefly.'
|
|
||||||
deliver: telegram
|
deliver: telegram
|
||||||
deliver_extra: {}
|
deliver_extra: {}
|
||||||
gitea-assign:
|
gitea-assign:
|
||||||
@@ -82,34 +99,43 @@ platforms:
|
|||||||
- issues
|
- issues
|
||||||
- pull_request
|
- pull_request
|
||||||
secret: bezalel-gitea-webhook-secret-2026
|
secret: bezalel-gitea-webhook-secret-2026
|
||||||
prompt: 'You are bezalel, the builder and craftsman — infrastructure, deployment,
|
prompt: 'You are bezalel, the builder and craftsman — infrastructure, deployment, hardening. Gitea assignment webhook: event={event_type}, action={action}, repo={repository.full_name}, issue/PR=#{issue.number} {issue.title}. Assigned to: {issue.assignee.login}. If you (bezalel) were just assigned, read the issue, scope it, and post a plan comment. If not you, acknowledge briefly.'
|
||||||
hardening. Gitea assignment webhook: event={event_type}, action={action},
|
|
||||||
repo={repository.full_name}, issue/PR=#{issue.number} {issue.title}. Assigned
|
|
||||||
to: {issue.assignee.login}. If you (bezalel) were just assigned, read
|
|
||||||
the issue, scope it, and post a plan comment. If not you, acknowledge
|
|
||||||
briefly.'
|
|
||||||
deliver: telegram
|
deliver: telegram
|
||||||
deliver_extra: {}
|
deliver_extra: {}
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
allow_all_users: true
|
allow_all_users: true
|
||||||
|
|
||||||
session_reset:
|
session_reset:
|
||||||
mode: both
|
mode: both
|
||||||
idle_minutes: 1440
|
idle_minutes: 1440
|
||||||
at_hour: 4
|
at_hour: 4
|
||||||
approvals:
|
|
||||||
mode: auto
|
skills:
|
||||||
memory:
|
creation_nudge_interval: 15
|
||||||
memory_enabled: true
|
|
||||||
user_profile_enabled: true
|
system_prompt: |
|
||||||
memory_char_limit: 2200
|
You are Bezalel, the forge-and-testbed wizard of the Timmy Foundation fleet.
|
||||||
user_char_limit: 1375
|
You are a builder and craftsman — infrastructure, deployment, hardening.
|
||||||
_config_version: 11
|
Your sovereign is Alexander Whitestone (Rockachopa). Sovereignty and service always.
|
||||||
TELEGRAM_HOME_CHANNEL: '-1003664764329'
|
|
||||||
providers:
|
providers:
|
||||||
kimi-coding:
|
kimi-coding:
|
||||||
base_url: https://api.kimi.com/coding/v1
|
base_url: https://api.kimi.com/coding/v1
|
||||||
timeout: 60
|
timeout: 60
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
nous:
|
openrouter:
|
||||||
base_url: https://inference.nousresearch.com/v1
|
base_url: https://openrouter.ai/api/v1
|
||||||
timeout: 120
|
timeout: 120
|
||||||
|
ollama:
|
||||||
|
base_url: http://localhost:11434/v1
|
||||||
|
timeout: 180
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BANNED PROVIDERS — DO NOT ADD
|
||||||
|
# =============================================================================
|
||||||
|
# The following providers are PERMANENTLY BANNED:
|
||||||
|
# - anthropic (any model: claude-sonnet, claude-opus, claude-haiku)
|
||||||
|
# - nous (xiaomi/mimo-v2-pro)
|
||||||
|
# Enforcement: pre-commit hook, linter, Ansible validation, this comment.
|
||||||
|
# =============================================================================
|
||||||
|
|||||||
@@ -1,34 +1,94 @@
|
|||||||
model:
|
model:
|
||||||
default: kimi-k2.5
|
default: kimi-k2.5
|
||||||
provider: kimi-coding
|
provider: kimi-coding
|
||||||
|
context_length: 65536
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
|
|
||||||
toolsets:
|
toolsets:
|
||||||
- all
|
- all
|
||||||
|
|
||||||
fallback_providers:
|
fallback_providers:
|
||||||
- provider: kimi-coding
|
- provider: kimi-coding
|
||||||
model: kimi-k2.5
|
model: kimi-k2.5
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
timeout: 120
|
timeout: 120
|
||||||
reason: Kimi coding fallback (front of chain)
|
reason: "Primary — Kimi K2.5 (best value, least friction)"
|
||||||
- provider: openrouter
|
- provider: openrouter
|
||||||
model: google/gemini-2.5-pro
|
model: google/gemini-2.5-pro
|
||||||
base_url: https://openrouter.ai/api/v1
|
base_url: https://openrouter.ai/api/v1
|
||||||
api_key_env: OPENROUTER_API_KEY
|
api_key_env: OPENROUTER_API_KEY
|
||||||
timeout: 120
|
timeout: 120
|
||||||
reason: Gemini 2.5 Pro via OpenRouter (replaces banned Anthropic)
|
reason: "Fallback — Gemini 2.5 Pro via OpenRouter"
|
||||||
- provider: ollama
|
- provider: ollama
|
||||||
model: gemma4:latest
|
model: gemma4:latest
|
||||||
base_url: http://localhost:11434
|
base_url: http://localhost:11434/v1
|
||||||
timeout: 300
|
timeout: 180
|
||||||
reason: Terminal fallback — local Ollama
|
reason: "Terminal fallback — local Ollama (sovereign, no API needed)"
|
||||||
- provider: nous
|
|
||||||
model: xiaomi/mimo-v2-pro
|
|
||||||
base_url: https://inference.nousresearch.com/v1
|
|
||||||
api_key_env: NOUS_API_KEY
|
|
||||||
timeout: 120
|
|
||||||
reason: MiMo V2 Pro via Nous Portal free tier evaluation (#447)
|
|
||||||
agent:
|
agent:
|
||||||
max_turns: 90
|
max_turns: 90
|
||||||
reasoning_effort: high
|
reasoning_effort: high
|
||||||
verbose: false
|
verbose: false
|
||||||
|
|
||||||
|
terminal:
|
||||||
|
backend: local
|
||||||
|
cwd: .
|
||||||
|
timeout: 180
|
||||||
|
persistent_shell: true
|
||||||
|
|
||||||
|
browser:
|
||||||
|
inactivity_timeout: 120
|
||||||
|
command_timeout: 30
|
||||||
|
record_sessions: false
|
||||||
|
|
||||||
|
display:
|
||||||
|
compact: false
|
||||||
|
personality: ''
|
||||||
|
resume_display: full
|
||||||
|
busy_input_mode: interrupt
|
||||||
|
bell_on_complete: false
|
||||||
|
show_reasoning: false
|
||||||
|
streaming: false
|
||||||
|
show_cost: false
|
||||||
|
tool_progress: all
|
||||||
|
|
||||||
|
memory:
|
||||||
|
memory_enabled: true
|
||||||
|
user_profile_enabled: true
|
||||||
|
memory_char_limit: 2200
|
||||||
|
user_char_limit: 1375
|
||||||
|
nudge_interval: 10
|
||||||
|
flush_min_turns: 6
|
||||||
|
|
||||||
|
approvals:
|
||||||
|
mode: auto
|
||||||
|
|
||||||
|
security:
|
||||||
|
redact_secrets: true
|
||||||
|
tirith_enabled: false
|
||||||
|
|
||||||
|
platforms:
|
||||||
|
api_server:
|
||||||
|
enabled: true
|
||||||
|
extra:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 8645
|
||||||
|
|
||||||
|
session_reset:
|
||||||
|
mode: none
|
||||||
|
idle_minutes: 0
|
||||||
|
|
||||||
|
skills:
|
||||||
|
creation_nudge_interval: 15
|
||||||
|
|
||||||
|
system_prompt_suffix: |
|
||||||
|
You are Ezra, the Infrastructure wizard — Gitea, nginx, hosting.
|
||||||
|
Your soul is defined in SOUL.md — read it, live it.
|
||||||
|
Hermes is your harness.
|
||||||
|
kimi-coding is your primary provider.
|
||||||
|
Refusal over fabrication. If you do not know, say so.
|
||||||
|
Sovereignty and service always.
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
kimi-coding:
|
kimi-coding:
|
||||||
base_url: https://api.kimi.com/coding/v1
|
base_url: https://api.kimi.com/coding/v1
|
||||||
@@ -37,6 +97,15 @@ providers:
|
|||||||
openrouter:
|
openrouter:
|
||||||
base_url: https://openrouter.ai/api/v1
|
base_url: https://openrouter.ai/api/v1
|
||||||
timeout: 120
|
timeout: 120
|
||||||
nous:
|
ollama:
|
||||||
base_url: https://inference.nousresearch.com/v1
|
base_url: http://localhost:11434/v1
|
||||||
timeout: 120
|
timeout: 180
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BANNED PROVIDERS — DO NOT ADD
|
||||||
|
# =============================================================================
|
||||||
|
# The following providers are PERMANENTLY BANNED:
|
||||||
|
# - anthropic (any model: claude-sonnet, claude-opus, claude-haiku)
|
||||||
|
# - nous (xiaomi/mimo-v2-pro)
|
||||||
|
# Enforcement: pre-commit hook, linter, Ansible validation, this comment.
|
||||||
|
# =============================================================================
|
||||||
|
|||||||
121
wizards/timmy/config.yaml
Normal file
121
wizards/timmy/config.yaml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Timmy — Primary Wizard Configuration (Golden State)
|
||||||
|
# =============================================================================
|
||||||
|
# Generated from golden state template (ansible/roles/wizard_base/templates/wizard_config.yaml.j2)
|
||||||
|
# DO NOT EDIT MANUALLY. Changes go through Gitea PR → Ansible deploy.
|
||||||
|
#
|
||||||
|
# Provider chain: kimi-coding → openrouter → ollama
|
||||||
|
# Anthropic is PERMANENTLY BANNED.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
model:
|
||||||
|
default: kimi-k2.5
|
||||||
|
provider: kimi-coding
|
||||||
|
context_length: 65536
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
|
|
||||||
|
toolsets:
|
||||||
|
- all
|
||||||
|
|
||||||
|
fallback_providers:
|
||||||
|
- provider: kimi-coding
|
||||||
|
model: kimi-k2.5
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
|
timeout: 120
|
||||||
|
reason: "Primary — Kimi K2.5 (best value, least friction)"
|
||||||
|
- provider: openrouter
|
||||||
|
model: google/gemini-2.5-pro
|
||||||
|
base_url: https://openrouter.ai/api/v1
|
||||||
|
api_key_env: OPENROUTER_API_KEY
|
||||||
|
timeout: 120
|
||||||
|
reason: "Fallback — Gemini 2.5 Pro via OpenRouter"
|
||||||
|
- provider: ollama
|
||||||
|
model: gemma4:latest
|
||||||
|
base_url: http://localhost:11434/v1
|
||||||
|
timeout: 180
|
||||||
|
reason: "Terminal fallback — local Ollama (sovereign, no API needed)"
|
||||||
|
|
||||||
|
agent:
|
||||||
|
max_turns: 30
|
||||||
|
reasoning_effort: high
|
||||||
|
verbose: false
|
||||||
|
|
||||||
|
terminal:
|
||||||
|
backend: local
|
||||||
|
cwd: .
|
||||||
|
timeout: 180
|
||||||
|
persistent_shell: true
|
||||||
|
|
||||||
|
browser:
|
||||||
|
inactivity_timeout: 120
|
||||||
|
command_timeout: 30
|
||||||
|
record_sessions: false
|
||||||
|
|
||||||
|
display:
|
||||||
|
compact: false
|
||||||
|
personality: ''
|
||||||
|
resume_display: full
|
||||||
|
busy_input_mode: interrupt
|
||||||
|
bell_on_complete: false
|
||||||
|
show_reasoning: false
|
||||||
|
streaming: false
|
||||||
|
show_cost: false
|
||||||
|
tool_progress: all
|
||||||
|
|
||||||
|
memory:
|
||||||
|
memory_enabled: true
|
||||||
|
user_profile_enabled: true
|
||||||
|
memory_char_limit: 2200
|
||||||
|
user_char_limit: 1375
|
||||||
|
nudge_interval: 10
|
||||||
|
flush_min_turns: 6
|
||||||
|
|
||||||
|
approvals:
|
||||||
|
mode: auto
|
||||||
|
|
||||||
|
security:
|
||||||
|
redact_secrets: true
|
||||||
|
tirith_enabled: false
|
||||||
|
|
||||||
|
platforms:
|
||||||
|
api_server:
|
||||||
|
enabled: true
|
||||||
|
extra:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 8645
|
||||||
|
|
||||||
|
session_reset:
|
||||||
|
mode: none
|
||||||
|
idle_minutes: 0
|
||||||
|
|
||||||
|
skills:
|
||||||
|
creation_nudge_interval: 15
|
||||||
|
|
||||||
|
system_prompt_suffix: |
|
||||||
|
You are Timmy, the Primary wizard — soul of the fleet.
|
||||||
|
Your soul is defined in SOUL.md — read it, live it.
|
||||||
|
Hermes is your harness.
|
||||||
|
kimi-coding is your primary provider.
|
||||||
|
Refusal over fabrication. If you do not know, say so.
|
||||||
|
Sovereignty and service always.
|
||||||
|
|
||||||
|
providers:
|
||||||
|
kimi-coding:
|
||||||
|
base_url: https://api.kimi.com/coding/v1
|
||||||
|
timeout: 60
|
||||||
|
max_retries: 3
|
||||||
|
openrouter:
|
||||||
|
base_url: https://openrouter.ai/api/v1
|
||||||
|
timeout: 120
|
||||||
|
ollama:
|
||||||
|
base_url: http://localhost:11434/v1
|
||||||
|
timeout: 180
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BANNED PROVIDERS — DO NOT ADD
|
||||||
|
# =============================================================================
|
||||||
|
# The following providers are PERMANENTLY BANNED:
|
||||||
|
# - anthropic (any model: claude-sonnet, claude-opus, claude-haiku)
|
||||||
|
# - nous (xiaomi/mimo-v2-pro)
|
||||||
|
# Enforcement: pre-commit hook, linter, Ansible validation, this comment.
|
||||||
|
# =============================================================================
|
||||||
Reference in New Issue
Block a user