{
  "projectPath": "C:\\xampp\\htdocs\\ALimpiezas",
  "slug": "c-xampp-htdocs-ALimpiezas",
  "updatedAt": "2026-06-26T02:36:05.267Z",
  "phases": {
    "explore": {
      "status": "done",
      "completedAt": "2026-06-23T00:20:46.439Z",
      "name": "Explorar",
      "summary": "Explorando el proyecto con restricciones ligeras: leeré archivos clave y listaré la raíz de forma superficial.",
      "fullText": "Explorando el proyecto con restricciones ligeras: leeré archivos clave y listaré la raíz de forma superficial."
    },
    "architecture": {
      "status": "done",
      "completedAt": "2026-06-23T00:26:12.350Z",
      "name": "Arquitectura",
      "summary": "Analizaré la arquitectura leyendo archivos clave y usando Serena para relaciones entre símbolos, respetando las restricciones de modo ligero.",
      "fullText": "Analizaré la arquitectura leyendo archivos clave y usando Serena para relaciones entre símbolos, respetando las restricciones de modo ligero."
    },
    "knowledge": {
      "status": "done",
      "completedAt": "2026-06-26T02:36:05.266Z",
      "name": "Conocimiento",
      "summary": "Consolidando el conocimiento del proyecto leyendo archivos clave sin escanear Core/Dinamic en profundidad.\n# ALimpiezas — Conocimiento consolidado del proyecto\n\n> **Proyecto:** `C:\\xampp\\htdocs\\ALimpiezas` · **Serena:** `project_name: ALimpiezas` · **Docs:** `docs/MANUAL-AGENTE-IA.md`, `docs/MANUAL-USUARIO.md`\n\n---\n\n## 1. Resumen ejecutivo\n\nERP **FacturaScripts 2026.4** (PHP 8.1+, MariaDB `alimpiezas_fs`) desplegado en **XAMPP**, acceso web `/ALimpiezas`. El valor custom está en el plugin **ALimpiezasMobile** (PWA móvil para autónomos: gastos, fiscal 303/130, voz). El código fuente del plugin ",
      "fullText": "Consolidando el conocimiento del proyecto leyendo archivos clave sin escanear Core/Dinamic en profundidad.\n# ALimpiezas — Conocimiento consolidado del proyecto\n\n> **Proyecto:** `C:\\xampp\\htdocs\\ALimpiezas` · **Serena:** `project_name: ALimpiezas` · **Docs:** `docs/MANUAL-AGENTE-IA.md`, `docs/MANUAL-USUARIO.md`\n\n---\n\n## 1. Resumen ejecutivo\n\nERP **FacturaScripts 2026.4** (PHP 8.1+, MariaDB `alimpiezas_fs`) desplegado en **XAMPP**, acceso web `/ALimpiezas`. El valor custom está en el plugin **ALimpiezasMobile** (PWA móvil para autónomos: gastos, fiscal 303/130, voz). El código fuente del plugin vive en `Plugins/`; tras deploy se materializa en `Dinamic/` (no editar `Dinamic/` ni `Core/` directamente). Microservicio **FastAPI** en `ai-service/` (puerto **8090**) para OCR (Tesseract), extracción fiscal y Ollama/Whisper. Operación local vía `tools/start-stack.ps1`, `tools/ai-service-manager.ps1` y `php tools/bootstrap-plugin.php`. Assets públicos solo bajo `Dinamic/Assets/` (Apache rewrite en `.htaccess`).\n\n---\n\n## 2. Glosario\n\n| Término | Definición |\n|---------|------------|\n| **FacturaScripts** | ERP open source (LGPL). Framework en `Core/`; capa dinámica en `Dinamic/`. |\n| **Dinamic** | Carpeta generada por `Plugins::deploy()`: symlinks/copias de plugins activos. Runtime real del código custom. |\n| **Plugin** | Extensión en `Plugins/<Nombre>/` con `facturascripts.ini`, `Init.php`, controladores, modelos. |\n| **ALimpiezasMobile** | Plugin principal (v2): PWA, gastos, fiscal, voz. Grupo settings `alimpiezas`. |\n| **PWA** | App instalable en iPhone/Android. `manifest.json` + `sw.js` en `Dinamic/Assets/`. |\n| **FacturaProveedor** | Factura de compra/gasto en FS. Destino final al confirmar un gasto móvil. |\n| **Proveedor** | Tercero emisor del gasto; se busca/crea por CIF en `ExpenseInvoiceCreator`. |\n| **ai_expense_queue** | Tabla/cola de gastos pendientes de revisión (OCR, manual o IA). |\n| **expense_mode** | Modo de registro: `manual` (sin 8090), `ocr` (Tesseract local), `ai` (OCR + Ollama). |\n| **303 / 130** | Modelos AEAT trimestrales: IVA repercutido/soportado (303) e IRPF estimación directa (130). Borradores en `FiscalModels`. |\n| **Autónomo** | Perfil fiscal objetivo; régimen por defecto `fiscal_regime=autonomo`. |\n| **ai-service** | Microservicio Python (`main.py`): `/health`, `/api/v1/extract-invoice`, `/api/v1/parse-intent`, `/api/v1/transcribe`. |\n| **Ollama** | LLM local (por defecto `qwen2.5:3b`); URL en settings `ollama_url` o env `OLLAMA_URL`. |\n| **Deploy / bootstrap** | `php tools/bootstrap-plugin.php` → `Plugins::enable` + `Plugins::deploy(true, true)` + limpia caché. |\n| **NeoTheme** | Plugin de tema escritorio (instalado, **desactivado**). No afecta la PWA móvil. |\n| **Bridge móvil** | Flujo Cursor desde iPhone/PWA; ver `docs/MOBILE-BRIDGE-TEST.md`. |\n| **AttachedFile** | Archivo adjunto FS; la imagen del ticket se vincula a `FacturaProveedor` al confirmar. |\n\n---\n\n## 3. FAQ dev\n\n### 1. ¿Dónde edito código custom?\nEn `Plugins/ALimpiezasMobile/` (y `ai-service/` para Python). **Nunca** editar `Dinamic/` manualmente: se regenera con deploy.\n\n### 2. ¿Cómo activo cambios del plugin tras editar?\n```powershell\ncd C:\\xampp\\htdocs\\ALimpiezas\nphp tools/bootstrap-plugin.php\n```\nEquivale a activar plugin + reconstruir `Dinamic/` + `Cache::clear()`.\n\n### 3. ¿Por qué 403 en CSS/JS del móvil?\nLas URLs deben apuntar a `Dinamic/Assets/...`, no a `Plugins/.../Assets/`. Twig: `{{ asset('Dinamic/Assets/CSS/mobile.css') }}`. Tras cambiar assets → bootstrap.\n\n### 4. ¿El móvil necesita siempre el puerto 8090?\n**No.** Modo `manual` funciona sin microservicio. OCR/IA requieren `ai-service` en `8090` y `expense_mode` ≠ `manual`. Fiscal, voz local y ayuda no dependen de 8090.\n\n### 5. ¿Cómo arranco todo el stack local?\n```powershell\n.\\tools\\start-stack.ps1          # Apache + MySQL + IA + bandeja\n.\\tools\\ai-service-manager.ps1 -Start   # solo IA\n.\\tools\\ai-service-manager.ps1 -Status  # health check\n```\n\n### 6. Error Errno 10048 / puerto 8090 ocupado\n```powershell\n.\\tools\\ai-service-manager.ps1 -Restart\n.\\tools\\port-services.ps1 -Port 8090\n```\n\n### 7. ¿Flujo completo de un gasto con foto?\n`MobileExpenses` POST `upload` → `AiExpenseProcessor` llama `8090/api/v1/extract-invoice` (si OCR/IA) → fila en `ai_expense_queue` (`pending`) → usuario edita modal → POST `confirm` → `ExpenseInvoiceCreator` crea `Proveedor` + `FacturaProveedor` + adjunto → `status=approved`, `idfacturaprov` relleno.\n\n### 8. ¿Settings del plugin y cómo leerlos?\nGrupo `alimpiezas` en tabla `settings`. PHP: `Tools::settings('alimpiezas', 'expense_mode')`. UI: `MobileSettings`. Claves: `expense_mode`, `ai_service_url`, `fiscal_regime`, `voice_use_service`, `mobile_theme`, `ollama_url`, `ollama_model`.\n\n---\n\n## 4. Checklist onboarding (nuevo dev)\n\n- [ ] **Requisitos:** XAMPP (Apache 2.4 + MariaDB), PHP ≥8.1, Composer, Python 3.10+ para `ai-service`, Tesseract OCR (Windows: `C:\\Program Files\\Tesseract-OCR\\`).\n- [ ] **Clonar/abrir** `C:\\xampp\\htdocs\\ALimpiezas`.\n- [ ] **Composer:** `composer install --ignore-platform-req=ext-pgsql` (PostgreSQL no usado en local).\n- [ ] **Config:** revisar `config.php` (DB `alimpiezas_fs`, `FS_INITIAL_USER`/`PASS`); no commitear secretos.\n- [ ] **Plugins:** `php tools/bootstrap-plugin.php` (activa ALimpiezasMobile).\n- [ ] **IA (opcional):** `pip install -r ai-service/requirements.txt` → `.\\tools\\ai-service-manager.ps1 -Start`.\n- [ ] **Verificar HTTP:** `http://localhost/ALimpiezas/MobileHome` (PC) · `curl http://127.0.0.1:8090/health`.\n- [ ] **Verificar assets:** `curl -I http://localhost/ALimpiezas/Dinamic/Assets/CSS/mobile.css` → 200.\n- [ ] **Leer docs:** `docs/MANUAL-AGENTE-IA.md` (técnico), `docs/MANUAL-USUARIO.md` (UX/PWA).\n- [ ] **Convención de trabajo:** editar `Plugins/` → bootstrap → probar en móvil vía IP LAN (`http://192.168.x.x/ALimpiezas/MobileHome`, no `localhost` en iPhone).\n- [ ] **Tests PHP:** `composer test` (suite FacturaScripts en `Test/`).\n- [ ] **Serena:** proyecto `ALimpiezas`, memoria en `.serena/memories/alimpiezas/project-knowledge.md`.\n\n---\n\n## 5. Prompts guardados\n\n### Bugfix\n```\n[Bugfix ALimpiezas] Tras confirmar gasto en MobileExpenses, la FacturaProveedor no se crea o queda sin IVA.\nContexto: Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php, AiExpenseProcessor.php.\nRepro: subir ticket → confirm → revisar ai_expense_queue.status e idfacturaprov.\nNo editar Dinamic/. Tras fix: php tools/bootstrap-plugin.php.\n```\n\n### Bugfix\n```\n[Bugfix assets 403] mobile.css o manifest.json devuelven 403/404 en /ALimpiezas.\nRevisar .htaccess RewriteCond Dinamic/Assets/, rutas Twig asset('Dinamic/Assets/...'), y ejecutar bootstrap-plugin.php.\n```\n\n### Feature\n```\n[Feature gastos] Añadir campo/concepto X al modal de confirmación de MobileExpenses.\nArchivos: View/MobileExpenses.html.twig, Assets/JS/mobile.js, AiExpenseQueue model, ExpenseInvoiceCreator.\nPersistir en ai_expense_queue si hace falta nueva columna en Table/ai_expense_queue.xml + Init::update().\n```\n\n### Feature\n```\n[Feature fiscal] Mejorar borrador modelo 303/130 en MobileFiscal.\nArchivos: Lib/FiscalModels.php, FiscalCalendar.php, View/MobileFiscal.html.twig.\nMantener disclaimer: no sustituye AEAT ni asesor.\n```\n\n### Refactor\n```\n[Refactor MobileConfig] Centralizar lectura de settings alimpiezas y modos expense_mode.\nSolo Plugins/ALimpiezasMobile/Lib/MobileConfig.php y consumidores; sin cambiar comportamiento observable.\n```\n\n### Refactor\n```\n[Refactor ai-service] Extraer reglas OCR/regex de main.py a módulo separado sin cambiar respuesta JSON de /api/v1/extract-invoice.\nMantener compatibilidad con AiExpenseProcessor.php.\n```\n\n### Deploy / ops\n```\n[Deploy local] Arrancar stack ALimpiezas completo y validar salud.\nEjecutar tools/start-stack.ps1, verificar Apache/MySQL, curl :8090/health y /ALimpiezas/MobileHome.\nSi 8090 ocupado: ai-service-manager.ps1 -Restart.\n```\n\n### Deploy / ops\n```\n[Deploy plugin] Cambios en Plugins/ALimpiezasMobile listos para probar.\nEjecutar php tools/bootstrap-plugin.php, limpiar caché Twig si UI rara, verificar Dinamic/Assets copiados.\n```\n\n### QA\n```\n[QA móvil PWA] Checklist iPhone: login MobileHome, Gastos manual+OCR, confirm→FacturaProveedor, Fiscal 303 preview, Voz command local.\nURL LAN, no localhost. Modo manual sin :8090; OCR con health OK.\n```\n\n### QA\n```\n[QA ai-service] Probar endpoints FastAPI: GET /health, POST /api/v1/extract-invoice (multipart PDF/JPG), POST /api/v1/parse-intent, POST /api/v1/transcribe.\nDocumentar Tesseract/Ollama/Whisper en respuesta health.\n```\n\n---\n\n## Mapa rápido de archivos\n\n| Tarea | Archivo |\n|-------|---------|\n| Settings / modos | `Plugins/ALimpiezasMobile/Lib/MobileConfig.php`, `Init.php` |\n| Cola y upload | `Lib/AiExpenseProcessor.php`, `Controller/MobileExpenses.php` |\n| Crear factura gasto | `Lib/ExpenseInvoiceCreator.php` |\n| OCR / LLM | `ai-service/main.py` |\n| Layout PWA | `View/Master/MobileLayout.html.twig` |\n| JS gastos | `Assets/JS/mobile.js` → deploy a Dinamic |\n| Ops | `tools/bootstrap-plugin.php`, `tools/start-stack.ps1`, `tools/ai-service-manager.ps1` |\n\n---\n\n## Restricciones para el agente\n\n- **Editar:** `Plugins/`, `ai-service/`, `tools/`, `docs/` (si se pide).\n- **No escanear masivamente:** `vendor/`, `node_modules/`, `Core/`, `Dinamic/` (salvo petición explícita).\n- **Shell:** timeouts en comandos; sin bucles infinitos.\n- **Commits:** solo si el usuario lo pide explícitamente.\n\n---\n\nPuedes copiar este bloque completo a Serena con `write_memory` en `alimpiezas/project-knowledge` o pegarlo en memoria local del agente. Si quieres que lo guarde en `.serena/memories/alimpiezas/project-knowledge.md` en el repo, indícalo."
    },
    "automation": {
      "status": "done",
      "completedAt": "2026-06-24T12:56:07.698Z",
      "name": "Automatizar",
      "summary": "Explorando el proyecto de forma focalizada para proponer automatizaciones concretas.\n# Automatizaciones propuestas — ALimpiezas\n\nERP FacturaScripts + plugin móvil `ALimpiezasMobile` + microservicio FastAPI (puerto 8090) en XAMPP. El proyecto ya tiene buena base operativa en `tools/`; lo que falta es unificar flujos, cubrir el plugin custom y reducir pasos manuales post-cambio.\n\n---\n\n## 1. Scripts existentes\n\n### Composer (`composer.json`)\n\n| Script | Comando | Estado |\n|--------|---------|--------|\n| `test` | `vendor/bin/phpunit` | ✅ Funcional (suite FacturaScripts en `Test/`) |\n| `test:covera",
      "fullText": "Explorando el proyecto de forma focalizada para proponer automatizaciones concretas.\n# Automatizaciones propuestas — ALimpiezas\n\nERP FacturaScripts + plugin móvil `ALimpiezasMobile` + microservicio FastAPI (puerto 8090) en XAMPP. El proyecto ya tiene buena base operativa en `tools/`; lo que falta es unificar flujos, cubrir el plugin custom y reducir pasos manuales post-cambio.\n\n---\n\n## 1. Scripts existentes\n\n### Composer (`composer.json`)\n\n| Script | Comando | Estado |\n|--------|---------|--------|\n| `test` | `vendor/bin/phpunit` | ✅ Funcional (suite FacturaScripts en `Test/`) |\n| `test:coverage` | PHPUnit + HTML coverage | ✅ |\n| `phpstan` | Analiza solo `Core/` | ⚠️ No incluye `Plugins/` |\n| `cs-check` / `cs-fix` | PHPCS / PHPCBF | ✅ (requiere config) |\n| `dev-server` | `php -S localhost:8000` | ⚠️ No usa XAMPP real |\n\nInstalación local documentada:\n```powershell\ncomposer install --ignore-platform-req=ext-pgsql\n```\n\n### npm raíz (`package.json`)\n\n| Script | Estado |\n|--------|--------|\n| `build`, `build:css`, `build:js`, `watch` | ❌ Placeholders (`echo \"Add your build process here\"`) |\n| `test` | ❌ Sin tests JS |\n\nDependencias: Bootstrap 5, jQuery, Chart.js, FontAwesome (ERP desktop, no el móvil).\n\n### npm bandeja (`tools/tray/package.json`)\n\n| Script | Comando |\n|--------|---------|\n| `start` | `node alimpiezas_tray.js` |\n\nUtilidades Node: `gen-icons.js`, `gen-icons-ico.js`.\n\n### PowerShell (`tools/`)\n\n| Script | Función |\n|--------|---------|\n| `start-stack.ps1` | Apache + MySQL + IA :8090 + bandeja + log en `data/alimpiezas-startup.log` |\n| `ai-service-manager.ps1` | `-Start` / `-Stop` / `-Restart` / `-Status` del microservicio |\n| `port-services.ps1` | Diagnóstico puertos LISTEN (8090, 80, 3306…) |\n| `install-autostart.ps1` | Tarea al login + HKCU Run para bandeja |\n| `capture-manual-screenshots.ps1` | Abre URLs móviles y guía capturas en `docs/img/` |\n\n### PHP CLI (`tools/`)\n\n| Script | Función |\n|--------|---------|\n| `bootstrap-plugin.php` | `Plugins::enable('ALimpiezasMobile')` + `deploy()` → reconstruye `Dinamic/` |\n| `create-ai-table.php` | Crea tabla `ai_expense_queue` vía `DbUpdater` |\n\n### Python\n\n| Script | Función |\n|--------|---------|\n| `tools/port-services.py` | Alternativa cross-platform a `port-services.ps1` (`--json`) |\n| `ai-service/main.py` | FastAPI OCR/voz (arranque vía `uvicorn`, no script dedicado) |\n\n### Make\n\nNo hay `Makefile`.\n\n### CI (`.github/workflows/`)\n\n| Workflow | Alcance |\n|----------|---------|\n| `test.yml` | PHPUnit × PHP 8.1–8.4 × MySQL/MariaDB/PostgreSQL + audit + PHPCS/PHPStan |\n| `test-countries.yml` | PHPUnit por país (COL, ECU, MEX…) |\n\nAmbos heredados de FacturaScripts upstream; **no prueban el plugin ALimpiezas ni el servicio IA**.\n\n---\n\n## 2. Tareas manuales repetitivas detectadas\n\n| Tarea | Frecuencia | Dolor actual |\n|-------|------------|--------------|\n| **Deploy plugin tras editar `Plugins/`** | Cada cambio PHP/Twig/Assets | `php tools/bootstrap-plugin.php` a mano |\n| **Verificar stack tras arrancar PC** | Diaria | Comprobar Apache, MySQL, `:8090/health`, CSS móvil |\n| **Resolver puerto 8090 ocupado** | Ocasional | `port-services.ps1` + restart manual |\n| **Instalar deps Python IA** | Tras clone/actualizar | `pip install -r ai-service/requirements.txt` sin script |\n| **QA post-cambio móvil** | Cada feature | 3–4 curls/URLs sueltos (documentados en manual) |\n| **Migración tabla cola** | Tras cambio XML | `create-ai-table.php` separado del bootstrap |\n| **Capturas manual usuario** | Al actualizar docs | Semi-automático; captura real sigue siendo manual |\n| **Recordar regla Assets** | Siempre | Editar `Plugins/.../Assets/` → bootstrap; URLs solo `Dinamic/Assets/` |\n| **No editar `Dinamic/` directamente** | Siempre | Riesgo de perder cambios en próximo deploy |\n| **Composer en Windows** | Setup/CI local | Flag `--ignore-platform-req=ext-pgsql` |\n| **Tests del plugin** | Nunca automatizado | 0 tests en `Test/` para ALimpiezasMobile |\n| **Sincronización Bridge móvil → PC** | Desarrollo remoto | Flujo manual vía Cursor Bridge |\n\n---\n\n## 3. Prompts reutilizables (copy-paste para Cursor móvil)\n\n### Prompt 1 — Deploy plugin tras cambios\n\n```\nProyecto: C:\\xampp\\htdocs\\ALimpiezas\nAcabo de editar Plugins/ALimpiezasMobile/ (NO tocar Dinamic/ directamente).\n\n1. Revisa que Assets usen asset('Dinamic/Assets/...') en Twig.\n2. Ejecuta: php tools/bootstrap-plugin.php\n3. Verifica:\n   - curl -I http://localhost/ALimpiezas/Dinamic/Assets/CSS/mobile.css\n   - curl http://127.0.0.1:8090/health\n4. Resume qué se desplegó y si hay errores.\n\nUsa Serena MCP. No escanees vendor/ ni Core/ salvo que sea necesario.\n```\n\n### Prompt 2 — Diagnóstico stack completo\n\n```\nProyecto ALimpiezas — algo no funciona en local.\n\nDiagnostica en orden:\n1. .\\tools\\port-services.ps1 -Port 80,3306,8090\n2. Estado Apache/MySQL (servicios Windows)\n3. curl http://127.0.0.1:8090/health\n4. curl -I http://localhost/ALimpiezas/MobileHome\n5. Si IA caída: .\\tools\\ai-service-manager.ps1 -Restart\n\nEntrega: tabla estado/puerto/proceso + acción concreta para arreglar.\nEjecuta comandos tú mismo; responde en español.\n```\n\n### Prompt 3 — Nueva feature en flujo gastos\n\n```\nFeature en ALimpiezasMobile — flujo gastos/OCR.\n\nContexto:\n- Upload → ai_expense_queue → confirm → ExpenseInvoiceCreator → FacturaProveedor\n- Modos: manual | ocr | ai (settings group alimpiezas)\n- Código fuente: Plugins/ALimpiezasMobile/Lib/AiExpenseProcessor.php, ExpenseInvoiceCreator.php\n- Microservicio: ai-service/main.py endpoint /api/v1/extract-invoice\n\nImplementa [DESCRIBE AQUÍ LA FEATURE].\nTras cambios: bootstrap-plugin.php + indica cómo probar desde MobileExpenses.\nNo modifiques Dinamic/ directamente.\n```\n\n### Prompt 4 — Cambio en microservicio IA\n\n```\nProyecto: ai-service/ (FastAPI puerto 8090)\n\nCambio solicitado: [DESCRIBE AQUÍ]\n\nArchivos clave: ai-service/main.py, requirements.txt\nTras editar:\n1. .\\tools\\ai-service-manager.ps1 -Restart\n2. curl http://127.0.0.1:8090/health\n3. Prueba POST /api/v1/extract-invoice con un PDF/imagen de ejemplo si aplica\n\nDocumenta el contrato JSON request/response actualizado.\n```\n\n### Prompt 5 — Review pre-commit del plugin móvil\n\n```\nReview de cambios en Plugins/ALimpiezasMobile/ antes de commit.\n\nComprueba:\n- ¿Se editó Dinamic/ por error? (debe ser solo vía bootstrap)\n- ¿Twig referencia Dinamic/Assets y no Plugins/Assets?\n- ¿Nuevos settings van a group alimpiezas en MobileConfig?\n- ¿Traducciones en Translation/es_ES.json?\n- ¿Init.php registra tablas/controladores nuevos?\n\nLista issues por severidad (bloqueante / menor / sugerencia).\nNo hagas commit salvo que lo pida explícitamente.\n```\n\n---\n\n## 4. CI/CD — qué falta o mejorar\n\n### Lo que ya existe (upstream)\n\n- Matriz amplia PHPUnit (PHP × BD × países)\n- `composer audit` de seguridad\n- PHPStan/PHPCS (pero con `continue-on-error: true` y sin config del plugin)\n\n### Gaps críticos para ALimpiezas\n\n| Gap | Impacto | Propuesta |\n|-----|---------|-----------|\n| **Sin tests del plugin** | Regresiones silenciosas en gastos/fiscal/voz | Crear `Test/Plugins/ALimpiezasMobile/` con tests de `ExpenseInvoiceCreator`, `VoiceCommandBridge` |\n| **CI no ejecuta bootstrap** | Plugin no se valida en pipeline | Job: `php tools/bootstrap-plugin.php` + smoke HTTP |\n| **Sin CI Python** | Roturas en OCR/voz no detectadas | Workflow `ai-service`: `pip install`, pytest básico de `/health` |\n| **PHPStan solo Core** | Errores en `Plugins/` ignorados | Ampliar a `Plugins/ALimpiezasMobile` |\n| **Workflows genéricos FS** | Matriz 4 PHP × 3 BD = lento y irrelevante para tu stack MariaDB local | Job ligero `alimpiezas-smoke.yml` solo en cambios `Plugins/**`, `ai-service/**`, `tools/**` |\n| **`composer.lock` sin trackear** | Builds no reproducibles | Añadir al repo |\n| **`Dinamic/` en `.gitignore` pero modificado en git** | Ruido en `git status`, confusión deploy | Decidir: o bien dejar de trackear (`git rm --cached Dinamic/`) o excluir del ignore |\n| **Sin deploy automatizado** | Manual en XAMPP | Opcional: script post-pull `tools/update-local.ps1` |\n| **Sin lint Twig/JS móvil** | Inconsistencias UI | ESLint/Stylelint solo en `Plugins/ALimpiezasMobile/Assets/` |\n| **Codecov upstream** | Poco valor para plugin custom | Desactivar o reorientar cobertura al plugin |\n\n### Workflow sugerido (esqueleto)\n\n```yaml\n# .github/workflows/alimpiezas-smoke.yml\non:\n  push:\n    paths: ['Plugins/ALimpiezasMobile/**', 'ai-service/**', 'tools/**']\njobs:\n  smoke:\n    runs-on: ubuntu-latest\n    services:\n      mysql: { image: mariadb:11, env: { MARIADB_ROOT_PASSWORD: root, MARIADB_DATABASE: test } }\n    steps:\n      - uses: actions/checkout@v5\n      - run: composer install --ignore-platform-req=ext-pgsql\n      - run: # crear config.php mínimo\n      - run: php tools/bootstrap-plugin.php\n      - run: vendor/bin/phpunit Test/Plugins/ALimpiezasMobile/  # cuando exista\n      - run: pip install -r ai-service/requirements.txt && pytest ai-service/tests/\n```\n\n---\n\n## 5. Quick wins (~1 hora, alto impacto)\n\n### 1. Script `tools/qa-smoke.ps1` (≈30 min)\n\nUnifica las comprobaciones del manual §13:\n\n```powershell\n# Propuesta: un solo comando post-cambio\n.\\tools\\qa-smoke.ps1\n# → bootstrap si -Deploy\n# → /health, mobile.css, MobileHome HTTP 200\n# → exit code ≠ 0 si algo falla\n```\n\n**Impacto:** Elimina 4–5 comandos manuales tras cada sesión de desarrollo.\n\n### 2. Alias Composer `deploy:mobile` (≈15 min)\n\nEn `composer.json`:\n\n```json\n\"deploy:mobile\": \"php tools/bootstrap-plugin.php\",\n\"deploy:mobile+table\": [\"@deploy:mobile\", \"php tools/create-ai-table.php\"]\n```\n\n**Impacto:** Un comando estándar que cualquier agente/humano recuerda.\n\n### 3. Script `tools/setup-ai-service.ps1` (≈45 min)\n\n```powershell\n# pip install -r requirements.txt\n# verifica Tesseract en C:\\Program Files\\Tesseract-OCR\\\n# arranca y valida /health\n```\n\n**Impacto:** Onboarding y reparación del servicio IA en un paso.\n\n### 4. Regla Cursor `.cursor/rules/alimpiezas.md` (≈20 min)\n\nContenido mínimo:\n- Editar solo `Plugins/ALimpiezasMobile/`\n- Tras cambios: `composer deploy:mobile`\n- Assets → `Dinamic/Assets/` en URLs\n- No tocar `Core/` salvo bug upstream\n\n**Impacto:** Agentes (Bridge móvil incluido) dejan de proponer edits en `Dinamic/`.\n\n### 5. Test PHPUnit mínimo del plugin (≈60 min)\n\nUn solo test: `ExpenseInvoiceCreator` crea `FacturaProveedor` con datos mock.\n\n**Impacto:** Primera red de seguridad real; desbloquea CI del plugin.\n\n### 6. Unificar `tools/dev.ps1` (≈45 min)\n\n```powershell\n.\\tools\\dev.ps1 -Action start|stop|restart|qa|deploy\n```\n\nEnvuelve `start-stack`, `bootstrap-plugin`, `qa-smoke`.\n\n**Impacto:** Un entry point para desarrollo diario desde iPhone vía agente.\n\n### 7. Pre-commit ligero con `tools/pre-commit.ps1` (≈30 min)\n\n- Detecta cambios en `Plugins/` sin bootstrap reciente (timestamp)\n- Avisa si hay edits en `Dinamic/` fuera de deploy\n\n**Impacto:** Evita commits rotos y olvidos de deploy.\n\n---\n\n## Priorización recomendada\n\n```mermaid\nflowchart LR\n    A[qa-smoke.ps1] --> B[composer deploy:mobile]\n    B --> C[.cursor/rules]\n    C --> D[1 test PHPUnit plugin]\n    D --> E[CI alimpiezas-smoke.yml]\n    E --> F[setup-ai-service.ps1]\n```\n\n| Orden | Acción | Tiempo | ROI |\n|-------|--------|--------|-----|\n| 1 | `qa-smoke.ps1` + `deploy:mobile` | 45 min | Alto — uso inmediato diario |\n| 2 | Regla Cursor | 20 min | Alto — Bridge móvil más fiable |\n| 3 | 1 test + workflow smoke | 2 h | Medio-alto — confianza en CI |\n| 4 | `dev.ps1` unificado | 45 min | Medio — ergonomía |\n| 5 | Pipeline Python | 1 h | Medio — cuando OCR cambie mucho |\n\n---\n\nSi quieres, el siguiente paso puede ser **implementar el quick win #1+#2** (`qa-smoke.ps1` + scripts Composer) en una sola pasada desde el Bridge."
    },
    "conventions": {
      "status": "done",
      "completedAt": "2026-06-25T01:52:58.201Z",
      "name": "Convenciones",
      "summary": "Voy a revisar la raíz del proyecto y archivos clave de configuración para extraer convenciones reales del código.\n# Convenciones del proyecto ALimpiezas\n\nERP basado en **FacturaScripts 2026.4** con plugin propio `ALimpiezasMobile` y microservicio Python `ai-service/`. Las convenciones siguen el framework upstream; el código custom vive casi siempre en `Plugins/`.\n\n---\n\n## 1. Naming\n\n### Namespaces (PSR-4)\n\nDefinidos en `composer.json`:\n\n| Prefijo | Carpeta | Uso |\n|---------|---------|-----|\n| `FacturaScripts\\Core\\` | `Core/` | Framework base (upstream) |\n| `FacturaScripts\\Dinamic\\` | `Dinamic",
      "fullText": "Voy a revisar la raíz del proyecto y archivos clave de configuración para extraer convenciones reales del código.\n# Convenciones del proyecto ALimpiezas\n\nERP basado en **FacturaScripts 2026.4** con plugin propio `ALimpiezasMobile` y microservicio Python `ai-service/`. Las convenciones siguen el framework upstream; el código custom vive casi siempre en `Plugins/`.\n\n---\n\n## 1. Naming\n\n### Namespaces (PSR-4)\n\nDefinidos en `composer.json`:\n\n| Prefijo | Carpeta | Uso |\n|---------|---------|-----|\n| `FacturaScripts\\Core\\` | `Core/` | Framework base (upstream) |\n| `FacturaScripts\\Dinamic\\` | `Dinamic/` | Capa desplegada (symlinks/copias tras `Plugins::deploy`) |\n| `FacturaScripts\\Plugins\\{NombrePlugin}\\` | `Plugins/{NombrePlugin}/` | **Código custom** |\n| `FacturaScripts\\Test\\` | `Test/` | Tests PHPUnit |\n\nEjemplo real del plugin:\n\n```3:9:Plugins/ALimpiezasMobile/Init.php\nnamespace FacturaScripts\\Plugins\\ALimpiezasMobile;\n\nuse FacturaScripts\\Core\\DbUpdater;\nuse FacturaScripts\\Core\\Template\\InitClass;\nuse FacturaScripts\\Core\\Tools;\n\nclass Init extends InitClass\n```\n\n### Archivos y carpetas del plugin\n\nEstructura estándar FacturaScripts + ALimpiezas:\n\n```\nPlugins/ALimpiezasMobile/\n├── facturascripts.ini      # metadatos del plugin\n├── Init.php                # hooks init/update/uninstall\n├── Controller/             # MobileExpenses.php, MobileHome.php…\n├── Lib/                    # lógica de negocio (MobileConfig, AiExpenseProcessor…)\n├── Model/                  # AiExpenseQueue.php\n├── Table/                  # ai_expense_queue.xml\n├── View/                   # MobileExpenses.html.twig, Master/MobileLayout.html.twig\n├── Assets/                 # CSS/JS → se copian a Dinamic/Assets/\n└── Translation/            # es_ES.json\n```\n\n| Tipo | Convención | Ejemplo |\n|------|------------|---------|\n| Plugin | PascalCase, sin espacios | `ALimpiezasMobile` |\n| Controlador | PascalCase = nombre de ruta | `MobileExpenses` → `/ALimpiezas/MobileExpenses` |\n| Clase Lib | PascalCase descriptivo | `ExpenseInvoiceCreator`, `VoiceCommandBridge` |\n| Modelo | PascalCase; tablas `snake_case` | `AiExpenseQueue` → `ai_expense_queue` |\n| Vista Twig | Mismo nombre que controlador | `MobileExpenses.html.twig` |\n| Layout | `View/Master/` | `MobileLayout.html.twig` |\n| Tabla XML | `Table/{nombre_tabla}.xml` | `ai_expense_queue.xml` |\n| Assets | `Assets/CSS/`, `Assets/JS/` | `mobile.css`, `mobile.js` |\n| Constantes de modo | `MODE_*` en clases Lib | `MobileConfig::MODE_OCR` |\n\n### Modelos del core (español de negocio)\n\nLos modelos ERP usan nombres en español alineados con tablas BD:\n\n- `FacturaProveedor`, `FacturaCliente`, `Proveedor`, `Cliente`\n- Líneas: `LineaFacturaProveedor`, `LineaAlbaranCliente`\n- Métodos estáticos obligatorios: `primaryColumn()`, `tableName()`\n\n```38:46:Plugins/ALimpiezasMobile/Model/AiExpenseQueue.php\n    public static function primaryColumn(): string\n    {\n        return 'id';\n    }\n\n    public static function tableName(): string\n    {\n        return 'ai_expense_queue';\n    }\n```\n\n### Funciones y métodos\n\n| Patrón | Convención |\n|--------|------------|\n| Hooks de controlador | `getPageData()`, `privateCore()` |\n| Hooks de plugin | `init()`, `update()`, `uninstall()` en `Init` |\n| Helpers estáticos en `Lib/` | `camelCase`: `processUpload()`, `createFromQueue()` |\n| Respuestas JSON/API | array con claves `ok`, `message` (y opcionalmente `reasons`, `hint`) |\n| Acciones POST | parámetro `action` con valores en minúsculas: `upload`, `confirm`, `reject` |\n| Settings | grupo en minúsculas: `'alimpiezas'` |\n| Claves i18n | kebab-case con prefijo plugin | `\"alimpiezas-mobile-expenses\"` |\n\n### Configuración global (`config.php`)\n\nConstantes con prefijo `FS_`:\n\n```3:19:config.php\ndefine('FS_ROUTE', '/ALimpiezas');\ndefine('FS_DB_TYPE', 'mysql');\ndefine('FS_DB_HOST', 'localhost');\ndefine('FS_DB_NAME', 'alimpiezas_fs');\ndefine('FS_LANG', 'es_ES');\ndefine('FS_TIMEZONE', 'Europe/Madrid');\ndefine('FS_DEBUG', false);\ndefine('FS_INITIAL_USER', 'admin');\n```\n\n### Estilo PHP\n\n- **PSR-12** (con excepciones documentadas en `phpcs.xml`)\n- `declare(strict_types=1)` no es obligatorio en todo el repo\n- PHPDoc con copyright FacturaScripts en archivos del core/tests\n- PHP ≥ 8.1\n\n---\n\n## 2. Patrones repetidos\n\n### Capas Core → Dinamic → Plugin\n\n```\nPlugins/ALimpiezasMobile/     ← editar aquí\n        ↓ Plugins::deploy()\nDinamic/                      ← generado, no versionar (.gitignore)\n        ↓ usa modelos/lib del core mergeados\nCore/                         ← upstream FacturaScripts\n```\n\nEn plugins, importar modelos del ERP vía **`FacturaScripts\\Dinamic\\Model\\*`**, no `Core\\Model`:\n\n```9:13:Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php\nuse FacturaScripts\\Dinamic\\Model\\AttachedFile;\nuse FacturaScripts\\Dinamic\\Model\\AttachedFileRelation;\nuse FacturaScripts\\Dinamic\\Model\\FacturaProveedor;\nuse FacturaScripts\\Dinamic\\Model\\Proveedor;\nuse FacturaScripts\\Plugins\\ALimpiezasMobile\\Model\\AiExpenseQueue;\n```\n\n### Hook `Init` del plugin\n\nSemilla de settings + migraciones de tabla en `update()`:\n\n```15:40:Plugins/ALimpiezasMobile/Init.php\n    public function update(): void\n    {\n        if (!Tools::settings('alimpiezas', 'ai_service_url')) {\n            Tools::settingsSet('alimpiezas', 'ai_service_url', 'http://127.0.0.1:8090');\n        }\n        // ...\n        DbUpdater::createTable('ai_expense_queue');\n        DbUpdater::updateTable('ai_expense_queue');\n    }\n```\n\n### Controlador: GET vista / POST JSON por `action`\n\n```31:75:Plugins/ALimpiezasMobile/Controller/MobileExpenses.php\n    public function privateCore(&$response, $user, $permissions)\n    {\n        parent::privateCore($response, $user, $permissions);\n\n        if ($this->request->method() !== 'POST') {\n            $this->setTemplate('MobileExpenses');\n            $this->title = 'Gastos e IA';\n            $this->loadQueue($user->nick);\n            return;\n        }\n\n        $action = $this->request->request->get('action', '');\n\n        switch ($action) {\n            case 'upload':\n                $this->jsonResponse($response, $this->handleUpload($user));\n                return;\n            // ...\n        }\n\n        $this->jsonResponse($response, ['ok' => false, 'message' => 'Acción no válida']);\n    }\n```\n\n`getPageData()` registra entrada de menú:\n\n```20:28:Plugins/ALimpiezasMobile/Controller/MobileExpenses.php\n    public function getPageData(): array\n    {\n        $data = parent::getPageData();\n        $data['menu'] = 'ALimpiezasMobile';\n        $data['title'] = 'alimpiezas-mobile-expenses';\n        $data['icon'] = 'fa-solid fa-camera';\n        $data['showonmenu'] = true;\n        $data['ordernum'] = 2;\n        return $data;\n    }\n```\n\n### Settings en BD (`Tools::settings`)\n\nPatrón central para configuración runtime (no `config.php`):\n\n```13:34:Plugins/ALimpiezasMobile/Lib/MobileConfig.php\n    public static function expenseMode(): string\n    {\n        $mode = Tools::settings('alimpiezas', 'expense_mode', self::MODE_OCR);\n        return in_array($mode, [self::MODE_MANUAL, self::MODE_OCR, self::MODE_AI], true)\n            ? $mode\n            : self::MODE_OCR;\n    }\n\n    public static function aiServiceUrl(): string\n    {\n        return rtrim(Tools::settings('alimpiezas', 'ai_service_url', 'http://127.0.0.1:8090'), '/');\n    }\n```\n\n| API | Uso |\n|-----|-----|\n| `Tools::settings('grupo', 'clave', $default)` | Lectura |\n| `Tools::settingsSet('grupo', 'clave', $valor)` | Escritura |\n| `Tools::config('route')` | Constantes de `config.php` |\n\n### Lib como servicios estáticos\n\nLa lógica pesada va en `Lib/`, no en controladores:\n\n- `AiExpenseProcessor` — cola, upload, confirmación\n- `ExpenseInvoiceCreator` — crea `FacturaProveedor` + `Calculator::calculate`\n- `MobileConfig` — lectura de settings\n- `VoiceCommandBridge` — parseo de intenciones de voz\n\nRespuesta estándar:\n\n```php\n['ok' => false, 'message' => '...', 'reasons' => [...], 'hint' => '...']\n```\n\n### Definición de tablas vía XML\n\n`Table/ai_expense_queue.xml` + `DbUpdater::createTable()` en `Init::update()`.\n\n### Despliegue de plugin (obligatorio tras cambios)\n\n```powershell\nphp tools/bootstrap-plugin.php\n```\n\nEquivale a `Plugins::enable('ALimpiezasMobile')` + `Plugins::deploy(true, true)`.\n\n### Assets y Twig\n\nSiempre referenciar assets **desplegados** en `Dinamic/Assets/`:\n\n```15:17:Plugins/ALimpiezasMobile/View/Master/MobileLayout.html.twig\n    <link rel=\"stylesheet\" href=\"{{ asset('node_modules/bootstrap/dist/css/bootstrap.min.css') }}\">\n    <link rel=\"stylesheet\" href=\"{{ asset('node_modules/@fortawesome/fontawesome-free/css/all.min.css') }}\">\n    <link rel=\"stylesheet\" href=\"{{ asset('Dinamic/Assets/CSS/mobile.css') }}?v=20260625e\">\n```\n\nHelpers Twig habituales: `asset()`, `formToken()`, variable `fsc`.\n\n### Microservicio IA (Python/FastAPI)\n\n- Variables de entorno: `TESSERACT_CMD`, `OPENAI_API_KEY`\n- Endpoints: `/health`, `/api/v1/extract-invoice`, `/api/v1/transcribe`\n- Gestión: `tools/ai-service-manager.ps1`\n\n### Apache rewrite\n\nSolo `Dinamic/Assets/` y `node_modules/` quedan fuera del front controller:\n\n```15:20:.htaccess\n<IfModule mod_rewrite.c>\n   RewriteEngine On\n   RewriteBase /ALimpiezas\n   RewriteCond %{REQUEST_URI} !Dinamic/Assets/ [NC]\n   RewriteCond %{REQUEST_URI} !node_modules/ [NC]\n   RewriteRule . index.php [L]\n</IfModule>\n```\n\n---\n\n## 3. Tests\n\n### Ubicación\n\n| Suite | Carpeta | Config |\n|-------|---------|--------|\n| Core | `Test/Core/` (Model, Lib, Controller…) | `phpunit.xml` |\n| Plugins | `Test/Plugins/` (definido, **aún vacío**) | `phpunit-plugins.xml` |\n| Fixtures | `Test/__files/` | — |\n| Traits reutilizables | `Test/Traits/` | `RandomDataTrait`, `DefaultSettingsTrait` |\n\n### Convención de nombres\n\n- Archivos: `*Test.php` (ej. `ToolsTest.php`, `FacturaProveedorTest.php`)\n- Clase: `{Nombre}Test extends TestCase`\n- Métodos: `test{Cosa}(): void`\n\n```27:32:Test/Core/ToolsTest.php\nfinal class ToolsTest extends TestCase\n{\n    public function testAscii(): void\n    {\n        $this->assertEquals(' aeiou aeiou aeiou aeiou ao cn ', Tools::ascii(' aeiou áéíóú àèìòù âêîôû ãõ çñ '));\n    }\n```\n\n### Cómo ejecutarlos\n\nRequiere `config.php` válido y BD accesible (PHPUnit usa la BD real).\n\n```powershell\ncd C:\\xampp\\htdocs\\ALimpiezas\ncomposer install --ignore-platform-req=ext-pgsql\n\n# Suite core (por defecto)\ncomposer test\n# o\nvendor/bin/phpunit\n\n# Suite plugins (cuando existan tests en Test/Plugins/)\nvendor/bin/phpunit -c phpunit-plugins.xml\n\n# Cobertura\ncomposer test:coverage\n\n# Análisis estático (solo Core/)\ncomposer phpstan\n\n# Estilo (Core/ + Test/)\ncomposer cs-check\ncomposer cs-fix\n```\n\nBootstrap: `Test/bootstrap.php` carga `config.php` del proyecto y limpia caché.\n\n**Nota ALimpiezas:** no hay tests propios del plugin en `Test/Plugins/` todavía; QA manual documentada en `docs/MANUAL-AGENTE-IA.md` (curl health, assets, bootstrap).\n\n---\n\n## 4. Config\n\n### Jerarquía de configuración\n\n| Fuente | Qué guarda | Versionar |\n|--------|------------|-----------|\n| `config.php` | BD, ruta (`FS_ROUTE`), idioma, timezone, debug, credenciales iniciales | **No** (`.gitignore`) |\n| Tabla `settings` vía `Tools::settings` | Config runtime del plugin (`alimpiezas.*`) | Sí (datos en BD) |\n| Variables de entorno (`ai-service`) | `TESSERACT_CMD`, `OPENAI_API_KEY` | **No** |\n| `.env` | Ignorado por git; no es el mecanismo principal de PHP | **No** |\n\n### `config.php` — constantes clave\n\n| Constante | Propósito |\n|-----------|-----------|\n| `FS_ROUTE` | Prefijo URL (`/ALimpiezas`) |\n| `FS_DB_*` | Conexión MariaDB |\n| `FS_LANG` / `FS_TIMEZONE` | Locale |\n| `FS_DEBUG` | Modo depuración |\n| `FS_INITIAL_USER` / `FS_INITIAL_PASS` | Usuario tras instalación |\n| `FS_HIDDEN_PLUGINS` | Plugins ocultos |\n\nAcceso en código: `Tools::config('route')`, `Tools::config('db_name')`, etc.\n\n### Settings del plugin (`grupo: alimpiezas`)\n\n| Clave | Default | Descripción |\n|-------|---------|-------------|\n| `expense_mode` | `ocr` | `manual` \\| `ocr` \\| `ai` |\n| `ai_service_url` | `http://127.0.0.1:8090` | URL microservicio |\n| `fiscal_regime` | `autonomo` | Régimen fiscal |\n| `voice_use_service` | `0` | Transcripción remota on/off |\n| `ollama_url` / `ollama_model` | (opcional) | LLM local |\n\n### Metadatos del plugin\n\n```1:5:Plugins/ALimpiezasMobile/facturascripts.ini\nname = 'ALimpiezasMobile'\ndescription = 'App móvil PWA e integración IA para autónomos (ALimpiezas)'\nversion = 1\nmin_version = 2026.001\nmin_php = 8.1\n```\n\n### Archivos generados (no editar)\n\n- `MyFiles/` — caché Twig, rutas, changelog BD\n- `Dinamic/` — despliegue dinámico del plugin\n\n---\n\n## 5. Anti-patrones a evitar\n\n| Anti-patrón | Por qué | Qué hacer |\n|-------------|---------|-----------|\n| Editar `Dinamic/` directamente | Se sobrescribe en cada `Plugins::deploy()` | Cambiar en `Plugins/ALimpiezasMobile/` + `php tools/bootstrap-plugin.php` |\n| Servir assets desde `Plugins/.../Assets/` | Apache no los expone; 403 en producción | Usar `asset('Dinamic/Assets/...')` tras deploy |\n| Commitear `config.php` con credenciales | Está en `.gitignore` por diseño | Mantener local; documentar plantilla sin secretos |\n| Usar `FacturaScripts\\Core\\Model\\*` en plugins | Pierde extensiones de otros plugins | Usar `FacturaScripts\\Dinamic\\Model\\*` |\n| Lógica de negocio en controladores | Dificulta tests y reutilización | Mover a `Lib/` (como `AiExpenseProcessor`) |\n| Olvidar `DbUpdater` en `Init::update()` | Tablas/columnas no migran | Definir `Table/*.xml` + llamar `createTable`/`updateTable` |\n| Modificar `Core/` para features custom | Conflictos al actualizar FacturaScripts | Extender vía plugin en `Plugins/` |\n| Asumir `.env` para PHP | El ERP usa `config.php` + `Tools::settings` | Reservar `.env` al microservicio Python |\n| Tests del plugin inexistentes | Regresiones silenciosas en cola IA/facturas | Añadir `Test/Plugins/ALimpiezasMobile/` cuando sea posible |\n| Hardcodear `/ALimpiezas` sin `FS_ROUTE` | Rompe en otros entornos | `Tools::config('route')` o `define('FS_ROUTE', ...)` |\n| POST sin validar `action` | Respuestas inconsistentes | `switch ($action)` con default `['ok' => false, ...]` |\n| Crear facturas sin `Calculator::calculate` | Totales/IVA incorrectos | Seguir patrón de `ExpenseInvoiceCreator` |\n\n---\n\n## Resumen rápido para agentes\n\n1. **Código nuevo** → `Plugins/ALimpiezasMobile/`\n2. **Tras cambios** → `php tools/bootstrap-plugin.php`\n3. **Modelos ERP** → `Dinamic\\Model\\...`\n4. **Config app** → `Tools::settings('alimpiezas', ...)`\n5. **Infra local** → `config.php` + MariaDB `alimpiezas_fs`\n6. **Tests** → `composer test` (core); plugin sin suite propia aún\n7. **Referencia técnica** → `docs/MANUAL-AGENTE-IA.md`"
    }
  },
  "prompts": [],
  "notes": []
}
