# 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:

```bash
./nimbly docs:list
./nimbly docs:search "router_accept"
./nimbly docs:section "Template Syntax > route.inc"
```

- `docs:list` prints every heading, to find the right term.
- `docs:search` finds one term in the text.
- `docs:section` prints one section by its heading path.

The same content is on this site as plain text. [`/llms.txt`](/llms.txt) lists every page, [`/llms-full.txt`](/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`](/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 with `tpl=`.
- `[#set#]` does not replace a value that is already set, unless `overwrite` is given.
- A static route has no `route.inc`. A dynamic route checks its input and answers with `router_accept()` or `router_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

```bash
./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.
