For LLMs — AI Skill Document
Preview feature: CLI AI Automation is currently a preview feature. If you encounter any issues, please report them at support.skipper18.com.
This page contains a machine-readable skill document that teaches AI assistants how to use the Skipper CLI. Copy the document below into your AI assistant's system prompt or instructions file, and it will be able to create projects, add entities, manage relationships, validate schemas, and export code on your behalf.
How to Use
Copy the skill document from the code block below and paste it into the instructions for your AI tool:
Claude Code: Add it to your project's
CLAUDE.mdfile or paste it into the project instructions.ChatGPT: Paste it into Custom Instructions or at the start of your conversation.
Cursor: Add it to your
.cursorrulesfile in the project root.Windsurf: Add it to your
.windsurfrulesfile in the project root.Generic / other tools: Add it to the system prompt or any instructions file your AI reads at startup.
Once the skill document is loaded, you can give natural-language instructions like "add a User entity with email and password fields" and the AI will know which Skipper CLI commands and patch operations to use.
Skipper CLI Skill Document
Last synced with docs/ai/skipper-skill.md on 2026-06-11.
Copy everything inside the code block below:
# Skipper CLI Skill
Skipper is an ORM design tool. The `-cli-*` flags let AI assistants and CI scripts drive it headlessly.
**Windows:** Use `SkipperCli.exe` (not `Skipper.exe`) for CLI operations.
**Linux/macOS:** Use `./Skipper` directly.
All CLI responses are JSON envelopes on stdout with fields: `status`, `command`, `data`, `warnings`, `meta`.
## Quick Start
```bash
# List all verbs (Windows: SkipperCli.exe, Linux/macOS: ./Skipper)
Skipper.exe -cli-help
# Detail for one verb
Skipper.exe -cli-help export
```
## Global Flags
| Flag | Effect |
|---|---|
| `-quiet` | Suppress stderr diagnostics |
| `-verbose` | Raise stderr to debug level (trace + progress) |
| `-include-timing` | Add `meta.timing_breakdown` to envelope |
## Envelope
Every verb emits a single JSON document on stdout. Schema: `docs/ai/skipper-envelope.schema.json`.
## Typical Workflow
1. `-cli-list-frameworks` — discover available ORM/MVC combinations
2. `-cli-create-project` or `-cli-import-project` or `-cli-import-database` — create/import a project
3. `-cli-apply-patch` — add modules, entities, fields, associations, indexes
4. `-cli-validate` — check for errors and warnings
5. `-cli-schema-summary` — inspect the result (JSON output, see `docs/ai/skipper-schema-shape.md`)
6. `-cli-export` — generate framework code (PHP classes, migrations, etc.)
7. `-cli-export-diagram` — render visual diagram (PNG/PDF, requires `Skipper.exe` GUI build)
For incremental changes, use `-cli-schema-summary` to read current state, then `-cli-apply-patch` or `-cli-apply-merge` to modify, then `-cli-validate` to verify. Use `-cli-list-orm-attributes` to discover which framework-specific ORM attributes (e.g. association `onDelete`/`onUpdate`) are settable before writing a `set_orm_attribute` op.
## Verbs
### `-cli-help`
List available verbs or show detail for one.
```
Skipper.exe -cli-help [<verb-name>]
```
**Errors:** `UNKNOWN_VERB` (if verb-name not recognised).
---
### `-cli-export`
Full export: classes + migrations using the project's configured framework.
```
Skipper.exe -cli-export <project.skipper>
```
**Data shape:** `{ files_written: [{path, bytes}], framework: "...", output_dir: "..." }`
**Errors:** `PROJECT_LOAD_FAILED`, `EXPORT_FAILED`.
**Note:** Files are written relative to the project file location, into each module's `export_path`. The export pipeline produces all files (classes + migrations) in a single pass. To filter by type, check output paths — migration files live under the module's `migrations-path` directory (e.g., `database/migrations/`).
---
### `-cli-export-diagram`
Render schema diagram as PNG or PDF. Available in `Skipper.exe` (GUI build), not in `SkipperCli.exe` (console build).
```
Skipper.exe -cli-export-diagram <project.skipper> -format <png|pdf> -output <path>
```
**Data shape:** `{ format: "...", output_path: "...", bytes: <int> }`
**Errors:** `INVALID_ARG_VALUE` (bad format), `NOT_IMPLEMENTED` (svg).
**Note:** On Linux, set `QT_QPA_PLATFORM=offscreen`.
---
### `-cli-validate`
Validate project consistency and migration freshness.
```
Skipper.exe -cli-validate <project.skipper>
```
**Data shape:** `{ issues: [{severity, code, object, message}], summary: {error_count, warning_count} }`
**Errors:** `PROJECT_LOAD_FAILED`, `VALIDATION_FAILED`.
---
### `-cli-schema-summary`
Dump entire project as JSON.
```
Skipper.exe -cli-schema-summary <project.skipper> [-with-visual]
```
**Data shape:** `{ project: {...} }` (see `docs/ai/skipper-schema-shape.md`)
---
### `-cli-compare`
Diff two `.skipper` files using the migration diff engine.
```
Skipper.exe -cli-compare <new.skipper> <old.skipper>
```
**Data shape:** `{ summary: {added, removed, modified, identical}, items: [{action, type, name, details?}], input_new: "...", input_old: "..." }`
---
### `-cli-apply-patch`
Apply a list of explicit operations (op-list, RFC 6902 spirit).
```
Skipper.exe -cli-apply-patch <project.skipper> -patch <ops.json> -output <out.skipper>
```
**Data shape (success):** `{ ops_executed: <int>, output_path: "...", results: [{op, result}] }` — `results` is index-aligned with the op list; each `result` is that op's method JSON (e.g. `{success, id, name, ...}`), so you get the created/updated object **ids** back. Use an id for a follow-up `set_orm_attribute object:<id>` (across invocations) or reference it inside the same patch via `$ref` (below).
**Data shape (failure):** `{ ops_attempted: <int>, failed_op_index: <int> }`
**Errors:** `INVALID_OP`, `OBJECT_NOT_FOUND`, `MALFORMED_INPUT_FILE`.
**Error behavior:** On failure at op N, operations 0 through N-1 are already applied. The output file is **not** written on failure. To retry, fix the failing op and re-run with the original input file.
#### Patch Operations Reference (36 ops)
Each op is `{"op": "<name>", "args": {...}}`. Operations are executed in order. Both `{"ops": [...]}` and bare `[...]` array formats are accepted.
**Operations are sequential:** Objects created by earlier ops are immediately available to later ops in the same patch. You can `add_entity` and then `add_field` referencing that entity in a single patch file.
**Inline ORM attributes (`orm_attributes`):** the id-addressed creators — `add_association`, `add_index`, `add_embedded`, `add_many_to_many` — accept an optional `orm_attributes` object (a `{path: value}` map applied atomically right after the object is created, using the same path semantics as `set_orm_attribute`; an unknown path aborts the op). `add_many_to_many` additionally accepts `owner_orm_attributes` / `inverse_orm_attributes` for the two M:N sides (`many-to-many-entity`) — e.g. Doctrine2 `orphan-removal` lives on the side, so set it via `owner_orm_attributes:{"orphan-removal":"true"}`. This avoids needing the object's id for attributes known at creation time.
**`$ref` — reference an earlier op's id within the same patch:** any op may declare a top-level `"ref": "<handle>"`; a later op may use the string value `"$ref:<handle>"` in any arg, resolved at execution to the producer op's result `id`. Lets you create an object and act on it (by id) in one patch without a CLI round-trip. The `$ref:` prefix is **reserved** in string arg values. Unknown / forward / duplicate handles are errors (`INVALID_OP`). Example: `{"op":"add_many_to_many","args":{...},"ref":"m1"}` then `{"op":"set_orm_attribute","args":{"object":"$ref:m1","object_type":"many-to-many","path":"...","value":"..."}}`.
Example patch file:
```json
[
{"op": "add_entity", "args": {"module": "MainBundle", "name": "Customer"}},
{"op": "add_field", "args": {"entity": "Customer", "name": "email", "type": "string", "size": 255, "unique": true}},
{"op": "add_field", "args": {"entity": "Customer", "name": "name", "type": "string", "size": 100}}
]
```
**Name resolution — prefer the FULL qualified name.** Entity and module references accept either the full qualified name (`\App\Crm\Address`) or a short name (`Address`, matched against `LocalName`). **The full qualified name is the primary, recommended form** — it is unambiguous. Short names are a convenience: if two objects share the same short name (legitimately — e.g. `\App\Crm\Address` and `\App\EShop\Address` in different modules/namespaces), a short name resolves to **the first match found and the others are silently ignored**, which can target the wrong object. Duplicate short names across modules are valid (the full namespace disambiguates them), so **always use the full qualified name when an object's short name may not be unique** (and prefer it generally in scripted/generated patches). You can also reference an object by the numeric `id` returned in the apply-patch `results` (or via `$ref`) — also unambiguous.
**Namespace values:** Use forward slashes (`/App/Entity`) or backslashes (`\App\Entity`) — both are accepted. Forward slashes are auto-converted to backslashes. Invalid JSON escape sequences in backslash paths (e.g. `\App` instead of `\\App`) are auto-corrected.
**Entity template auto-creates PK:** `add_entity` automatically creates an `id` integer primary key field (auto-increment) from the framework template. Do **not** add a PK field manually — you will end up with duplicate `id` + `id2`. Only add non-PK fields after `add_entity`.
**Entity** (3):
- `add_entity` — `{module, name, [namespace], [description], [region]}` — auto-creates `id` PK field from template; do not add PK manually. `region` places the entity inside one of the module's regions (by the region's `caption`); the region then drives the entity's export sub-namespace / sub-path.
- `update_entity` — `{entity, [name], [namespace], [description]}` — use this to rename entities (not remove+add)
- `remove_entity` — `{entity}` — also removes associated fields, associations, indexes, and M:N relationships
**Field** (4):
- `add_field` — `{entity, name, [type], [size], [primary], [nullable], [unique], [auto_increment], [unsigned], [default]}` — fields are NOT NULL by default; pass `"nullable": true` explicitly for nullable fields. If a field with the same name already exists, the operation is silently skipped. For an unsigned integer column use a real type plus `"unsigned": true` (e.g. `{type: "bigInteger", unsigned: true}`) — there is no `unsignedBigInteger` type
- `update_field` — `{entity, field, [name], [type], [size], [primary], [nullable], [unique], [auto_increment], [unsigned], [default]}`
- `remove_field` — `{entity, field}`
- `reorder_field` — `{entity, field, sort_order}`
**Association** (3):
- `add_association` — `{from, to, [type], [required], [owner_alias], [inverse_alias], [kind], [morph_name], [morph_id_field], [morph_type_field], [orm_attributes]}` — `type` is `one-to-one`/`one-to-many` (default), `required` makes the FK not-null. `kind:"polymorphic"` creates a morph (Laravel `morphTo`-style) association. `orm_attributes` is a `{path:value}` map applied right after creation (e.g. `onDelete`/`onUpdate`).
- `update_association` — `{id, [owner_alias], [inverse_alias]}` — `id` is the numeric ID from `schema-summary`
- `remove_association` — `{id}` — `id` is the numeric ID from `schema-summary`
**Module** (3):
- `add_module` — `{name, [namespace], [description], [export_path], [export_format]}` — if `export_format` is omitted, uses the framework default
- `update_module` — `{module, [name], [namespace], [description], [export_path], [export_format]}` — use this to rename/reconfigure existing modules (not remove+add)
- `remove_module` — `{module}`
**Index** (3):
- `add_index` — `{entity, name, [unique], [fields: ["f1","f2"]], [orm_attributes]}` — `fields` also accepts a single column name as a plain string
- `update_index` — `{entity, index, [name], [unique], [fields: ["f1","f2"]]}` — when `fields` is provided, the listed columns are **added** to the index (idempotent). To remove a column, delete the index and recreate it
- `remove_index` — `{entity, index}`
**ManyToMany** (3):
- `add_many_to_many` — `{owner, inverse, [mn_entity], [owner_alias], [inverse_alias], [kind], [morph_name], [morph_id_field], [morph_type_field], [orm_attributes], [owner_orm_attributes], [inverse_orm_attributes]}` — automatically creates a join entity with composite PK foreign key fields. `kind:"polymorphic"` makes it a morph (Laravel `morphToMany`-style) relation. `owner_orm_attributes` / `inverse_orm_attributes` set attributes on the two sides (e.g. Doctrine2 `orphan-removal`)
- `update_many_to_many` — `{id, [owner_alias], [inverse_alias]}`
- `remove_many_to_many` — `{id}`
**Embedded** (3):
- `add_embedded` — `{from, to, [owner_alias], [inverse_alias], [orm_attributes]}`
- `update_embedded` — `{id, [owner_alias], [inverse_alias]}`
- `remove_embedded` — `{id}`
**Inheritance** (3):
- `add_inheritance` — `{parent, child, [type], [discriminator_value]}` — valid types: `SINGLE_TABLE`, `JOINED`, `MAPPED_SUPERCLASS`
- `update_inheritance` — `{id, [discriminator_value]}`
- `remove_inheritance` — `{id}`
**Region** (3):
- `add_region` — `{module, [caption], [namespace], [description], [export_path]}` — a region groups entities under a sub-namespace (`namespace`) and/or sub-path (`export_path`); assign entities with `add_entity`'s `region` arg (matched by the region's `caption`)
- `update_region` — `{id, [caption], [namespace], [description]}`
- `remove_region` — `{id}`
**Comment** (3):
- `add_comment` — `{module, [caption], [description]}`
- `update_comment` — `{id, [caption], [description]}`
- `remove_comment` — `{id}`
**ORM Attributes** (4):
- `set_orm_attribute` — `{object, [object_type], [entity], path, value}`
- `remove_orm_attribute` — `{object, [object_type], [entity], path}`
- `add_orm_attribute_item` — `{object, [object_type], [entity], path, [key], [values: {child: val, ...}]}`
- `remove_orm_attribute_item` — `{object, [object_type], [entity], path}`
**Visual Layout** (1):
- `reset_layout` — `{}` (no args) — resets visual positions on all entities/modules/regions/comments so auto-arranger runs on next GUI open
`object_type` defaults to `"entity"`. Supported types for ORM attribute write operations (all 10): `project`, `entity`, `field`, `module`, `association`, `index`, `many-to-many`, `many-to-many-entity`, `embedded`, `region`. Name-addressed types (`entity`/`field`/`module`/`project`) take a name in `object`; id-addressed types (`association`/`index`/`many-to-many`/`many-to-many-entity`/`embedded`/`region`) take the numeric `id` returned by the object's `add_*` op in `object` (capture it from the apply-patch `results`, or reference it in the same patch via `$ref`). For attributes known at creation time, prefer the inline `orm_attributes` arg on the `add_*` op instead.
**ORM Attribute addressing for fields:** For `object_type: "field"`, use one of these formats to identify the field:
- **Dotted notation (recommended):** `"object": "Customer.email"` — `Entity.field` format
- **With entity parameter:** `"object": "email", "entity": "Customer"` — separate entity lookup
- **Field name only:** `"object": "email"` — searches all entities (fails if ambiguous)
**Attribute paths:** Use attribute names without framework prefix. Write `"table"` not `"orm:table"`. Use `/` to separate nested paths (e.g. `"options/charset"`). The `orm:` prefix is auto-stripped if present, but avoid it.
Examples:
```json
{"op": "set_orm_attribute", "args": {"object": "Product", "path": "table", "value": "app_products"}}
{"op": "set_orm_attribute", "args": {"object": "Product.price", "object_type": "field", "path": "precision", "value": "10"}}
{"op": "set_orm_attribute", "args": {"object": "price", "object_type": "field", "entity": "Product", "path": "scale", "value": "2"}}
{"op": "set_orm_attribute", "args": {"object": "Product", "path": "options/charset", "value": "utf8mb4"}}
{"op": "set_orm_attribute", "args": {"object": "Product", "path": "change-tracking-policy", "value": "DEFERRED_EXPLICIT"}}
{"op": "add_orm_attribute_item", "args": {"object": "Product", "path": "lifecycle-callbacks", "values": {"type": "prePersist", "method": "onPrePersist"}}}
```
**Referencing associations and M:N by ID:** `update_association`, `remove_association`, `update_many_to_many`, `remove_many_to_many` require a numeric `id` from `-cli-schema-summary` output. IDs are project-scoped auto-increment numbers. To modify objects created in the same patch, run `schema-summary` after the patch to discover their IDs, then apply a second patch.
**Referencing indexes by name:** `update_index` and `remove_index` use `{entity, index}` where `index` is the index **name** (string), not a numeric ID.
---
### `-cli-list-frameworks`
List available ORM and MVC frameworks.
```
Skipper.exe -cli-list-frameworks
```
**Data shape:** `{ orm_frameworks: [{name: "..."}], mvc_frameworks: [{name: "..."}] }`
---
### `-cli-list-orm-attributes`
Discover the framework's declared ORM attributes per object type — so you know what is settable via `set_orm_attribute` (name, path, type, enum values) without reading the cfg. Read-only.
```
Skipper.exe -cli-list-orm-attributes <project.skipper> [-object-type association]
```
Without `-object-type`, lists every object type (entity, field, association, many-to-many, many-to-many-entity, module, index, embedded, region). Pass `-object-type project` explicitly to list project-level attributes.
**Data shape:** `{ object_types: [{ object_type: "association", attributes: [{ name, path, type, [enum_values], [container] }] }] }`
**Example — find how to set a FK cascade (Laravel/Knex):** `-cli-list-orm-attributes p.skipper -object-type association` returns `onDelete` / `onUpdate` (type `enum`, values `cascade|restrict|set null|no action`). Set them with `set_orm_attribute object_type:association path:onDelete value:cascade` — the exported **migration** then emits `->onDelete('cascade')`.
---
### `-cli-export-ops`
Emit the apply-patch op-list that rebuilds a project's schema on an empty project — the inverse of `apply-patch` (and a more actionable form of `schema-summary`). Read-only.
```
Skipper.exe -cli-export-ops <project.skipper>
```
**Data shape:** `{ ops: [ { op, args }, ... ] }` — feed it straight back into `-cli-apply-patch` (the `{ops:[...]}` envelope is accepted) against an empty project to reproduce the schema.
**Note:** relationship-generated objects are emitted as their relationship op (the FK field, M:N pivot entity, morph_id/morph_type, discriminator are created by `add_association`/`add_many_to_many`, not as `add_field`). Inheritance and id-referenced orm-attributes are not yet emitted.
---
### `-cli-apply-merge`
Apply a declarative sparse target state (RFC 7396 spirit).
```
Skipper.exe -cli-apply-merge <project.skipper> -merge <state.json> -output <out.skipper>
```
**Data shape (success):** `{ objects_changed: <int>, output_path: "..." }`
**Errors:** `INVALID_MERGE_INPUT`, `OBJECT_NOT_FOUND`, `MALFORMED_INPUT_FILE`.
**Input structure:** Must have a `project` root key mirroring `schema-summary` structure. Only include objects you want to change — unmentioned objects are left as-is. Merge updates existing fields by name and adds new ones. Warnings are emitted for newly added fields.
```json
{
"project": {
"modules": [
{
"name": "MainBundle",
"entities": [
{
"name": "Customer",
"fields": [
{"name": "email", "type": "string", "size": 320},
{"name": "middleName", "type": "string", "size": 100, "nullable": true}
]
}
]
}
]
}
}
```
---
### `-cli-create-migration`
Create a new migration revision. Optionally attach custom SQL to the just-created revision (same slots as `-cli-set-migration-sql`).
```
Skipper.exe -cli-create-migration <project.skipper>
[-before_sql "..."] [-after_sql "..."] [-before_down_sql "..."] [-after_down_sql "..."]
```
**Data shape:** `{ created: true, revision: "<uuid>" }` (success) or `{ created: false, reason: "no_changes_pending" }` (no changes)
**Errors:** `MIGRATIONS_DISABLED`.
---
### `-cli-set-migration-sql`
Attach **custom raw SQL** to a migration revision. The SQL is injected verbatim into the generated migration: `before_sql`/`after_sql` at the start/end of `up()`, `before_down_sql`/`after_down_sql` at the start/end of `down()`. Emitted as `DB::statement('...')` (Laravel) or chained `.raw('...')` (Knex).
Use this for things Skipper cannot express declaratively — e.g. dropping a leftover PostgreSQL `CHECK` constraint when changing an `enum` field to `string`:
`ALTER TABLE "hardware" DROP CONSTRAINT IF EXISTS "hardware_state_check"`.
```
Skipper.exe -cli-set-migration-sql <project.skipper> -revision <uuid|latest>
[-before_sql "..."] [-after_sql "..."] [-before_down_sql "..."] [-after_down_sql "..."]
```
- `-revision` accepts a revision uuid or the literal `latest` (newest revision). Required.
- At least one slot flag is required. **Replace semantics:** a present flag overwrites that slot; passing an empty value (`-before_sql ""`) clears it.
- The SQL is stored in the `.skipper` file and survives load/save. Frameworks without migration generation (Doctrine/Propel/CakePHP/Symfony) ignore it; only Laravel and Knex emit it.
- **Multi-module limitation:** the SQL is attached to the **first** module log that contains the revision uuid, and is emitted only into that module's generated migration. Single-module projects (the common case) are unaffected.
**Data shape:** `{ revision: "<uuid>", slots_set: ["before_sql", ...] }`
**Errors:** `MISSING_REQUIRED` (no project / no `-revision` / no slot flag), `OBJECT_NOT_FOUND` (revision uuid not found), `INTERNAL_ERROR` (save/serialization failure), `PROJECT_LOAD_FAILED`.
---
### `-cli-create-project`
Create an empty project with chosen ORM/MVC framework.
```
Skipper.exe -cli-create-project -output <path> -orm <name> -mvc <name> [-name <project-name>]
```
**Data shape:** `{ output_path: "...", orm: "...", mvc: "..." }`
**Errors:** `UNKNOWN_FRAMEWORK`, `IO_ERROR`, `VIEWER_LICENSE`.
---
### `-cli-import-project`
Scan a codebase directory and build a `.skipper` from discovered files.
```
Skipper.exe -cli-import-project <scan-dir> -output <path> -orm <name> -mvc <name> [-name <project-name>]
```
**Data shape:** `{ output_path: "...", orm: "...", mvc: "...", modules_created: <int>, entities_imported: <int> }`
**Errors:** `IO_ERROR`, `VIEWER_LICENSE`.
---
### `-cli-import-database`
Reverse-engineer a `.skipper` from a live database.
```
Skipper.exe -cli-import-database -output <path> -db-class <sqlite|mysql|postgres|sqlserver> -connection <conn> -orm <name> -mvc <name> [-name <project-name>] [-skip-initial-migrations]
```
**Errors:** `DB_CONNECTION_FAILED`, `DB_IMPORT_FAILED`, `UNKNOWN_FRAMEWORK`, `VIEWER_LICENSE`.
---
## Legacy / Compatibility
### `--export-model-to-image`
Legacy flag for diagram export. Still functional. Prefer `-cli-export-diagram` for new uses.Prompt Examples
Here are example prompts you can give your AI assistant once the skill document is loaded, along with what the AI should do for each.
1. "Add a User entity with email and password fields to my Doctrine2 project"
The AI should create a patch file with add_entity for User (which auto-creates the id PK field), then add_field for email (string, 255, unique) and password (string, 255). Do not add a PK field manually. Then apply it:
# The AI writes ops.json:
# [
# {"op":"add_entity","args":{"module":"Default","name":"User"}},
# {"op":"add_field","args":{"entity":"User","name":"email","type":"string","size":255,"unique":true}},
# {"op":"add_field","args":{"entity":"User","name":"password","type":"string","size":255}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper2. "Create a many-to-many relationship between Post and Tag"
The AI should use add_many_to_many with Post as owner and Tag as inverse, creating a join entity (PostTag):
# ops.json:
# [
# {"op":"add_many_to_many","args":{"owner":"Post","inverse":"Tag","mn_entity":"PostTag","owner_alias":"tags","inverse_alias":"posts"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper3. "Set up SINGLE_TABLE inheritance for Vehicle with Car and Truck"
The AI should first ensure all three entities exist (using add_entity which auto-creates PK fields), then use add_inheritance twice to establish the parent-child relationships with discriminator values:
# ops.json:
# [
# {"op":"add_entity","args":{"module":"Default","name":"Vehicle"}},
# {"op":"add_entity","args":{"module":"Default","name":"Car"}},
# {"op":"add_entity","args":{"module":"Default","name":"Truck"}},
# {"op":"add_inheritance","args":{"parent":"Vehicle","child":"Car","type":"SINGLE_TABLE","discriminator_value":"car"}},
# {"op":"add_inheritance","args":{"parent":"Vehicle","child":"Truck","type":"SINGLE_TABLE","discriminator_value":"truck"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper4. "Validate my project and fix any errors"
The AI should first run validation, read the issues from the JSON output, then create a patch to fix them (e.g., adding missing primary keys or removing orphaned references):
# Step 1: Validate
Skipper.exe -cli-validate project.skipper
# Step 2: Read the issues array from the JSON output
# Step 3: For each fixable issue, create the appropriate patch ops
# Step 4: Apply fixes
Skipper.exe -cli-apply-patch project.skipper -patch fixes.json -output project.skipper
# Step 5: Re-validate to confirm clean
Skipper.exe -cli-validate project.skipper5. "Export my project to PHP attribute annotations"
The AI should configure the export format via ORM attributes if needed, then run the export verb. The export format is controlled by the project's framework configuration:
# If the project's export format needs to change, set the module-level ORM attribute:
# [{"op":"set_orm_attribute","args":{"object":"Default","object_type":"module","path":"export-format","value":"php-attributes"}}]
# Skipper.exe -cli-apply-patch project.skipper -patch format.json -output project.skipper
# Then export:
Skipper.exe -cli-export project.skipper6. "Rename entity User to Account and update all references"
The AI should use update_entity to change the entity name. Skipper automatically updates all association and relationship references that point to the renamed entity:
# ops.json:
# [
# {"op":"update_entity","args":{"entity":"User","name":"Account"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper7. "Add a unique index on the email field of the Customer entity"
The AI should use add_index with the unique flag and reference the target field:
# ops.json:
# [
# {"op":"add_index","args":{"entity":"Customer","name":"idx_customer_email","unique":true,"fields":["email"]}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper8. "Set the table name for entity Order to 'shop_orders'"
The AI should use set_orm_attribute to set the framework-specific table name attribute on the entity:
# ops.json:
# [
# {"op":"set_orm_attribute","args":{"object":"Order","object_type":"entity","path":"table","value":"shop_orders"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper9. "Add a region called 'Domain Models' to the AppBundle module"
The AI should use add_region to create a visual grouping region inside the specified module:
# ops.json:
# [
# {"op":"add_region","args":{"module":"AppBundle","caption":"Domain Models"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper10. "Remove the description field from the Product entity"
The AI should use remove_field specifying the entity and field name:
# ops.json:
# [
# {"op":"remove_field","args":{"entity":"Product","field":"description"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper11. "Show me the full schema of my project as JSON"
The AI should use -cli-schema-summary to dump the entire project structure as JSON. No patch file is needed -- this is a read-only operation:
Skipper.exe -cli-schema-summary project.skipper -quiet12. "Compare my current project with the previous version and show what changed"
The AI should use -cli-compare to diff two .skipper files. If working with version control, it can extract the previous version from git first:
# Extract the previous version from version control
git show HEAD~1:project.skipper > /tmp/project-old.skipper
# Compare current vs. previous
Skipper.exe -cli-compare project.skipper /tmp/project-old.skipper -quiet13. "List available frameworks"
The AI should use -cli-list-frameworks to discover which ORM and MVC frameworks are available for project creation:
Skipper.exe -cli-list-frameworks -quiet14. "Set decimal precision on the price field of Product"
The AI should use set_orm_attribute with the dotted Entity.field notation to target the specific field's ORM attribute:
# ops.json:
# [
# {"op":"set_orm_attribute","args":{"object":"Product.price","object_type":"field","path":"precision","value":"10"}},
# {"op":"set_orm_attribute","args":{"object":"Product.price","object_type":"field","path":"scale","value":"2"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper15. "Add a prePersist lifecycle callback to the Product entity"
The AI should use add_orm_attribute_item with the values parameter to add a lifecycle callback with its type and method:
# ops.json:
# [
# {"op":"add_orm_attribute_item","args":{"object":"Product","path":"lifecycle-callbacks","values":{"type":"prePersist","method":"onPrePersist"}}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper16. "Merge a nullable middle name field into the Customer entity"
The AI should use -cli-apply-merge with a declarative state file that specifies the new field with nullable: true:
# state.json:
# {
# "project": {
# "modules": [{
# "name": "Default",
# "entities": [{
# "name": "Customer",
# "fields": [
# {"name": "middleName", "type": "string", "size": 100, "nullable": true}
# ]
# }]
# }]
# }
# }
Skipper.exe -cli-apply-merge project.skipper -merge state.json -output project.skipper17. "Drop the leftover Postgres CHECK constraint after I changed the enum field to a string"
Skipper cannot express a raw DROP CONSTRAINT declaratively, so the AI should attach custom SQL to the migration revision with -cli-set-migration-sql. Use -revision latest to target the newest revision, and put the statement at the start of up():
Skipper.exe -cli-set-migration-sql project.skipper -revision latest \
-before_sql 'ALTER TABLE "hardware" DROP CONSTRAINT IF EXISTS "hardware_state_check"'18. "Add an ON DELETE CASCADE foreign key from Order to Customer (Laravel)"
Association onDelete is an ORM attribute, not an add_association arg. The AI should set it inline via the orm_attributes map on the creator, so no second round-trip or numeric id is needed:
# ops.json:
# [
# {"op":"add_association","args":{"from":"Order","to":"Customer","orm_attributes":{"onDelete":"cascade"}}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper19. "Create a M:N between Post and Tag and set Doctrine2 orphan-removal on it in one patch"
The AI should create the relationship and reference its id within the same patch using $ref. Tag the creator op with a ref handle, then address the side via $ref:<handle> (or set it inline with owner_orm_attributes):
# ops.json:
# [
# {"op":"add_many_to_many","args":{"owner":"Post","inverse":"Tag","owner_alias":"tags","inverse_alias":"posts"},"ref":"m1"},
# {"op":"set_orm_attribute","args":{"object":"$ref:m1","object_type":"many-to-many-entity","path":"orphan-removal","value":"true"}}
# ]
Skipper.exe -cli-apply-patch project.skipper -patch ops.json -output project.skipper20. "Discover which framework ORM attributes I can set on an association"
Before writing a set_orm_attribute op, the AI should query the settable attributes (name, path, type, enum values) with -cli-list-orm-attributes — this is read-only:
Skipper.exe -cli-list-orm-attributes project.skipper -object-type association -quiet21. "Give me the apply-patch ops that rebuild this schema so I can seed a new project"
The AI should use -cli-export-ops to emit the op-list, then feed it straight back into -cli-apply-patch against an empty project:
# Emit the ops that build the schema
Skipper.exe -cli-export-ops project.skipper -quiet > ops.json
# Reproduce them on a fresh project
Skipper.exe -cli-apply-patch empty.skipper -patch ops.json -output rebuilt.skipper