feat(doctor): add fn doctor CLI + 14 functions for system management
Adds `fn doctor` read-only diagnostic command with subcommands artefacts, services, sync, uses-functions, unused, and --json flag for agents. Each subcommand wraps a registry function in functions/infra/. New functions: - artefact_doctor, services_status, pc_locations_drift, audit_uses_functions, find_unused_functions (Go diagnostics) - backup_sqlite_db, rotate_backups, wait_for_http, wait_for_port, port_kill, tail_journal, pre_commit_hook_install (bash utilities) - notify_telegram (Go HTTP) - backup_all pipeline (tag launcher) Plus prior session leftovers (scan_secrets_in_dirty, append_diary_entry, git utilities, http_session_cookie_middleware, compile/full-git pipelines). Fixes pc_locations_drift filepath.Join bug with absolute dir_path. Documents fn doctor in CLAUDE.md, .claude/rules/fn_doctor.md (rule 23), docs/architecture.md, CHANGELOG.md (2026-05-07), and diary entry. First fn doctor uses-functions run found drift in 7/12 apps (deuda para sincronizar app.md con imports reales). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-197
@@ -1,208 +1,37 @@
|
||||
# /compile — Compila la app actual y la copia al escritorio de Windows
|
||||
# /compile — Compila app C++ y la copia al escritorio de Windows
|
||||
|
||||
Compila una app del registry para los targets que soporte (Windows via MinGW, Android via Gradle/NDK si esta configurado) y deja el resultado en `/mnt/c/Users/lucas/Desktop/apps/<app>/`, listo para usar desde Windows.
|
||||
Wrapper sobre el pipeline `compile_cpp_app_bash_pipelines`. Toda la lógica vive en el registry (resolver app desde CWD/arg, cross-compile MinGW, copiar exe + DLLs + assets/ + enrichers/ + runtime/ a `/mnt/c/Users/lucas/Desktop/apps/<app>/`, taskkill previo, preservar `local_files/`).
|
||||
|
||||
Pensado para apps C++ del workspace `cpp/` (donde ya hay toolchain `mingw-w64.cmake` y build dir `cpp/build/windows/`). Si en el futuro hay apps Android (Gradle wrapper o NDK), tambien las detecta.
|
||||
```bash
|
||||
cd /home/lucas/fn_registry
|
||||
./fn run compile_cpp_app "$ARGUMENTS"
|
||||
```
|
||||
|
||||
## Argumento
|
||||
|
||||
`$ARGUMENTS` — opcional. Nombre de la app a compilar (ej: `chart_demo`, `registry_dashboard`).
|
||||
`$ARGUMENTS` — opcional. Nombre de app (ej: `chart_demo`).
|
||||
|
||||
- Sin argumento: detectar la app desde `pwd` (si estas dentro de `cpp/apps/<X>/` o `projects/*/apps/<X>/`).
|
||||
- Si no hay app deducible y no se pasa argumento → listar apps disponibles y pedir nombre.
|
||||
- Si se pasa argumento, usarlo directamente.
|
||||
- Sin argumento: deduce desde `pwd` si estás dentro de `cpp/apps/<X>/` o `projects/*/apps/<X>/`.
|
||||
- Si no se puede deducir y no se pasa argumento, el pipeline lista las apps disponibles en stderr y aborta.
|
||||
|
||||
## Pasos
|
||||
## Qué hace el pipeline
|
||||
|
||||
### 1. Resolver la app y su directorio fuente
|
||||
|
||||
```bash
|
||||
ROOT=/home/lucas/fn_registry
|
||||
APP_ARG="$ARGUMENTS"
|
||||
|
||||
# Detectar desde CWD si no hay argumento
|
||||
if [ -z "$APP_ARG" ]; then
|
||||
CWD="$(pwd)"
|
||||
case "$CWD" in
|
||||
"$ROOT"/cpp/apps/*|"$ROOT"/projects/*/apps/*)
|
||||
APP_ARG="$(basename "$CWD")" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Si sigue vacio, listar apps y abortar
|
||||
if [ -z "$APP_ARG" ]; then
|
||||
echo "Apps disponibles:"
|
||||
ls "$ROOT"/cpp/apps/ 2>/dev/null
|
||||
ls "$ROOT"/projects/*/apps/ 2>/dev/null
|
||||
echo "Uso: /compile <app_name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Buscar el directorio real
|
||||
APP_DIR=""
|
||||
for cand in "$ROOT/cpp/apps/$APP_ARG" "$ROOT"/projects/*/apps/"$APP_ARG"; do
|
||||
[ -d "$cand" ] && APP_DIR="$cand" && break
|
||||
done
|
||||
|
||||
if [ -z "$APP_DIR" ]; then
|
||||
echo "No encuentro app '$APP_ARG' en cpp/apps/ ni projects/*/apps/"
|
||||
exit 1
|
||||
fi
|
||||
echo "App: $APP_ARG"
|
||||
echo "Dir: $APP_DIR"
|
||||
```
|
||||
|
||||
### 2. Detectar targets soportados
|
||||
|
||||
Examinar el `app.md` y los archivos del directorio para decidir que se puede compilar:
|
||||
|
||||
- **Windows (MinGW)**: si la app tiene `CMakeLists.txt` y se registra en `cpp/CMakeLists.txt` (es decir, aparece como subdirectorio en `cpp/build/windows/apps/<APP>/`). Default para apps C++.
|
||||
- **Android**: si existe `AndroidManifest.xml`, `build.gradle`, `build.gradle.kts` o carpeta `android/` dentro de `$APP_DIR`. (Hoy no hay ninguna; saltar silenciosamente.)
|
||||
- **Linux** (opcional, no por defecto): el build dir `cpp/build/` ya genera el binario para Linux. Solo se hace si el usuario lo pide explicitamente.
|
||||
|
||||
```bash
|
||||
TARGETS=()
|
||||
[ -f "$APP_DIR/CMakeLists.txt" ] && TARGETS+=("windows")
|
||||
|
||||
if [ -f "$APP_DIR/AndroidManifest.xml" ] || \
|
||||
[ -f "$APP_DIR/build.gradle" ] || \
|
||||
[ -f "$APP_DIR/build.gradle.kts" ] || \
|
||||
[ -d "$APP_DIR/android" ]; then
|
||||
TARGETS+=("android")
|
||||
fi
|
||||
|
||||
if [ ${#TARGETS[@]} -eq 0 ]; then
|
||||
echo "No se detecta ningun target compilable en $APP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
echo "Targets: ${TARGETS[*]}"
|
||||
```
|
||||
|
||||
### 3. Compilar Windows (cross-compile MinGW)
|
||||
|
||||
Solo si `windows` esta en TARGETS.
|
||||
|
||||
```bash
|
||||
BUILD_WIN="$ROOT/cpp/build/windows"
|
||||
|
||||
# Configurar build dir si no existe
|
||||
if [ ! -f "$BUILD_WIN/CMakeCache.txt" ]; then
|
||||
mkdir -p "$BUILD_WIN"
|
||||
cmake -S "$ROOT/cpp" -B "$BUILD_WIN" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$ROOT/cpp/toolchains/mingw-w64.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
fi
|
||||
|
||||
# Compilar SOLO el target de la app (no todo el arbol)
|
||||
cmake --build "$BUILD_WIN" --target "$APP_ARG" -j"$(nproc)"
|
||||
```
|
||||
|
||||
Si el target no existe en CMake (porque la app no esta registrada en `cpp/CMakeLists.txt`), reportar y proponer registrarla siguiendo `cpp_apps.md` §5. NO autoregistrarla sin confirmacion del usuario.
|
||||
|
||||
### 4. Copiar a `/mnt/c/Users/lucas/Desktop/apps/<APP>/`
|
||||
|
||||
Layout estandar (convencion `assets/` + `local_files/`, ver `cpp_apps.md` §7):
|
||||
|
||||
```
|
||||
Desktop/apps/<APP>/
|
||||
├── <APP>.exe ← binario (top level por convencion Windows DLL)
|
||||
├── *.dll ← DLLs nativas (Windows las busca junto al exe)
|
||||
├── assets/ ← read-only, ships con el zip
|
||||
│ ├── *.ttf ← fuentes (vienen de add_imgui_app)
|
||||
│ ├── enrichers/ ← si <app_dir>/enrichers existe
|
||||
│ ├── runtime/ ← Python embed si app.md tiene python_runtime: true
|
||||
│ ├── gx-cli, gx-cli.exe ← si la app necesita un MCP server
|
||||
│ └── ... ← cualquier otro asset distribuible
|
||||
└── local_files/ ← writable, per-PC, creado por la app al
|
||||
primer arranque. NUNCA borrar al recompilar.
|
||||
```
|
||||
|
||||
```bash
|
||||
DEST="/mnt/c/Users/lucas/Desktop/apps/$APP_ARG"
|
||||
ASSETS="$DEST/assets"
|
||||
mkdir -p "$DEST" "$ASSETS"
|
||||
|
||||
EXE_SRC="$BUILD_WIN/apps/$APP_ARG/$APP_ARG.exe"
|
||||
if [ ! -f "$EXE_SRC" ]; then
|
||||
echo "ERROR: no se ha generado $EXE_SRC"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 1. Binario + DLLs en el top level (Windows DLL search convention).
|
||||
cp -v "$EXE_SRC" "$DEST/"
|
||||
find "$BUILD_WIN/apps/$APP_ARG" -maxdepth 1 -type f -name '*.dll' \
|
||||
-exec cp -v {} "$DEST/" \;
|
||||
|
||||
# 2. assets/ — TTFs (las copia add_imgui_app a build/<app>/assets/) y
|
||||
# cualquier asset extra del build (build/<app>/assets/*).
|
||||
if [ -d "$BUILD_WIN/apps/$APP_ARG/assets" ]; then
|
||||
rsync -a --delete "$BUILD_WIN/apps/$APP_ARG/assets/" "$ASSETS/"
|
||||
fi
|
||||
|
||||
# 3. enrichers/ del app_dir -> assets/enrichers/.
|
||||
if [ -d "$APP_DIR/enrichers" ]; then
|
||||
rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
|
||||
"$APP_DIR/enrichers/" "$ASSETS/enrichers/"
|
||||
fi
|
||||
|
||||
# 4. runtime/ Python embebido -> assets/runtime/ (si la app lo declara).
|
||||
if grep -q '^python_runtime:[[:space:]]*true' "$APP_DIR/app.md" 2>/dev/null; then
|
||||
if [ ! -d "$APP_DIR/runtime/python" ] || \
|
||||
[ "$APP_DIR/app.md" -nt "$APP_DIR/runtime/.lock" ]; then
|
||||
echo "[freeze] regenerando runtime Python (Windows) para $APP_ARG"
|
||||
"$APP_DIR/tools/freeze_python_runtime.sh" "$APP_DIR" windows
|
||||
fi
|
||||
rsync -a --delete --exclude '__pycache__' --exclude '*.pyc' \
|
||||
"$APP_DIR/runtime/" "$ASSETS/runtime/"
|
||||
fi
|
||||
|
||||
# 5. Otros assets sueltos del app_dir (gx-cli, scripts varios). El
|
||||
# convention es: si vive en <app_dir>/ y no es codigo fuente, va a
|
||||
# assets/. Ahora mismo la unica excepcion es gx-cli (graph_explorer).
|
||||
for extra in gx-cli gx-cli.exe; do
|
||||
if [ -f "$APP_DIR/$extra" ]; then
|
||||
cp -v "$APP_DIR/$extra" "$ASSETS/"
|
||||
fi
|
||||
done
|
||||
|
||||
# 6. NO TOCAR local_files/. Si existe en $DEST, preservar — contiene
|
||||
# estado del usuario (DBs, settings, layouts ImGui, proyectos).
|
||||
echo "OK: $APP_ARG -> $DEST"
|
||||
[ -d "$DEST/local_files" ] && echo " local_files/ preservado: $(du -sh "$DEST/local_files" | cut -f1)"
|
||||
```
|
||||
|
||||
### 5. Compilar Android (solo si TARGETS contiene `android`)
|
||||
|
||||
Hoy no hay apps Android en el registry, asi que esta rama no se ejecuta. Cuando se anada la primera, este es el patron previsto:
|
||||
|
||||
```bash
|
||||
if [[ " ${TARGETS[*]} " == *" android "* ]]; then
|
||||
ANDROID_DIR="$APP_DIR"
|
||||
[ -d "$APP_DIR/android" ] && ANDROID_DIR="$APP_DIR/android"
|
||||
|
||||
cd "$ANDROID_DIR"
|
||||
if [ -x "./gradlew" ]; then
|
||||
./gradlew assembleRelease
|
||||
APK="$(find "$ANDROID_DIR/app/build/outputs/apk/release" -name '*.apk' | head -n1)"
|
||||
[ -n "$APK" ] && cp -v "$APK" "/mnt/c/Users/lucas/Desktop/apps/$APP_ARG/"
|
||||
else
|
||||
echo "android: no hay ./gradlew en $ANDROID_DIR — saltando."
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
Cuando llegue la primera app Android, este bloque puede ampliarse (firma, ABI splits, etc.).
|
||||
|
||||
### 6. Resumen
|
||||
|
||||
Imprime al final una linea por target con:
|
||||
- Tamano del binario (`ls -lh`)
|
||||
- Path final en `/mnt/c/Users/lucas/Desktop/apps/<APP>/`
|
||||
- Nombre del exe/apk
|
||||
1. `resolve_cpp_app_dir_bash_infra` — resuelve `<app_name>` y `<dir absoluto>` desde arg o CWD.
|
||||
2. Verifica `CMakeLists.txt` en el dir resuelto.
|
||||
3. `build_cpp_windows_bash_infra <app>` — cross-compila el target específico con `cpp/build/windows/` (configura toolchain `mingw-w64.cmake` la primera vez).
|
||||
4. `deploy_cpp_exe_to_windows_bash_infra <app> <dir>`:
|
||||
- `taskkill.exe /IM <app>.exe /F` (pre-autorizado).
|
||||
- Copia `<app>.exe` + DLLs al top-level de `Desktop/apps/<app>/`.
|
||||
- rsync `cpp/build/windows/apps/<app>/assets/` → `Desktop/apps/<app>/assets/`.
|
||||
- rsync `<app_dir>/enrichers/` → `assets/enrichers/` si existe.
|
||||
- Si `app.md` declara `python_runtime: true`, regenera `runtime/` con `tools/freeze_python_runtime.sh` y rsync a `assets/runtime/`.
|
||||
- Copia `gx-cli`/`gx-cli.exe` si existen.
|
||||
- **NUNCA** toca `local_files/` (estado del usuario).
|
||||
5. Imprime `ls -lh` del `.exe` final.
|
||||
|
||||
## Notas
|
||||
|
||||
- El build de Windows usa `cpp/build/windows/` (no `cpp/build/`). El de Linux es `cpp/build/`. Coexisten sin conflicto.
|
||||
- El toolchain `mingw-w64.cmake` ya configura linkado estatico (`-static-libgcc -static-libstdc++ -lwinpthread`) — el `.exe` resultante es self-contained y no necesita DLLs en el escritorio.
|
||||
- Si se pasa una app que vive en `projects/<proj>/apps/<APP>/`, el target CMake sigue siendo `<APP>` (registrado en `cpp/CMakeLists.txt` con `add_subdirectory(${PROJ_DIR}/apps/<APP> ${CMAKE_BINARY_DIR}/apps/<APP>)`).
|
||||
- NO tocar `AdminLocal` ni instalar nada en `Program Files` — solo el escritorio del usuario.
|
||||
- Solo target Windows hoy. Android / Linux quedan fuera (Linux ya lo da `cpp/build/`).
|
||||
- Variables override-ables: `BUILD_WIN`, `WIN_DESKTOP_APPS`, `FN_REGISTRY_ROOT`.
|
||||
- Si la app no está registrada en `cpp/CMakeLists.txt`, `cmake --build --target <app>` falla. Registrar siguiendo `.claude/rules/cpp_apps.md` §5.
|
||||
- Para tocar la lógica: editar `bash/functions/{infra,pipelines}/{resolve_cpp_app_dir,deploy_cpp_exe_to_windows,compile_cpp_app}.sh`, no este wrapper.
|
||||
|
||||
Reference in New Issue
Block a user