This commit is contained in:
Matheus
2026-09-23 12:29:08 -03:00
commit f5d231ab8f
59 changed files with 16658 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
---
name: vitruvio-adicionar-menu
description: >
Use ONLY when the user explicitly asks to add a menu item or menu entry in a Vitruvio repository.
Triggers: "add menu item", "add to menu", "adicionar ao menu", "criar item de menu",
"add panel to menu", "adicionar painel no menu", or any explicit request to register
something in the vitruvio.json "menu" array.
Do NOT trigger for general panel/process/script creation — menu entries are separate.
---
# Add Vitruvio Menu Item
> All messages shown to the user must be written in Portuguese.
You are adding an entry to the `menu` array in `vitruvio.json`. Menu entries are **never created automatically** — only when the user explicitly requests it. Follow these steps in order.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Show the current menu structure
Read `vitruvio.json` and print the menu tree as a readable outline. Example format:
```
menu:
[0] menu-root "Meu Módulo" (MENU)
[0] menu-panel "Meu Painel" (PAINEL → my-panel)
[1] menu-report "Meu Relatório" (RELATORIO → my-report)
```
If the `menu` array is empty or absent, say so.
## Step 3 — Collect item details
Ask the user (in a single message, only ask what is missing):
- **Type** — one of:
- `PAINEL` — links to a panel (`panelKey`)
- `RELATORIO` — links to a report (`reportKey`)
- `PROCESSO` — links to a process (`processKey`)
- `MENU` — a submenu/folder (no artifact link, has `children`)
- **Target artifact key** — the `key` of the panel/report/process to link (skip if type is `MENU`)
- **Name** — the label shown in the menu
- **Key** — unique key for this menu entry (suggest `menu-<artifactKey>` as default)
- **Parent** — root level, or inside which existing `MENU` item? Show the options from Step 2.
- **Order** — integer position within its parent (suggest next available based on existing siblings)
- **Icon** — only relevant for root-level `MENU` items. Vitruvio uses FontAwesome numeric codes (e.g. `61441` = fa-adjust). For children, always `null`.
## Step 4 — Build the JSON entry
### Type: PAINEL
```json
{
"key": "<key>",
"name": "<name>",
"icon": null,
"order": <order>,
"type": "PAINEL",
"panelKey": "<panelKey>",
"children": []
}
```
### Type: RELATORIO
```json
{
"key": "<key>",
"name": "<name>",
"icon": null,
"order": <order>,
"type": "RELATORIO",
"reportKey": "<reportKey>",
"children": []
}
```
### Type: PROCESSO
```json
{
"key": "<key>",
"name": "<name>",
"icon": null,
"order": <order>,
"type": "PROCESSO",
"processKey": "<processKey>",
"children": []
}
```
### Type: MENU (submenu / root folder)
```json
{
"key": "<key>",
"name": "<name>",
"icon": <iconCode or null>,
"order": <order>,
"type": "MENU",
"children": []
}
```
## Step 5 — Insert into vitruvio.json
- Read `vitruvio.json`.
- If `menu` array does not exist, create it as an empty array first.
- If the user chose **root level**: append the entry to the top-level `menu` array.
- If the user chose a **parent item**: find the parent entry by key inside `menu` (search recursively if needed) and append to its `children` array.
- Check that the chosen `key` is not already used anywhere in the menu tree before inserting.
- Write the updated file back preserving formatting (2-space indent).
## Step 6 — Report
Tell the user:
- Added `<type>` entry `<key>` ("Name") at `<location>` with order `<order>`
- Remind them: menu order is relative within siblings — reorder adjacent items if needed
- Remind them: `icon` values are FontAwesome numeric codes; use `null` for leaf items
@@ -0,0 +1,100 @@
# Create Vitruvio Library
> All messages shown to the user must be written in Portuguese.
You are creating a new static file library inside a Vitruvio repository. Libraries are static file bundles (JS, CSS, images) served by the platform as HTTP resources. They are different from scripts — scripts run server-side on Rhino; libraries are served to clients as-is. Follow these steps in order.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Collect library details
Ask the user (in a single message, only ask what is missing from their original request):
- **Key (sigla)** — kebab-case unique identifier. Used to reference the library and build its endpoint URL. Stable — changing it breaks any code that references it.
- **Name** — human-readable label shown in the Vitruvio UI.
- **Description** — one sentence about what this library provides (optional).
- **Auth mode** — one of:
- `PUBLIC` — accessible without authentication (default; use for most JS/CSS bundles)
- `STATIC_TOKEN` — token required; use when files must not be publicly accessible
- **Mobile enabled?** — `true` if mobile clients need to load these files; `false` otherwise (default: `false`).
- **What files will this library contain?** — brief description so you can create a useful starting placeholder (e.g. "custom JS utilities for panels", "CSS theme overrides", "image assets").
## Step 3 — Create the directory and placeholder file
```bash
mkdir -p libraries/<key>
```
Create a placeholder file appropriate to what the user described:
- For a **JS library**: `libraries/<key>/<key>.js`
- For a **CSS library**: `libraries/<key>/<key>.css`
- For an **image/mixed library**: `libraries/<key>/README.md` explaining what belongs here
### JS placeholder
```javascript
/**
* Library: <name>
* Key: <key>
* Description: <description>
*
* These files are served as static HTTP resources.
* Access URL: vBibliotecaService.buildEndpointUrl('<key>', '<key>.js')
*/
// Add your client-side JavaScript here.
// This runs in the browser — full ES6+ is supported (unlike server-side Rhino scripts).
```
### CSS placeholder
```css
/**
* Library: <name>
* Key: <key>
* Description: <description>
*
* These files are served as static HTTP resources.
* Access URL: vBibliotecaService.buildEndpointUrl('<key>', '<key>.css')
*/
/* Add your styles here */
```
## Step 4 — Register in vitruvio.json
Read `vitruvio.json`, find or create the `"libraries"` array, and add:
```json
{
"key": "<key>",
"name": "<name>",
"description": "<description>",
"type": "LOCAL",
"authMode": "<authMode>",
"authToken": null,
"mobileEnabled": <mobileEnabled>,
"files": "libraries/<key>/"
}
```
Omit `"description"` if not provided. Preserve the existing file structure and all other entries.
## Step 5 — Report
Tell the user:
- Directory created: `libraries/<key>/`
- Placeholder file(s) created
- Registered in `vitruvio.json` with key `<key>`
- How to get the serving URL at runtime:
```javascript
var url = vBibliotecaService.buildEndpointUrl('<key>', 'filename.js');
```
- Remind them: files in this directory are served as-is — client-side JS here can use modern ES6+, unlike server-side Rhino scripts
+109
View File
@@ -0,0 +1,109 @@
# Create Vitruvio Endpoint
> All messages shown to the user must be written in Portuguese.
You are creating a new REST endpoint inside a Vitruvio repository. Follow these steps in order.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Collect endpoint details
Ask the user (in a single message, only ask what is missing from their original request):
- **Key** — kebab-case unique identifier. Forms the URL path. Changing it later breaks external integrations.
- **Name** — human-readable label shown in the Vitruvio UI.
- **Description** — one sentence about what this endpoint does.
- **HTTP verbs** — which methods to implement: GET, POST, PUT, PATCH, DELETE. Only scaffold the ones actually needed.
- **Auth mode** — one of:
- `PUBLIC` — no authentication (default)
- `STATIC_TOKEN` — token in query param `_x_token_auth` or header `X-WS-TOKEN-AUTH`
- `VITRUVIO_WS_USER_AUTH` — Vitruvio bearer token; `loggedUser` is available in the script
- `HTTP_BASIC_AUTH` — HTTP basic auth; `loggedUser` is available; roles validated by Vitruvio admin
## Step 3 — Create the file
Target path: `endpoints/<key>.js`
URL after deploy:
| authMode | URL |
|---|---|
| `PUBLIC` | `/api/integration/public/<key>` |
| `STATIC_TOKEN` | `/api/integration/tokenauth/<key>` |
| `VITRUVIO_WS_USER_AUTH` | `/api/integration/bearerauth/<key>` |
| `HTTP_BASIC_AUTH` | `/api/integration/bauth/<key>` |
Template (include only the requested verbs):
```javascript
/**
* Nome: <name>
* Sigla: <key>
* Descrição: <description>
* Auth: <authMode>
*/
function WebService() {
// this.onGet = function(params) { ... } ← GET / DELETE: params has .headers and .query
// this.onPost = function(params) { ... } ← POST / PUT / PATCH: params also has .requestBody (string, always JSON.parse before use)
this.onPost = function(params) {
try {
var body = JSON.parse(params.requestBody);
if (!body.id) throw 'Missing required field: id';
// implementation here
return JSON.stringify({ success: true });
} catch (e) {
return JSON.stringify({ error: e.toString() });
}
};
}
module.exports = new WebService();
```
Rules (Rhino ES5 — no exceptions):
- No `let`, `const`, arrow functions, template literals, destructuring, spread, `class`, or `import/export`
- Use `var` everywhere
- Always `JSON.parse(params.requestBody)` before accessing the body — never trust it raw
- Always return strings — `JSON.stringify(obj)`, not raw objects
- Return `null` or nothing for `204 No Content`; return a string for `200 OK`
- Remove unused verb stubs entirely — don't leave placeholder bodies
- Never concatenate user input into SQL strings; use named bind params (`:paramName`)
- Don't hardcode datasource names or tokens — read from `vConfigService` or a DB config table
- Put heavy logic in a separate script loaded via `libService.loadScript`, not inline in the endpoint
## Step 4 — Register in vitruvio.json
Read `vitruvio.json`, find or create the `"endpoints"` array, and add:
```json
{
"key": "<key>",
"name": "<name>",
"description": "<description>",
"language": "javascript",
"authMode": "<authMode>",
"active": true,
"source": "endpoints/<key>.js"
}
```
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
## Step 5 — Report
Tell the user:
- File created: `endpoints/<key>.js`
- Registered in `vitruvio.json` with key `<key>`
- URL once deployed (based on authMode)
- Which verbs were scaffolded
@@ -0,0 +1,279 @@
# Create Vitruvio Desktop Form
> All messages shown to the user must be written in Portuguese.
Desktop forms are Vaadin 8 forms defined in XML and rendered by the Vitruvio engine. This
skill creates the **desktop** form. There are two variants that share almost all of their
component vocabulary but differ in their root element and how variables flow — see
**Differences: panel vs process** below.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Determine variant and target
Ask (only what is missing):
- **Panel or process form?** (decides root element / file location — see below)
- **Key** — the panel or process key (the folder name).
- For a **process**, which `formKey`(s) are needed — they must match the `activiti:formKey`
values in `processes/<key>/<key>.bpmn`.
- **What should the form show/do?** — fields and behaviour, so you can scaffold something useful.
## Differences: panel vs process
| | Panel | Process |
|----------------|-------|---------|
| File | `panels/<key>/<key>-desktop.xml` | `processes/<key>/<key>-desktop.xml` |
| Root element | `<panel-form>` | `<forms>` (with `processKey` attribute optional) |
| Namespace | `http://www.davinti.com.br/vitruvio/form/panel` | `http://www.davinti.com.br/vitruvio/form` |
| XSD | `vitruvio-panel-form.xsd` | `vitruvio-form.xsd` |
| Forms per file | exactly one `<form>` | **one `<form formKey>` per BPMN `activiti:formKey`** |
| Variables | none built-in; use `engine.getGlobalVariable` | process variables: `engine.getVariable`/`setVariable`; submitted field `id="X"` in `formKey="A"` → variable `A_X` |
| `<library>` | not used | optional: shared `<complex-component id>` reused via `<component-ref refId>` |
Everything below (components, DBTable, engine API, ES5 rules) is **identical** for both.
## Step 3 — Scaffold the file
### Panel variant — `panels/<key>/<key>-desktop.xml`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<panel-form xmlns="http://www.davinti.com.br/vitruvio/form/panel"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form/panel https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-panel-form.xsd">
<form formKey="<key>" width="100%" height="100%">
<name><name></name>
<description><description></description>
<initScript language="JavaScript">
<![CDATA[
function run() {
// called once when the panel opens
}
]]>
</initScript>
<components>
<VerticalLayout spacing="true" margin="true" width="100%" height="100%">
<!-- add widgets here -->
</VerticalLayout>
</components>
</form>
</panel-form>
```
### Process variant — `processes/<key>/<key>-desktop.xml`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<forms xmlns="http://www.davinti.com.br/vitruvio/form"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-form.xsd">
<!-- Optional: shared components used across multiple forms -->
<library>
<!-- <complex-component id="libShared">...</complex-component> -->
</library>
<!-- One <form> per activiti:formKey in the BPMN -->
<form formKey="formAbertura" width="100%">
<name>Abertura</name>
<description>Abertura do processo</description>
<initScript language="JavaScript">
<![CDATA[
function run() {
// called when the form opens
}
]]>
</initScript>
<components>
<VerticalLayout spacing="true" margin="true" width="100%">
<TextField id="descricao" type="string" caption="Descrição" width="100%" required="true" />
</VerticalLayout>
</components>
</form>
<form formKey="formExecutar" width="100%">
<name>Executar</name>
<description>Etapa de execução</description>
<initScript language="JavaScript">
<![CDATA[
function run() {
// read a process variable set during a previous step
// var valor = engine.getVariable('formAbertura_descricao');
}
]]>
</initScript>
<components>
<VerticalLayout spacing="true" margin="true" width="100%">
<TextField id="resultado" type="string" caption="Resultado" width="100%" required="true" />
<ComboBox id="aprovado" type="string" caption="Aprovado?" required="true" allowNullSelection="false">
<entry key="1" value="Sim"/>
<entry key="0" value="Não"/>
</ComboBox>
</VerticalLayout>
</components>
</form>
</forms>
```
## Components (78 desktop components)
Before using a component you are unsure about, read its reference:
`~/.local/share/vitruvio-platform/docs/components/desktop/<ComponentName>.md`
(full list in `docs/components/INDEX.md`).
### Layout
| Component | Common attributes |
|---|---|
| `VerticalLayout` | `spacing`, `margin`, `width`, `height`, `expandRatio` |
| `HorizontalLayout` | same as above |
| `Panel` | `id`, `caption`, `width`, `height`, `margin` |
| `TabLayout` | `id`, `width`, `framed` — contains `<Tab caption="...">` children |
### Widgets
| Component | Key attributes |
|---|---|
| `TextField` | `id`, `type` (`string`/`number`), `caption`, `width`, `required` |
| `NumericField` | `id`, `type`, `caption`, `width`, `visible` |
| `DateField` | `id`, `type` (`date`/`datetime`), `caption`, `resolution` (`DAY`/`MINUTE`) |
| `ComboBox` | `id`, `type`, `caption`, `allowNullSelection` — children: `<entry key="..." value="..."/>` |
| `Label` | `id`, `width`, `contentMode` (`HTML`/`TEXT`) — child: `<value>...</value>` |
| `ButtonWidget` | `id`, `caption`, `style` (`GREEN`/`RED`/`DEFAULT`), `defaultIcon` — child: `<onClickScript>` |
| `RichTextArea` | `id`, `type`, `caption`, `width`, `height` |
| `ImageWidget` | `id`, `width`, `height` — child: `<image><base64 extension="png">...</base64></image>` |
### DBTable (data grid)
```xml
<DBTable id="tbDados" type="string" width="100%" rows="8"
exportXLS="true" showRowCount="true" selectable="true">
<datasource>
<!-- Option A: static query -->
<freeQuery connection-key="vitruvio_producao">
<![CDATA[
SELECT col1, col2
FROM my_table
WHERE param = ${myParam}
]]>
</freeQuery>
<!-- Option B: dynamic query built in JS -->
<sqlBuilderDataSource connection-key="vitruvio_producao" language="JavaScript">
<![CDATA[
function buildSQL(params) {
var sql = 'SELECT col1, col2 FROM my_table WHERE 1=1';
var val = engine.getField('myFilter').getValue();
if (val) {
sql += ' AND col1 = ${val}';
params.put('val', val);
}
return sql;
}
]]>
</sqlBuilderDataSource>
</datasource>
<key-field>CHAVE</key-field>
<columns>
<column name="COL1" caption="Column 1" expand-ratio="1"/>
<column name="COL2" caption="Column 2" expand-ratio="2"/>
<generated name="Action" expand-ratio="0.5">
<scriptColumnGenerator language="JavaScript">
<![CDATA[
function Generator() {
var com = libService.loadScript('vaadinComponents');
this.generate = function(itemId, columnId, item, container) {
var btn = com.buttonIcon('action', function() {
var id = item.getItemProperty('CHAVE').getValue();
// do something
}, 'edit');
return com.horizontalLayout([btn]);
}
}
var script = new Generator();
]]>
</scriptColumnGenerator>
</generated>
</columns>
<bind>
<parameter value-type="number" defaultValue="0" parameterName="myParam" field-ref="otherTable"/>
</bind>
</DBTable>
```
**SQL in datasources:** use `${paramName}` for substitution — NOT `:paramName`. Named
params (`:paramName`) are only for `queries/*.sql` files.
## engine API
```javascript
// Fields
engine.getField('id').getValue()
engine.getField('id').getConvertedValue() // typed value (number, date, etc.)
engine.getField('id').setValue(value)
engine.getField('id').setEnabled(bool)
engine.getField('id').setVisible(bool)
engine.getField('id').setRequired(bool)
engine.getField('id').setCaption('new caption')
engine.getField('id').refresh() // DBTable — re-run its query
// Widgets / layouts
engine.getWidgetController('id').getButton()
engine.getLayout('id').getSelectedTab()
// User
engine.getLoggedUser().getLogin()
engine.getLoggedUser().getNome()
// Global variables (survive tab changes within a session)
engine.setGlobalVariable('key', value)
engine.getGlobalVariable('key')
// Process forms only — process variables:
engine.getVariable('varName')
engine.setVariable('varName', value)
engine.getProcessDefinitionId()
engine.formKey() // current form's formKey
engine.getFormName()
// Open another panel / load a library
var vUI = libService.loadScript('vUI');
vUI.showPanel('panelKey', { param1: value1 });
var lib = libService.loadScript('scriptKey');
```
## Script rules (Rhino ES5)
Desktop form scripts run on **Rhino ES5** — no `let`/`const`, arrow functions, template
literals, destructuring, `class`, `import/export`. Use `var`, string `+` concat,
`JSON.parse`/`JSON.stringify`, `importClass(Packages.some.java.Class)` for Java interop.
(See the repo `CLAUDE.md` "JavaScript — ES5 / Rhino Engine" section.)
> Note: this ES5 rule is for **desktop**. Mobile forms use modern JS on the client — see
> **vitruvio-criar-form-mobile**.
## Step 4 — Manifest
If run **standalone**, set `"forms"."desktop"` to the file path on the existing panel or
process entry in `vitruvio.json`. Full entry creation is handled by **vitruvio-criar-painel**
/ **vitruvio-criar-processo**.
## Step 5 — Report
Tell the user:
- File created/updated and which variant (panel/process).
- For processes: each `<form formKey>` must match an `activiti:formKey` in the BPMN, and
submitted field `id="X"` in `formKey="A"` becomes process variable `A_X`.
- `run()` in `<initScript>` is called every time the form opens.
- `${paramName}` for SQL substitution in datasources; `:paramName` only in named query files.
@@ -0,0 +1,255 @@
# Create Vitruvio Mobile Form
> All messages shown to the user must be written in Portuguese.
Mobile forms are **not** desktop forms with a different skin. They use a different schema,
a smaller component set, a different JavaScript runtime, and an explicit server↔client data
contract. Read this whole skill before scaffolding — the mistakes here are not the desktop
mistakes.
## The four things that make mobile different
1. **Different schema.** Root is `<mobile-forms>`, namespace
`http://www.davinti.com.br/vitruvio/mobile-form`, XSD `vitruvio-mobile-form.xsd`.
2. **Only 18 components** (vs 78 desktop). Do **not** assume a desktop widget exists on
mobile. The full list:
`CheckBox, ComboBox, GoogleMapsField, HorizontalLayout, ImageLibraryByFieldValue,
ImageWidget, Label, MaskedField, MoneyField, NumericField, OptionGroup,
ProgressBarWidget, RatingStars, SignaturePadField, TabLayout, TextField, Toggle,
VerticalLayout` (plus structural `SubForm`/`ItemList`). Read
`~/.local/share/vitruvio-platform/docs/components/mobile/<Component>.md` before using one.
3. **Two JavaScript runtimes.**
- **Client-side** (`initScript`, `discoveryScript`, validators, component event scripts):
**modern React-Native JS** — arrow functions, Promises, `.then()/.catch()` are fine and
expected. Most APIs are **async** and return Promises.
- **Server-side** (`<ServerSide><Bridge>` `execute(...)` bodies): **Rhino ES5**, same rules
as scripts/desktop. This is the only place with access to platform libs and services
(`libService`, `runtimeService`, db, etc.).
4. **Data is explicit.** Nothing is auto-injected the way desktop process variables are. Every
piece of server data the form needs must be declared — via an `<Autoload>` variable, a
`<QueryDataSource>` (synced to the device, works offline), or fetched on demand from a
named `<Bridge>` with `vCommunicationService.executeOnServer(...)`.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Determine variant and gather the data contract
Ask (only what is missing):
- **Panel or process mobile form?** (see **Differences: panel vs process** below)
- **Key** — the panel or process key (folder name).
- For a **process**: which `formKey`(s) — they must match the `activiti:formKey` in
`processes/<key>/<key>.bpmn`.
- **Which variables does the form need?** Because nothing is auto-injected, ask explicitly:
- process variables to read (each becomes an `<Autoload>` `<variable>` and/or a Bridge call)
- reference/lookup data (each becomes a `<QueryDataSource>` backed by a `queries/*.sql`)
- any server-only logic / libs needed (each becomes a `<Bridge>`)
- **Offline?** Whether the data sources must be available without connectivity (affects
`autoSyncOnInit` / `autoSyncOnDiscovery`).
## Differences: panel vs process
The schema, component set, ServerSide/Bridge mechanics and JS runtimes below are **identical**
for both. Only these differ:
| | Panel mobile form | Process mobile form |
|----------------|-------------------|---------------------|
| File | `panels/<key>/<key>-mobile.xml` | `processes/<key>/<key>-mobile.xml` |
| Root attribute | `<mobile-forms>` (no `processKey`) | `<mobile-forms processKey="<key>">` |
| Forms per file | one `<form>` | **one `<form formKey>` per BPMN `activiti:formKey`** (must match) |
| Manifest | set `forms.mobile` **and** `showInMobileList: true` on the panel entry | set `forms.mobile` (and optionally `forms.mobileAlternative`) on the process entry |
| Variables | use `engine.getGlobalVariable` / Autoload | process variables are fetched server-side via a Bridge (`runtimeService.getVariable`) and/or declared in `<Autoload>` |
> Filename: name files by the **artifact key + suffix** — `<key>-mobile.xml` (and
> `<key>-desktop.xml`, `<key>.bpmn`). This keeps every form searchable by its key instead of
> dozens of identical `form-mobile.xml` tabs. Older content used `form-mobile.xml` /
> `form_web_mobile.xml`; the real path is whatever `forms.mobile` points to, so legacy files
> still work — but new scaffolds use `<key>-mobile.xml`.
## Step 3 — Scaffold the file
### Skeleton (process variant shown; for a panel drop `processKey` and use a single `<form>`)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<mobile-forms processKey="<key>"
xmlns="http://www.davinti.com.br/vitruvio/mobile-form"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/mobile-form https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-mobile-form.xsd">
<form formKey="formColeta">
<name>Coleta</name>
<description>Etapa de coleta no app</description>
<!-- Runs when the form opens. CLIENT-SIDE: modern JS, async APIs return Promises. -->
<initScript language="JavaScript">
<![CDATA[
function run() {
// Autoloaded variables land in the global scope:
engine.getField('nomeLoja').setValue(engine.getGlobalVariable('nomeLoja'));
}
]]>
</initScript>
<!-- Optional: pull server data when the task is discovered (before the user opens it). -->
<discoveryScript language="JavaScript">
<![CDATA[
function run() {
var params = { id: execution.getProcessInstanceId() };
vCommunicationService.executeOnServer('bridgeNumeroCarga', params).then(result => {
var n = parseInt(result, 10);
execution.setVariable('numeroCarga', n).then(ok => {}).catch(err => {
console.log('Erro ao setar numeroCarga localmente', err);
});
}).catch(err => {
console.log('Erro ao coletar numeroCarga no server', err);
});
}
]]>
</discoveryScript>
<!-- Block completing the task when a rule is not met. -->
<validators>
<ScriptValidator execution="COMPLETE" language="JavaScript" id="validadorComplete">
<![CDATA[
function Validator() {
var msg;
this.getMessage = function() { return msg; };
this.isValid = function() {
if (engine.getField('confirma').getValue() == 'Sim') { return true; }
msg = 'Confirme a execução antes de finalizar.';
return false;
};
}
var validator = new Validator();
]]>
</ScriptValidator>
</validators>
<components>
<VerticalLayout spacing="true" margin="true" width="100%">
<TextField id="nomeLoja" type="string" caption="Loja" readOnly="true" />
<OptionGroup id="confirma" type="string" caption="Executou?" required="true">
<entry key="Sim" value="Sim" />
<entry key="Nao" value="Não" />
</OptionGroup>
<SignaturePadField id="assinatura" caption="Assinatura" />
</VerticalLayout>
</components>
<!-- Explicitly declared variables auto-injected into the form's scope on load. -->
<Autoload>
<variables autoInjectionScope="ENGINE_GLOBAL_SCOPE" autoPersist="true">
<variable>nomeLoja</variable>
<variable>numeroCarga</variable>
</variables>
</Autoload>
<!-- Everything the server provides to this form. -->
<ServerSide>
<!-- Named queries (queries/*.sql) synced to the device for offline lookups. -->
<DataSources>
<QueryDataSource key="qry_produtos_carga" autoSyncOnInit="true" autoSyncOnDiscovery="true" refreshInSeconds="60" />
</DataSources>
<!-- Server-side functions. Rhino ES5. Full access to libService / runtimeService / db.
Called from the client via vCommunicationService.executeOnServer('id', params). -->
<Bridges>
<Bridge language="JavaScript" id="bridgeNumeroCarga">
<![CDATA[
function execute(params) {
var numeroCarga = runtimeService.getVariable(params.id, 'numeroCarga');
return numeroCarga ? numeroCarga : -1;
}
]]>
</Bridge>
</Bridges>
</ServerSide>
</form>
</mobile-forms>
```
## How libs and process variables reach the mobile form
The mobile app cannot call `libService.loadScript(...)` or read process variables directly —
those live on the server. The pattern is always **declare a Bridge, call it from the client**:
```xml
<!-- SERVER-SIDE (Rhino ES5): a lib used to build a barcode image -->
<Bridge language="JavaScript" id="imagemCodBarras">
<![CDATA[
function execute(codbarras) {
var generator = libService.loadScript('barcode-gen');
return ',' + generator.generateEAN13BarcodeImageAsBase64({ value: codbarras });
}
]]>
</Bridge>
```
```javascript
// CLIENT-SIDE (modern JS): call the bridge, use the Promise result
vCommunicationService.executeOnServer('imagemCodBarras', codigoBarras).then(base64 => {
engine.getField('codigoImagem').setValue(base64);
}).catch(err => console.log('Erro no bridge imagemCodBarras', err));
```
Rules of thumb:
- Anything needing a **platform lib, the database, or a platform service** → put it in a
**Bridge** (server, ES5) and call it with `vCommunicationService.executeOnServer`.
- **Reference/lookup tables** the form reads repeatedly → a **`QueryDataSource`** backed by a
`queries/*.sql` (works offline once synced).
- **Process variables** the form needs → either declare them in `<Autoload>` or fetch them
in a Bridge via `runtimeService.getVariable(processInstanceId, 'varName')` and store locally
with `execution.setVariable(...)`.
- Client-side `execution.getVariable(...)` / `execution.setVariable(...)` are **async** and
return Promises — use `.then()`.
## List-based entry: SubForm + ItemList
For "add many items" screens (collect a list of rows), use a `SubForm` with an `<ItemList>`:
```xml
<SubForm formKey="executarAcao">
<name>Executar AÇÃO</name>
<initScript language="JavaScript">
<![CDATA[ function run(apply) { if (apply) { apply(); } } ]]>
</initScript>
<ItemList addItemButtonCaption="Gravar na Lista" caption="Itens">
<property id="OBSERVACAO" caption="Observação" />
</ItemList>
<components>
<VerticalLayout width="100%" spacing="true" margin="true">
<!-- fields captured per item -->
</VerticalLayout>
</components>
</SubForm>
```
Validate list completeness with `engine.getSubFormListSizeByStatus(function(size){ ... }, "all")`
inside a `COMPLETE` validator.
## Step 4 — Manifest
If run **standalone**, update the matching entry in `vitruvio.json`:
- **Panel**: set `forms.mobile = "panels/<key>/<key>-mobile.xml"` and `showInMobileList: true`.
- **Process**: set `forms.mobile = "processes/<key>/<key>-mobile.xml"`.
Full entry creation is handled by **vitruvio-criar-painel** / **vitruvio-criar-processo**.
## Step 5 — Report
Tell the user:
- File created/updated and variant (panel/process).
- Which variables/queries/bridges were declared, and that **only declared data is available**
on the device — anything else must be added as an Autoload variable, QueryDataSource, or Bridge.
- Reminder: client scripts are modern JS (Promises); Bridge bodies are server-side Rhino ES5.
- For processes: each `<form formKey>` must match an `activiti:formKey` in the BPMN.
- Suggest reading `docs/components/mobile/` and an example
(`~/.local/share/vitruvio-platform/examples/processes/*/form_web_mobile.xml`) for richer screens.
+92
View File
@@ -0,0 +1,92 @@
# Create Vitruvio Panel (orchestrator)
> All messages shown to the user must be written in Portuguese.
A panel is one or two form files plus a manifest entry:
| Part | Skill that owns it |
|------|--------------------|
| `panels/<key>/<key>-desktop.xml` (web form) | **vitruvio-criar-form-desktop** (panel variant) |
| `panels/<key>/<key>-mobile.xml` (app form, optional) | **vitruvio-criar-form-mobile** (panel variant) |
This skill collects the intent once, drives those skills, and registers the panel in
`vitruvio.json`. Do the file work by following the referenced skills — do not re-derive
their templates here.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Collect panel details
Ask the user (in a single message, only ask what is missing):
- **Key** — PascalCase or snake_case unique identifier. Used in `engine.showPanel(key)` and
as the folder name.
- **Name** — human-readable label shown in the Vitruvio menu.
- **Category** — display category, hierarchical with `/` (e.g. `"Comercial"`,
`"Auditoria/Gondola"`).
- **Description** — one sentence (optional).
- **Open in new window?** — `true`/`false`. Default `true`.
- **Show in mobile list?** — `true`/`false`. Default `false`.
- **Needs a mobile form?** — if yes, a `<key>-mobile.xml` is created too. Mobile is a different
schema with explicit data wiring (the mobile skill will ask for variables/lookups/libs).
- **What should the panel do?** — fields/behaviour, so the form scaffold is useful.
## Step 3 — Scaffold
```bash
vitruvio new panel <key> --name "<name>"
```
This creates `panels/<key>/<key>-desktop.xml` and the `vitruvio.json` entry. Then replace the
generated form using the focused skills:
1. **Desktop form** — follow **vitruvio-criar-form-desktop** (panel variant) to write
`panels/<key>/<key>-desktop.xml` from the user's description.
2. **Mobile form (if requested)** — follow **vitruvio-criar-form-mobile** (panel variant) to
write `panels/<key>/<key>-mobile.xml`. `vitruvio new` does not create it.
## Step 4 — Update vitruvio.json entry
`vitruvio new` already added the entry. Full shape:
```json
{
"key": "<key>",
"name": "<name>",
"description": "<description>",
"category": "<category>",
"displayOrder": 0,
"showInPresentation": false,
"openInNewWindow": true,
"showInMobileList": false,
"displayTimeInSeconds": 0,
"allowedGroups": [],
"allowedUsers": [],
"forms": {
"desktop": "panels/<key>/<key>-desktop.xml"
}
}
```
Fields `vitruvio new` typically leaves at defaults that need updating:
- `"category"` — set to the user's category (default `""`).
- `"description"` — add if provided.
- `"openInNewWindow"` — set to `false` for same-window.
- `"showInMobileList"` — set to `true` for mobile visibility.
- If a mobile form was created, add `"mobile": "panels/<key>/<key>-mobile.xml"` inside `"forms"`
(and set `showInMobileList: true`).
## Step 5 — Report
Tell the user:
- Files created: `<key>-desktop.xml` (and `<key>-mobile.xml` if applicable).
- Registered in `vitruvio.json` with key `<key>`.
- `run()` in `<initScript>` is called every time the panel opens.
- `${paramName}` for SQL substitution in datasource blocks; `:paramName` only in named query files.
- For mobile: only explicitly declared data is available on the device — see vitruvio-criar-form-mobile.
+120
View File
@@ -0,0 +1,120 @@
# Create Vitruvio Patch
> All messages shown to the user must be written in Portuguese.
You are creating a new Liquibase database migration patch inside a Vitruvio repository. Follow these steps in order.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Inspect existing patches to determine the next changeset ID
```bash
find patches/ -name "*.xml" | xargs grep -h 'id="[0-9]' 2>/dev/null | grep -oP 'id="\K[0-9]+' | sort -n | tail -1
```
The next ID = last found ID + 1. If no patches exist yet, start at 1.
Also check whether oracle and postgresql subdirectories already exist:
```bash
ls patches/
```
## Step 3 — Collect migration details
Ask the user (in a single message, only ask what is missing):
- **What this migration does** — describe the change (e.g. "add column STATUS to table PEDIDO", "create table AUDIT_LOG", "insert config rows").
- **Author** — Vitruvio username (e.g. `joao.felix`). Use `git config user.name` if unsure.
- **Module key** — used in the filename (e.g. `GO`, `checklist`, `faturamento`). Default: the repo's `metadata.key` from `vitruvio.json`.
## Step 4 — Create the patch files
**Both oracle/ and postgresql/ files must always be created and kept in sync.**
Filename convention: `{YYYYMMDDHHmm}_{MODULE_KEY}.xml` (e.g. `202506011430_checklist.xml`). Use the current date and time.
```bash
mkdir -p patches/oracle patches/postgresql
```
### Absolute rules
- **Append-only.** Never edit or delete existing `<changeSet>` entries — modifying a checksum that Liquibase already recorded breaks deployment.
- **Unique numeric IDs.** Each `<changeSet id="...">` must have a unique ID within the repo. Increment from the last found.
- **Always use `<preConditions onFail="MARK_RAN">`** — every changeset must be idempotent and safe to re-run on any DB state.
- **Oracle ≠ PostgreSQL.** Write each file for its target DB — data types, sequences, and quoting differ. Never copy-paste blindly.
- **One logical change per changeset** — don't batch unrelated changes into a single `<changeSet>`.
### File skeleton
```xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd
http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
<changeSet author="<author>" id="<next-id>" objectQuotingStrategy="LEGACY">
<preConditions onError="WARN" onFail="MARK_RAN" onSqlOutput="IGNORE">
<!-- guard appropriate for the operation — see examples below -->
</preConditions>
<sql endDelimiter=";" splitStatements="true" stripComments="false">
-- SQL for this DB dialect
</sql>
</changeSet>
</databaseChangeLog>
```
### preConditions reference
| Operation | Guard to use |
|-----------|-------------|
| CREATE TABLE | `<not><tableExists tableName="MY_TABLE"/></not>` |
| ADD COLUMN | `<not><columnExists tableName="MY_TABLE" columnName="MY_COL"/></not>` |
| CREATE INDEX | `<not><indexExists indexName="IDX_NAME"/></not>` |
| ADD CONSTRAINT / FK | `<not><foreignKeyConstraintExists foreignKeyName="FK_NAME"/></not>` |
| INSERT row (by PK) | `<sqlCheck expectedResult="0">SELECT COUNT(*) FROM MY_TABLE WHERE ID = 1</sqlCheck>` |
| DROP TABLE | `<tableExists tableName="MY_TABLE"/>` |
| DROP COLUMN | `<columnExists tableName="MY_TABLE" columnName="MY_COL"/>` |
### Oracle vs PostgreSQL differences to watch
| | Oracle | PostgreSQL |
|---|---|---|
| Auto-increment | Separate `CREATE SEQUENCE` + trigger or `DEFAULT seq.NEXTVAL` | `SERIAL` or `GENERATED ALWAYS AS IDENTITY` |
| String type | `VARCHAR2(n)` | `VARCHAR(n)` |
| Boolean | `NUMBER(1)` | `BOOLEAN` |
| Date/time | `DATE`, `TIMESTAMP` | `DATE`, `TIMESTAMP` |
| Current timestamp | `SYSDATE` | `CURRENT_TIMESTAMP` |
| Quoting | `LEGACY` strategy (unquoted) | Same |
## Step 5 — Check vitruvio.json patches registration
The patches directory only needs to be registered once. Check if it is already there:
```bash
grep -A2 '"patches"' vitruvio.json
```
If not registered, add to `vitruvio.json`:
```json
"patches": "patches/"
```
## Step 6 — Report
Tell the user:
- Files created: `patches/oracle/<filename>.xml` and `patches/postgresql/<filename>.xml`
- Changeset IDs used
- Summary of what each changeset does
- Reminder: never edit existing changesets once committed — add new ones instead
@@ -0,0 +1,216 @@
# Create Vitruvio Process BPMN
> All messages shown to the user must be written in Portuguese.
You are creating the **BPMN workflow file** of a Vitruvio process. This skill is focused
on the `.bpmn` file only — the desktop form is handled by **vitruvio-criar-form-desktop**
(process variant) and the mobile form by **vitruvio-criar-form-mobile** (process variant).
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Collect flow details
Ask the user (in a single message, only ask what is missing):
- **Key** — snake_case or camelCase unique identifier. Becomes the BPMN process ID and the
folder name. Must match the `key` used for the process in `vitruvio.json`.
- **Name** — human-readable label.
- **Who can start it?** — group key(s) allowed to open the process (e.g. `gestao`,
`admin`). Used in `activiti:candidateStarterGroups`.
- **Steps** — the human tasks (user tasks), and which group handles each. A simple linear
flow is enough to start.
- **Decisions / branches?** — any exclusive gateways with conditions.
- **Automatic steps?** — any script tasks running between user tasks.
## Step 3 — Write the BPMN file: `processes/<key>/<key>.bpmn`
### Critical rules
- The `<bpmn2:process id="...">` value is the canonical process identity. The importer
reads it from the BPMN, not from vitruvio.json. **It must match the `key`.**
- Every node must appear in a `<bpmn2:laneSet>` / `<bpmn2:lane>` **and** in the
`<bpmndi:BPMNDi>` section — Vitruvio renders the diagram.
- Each `activiti:formKey` on the start event and user tasks must match a
`<form formKey="...">` in the desktop form XML (and mobile form, if present).
- Process variables from submitted forms are auto-named `{formKey}_{fieldId}`
(e.g. `formAbertura_status`). Gateway conditions reference them.
- Gateway conditions use `#{variable == 'value'}` (JUEL expression language).
- Script tasks call `vScriptService.loadScript('scriptKey', 'javascript')`, **not**
`libService`.
### Minimal skeleton (start → user task → end, single lane)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:activiti="http://activiti.org/bpmn"
id="sample-diagram"
targetNamespace="http://bpmn.io/schema/bpmn"
exporter="bpmn-js (https://demo.bpmn.io)"
exporterVersion="8.2.0"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:collaboration id="Collaboration_<key>">
<bpmn2:participant id="processo_<key>" name="<Name>" processRef="<key>" />
</bpmn2:collaboration>
<bpmn2:process id="<key>" name="<Name>" isExecutable="true"
activiti:candidateStarterGroups="<starterGroup>">
<bpmn2:laneSet>
<bpmn2:lane id="lane_execucao" name="Execução">
<bpmn2:flowNodeRef>inicio</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_executar</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>fim</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<!-- Start event: activiti:initiator stores the login of who opened the process -->
<bpmn2:startEvent id="inicio" name="Início"
activiti:formKey="formAbertura"
activiti:initiator="iniciador">
<bpmn2:outgoing>flow_inicio_task</bpmn2:outgoing>
</bpmn2:startEvent>
<!-- User task: candidateGroups controls who sees it in their inbox -->
<bpmn2:userTask id="task_executar" name="Executar"
activiti:formKey="formExecutar"
activiti:candidateGroups="${vStringUtils.validateRoles(gr_executores)}">
<bpmn2:incoming>flow_inicio_task</bpmn2:incoming>
<bpmn2:outgoing>flow_task_fim</bpmn2:outgoing>
</bpmn2:userTask>
<!-- Script task example (omit if not needed):
<bpmn2:scriptTask id="script_processar" name="Processar" scriptFormat="javascript">
<bpmn2:incoming>flow_task_script</bpmn2:incoming>
<bpmn2:outgoing>flow_script_fim</bpmn2:outgoing>
<bpmn2:script>var f = vScriptService.loadScript('meu_script', 'javascript');
f(execution);</bpmn2:script>
</bpmn2:scriptTask>
-->
<!-- Exclusive gateway example (omit if not needed):
<bpmn2:exclusiveGateway id="gw_decisao" name="Aprovado?">
<bpmn2:incoming>flow_task_gw</bpmn2:incoming>
<bpmn2:outgoing>flow_sim</bpmn2:outgoing>
<bpmn2:outgoing>flow_nao</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="flow_sim" name="Sim" sourceRef="gw_decisao" targetRef="fim">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">#{formExecutar_aprovado == '1'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="flow_nao" name="Não" sourceRef="gw_decisao" targetRef="task_executar">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">#{formExecutar_aprovado == '0'}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
-->
<bpmn2:endEvent id="fim" name="Fim">
<bpmn2:incoming>flow_task_fim</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="flow_inicio_task" sourceRef="inicio" targetRef="task_executar" />
<bpmn2:sequenceFlow id="flow_task_fim" sourceRef="task_executar" targetRef="fim" />
</bpmn2:process>
<!-- BPMNDi: visual layout — required for the diagram to render -->
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_<key>">
<bpmndi:BPMNShape id="Participant_di" bpmnElement="processo_<key>" isHorizontal="true">
<dc:Bounds x="100" y="80" width="750" height="180" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_execucao_di" bpmnElement="lane_execucao" isHorizontal="true">
<dc:Bounds x="130" y="80" width="720" height="180" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="inicio_di" bpmnElement="inicio">
<dc:Bounds x="192" y="152" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="195" y="195" width="30" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="task_executar_di" bpmnElement="task_executar">
<dc:Bounds x="310" y="130" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="fim_di" bpmnElement="fim">
<dc:Bounds x="492" y="152" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="497" y="195" width="19" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="flow_inicio_task_di" bpmnElement="flow_inicio_task">
<di:waypoint x="228" y="170" />
<di:waypoint x="310" y="170" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="flow_task_fim_di" bpmnElement="flow_task_fim">
<di:waypoint x="410" y="170" />
<di:waypoint x="492" y="170" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>
```
### Multi-lane pattern (when tasks belong to different roles)
Add each lane inside `<bpmn2:laneSet>`, list the node IDs inside each lane, and adjust
the BPMNDi bounds:
```xml
<bpmn2:laneSet>
<bpmn2:lane id="lane_gestao" name="Gestão">
<bpmn2:flowNodeRef>inicio</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>fim</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_execucao" name="Execução">
<bpmn2:flowNodeRef>task_executar</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
```
### Script task — script side
```javascript
// In <bpmn2:script> inside a scriptTask:
var f = vScriptService.loadScript('meu_script', 'javascript');
f(execution);
// In the script file itself (pattern: process/task script — see vitruvio-criar-script):
(function(execution) {
var db = libService.loadScript('db');
var banco = new db(db.VITRUVIO_DATASOURCE);
var status = execution.getVariable('formAbertura_status');
// ...
})(execution)
```
## Step 4 — Manifest
If this skill is run **standalone** (the process already exists in `vitruvio.json`),
ensure its entry has `"bpmn": "processes/<key>/<key>.bpmn"`. Do not create or modify
the rest of the entry here — full registration is handled by **vitruvio-criar-processo**.
## Step 5 — Report
Tell the user:
- File created/updated: `processes/<key>/<key>.bpmn`
- The process identity is the `<bpmn2:process id>` — it must match the key.
- Each `activiti:formKey` must have a matching `<form formKey="...">` in the form XML
(create/update it with **vitruvio-criar-form-desktop** / **vitruvio-criar-form-mobile**).
- Submitted field `id="X"` in `formKey="formAbertura"` becomes variable `formAbertura_X`.
- For complex flows, recommend editing the BPMN in bpmn.io or Camunda Modeler before deploying.
@@ -0,0 +1,87 @@
# Create Vitruvio Process (orchestrator)
> All messages shown to the user must be written in Portuguese.
A process is made of up to three artifacts plus a manifest entry:
| Part | Skill that owns it |
|------|--------------------|
| `processes/<key>/<key>.bpmn` (workflow) | **vitruvio-criar-processo-bpmn** |
| `processes/<key>/<key>-desktop.xml` (web form) | **vitruvio-criar-form-desktop** (process variant) |
| `processes/<key>/<key>-mobile.xml` (app form, optional) | **vitruvio-criar-form-mobile** (process variant) |
This skill collects the intent once, drives those skills, and registers the process in
`vitruvio.json`. Do the file work by following the referenced skills — do not re-derive
their templates here.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Collect process details
Ask the user (in a single message, only ask what is missing):
- **Key** — snake_case or camelCase unique identifier. Becomes the BPMN process ID and the
folder name.
- **Name** — human-readable label.
- **Description** — one sentence (optional).
- **Who can start it?** — group key(s) for `activiti:candidateStarterGroups`.
- **Steps** — the human tasks and which group handles each. A simple linear flow is enough.
- **Needs a script task?** — automatic steps running a script between user tasks.
- **Needs a mobile form?** — if yes, a `<key>-mobile.xml` is created too. Remember mobile is a
different schema with explicit data wiring — variables, lookups and libs must be named
up front (the mobile skill will ask).
## Step 3 — Scaffold
```bash
vitruvio new process <key> --name "<name>"
```
This creates `processes/<key>/<key>.bpmn`, `processes/<key>/<key>-desktop.xml`, and the
`vitruvio.json` entry. Then replace the generated files using the focused skills:
1. **BPMN** — follow **vitruvio-criar-processo-bpmn** to write `processes/<key>/<key>.bpmn`
from the user's steps/branches.
2. **Desktop form** — follow **vitruvio-criar-form-desktop** (process variant) to write
`processes/<key>/<key>-desktop.xml`, one `<form formKey>` per `activiti:formKey` in the BPMN.
3. **Mobile form (if requested)** — follow **vitruvio-criar-form-mobile** (process variant) to
write `processes/<key>/<key>-mobile.xml`. `vitruvio new` does not create it.
## Step 4 — Update vitruvio.json entry
`vitruvio new` already added the entry. Full shape:
```json
{
"key": "<key>",
"name": "<name>",
"description": "<description>",
"bpmn": "processes/<key>/<key>.bpmn",
"forms": {
"desktop": "processes/<key>/<key>-desktop.xml"
},
"schedules": []
}
```
- Add `"description"` if the user provided one — `vitruvio new` does not set it.
- If a mobile form was created, add `"mobile": "processes/<key>/<key>-mobile.xml"` inside `"forms"`.
- Add `schedules` only if the process runs on a timer (cron/simple interval).
## Step 5 — Report
Tell the user:
- Files created: `<key>.bpmn`, `<key>-desktop.xml` (and `<key>-mobile.xml` if applicable).
- Registered in `vitruvio.json` with key `<key>`.
- The process identity is the `<bpmn2:process id>` — it must match the key.
- Each `activiti:formKey` in the BPMN must have a matching `<form formKey="...">` in **every**
form file (desktop and mobile).
- Submitted field `id="X"` in `formKey="formAbertura"` becomes process variable `formAbertura_X`
(desktop auto-injects these; mobile must declare/fetch them — see vitruvio-criar-form-mobile).
- For complex flows, recommend editing the BPMN in bpmn.io or Camunda Modeler before deploying.
+63
View File
@@ -0,0 +1,63 @@
# Create Vitruvio Query
> All messages shown to the user must be written in Portuguese.
You are creating a new named SQL query inside a Vitruvio repository. Follow these steps in order.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Collect query details
Ask the user (in a single message, only ask what is missing from their original request):
- **Key** — kebab-case unique identifier. Used to reference this query in reports, DBTable components, and scripts.
- **Name** — human-readable label shown in the Vitruvio UI.
- **SQL** — the query itself, or enough context to write it.
- **Connection** — datasource name (default: `vitruvio_producao`). Ask only if the user mentions a specific datasource.
## Step 3 — Create the file
Target path: `queries/<key>.sql`
Rules:
- One `SELECT` per file. No multiple statements, no DDL, no INSERT/UPDATE/DELETE.
- Named bind parameters use `:paramName` syntax — never concatenate user input into SQL.
- Write ANSI SQL where possible. If DB-specific syntax is unavoidable, note it in a comment.
- Keep Oracle and PostgreSQL compatibility in mind — avoid syntax that only works in one.
```sql
SELECT col1,
col2
FROM my_table
WHERE active = 1
AND id = :id
ORDER BY col1
```
## Step 4 — Register in vitruvio.json
Read `vitruvio.json`, find or create the `"queries"` array, and add:
```json
{
"key": "<key>",
"name": "<name>",
"source": "queries/<key>.sql",
"connection": "<connection>"
}
```
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
## Step 5 — Report
Tell the user:
- File created: `queries/<key>.sql`
- Registered in `vitruvio.json` with key `<key>`
- How it can be used: as a datasource in a report, in a DBTable component, or loaded in a script via `db.executeNamedQuery('<key>', params)`
@@ -0,0 +1,118 @@
# Create Vitruvio Report
> All messages shown to the user must be written in Portuguese.
You are registering a new report inside a Vitruvio repository. Follow these steps in order.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Identify the report type
Ask the user which type applies (if not already clear from the request):
| Type | When to use |
|------|-------------|
| **MODELO_ESTATICO** | A Jasper report whose layout is fully designed in a `.jrxml` file (Jaspersoft Studio). The dev provides or will provide the `.jrxml`. |
| **DINAMICO_QUERY_SQL** | A Vitruvio-managed report where data comes from a named query and columns/layout are configured in `vitruvio.json`. The `.jrxml` is still needed but is simpler — Vitruvio drives the structure. |
## Step 3 — Collect details
Ask the user (in a single message, only ask what is missing):
**Both types:**
- **Key** — kebab-case or snake_case unique identifier.
- **Name** — human-readable label shown in the UI.
- **Category** — display category (e.g. `"Compras"`, `"Auditoria/Gondola"`).
- **Owner** — Vitruvio username of the responsible person.
- **Orientation** — `RETRATO` (portrait) or `PAISAGEM` (landscape). Default: `RETRATO`.
- **Allowed groups / users** — who can access this report (can be empty arrays).
- **Has parameters?** — does the user fill in parameters before running it? If yes, a params form is needed.
**DINAMICO_QUERY_SQL only:**
- **Query key** — the named query that feeds the report (must be registered in `vitruvio.json`).
- **Columns** — list of columns: name (DB column), label, alignment (`LEFT`/`CENTER`/`RIGHT`), width (px), aggregation (`null`, `SUM`, `COUNT`, etc.).
## Step 4 — Create the files
### MODELO_ESTATICO
Files live flat in `reports/`:
- `reports/<key>.jrxml` — **do not generate this file**; tell the user to place the Jaspersoft-designed template here. Must target **JasperReports 6.21.2** — do not save with a newer version.
- `reports/<key>-params.xml` — only if the report has parameters (follows the same Vaadin XML form schema as panels).
### DINAMICO_QUERY_SQL
Files live in a subdirectory:
- `reports/<key>/template.jrxml` — **do not generate this file**; tell the user to place the template here.
- `reports/<key>/params.xml` — only if the report has parameters.
## Step 5 — Register in vitruvio.json
Read `vitruvio.json`, find or create the `"reports"` array, and add the entry.
### MODELO_ESTATICO entry
```json
{
"key": "<key>",
"name": "<name>",
"type": "MODELO_ESTATICO",
"category": "<category>",
"owner": "<owner>",
"template": "reports/<key>.jrxml",
"parameterForm": "reports/<key>-params.xml",
"orientation": "RETRATO",
"allowedGroups": [],
"allowedUsers": [],
"columns": [],
"schedules": []
}
```
Omit `"parameterForm"` if no params form.
### DINAMICO_QUERY_SQL entry
```json
{
"key": "<key>",
"name": "<name>",
"description": "<description>",
"type": "DINAMICO_QUERY_SQL",
"category": "<category>",
"owner": "<owner>",
"template": "reports/<key>/template.jrxml",
"parameterForm": "reports/<key>/params.xml",
"query": "<query-key>",
"orientation": "RETRATO",
"allowedGroups": [],
"allowedUsers": [],
"columns": [
{
"name": "COLUMN_NAME",
"label": "Column Label",
"align": "LEFT",
"width": 100,
"aggregation": null
}
]
}
```
Omit `"parameterForm"` if no params form.
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
## Step 6 — Report
Tell the user:
- Entry registered in `vitruvio.json` with key `<key>` and type `<type>`
- For MODELO_ESTATICO: remind them to place the `.jrxml` at `reports/<key>.jrxml`, designed in Jaspersoft Studio 6.21.2
- For DINAMICO_QUERY_SQL: remind them to place the template at `reports/<key>/template.jrxml`
- If a params form is needed: what file to create and that it follows the same Vaadin XML schema as panels
+97
View File
@@ -0,0 +1,97 @@
# Create Vitruvio Script
> All messages shown to the user must be written in Portuguese.
You are creating a new script inside a Vitruvio repository. Follow these steps in order.
## Step 1 — Confirm you are inside a Vitruvio repo
```bash
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
```
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
## Step 2 — Collect script details
Ask the user (in a single message, only ask what is missing from their original request):
- **Key (sigla)** — unique identifier used in `libService.loadScript('key')`. Snake_case. Must be unique across all scripts in the repo. If the user already provided a name, suggest a key derived from it.
- **Name** — human-readable label shown in the Vitruvio UI.
- **Pattern** — which of the two patterns applies:
- **Library** — reusable module, loaded by other scripts/endpoints/panels via `libService.loadScript`. Wrap in `({...})`.
- **Process/task script** — runs directly from a process task or scheduler. Top-level execution, no export.
- **Description** — one sentence about what this script does (optional, but ask if not provided).
- **Domain** — `USUARIO` (user-level, default) or `SISTEMA` (system-level).
## Step 3 — Create the file
Target path: `scripts/<key>.js`
### Library template
```javascript
/**
* Nome: <name>
* Sigla: <key>
* Descrição: <description>
*/
({
// example function — replace with actual implementation
run: function(params) {
var db = libService.loadScript('db');
var banco = new db(db.VITRUVIO_DATASOURCE);
// implementation here
return {};
}
})
```
### Process / task script template
```javascript
/**
* Nome: <name>
* Sigla: <key>
* Descrição: <description>
*/
(function(execution) {
var db = libService.loadScript('db');
var banco = new db(db.VITRUVIO_DATASOURCE);
// implementation here
})(execution)
```
Rules (Rhino ES5 — no exceptions):
- No `let`, `const`, arrow functions, template literals, destructuring, spread, `class`, or `import/export`
- Use `var` everywhere
- No `require`, `process`, `window`, or Node/browser globals
- String concatenation with `+`, not template literals
- `JSON.parse` / `JSON.stringify` for serialization
## Step 4 — Register in vitruvio.json
Read `vitruvio.json`, find or create the `"scripts"` array, and add:
```json
{
"key": "<key>",
"name": "<name>",
"description": "<description>",
"language": "javascript",
"domain": "<USUARIO|SISTEMA>",
"source": "scripts/<key>.js"
}
```
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
## Step 5 — Report
Tell the user:
- File created: `scripts/<key>.js`
- Registered in `vitruvio.json` with key `<key>`
- How to load it from another script or endpoint: `var lib = libService.loadScript('<key>');`