> For the complete documentation index, see [llms.txt](https://docs.mikopbx.com/mikopbx-development/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mikopbx.com/mikopbx-development/ai-assisted-development/what-it-generates.md).

# What it generates

When you approve a generation plan, the **`/mikopbx-module`** skill emits a complete module tree, then runs a fixed set of self-checks and prints a report. This page documents exactly what comes out, in what order, the code conventions it enforces, and — most importantly — how to re-verify everything by hand so you never have to take the skill's word for it.

The running example is the fictional spam-blocking module **ModuleBlackList** (config class `BlackListConf`, main class `BlackListMain`, model `BlackListNumbers` backed by table `m_BlackListNumbers`, front-end script `module-black-list.js`). Most patterns below are also anchored to a real, shipped example module you can open and compare against; the few that are not (notably the `App/Providers/` files) are flagged inline.

## File generation order

The skill emits files in dependency order so that each file can reference the ones generated before it. A full "create" session with the `base`, `ui`, `rest-api`, `dialplan` and `agi` recipes selected produces the tree in this sequence:

1. **`module.json`** — module metadata (unique id, version, `min_pbx_version`, namespace).
2. **`Setup/PbxExtensionSetup.php`** — the installer (creates tables, registers the module).
3. **`Models/*.php`** — database models (e.g. `Models/BlackListNumbers.php`).
4. **`Lib/{Feature}Conf.php`** — the configuration class with dialplan / config hooks (e.g. `Lib/BlackListConf.php`).
5. **`Lib/{Feature}Main.php`** — business logic, where the recipe needs it (e.g. `Lib/BlackListMain.php`).
6. **`App/Controllers/`** — web controllers (`ui` recipe).
7. **`App/Forms/`** — Phalcon forms (`ui` recipe).
8. **`App/Views/`** — Volt templates (`ui` recipe).
9. **`App/Providers/`** — provider classes such as `AssetProvider` / `MenuProvider` (`ui` recipe). Note: the canonical example ships an empty `App/Providers/` directory (`.gitkeep` only), and no shipped module currently contains these per-module provider files — asset and menu wiring in the examples lives in `App/Module.php` and the controllers (which reference the *core* `MikoPBX\AdminCabinet\Providers\AssetProvider`). Treat generated provider files as unverified against a real example until confirmed.
10. **`public/assets/js/src/`** — ES6+ JavaScript source (`ui` recipe).
11. **`public/assets/css/`** — CSS styles (`ui` recipe).
12. **`Lib/RestAPI/`** — REST API controllers and actions (`rest-api` recipe).
13. **`bin/`** — worker scripts (`workers` recipe).
14. **`agi-bin/`** — AGI scripts (`agi` recipe).
15. **`Messages/`** — translation files, generated via the `/translations` skill.

{% hint style="info" %}
The order is not cosmetic. `module.json` defines the namespace that every later file `use`s; the model exists before the `Conf`/`Main` classes that query it; the providers and JS come after the controller they wire up. If you generate files yourself, follow the same order to avoid forward references.
{% endhint %}

A representative tree for **ModuleBlackList**:

```
Extensions/ModuleBlackList/
├── module.json
├── Setup/
│   └── PbxExtensionSetup.php
├── Models/
│   └── BlackListNumbers.php
├── Lib/
│   ├── BlackListConf.php
│   ├── BlackListMain.php
│   └── RestAPI/
│       └── Numbers/
│           ├── Controller.php
│           ├── Processor.php
│           ├── DataStructure.php
│           └── Actions/
│               └── GetListAction.php
├── App/
│   ├── Controllers/ModuleBlackListController.php
│   ├── Forms/ModuleBlackListForm.php
│   ├── Views/ModuleBlackList/index.volt
│   └── Providers/                 # see note in "File generation order" — not shipped in examples
│       ├── AssetProvider.php       # (generated by the ui recipe; unverified against a real module)
│       └── MenuProvider.php
├── public/assets/
│   ├── js/src/module-black-list.js
│   └── css/module-black-list.css
├── agi-bin/
│   └── check-blacklist.php
└── Messages/
    ├── ru.php
    └── … (28 more languages)
```

Open the canonical examples to see each slice in a real module:

* Web UI page: `Extensions/EXAMPLES/WebInterface/ModuleExampleForm/`
* REST API (current version): `Extensions/EXAMPLES/REST-API/ModuleExampleRestAPIv3/`
* AMI / background worker: `Extensions/EXAMPLES/AMI/ModuleExampleAmi/`

## Code conventions enforced

Every generated PHP file follows the same baseline.

### Strict types and no closing tag

Each PHP file opens with strict-types and never carries a closing `?>` tag:

{% code title="Lib/BlackListConf.php" %}

```php
<?php

declare(strict_types=1);

namespace Modules\ModuleBlackList\Lib;
```

{% endcode %}

### PHP 8.4 idioms

The skill uses modern PHP wherever it applies — typed properties, constructor property promotion, `match` expressions, named arguments and enums:

```php
public readonly string $moduleUniqueId;

public function __construct(
    private readonly string $moduleUniqueId,
) {}

$result = match ($action) {
    'check'  => $this->check(),
    'reload' => $this->reload(),
    default  => throw new \InvalidArgumentException("Unknown action: $action"),
};
```

### The Phalcon model exception

Model column properties are the one place the skill does **not** apply the typed-property rules above. They follow Phalcon's SQLite ORM convention instead: an untyped primary key, nullable string columns (integers are stored as strings in SQLite), and nullable int foreign keys:

{% code title="Models/BlackListNumbers.php" %}

```php
// Primary key — always untyped
public $id;

// String column — nullable, string default
public ?string $number = '';

// Integer-as-string column
public ?string $enabled = '0';

// Integer foreign key — nullable int
public ?int $userid = null;
```

{% endcode %}

This matches the core models in `Core/src/Common/Models/` — compare `Extensions.php`, `Sip.php` or `CallQueues.php`.

### The `Phalcon\Di\Di` import

The skill always imports the DI container by its full class name. The short form is a frequent mistake that breaks under Phalcon 5:

```php
// CORRECT
use Phalcon\Di\Di;

// WRONG — class not found
use Phalcon\Di;
```

## Post-generation checks the skill runs

After the last file is written, the skill runs three checks and refuses to declare success silently if any fail.

### 1. PHP syntax — `php -l` on every file

```bash
find Extensions/ModuleBlackList -name "*.php" -exec php -l {} \;
```

### 2. JavaScript transpilation via the `/babel-compiler` skill

If the `ui` recipe produced JavaScript, the skill transpiles the ES6+ source to ES5 with the Dockerized Babel compiler (`ghcr.io/mikopbx/babel-compiler:latest`). For an extension module, the source under `public/assets/js/src/` is concatenated into a single `public/assets/js/module-black-list.js`:

```bash
docker run --rm -v "$(pwd)":/workspace \
  ghcr.io/mikopbx/babel-compiler:latest \
  /workspace/Extensions/ModuleBlackList/public/assets/js/src/module-black-list.js \
  extension
```

{% hint style="info" %}
The target argument is `extension` for module files (single concatenated output named after the module) and `core` for admin-cabinet files (output mirrors the source directory tree). Mount your own checkout at `/workspace`. The Babel preset is fixed inside the image — do not override it.
{% endhint %}

### 3. `module.json` validity

```bash
php -r "json_decode(file_get_contents('Extensions/ModuleBlackList/module.json'), true) ?: exit(1);"
```

### The report

When the checks finish, the skill prints a summary: the recipe set it applied, the full list of files it created, and the pass/fail result of each check, followed by suggested next steps:

```
Module: ModuleBlackList
Location: Extensions/ModuleBlackList/
Recipes: base, ui, rest-api, dialplan, agi

Files created:
  Setup/PbxExtensionSetup.php
  Lib/BlackListConf.php
  …
  agi-bin/check-blacklist.php
  Messages/ru.php (+ 28 languages via /translations)
  module.json

Checks:
  PHP syntax: PASS (18/18 files)
  Babel: PASS
  module.json: VALID

Next steps:
  1. Review generated code
  2. Install module in MikoPBX
  3. Test functionality
  4. Customize business logic
```

## Generated AGI scripts: use the real Core API

{% hint style="danger" %}
**Known skill defect — fix AGI scripts after generation.** The `agi` recipe templates in the skill (`templates/agi-recipe.md` and `reference/recipes.md`) emit a class and method names that **do not exist in the MikoPBX Core**:

```php
use AGI\AgiClient;          // ❌ no such class
$agi = new AgiClient();     // ❌
$agi->getVariable(...);     // ❌ no such method
$agi->setVariable(...);     // ❌ no such method
```

The real AGI client is `MikoPBX\Core\Asterisk\AGI` (see `Core/src/Core/Asterisk/AGI.php`). Its accessor methods are **snake\_case**: `get_variable()` and `set_variable()`. After the skill generates an AGI script, replace the recipe's form with the correct one below.
{% endhint %}

The correct, Core-verified pattern — confirmed against `Core/src/Core/Asterisk/AGI.php`:

{% code title="agi-bin/check-blacklist.php" %}

```php
#!/usr/bin/php
<?php

declare(strict_types=1);

use MikoPBX\Core\Asterisk\AGI;
use MikoPBX\Core\System\Util;

require_once 'Globals.php';

try {
    $agi    = new AGI();
    $number = $agi->request['agi_callerid'];

    // Read a channel variable (second arg true returns the value directly)
    $exten = $agi->get_variable('EXTEN', true);

    // Your lookup logic here, e.g. query BlackListNumbers …
    $blocked = '1';

    // Write a result variable back for the dialplan
    $agi->set_variable('BLACKLIST_RESULT', $blocked);
    $agi->noop("Blacklist check for $number -> $blocked");
} catch (\Throwable $e) {
    Util::sysLogMsg('ModuleBlackList', $e->getMessage(), LOG_ERR);
}
```

{% endcode %}

Confirmed `AGI` methods you can call (from `AGI.php`): `get_variable(string $variable, bool $getvalue = false)`, `set_variable(string $variable, string $value)`, `set_var(string $pVariable, string|int|float $pValue)`, `verbose()`, `answer()`, `noop()`, `exec()`, `exec_dial()`, `exec_goto()`, `stream_file()`, `getData()`, `wait_for_digit()`, `set_callerid()`, `getCallerIdName()`, `database_get()`, `databasePut()`. Incoming AGI parameters are read from the `$agi->request` array (for example `$agi->request['agi_callerid']`).

See the working example in `Extensions/ModuleCTIClientV5/agi-bin/set-caller-id.php`, which uses `new AGI()` and `$agi->set_variable('CALLERID(name)', $callerIDName)`. The recipe also points at two more real lookups worth reading: `Extensions/ModulePhoneBook/agi-bin/agi_phone_book.php` and `Extensions/ModuleTelegramProvider/agi-bin/saveSipHeadersInRedis.php`.

## Translations: the 29-language chain

The `Messages/` step does not call an AI translator inline — it delegates to the `/translations` skill, which enforces a strict Russian-first workflow across all 29 supported languages.

1. **Russian is the source of truth.** The skill writes `Messages/ru.php` first, with every key the generated controllers, forms and views reference, using the module prefix (`module_black_list_` for ModuleBlackList) and the `%placeholder%` format.
2. **The other 28 languages are derived from Russian** — never edited by hand. `/translations` processes them **one language and one file at a time**, translating only missing keys and preserving any existing ones.
3. **Key-count validation gates every step.** After each language is merged, its key count must match the Russian source *exactly*; on a mismatch the skill stops rather than ship an inconsistent file. Each merged file is also `php -l`-checked, and placeholder names must be identical to the Russian original.

Technical terms (SIP, IAX, AMI, PJSIP, RTP, CDR, IVR, DTMF, codec, trunk, extension, …) are left untranslated in every language. See [Module translations](/mikopbx-development/module-developement/translations.md) for the full key-naming, prefix and placeholder rules.

## How to verify the output yourself

Treat the skill's report as a claim, not proof. Re-run every check independently before you install the module.

1. **Re-lint every PHP file:**

   ```bash
   find Extensions/ModuleBlackList -name "*.php" -exec php -l {} \;
   ```
2. **Re-transpile the JavaScript** and confirm the ES5 output exists and is non-trivial:

   ```bash
   docker run --rm -v "$(pwd)":/workspace \
     ghcr.io/mikopbx/babel-compiler:latest \
     /workspace/Extensions/ModuleBlackList/public/assets/js/src/module-black-list.js \
     extension
   ```
3. **Re-validate `module.json`:**

   ```bash
   php -r "json_decode(file_get_contents('Extensions/ModuleBlackList/module.json'), true) ?: exit(1);"
   ```
4. **Run the style skills** to check conventions beyond bare syntax — `/php-style` validates PSR-1/PSR-4/PSR-12 and PHP 8.4 idioms; `/js-style` validates the ES6+ source against the MikoPBX JavaScript standards.
5. **Verify translation consistency** — confirm every language file has the same key count as `Messages/ru.php` (the `/translations` skill ships consistency-check commands; a quick spot check is `php -r "echo count(include 'Extensions/ModuleBlackList/Messages/ru.php');"` compared against each language).
6. **Install and test live** with the `/container-inspector` skill, which gives you the running MikoPBX container's connection parameters and lets you restart workers — install the module into the container, exercise the settings page and the REST API, and trigger the dialplan / AGI path to confirm runtime behavior, not just static checks.

{% hint style="warning" %}
Static checks (`php -l`, Babel, JSON validation) prove the files *parse*. They do not prove the module *works*. Always finish with a live install and functional test before treating a generated module as done — and remember to apply the AGI correction above first.
{% endhint %}

## Related pages

* [Using the skill](/mikopbx-development/ai-assisted-development/using-the-skill.md) — the discovery-to-approval workflow that precedes generation.
* [Module recipes](/mikopbx-development/module-developement/recipes.md) — the full specification of each recipe and the files it contributes to the tree.
* [Module translations](/mikopbx-development/module-developement/translations.md) — the Russian-first, 29-language translation rules the `Messages/` step relies on.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.mikopbx.com/mikopbx-development/ai-assisted-development/what-it-generates.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
