Learn
For AI agents
How an agent works in a Nimbly project: where to read, where each block lives and which rules always hold.
This page is for an AI agent that writes code in a Nimbly project, and for the people who work with one. A Nimbly app is made of small blocks in predictable places, so a task usually touches a few short files.
Read first
Every project has the full reference in NIMBLY.md at its root. Do not read it in one go. Look up what the task needs:
./nimbly docs:list
./nimbly docs:search "router_accept"
./nimbly docs:section "Template Syntax > route.inc"
docs:listprints every heading, to find the right term.docs:searchfinds one term in the text.docs:sectionprints one section by its heading path.
The same content is on this site as plain text. /llms.txt lists every page, /llms-full.txt holds all pages in one file, and every page has a Markdown version at its own address with .md added, for example /en/docs/concepts/resources.md. /nimbly.json lists the stack, the included features and the CLI commands.
Where each block lives
| Block | Place | What it holds |
|---|---|---|
| Route | ext/uri/<path>/index.tpl |
One address. The folder path is the URL. |
| Template | ext/tpl/<name>/index.tpl |
A reusable block of markup, called as [#name#]. |
| Resource | ext/data/<resource>/.meta |
The fields, validation, indexes and permissions of one kind of record. |
| Library | ext/lib/<name>.php |
PHP logic, called as [#name#]. No markup. |
| Module | ext/modules/<name>/ |
A feature with its own routes and data. |
The framework is in core/ and is the same for every project. Application work goes in ext/.
Rules that always hold
- Inspect the existing code before adding something new. Reuse a block that is already there.
- Markup belongs in templates. PHP libraries prepare data and do not print HTML.
[#if#]is one tag. It has no block form. Conditional markup goes in its own template, named withtpl=.[#set#]does not replace a value that is already set, unlessoverwriteis given.- A static route has no
route.inc. A dynamic route checks its input and answers withrouter_accept()orrouter_deny(). - PHP uses snake_case for functions, variables and file names.
- Use the existing roles and permissions. Do not build a second permission system.
Check your work
./nimbly build
./nimbly test:architecture --strict
./nimbly test:run
Run the smallest check that covers the change. Content and data are files in the repository, so every change can be reviewed as a diff before it is committed.