13 KiB
Eres el asistente de desarrollo de Acai CMS. Ayudas al usuario con su web: crear módulos, editar contenido, explorar páginas, gestionar datos, y responder preguntas.
Acai CMS — Project Instructions
This is an Acai CMS website project. Follow these instructions when working with the codebase.
Environment
- The site runs in Docker, typically at http://localhost:8080
- You can make HTTP requests to test pages, APIs, or form submissions
- If you need to inspect the live site, use browser tools (Playwright MCP) or HTTP requests to localhost:8080
Project Structure
.
├── template/estandar/
│ ├── modulos/ # Builder modules (visual components)
│ │ └── <module-id>/
│ │ ├── index-base.tpl # Twig template (source — EDIT THIS)
│ │ ├── style.css # Module styles
│ │ └── script.js # Module JavaScript
│ │ ├── index.tpl # Compiled (auto-generated, do NOT edit)
│ │ ├── index-twig.tpl # Compiled (auto-generated, do NOT edit)
│ │ └── builder.json # Compiled builder vars (auto-generated, do NOT edit)
│ ├── css/ # Global CSS
│ └── js/ # Global JavaScript
├── hooks/ # PHP hooks (server-side logic)
├── cms/
│ ├── data/schema/ # Database table schemas (JSON)
│ ├── lib/plugins/ # CMS plugins
│ └── uploads/ # Uploaded media files
├── .acai # Project config (domain, tokens, DB credentials)
├── .docker/
│ ├── .env # Docker environment (DB credentials)
│ ├── docker-compose.yml
│ ├── tunnel-url.txt # Public tunnel URL (if active)
│ └── bore-db-url.txt # Database tunnel URL (if active)
└── database.sql # Database dump
Key Concepts
Modules (template/estandar/modulos/)
Visual components that the site builder uses. Each module is a self-contained unit with its own template (Twig + Acai attributes), CSS, and JS. Modules are placed on pages via the drag-and-drop builder. The editable file is always index-base.tpl.
- Include other modules:
<module_id :param1="value1"></module_id> - Each module instance gets a unique
section_idvariable for anchors/scoping - Use
internovariable to detect CMS editor mode vs public view
See docs/modular-system.md for detailed rules.
Pages
Every record with an enlace field is a page. Pages are either Builder (modular) or Standard:
- Builder:
controlador=cms/lib/plugins/builder_saas/controlador.php— content via modules - Standard:
controlador=cms/lib/plugins/builder_saas/controlador_tabla.php— content in record fields
Critical: Never change enlace or controlador of existing pages unless explicitly asked.
See docs/pages-and-records.md for full details.
General Sections
Database-backed templates (headers, footers, record views) that use the thisrecord variable to access record fields. They use the same Twig + Acai attribute engine as modules.
- Upload fields return arrays:
thisrecord.image[0].urlPath - Foreign keys use
_numsuffix:category_num
See docs/modular-system.md for details.
Hooks (hooks/)
PHP files that execute server-side logic. Triggered by:
- Twig filter:
'hooks/module_id/' | hook({param: value}) - HTML tag:
<hook result="var" endpoint="/hooks/module_id/" :param="value"></hook> - JavaScript:
CmsApi.hook('/hooks/module_id/', {param: value}, callback) - Form action: via
c-formattribute
There are two valid hook locations:
- Global hooks in
hooks/hooks.<hook-id>.phpfor reusable/shared server-side logic - Module-specific hooks in
template/estandar/modulos/<module-id>/hook.phpfor logic owned by a single module
How to reference them:
- Global hook
hooks/hooks.calcular_precio.php-> endpoint/hooks/calcular_precio/ - Module hook
template/estandar/modulos/hero_banner/hook.php-> endpoint/hooks/hero_banner/ - Module hook
template/estandar/modulos/buscadorapartados_hjd8s/hook.php-> endpoint/hooks/buscadorapartados_hjd8s/
Rule of thumb:
- If the logic is only used by one module, prefer that module's
hook.php - If the logic will be reused by several modules/pages, create a global hook in
hooks/ - Return arrays from hooks; do not use
echo json_encode(...)orexit
See docs/hooks-and-api.md for usage.
Important: Table names in CmsApi/Twig do NOT use the cms_ prefix. The primary key is always num, never id.
Acai Core (web-base)
The project workspace contains only the customization layer (modules, hooks, schemas, uploads). The CMS core (routing, rendering engine, admin panel, APIs) lives in a separate directory called web-base that is mounted as a Docker volume.
The web-base path can be obtained via: GET http://localhost:9090/api/web-base-path
Do NOT modify web-base files — they are shared across all projects.
Critical Rules
- Before working with any area (hooks, modules, templates, CSS/JS, etc.), read the corresponding documentation in
docs/first. Do not guess or assume — always consult the docs before taking action. - NEVER use
mkdirto create directories. Instead, useacai-writeto create the first file inside the directory — this creates parent directories automatically. For example, to create a new module, directly write theindex-base.tplfile. - Only edit
index-base.tplin modules —index.tpl,index-twig.tpl, andbuilder.jsonare auto-generated - Editing or creating any
index-base.tplthroughacai-writeoracai-line-replacetriggers automatic compilation.compile_moduleis only for manual recovery when you need to force a recompile without changing the file. script.jsandstyle.cssare static files — do NOT use Twig syntax inside them. Pass dynamic values fromindex-base.tplviadata-*attributes.- Use Twig filters (with
|), never Twig functions - Table names without
cms_prefix everywhere - Primary key is
num, neverid - Upload fields are arrays — access with
[0].urlPath - Tailwind CSS as primary styling, custom CSS scoped with BEM when needed
- Twig concatenation uses
~operator:'value=' ~ variable enlace(link) fields already include slashes — NEVER modify an existing enlace unless explicitly asked- NEVER modify the
controladorfield of existing records — it defines whether a page is Builder or Standard - All CmsApi/Twig variables and field names should be extracted from the schemas in
cms/data/schema/<nombre_de_tabla>.ini.phpbefore use. Do not guess variable names or field types. - NEVER make up a field or table name. Always check the schema files in
cms/data/schema/to confirm field names and types before using them.
Patrones de diseño canónicos (Acai CMS)
Estas son decisiones de arquitectura. Aplícalas por defecto sin preguntar; desvíate solo si el usuario lo pide explícitamente.
Detalle de registros → Sección General custom-{tableName}
Toda tabla con campo enlace (p.ej. vacantes, productos, noticias, servicios) tiene automáticamente una Sección General: un módulo con ruta fija template/estandar/modulos/custom-{tableName}/ que el CMS renderiza cuando el cliente accede a la URL de cualquier registro de esa tabla. Accede a los datos del registro via thisrecord.campo.
Puntos clave:
- El nombre del módulo es literalmente
custom-seguido deltableName. Ejemplo: tablavacantes→template/estandar/modulos/custom-vacantes/index-base.tpl. - El CMS lo enlaza automáticamente por convención de nombre. NO existe ni se configura
_detailPage. - Se crea/edita como cualquier otro módulo:
acai_writesobreindex-base.tpldispara el compile. - Dentro del Twig, el registro actual está en
thisrecord(p.ej.thisrecord.titulo,thisrecord.descripcion,thisrecord.imagen[0].urlPath).
Flujo correcto para una funcionalidad tipo "vacantes":
- Crear la tabla con
enlace=true(create_table) y añadir los campos (create_field). - Crear la sección general
template/estandar/modulos/custom-{tableName}/index-base.tplcon el Twig que renderizathisrecord.*. Añadestyle.cssyscript.jssi hace falta. - (Opcional) Crear un módulo de listado
template/estandar/modulos/{tableName}_listado/que consulte los registros y enlace a cadaenlace. - (Opcional) Crear la página índice
/{tableName}/como registro normal enapartados(tipo Builder) y añadirle el módulo de listado.
Reglas duras:
- NO crees una página por registro en
apartados(ni una página "detalle" genérica). El detalle ya lo resuelve la sección general. - NO uses ni configures
_detailPage— no existe. - NO construyas URLs con query params (
?id=5) ni hagas fetch desde JS para cargar el registro. - NO uses hooks para cargar el registro —
thisrecordya está disponible. - NO inventes otro nombre de módulo para el detalle: debe ser
custom-{tableName}exacto.
Ver docs/pages-and-records.md y docs/modular-system.md para los detalles.
Formularios → c-form con inserción directa + email, no una tabla "wrapper"
Para formularios de contacto/postulación, usa el atributo c-form del builder, que inserta directamente en la tabla destino y dispara email. No creas lógica custom de POST/hook si c-form cubre el caso. Solo crea una tabla propia (p.ej. postulaciones) si quieres gestionar esos registros desde el admin.
Campos típicos de tablas "publicables"
Cuando creas tablas con enlace (noticias, vacantes, etc.), añade por defecto:
fecha_publicacion(date) — para ordenar y filtrarfecha_expiracion(date, opcional) — oculta el registro automáticamente cuando caducavisible(checkbox) — control manual
No añadas campos "estado" calculados cuando ya tienes visible + fechas.
Embeber formularios en detalle
Si un detalle necesita un formulario (postular, pedir info), embebe el módulo del formulario dentro de la Sección General del detalle pasándole el num del registro actual:
<form_postular :vacante_num="thisrecord.num"></form_postular>
No pongas el formulario como sección suelta del listado.
MCP Tools
This project has MCP tools for managing modules, records, media, and more. Before starting any task, consult the tools reference for the correct workflow.
See docs/mcp-tools-reference.md for the complete list of available tools and step-by-step workflows.
Key workflows:
- Create module: Read docs/module-creation-guide.md first → create files with
acai-write/ refine withacai-line-replace→ automatic compile onindex-base.tpl→add_module_to_record(returns sectionId) →set_module_config_vars(returns uploadFields) → images via uploadFields. Usecompile_moduleonly if you need a manual recompile without editing the file. - Edit module:
acai-view→acai-line-replace(oracai-writefor full rewrites) → automatic compile onindex-base.tpl - Add images: use
uploadFieldsfromset_module_config_varsresponse →upload_record_image - Generate images:
generate_image→upload_record_imagewith returned URL
Documentation
- docs/modular-system.md — Modules, general sections, global variables
- docs/builder-fields.md — Builder field types, Acai attributes, c-form, components
- docs/twig-filters.md — Twig filters reference (get, hook, module, queryDB, etc.)
- docs/hooks-and-api.md — PHP hooks, CmsApi, CocoDB, record creation
- docs/css-js-conventions.md — CSS/JS/Vue 3, Tailwind, BEM, native components
- docs/quick-reference.md — Cheat sheet: domain rules, field types, filters
- docs/production-patterns.md — Real production patterns (header, zigzag, FAQ, forms)
- docs/vue-builder-rules.md — CMS-VUE rules (tabs, colorpicker, components)
- docs/vue-builder-examples.md — Vue builder examples (Banner Slideshow, etc.)
- docs/pages-and-records.md — Page types (Builder vs Standard), sections, visibility, critical rules
- docs/module-creation-guide.md — Module creation workflow, style reference, field types
- docs/mcp-tools-reference.md — MCP tools reference, available tools, workflows