# Session 0024: Toolkit and CLI Rewrite and Dashboard Migration **Date:** 2026-02-23 **Status:** Completed **Origin:** MDF Webseiten session 0046 --- ## Work Done ### Phase 3: Shared Toolkit - [x] Completed 5 missing toolkit modules at `/opt/infrastructure/toolkit/`: - `cli.py` — main CLI entry point with all commands (status, start, stop, build, rebuild, destroy, backup, restore, sync, promote, logs, health, disk, backups, offsite, gen-timers, init) - `output.py` — formatted output (Rich tables, JSON mode, plain text fallback) - `restore.py` — restore operations with CLI delegation support - `sync.py` — data sync between environments with CLI delegation - `promote.py` — code promotion (git, rsync, script) with adjacency enforcement - [x] 7 modules already existed from prior sessions: `__init__.py`, `descriptor.py`, `docker.py`, `backup.py`, `database.py`, `health.py`, `discovery.py` ### Phase 4: Ops CLI Rewrite - [x] Replaced 950-line bash ops CLI with 7-line bash shim → `python3 -m toolkit.cli` - [x] Old CLI backed up as `ops.bak.20260223` - [x] New commands added: `start`, `stop`, `build`, `destroy`, `logs`, `restart`, `init` - [x] All commands read from `project.yaml` descriptors — no `registry.yaml` dependency - [x] Container prefix matching fixed: handles `{env}` placeholder expansion in `container_prefix` ### Phase 5: Dashboard Adaptation - [x] Rewrote 4 dashboard routers to use project.yaml: - `registry.py` — imports `toolkit.discovery.all_projects()` instead of parsing registry.yaml - `services.py` — uses `toolkit.descriptor.find()` for container name resolution - `rebuild.py` — massive rewrite: 707 → 348 lines, removed ALL Coolify API code, uses direct docker compose - `schedule.py` — reads from descriptors for GET, still writes to registry.yaml for PUT (gen-timers compatibility) - [x] Verified all API endpoints working: - `/api/registry/` — returns all 5 projects from descriptors - `/api/status/` — shows 25 containers - `/api/schedule/` — shows backup schedules for all 5 projects - `/api/services/logs/mdf/prod/wordpress` — correctly resolves container name ## Key Decisions / Learnings - `rebuild.py` now uses `_compose_cmd()` helper that finds compose file (.yaml/.yml), env-file (.env.{env}/.env), and adds `--profile {env}` — removes all Coolify API dependency - Dashboard container has `/opt/infrastructure` mounted → can import toolkit directly via Python - pyyaml 6.0.3 confirmed available in dashboard container - `schedule.py` still writes to `registry.yaml` for PUT/gen-timers — full descriptor migration is a future task - `container_prefix_for(env)` expands `{env}` in prefix, then matches `{prefix}-*` containers ## Files Changed - `/opt/infrastructure/toolkit/cli.py` — new (all CLI commands) - `/opt/infrastructure/toolkit/output.py` — new (Rich/JSON/plain output) - `/opt/infrastructure/toolkit/restore.py` — new - `/opt/infrastructure/toolkit/sync.py` — new - `/opt/infrastructure/toolkit/promote.py` — new - `/usr/local/bin/ops` — rewritten as 7-line bash shim - `app/routers/registry.py` — uses toolkit.discovery - `app/routers/services.py` — uses toolkit.descriptor - `app/routers/rebuild.py` — 707→348 lines, Coolify removed - `app/routers/schedule.py` — descriptor-backed GET --- **Tags:** #Session #OpsToolkit #OpsCLI #OpsDashboard