Examples

Preview Feature: CLI AI Automation is a preview feature. If you encounter any issues, please report them at support.skipper18.com.

This page contains practical examples showing how to use the Skipper CLI for common tasks. Each example includes the command and its expected output. For the full verb and operation reference, see the CLI Reference.

Get Project Schema

Dump the entire project as a JSON summary. This is useful for inspecting entities, fields, associations, and modules programmatically.

Skipper.exe -cli-schema-summary myproject.skipper

Sample output (abbreviated):

{
  "status": "ok",
  "command": "schema-summary",
  "data": {
    "project": {
      "name": "MyApp",
      "orm": "Doctrine2",
      "modules": [
        {
          "name": "Default",
          "entities": [
            {
              "name": "User",
              "fields": [
                { "name": "id", "type": "integer", "primary": true },
                { "name": "email", "type": "string", "size": 255 }
              ],
              "associations": [ ... ]
            }
          ]
        }
      ]
    }
  },
  "warnings": [],
  "meta": { "duration_ms": 320, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Validate a Project

Check your project for consistency issues such as missing primary keys, orphaned associations, or migration staleness.

Skipper.exe -cli-validate myproject.skipper

Sample output with warnings:

{
  "status": "ok",
  "command": "validate",
  "data": {
    "issues": [
      { "severity": "warning", "code": "MISSING_PRIMARY_KEY", "object": "Session", "message": "Entity 'Session' has no primary key field." },
      { "severity": "warning", "code": "UNUSED_ENTITY", "object": "TempLog", "message": "Entity 'TempLog' is not referenced by any association." }
    ],
    "summary": { "error_count": 0, "warning_count": 2 }
  },
  "warnings": [],
  "meta": { "duration_ms": 150, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Add Entity with Fields

Create a patch file (ops.json) that adds a new entity and two fields, then apply it. The add_entity operation automatically creates an id primary key field from the framework template, so you only need to add non-PK fields.

Contents of ops.json:

[
  { "op": "add_entity", "args": { "module": "Default", "name": "Product" } },
  { "op": "add_field", "args": { "entity": "Product", "name": "name", "type": "string", "size": 255 } },
  { "op": "add_field", "args": { "entity": "Product", "name": "price", "type": "decimal" } }
]

Apply the patch:

Skipper.exe -cli-apply-patch myproject.skipper -patch ops.json -output myproject.skipper

Compare Two Projects

Diff two .skipper files to see what changed between versions. The comparison uses the migration diff engine.

Skipper.exe -cli-compare new.skipper old.skipper

Sample output:

{
  "status": "ok",
  "command": "compare",
  "data": {
    "summary": { "added": 2, "removed": 0, "modified": 1, "identical": 5 },
    "items": [
      { "action": "added", "type": "entity", "name": "Payment" },
      { "action": "added", "type": "field", "name": "Payment.amount" },
      { "action": "modified", "type": "entity", "name": "Order", "details": "Added field: total_price" }
    ],
    "input_new": "new.skipper",
    "input_old": "old.skipper"
  },
  "warnings": [],
  "meta": { "duration_ms": 280, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Create Migration

Generate a new migration revision from pending schema changes. Migrations must be enabled in the project.

Skipper.exe -cli-create-migration myproject.skipper

If there are pending changes, the migration is created and its revision uuid is returned:

{
  "status": "ok",
  "command": "create-migration",
  "data": { "created": true, "revision": "8f3c1a2e-..." },
  "warnings": [],
  "meta": { "duration_ms": 110, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

If the schema is already up to date:

{
  "status": "ok",
  "command": "create-migration",
  "data": { "created": false, "reason": "no_changes_pending" },
  "warnings": [],
  "meta": { "duration_ms": 95, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Attach Custom SQL to a Migration

Some changes cannot be expressed declaratively in the model. A classic example: when you change a PostgreSQL enum field to a plain string, the old CHECK constraint is left behind and must be dropped by hand. Attach the raw SQL to the migration revision with -cli-set-migration-sql — it is emitted into the generated migration as DB::statement('...') (Laravel) or chained .raw('...') (Knex).

# Target the newest revision and drop the leftover CHECK constraint at the start of up()
Skipper.exe -cli-set-migration-sql myproject.skipper -revision latest \
  -before_sql 'ALTER TABLE "hardware" DROP CONSTRAINT IF EXISTS "hardware_state_check"'

Sample output:

{
  "status": "ok",
  "command": "set-migration-sql",
  "data": { "revision": "8f3c1a2e-...", "slots_set": ["before_sql"] },
  "warnings": [],
  "meta": { "duration_ms": 60, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

There are four SQL slots: -before_sql / -after_sql (start/end of up()) and -before_down_sql / -after_down_sql (start/end of down()). Replace semantics: a present flag overwrites that slot; passing an empty value (-before_sql "") clears it. The SQL persists in the .skipper file. The same four flags can also be passed directly to -cli-create-migration to attach SQL to the revision it creates.

Import from Codebase

Scan an existing codebase directory to reverse-engineer a Skipper project from your source files.

Skipper.exe -cli-import-project ./src -output imported.skipper -orm Doctrine2 -mvc Symfony

Skipper scans the directory for entity definitions (annotations, attributes, XML mappings, YAML mappings) matching the chosen ORM framework and creates a .skipper file with all discovered entities, fields, and relationships.

List Available Frameworks

Query which ORM and MVC frameworks are available in your Skipper installation. This is useful for scripting project creation or validating framework names before use.

SkipperCli.exe -cli-list-frameworks

Expected output (abbreviated):

{
  "status": "ok",
  "command": "list-frameworks",
  "data": {
    "orm_frameworks": [
      { "name": "Doctrine2" },
      { "name": "Laravel" },
      { "name": "Propel2" },
      { "name": "CakePHP" }
    ],
    "mvc_frameworks": [
      { "name": "Symfony" },
      { "name": "Zend" },
      { "name": "Without MVC" }
    ]
  },
  "warnings": [],
  "meta": { "duration_ms": 50, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Set ORM Attributes on Fields

Configure ORM-specific attributes on individual fields using set_orm_attribute. Fields are addressed with Entity.field dotted notation and object_type set to "field".

This example sets the precision and scale on a decimal field:

[
  { "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 myproject.skipper -patch field-attrs.json -output myproject.skipper

Note: Use Entity.field dotted notation (e.g. Product.price) when targeting fields. For entity-level attributes, use just the entity name and "object_type": "entity".

Add Lifecycle Callbacks

Add ORM lifecycle callbacks to an entity using add_orm_attribute_item. Each callback specifies the event type and the method to invoke:

[
  { "op": "add_orm_attribute_item", "args": { "object": "Product", "path": "lifecycle-callbacks", "values": { "type": "prePersist", "method": "onPrePersist" } } },
  { "op": "add_orm_attribute_item", "args": { "object": "Product", "path": "lifecycle-callbacks", "values": { "type": "preUpdate", "method": "onPreUpdate" } } }
]
Skipper.exe -cli-apply-patch myproject.skipper -patch lifecycle.json -output myproject.skipper

Inline ORM Attributes on Creation

The id-addressed creators (add_association, add_index, add_embedded, add_many_to_many) accept an orm_attributes map that is applied right after the object is created — so you do not need the object's numeric id for attributes known at creation time. This example creates an association with an ON DELETE CASCADE foreign key (Laravel/Knex):

[
  { "op": "add_association", "args": { "from": "Order", "to": "Customer", "orm_attributes": { "onDelete": "cascade" } } }
]

For a many-to-many, owner_orm_attributes / inverse_orm_attributes target the two sides (the many-to-many-entity objects) — e.g. Doctrine2 orphan-removal lives on the side:

[
  { "op": "add_many_to_many", "args": { "owner": "Post", "inverse": "Tag", "owner_alias": "tags", "inverse_alias": "posts", "owner_orm_attributes": { "orphan-removal": "true" } } }
]

Reference an Object by $ref in One Patch

To create an object and then act on it by id within the same patch (no CLI round-trip), tag the creator op with a top-level ref handle, then reference it in a later op as the string $ref:<handle>. At execution, $ref:m1 resolves to the producer op's result id:

[
  { "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" } }
]

The $ref: prefix is reserved in string arg values. Unknown, forward, or duplicate handles fail with INVALID_OP. The apply-patch success envelope returns a results array (index-aligned with the ops) containing each op's created/updated object id, which you can also capture for follow-up patches.

Discover Settable ORM Attributes

Before writing a set_orm_attribute op, use -cli-list-orm-attributes to discover which framework attributes are settable per object type (name, path, type, enum values). This is read-only:

Skipper.exe -cli-list-orm-attributes myproject.skipper -object-type association

Sample output (abbreviated):

{
  "status": "ok",
  "command": "list-orm-attributes",
  "data": {
    "object_types": [
      {
        "object_type": "association",
        "attributes": [
          { "name": "On Delete", "path": "onDelete", "type": "enum", "enum_values": ["cascade", "restrict", "set null", "no action"] },
          { "name": "On Update", "path": "onUpdate", "type": "enum", "enum_values": ["cascade", "restrict", "set null", "no action"] }
        ]
      }
    ]
  },
  "warnings": [],
  "meta": { "duration_ms": 70, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Export the Ops That Rebuild a Schema

Use -cli-export-ops to emit the apply-patch op-list that rebuilds a project on an empty project — the inverse of apply-patch. Useful to copy a schema or seed a new project:

# Emit the ops that build this schema
Skipper.exe -cli-export-ops myproject.skipper -quiet > ops.json

# Reproduce them on a fresh empty project
Skipper.exe -cli-apply-patch empty.skipper -patch ops.json -output rebuilt.skipper

Apply Merge with Nullable Fields

Use -cli-apply-merge to declaratively merge a desired state into an existing project. Fields are NOT NULL by default; set "nullable": true explicitly where needed:

{
  "project": {
    "modules": [{
      "name": "MainBundle",
      "entities": [{
        "name": "Customer",
        "fields": [
          { "name": "email", "type": "string", "size": 320 },
          { "name": "middleName", "type": "string", "size": 100, "nullable": true }
        ]
      }]
    }]
  }
}
SkipperCli.exe -cli-apply-merge project.skipper -merge state.json -output updated.skipper

The merge adds or updates the Customer entity and its fields. Existing entities not mentioned in the merge file are left untouched.

Reset Visual Layout

Reset all visual positions in the diagram so that the auto-arranger runs on the next GUI open. Useful after batch-adding many entities via CLI:

[
  { "op": "reset_layout", "args": {} }
]
Skipper.exe -cli-apply-patch myproject.skipper -patch reset.json -output myproject.skipper

After applying this patch, opening the project in the Skipper GUI will trigger the automatic layout engine to arrange all entities.

Tutorial: Build a Blog Schema

This tutorial walks through building a complete blog schema from scratch using the CLI. We will create a Doctrine2 project with four entities (User, Post, Tag, Comment), add relationships between them, configure indexes, set ORM attributes, validate, and export.

Step 1: Create the Project

Start by creating an empty Skipper project configured for Doctrine2:

Skipper.exe -cli-create-project -output blog.skipper -orm Doctrine2 -mvc "Without MVC" -name Blog

Step 2: Add Entities and Fields

Create a patch file (blog-entities.json) with all four entities and their fields:

Note: The add_entity operation automatically creates an id integer primary key field (auto-increment) from the framework template. The explicit add_field calls for id below are shown for clarity, but in practice you can omit them — adding a PK field manually when one is already auto-created will result in a duplicate field (e.g. id + id2).

[
  { "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": "username", "type": "string", "size": 100 } },
  { "op": "add_field", "args": { "entity": "User", "name": "created_at", "type": "datetime" } },

  { "op": "add_entity", "args": { "module": "Default", "name": "Post" } },
  { "op": "add_field", "args": { "entity": "Post", "name": "title", "type": "string", "size": 255 } },
  { "op": "add_field", "args": { "entity": "Post", "name": "body", "type": "text" } },
  { "op": "add_field", "args": { "entity": "Post", "name": "published_at", "type": "datetime" } },

  { "op": "add_entity", "args": { "module": "Default", "name": "Tag" } },
  { "op": "add_field", "args": { "entity": "Tag", "name": "label", "type": "string", "size": 50 } },

  { "op": "add_entity", "args": { "module": "Default", "name": "Comment" } },
  { "op": "add_field", "args": { "entity": "Comment", "name": "content", "type": "text" } },
  { "op": "add_field", "args": { "entity": "Comment", "name": "created_at", "type": "datetime" } }
]

Apply the patch:

Skipper.exe -cli-apply-patch blog.skipper -patch blog-entities.json -output blog.skipper

Step 3: Add Relationships

Create a second patch file (blog-relations.json) to wire the entities together with associations and a many-to-many relationship:

[
  { "op": "add_association", "args": { "from": "User", "to": "Post", "owner_alias": "posts", "inverse_alias": "author" } },
  { "op": "add_association", "args": { "from": "Post", "to": "Comment", "owner_alias": "comments", "inverse_alias": "post" } },
  { "op": "add_many_to_many", "args": { "owner": "Post", "inverse": "Tag", "mn_entity": "PostTag", "owner_alias": "tags", "inverse_alias": "posts" } }
]

Apply:

Skipper.exe -cli-apply-patch blog.skipper -patch blog-relations.json -output blog.skipper

Step 4: Add Indexes

Add a unique index on User email and a regular index on Post published_at for query performance:

[
  { "op": "add_index", "args": { "entity": "User", "name": "idx_user_email", "unique": true, "fields": ["email"] } },
  { "op": "add_index", "args": { "entity": "Post", "name": "idx_post_published", "fields": ["published_at"] } }
]
Skipper.exe -cli-apply-patch blog.skipper -patch blog-indexes.json -output blog.skipper

Step 5: Set ORM Attributes

Configure Doctrine2 table names for each entity using set_orm_attribute:

[
  { "op": "set_orm_attribute", "args": { "object": "User", "object_type": "entity", "path": "table", "value": "app_users" } },
  { "op": "set_orm_attribute", "args": { "object": "Post", "object_type": "entity", "path": "table", "value": "app_posts" } },
  { "op": "set_orm_attribute", "args": { "object": "Tag", "object_type": "entity", "path": "table", "value": "app_tags" } },
  { "op": "set_orm_attribute", "args": { "object": "Comment", "object_type": "entity", "path": "table", "value": "app_comments" } }
]
Skipper.exe -cli-apply-patch blog.skipper -patch blog-attributes.json -output blog.skipper

Step 6: Validate

Run validation to ensure the project is consistent:

Skipper.exe -cli-validate blog.skipper

Expected output with no issues:

{
  "status": "ok",
  "command": "validate",
  "data": {
    "issues": [],
    "summary": { "error_count": 0, "warning_count": 0 }
  },
  "warnings": [],
  "meta": { "duration_ms": 120, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Step 7: Export

Export the project to generate Doctrine2 entity classes and mappings:

Skipper.exe -cli-export blog.skipper

Sample output:

{
  "status": "ok",
  "command": "export",
  "data": {
    "files_written": [
      { "path": "src/Entity/User.php", "bytes": 1842 },
      { "path": "src/Entity/Post.php", "bytes": 2105 },
      { "path": "src/Entity/Tag.php", "bytes": 1024 },
      { "path": "src/Entity/Comment.php", "bytes": 1356 },
      { "path": "src/Entity/PostTag.php", "bytes": 890 }
    ],
    "framework": "Doctrine2",
    "output_dir": "src/"
  },
  "warnings": [],
  "meta": { "duration_ms": 450, "skipper_version": "3.3.8.1844", "schema_version": "1.0", "started_at": "..." }
}

Your blog schema is now complete. You can open blog.skipper in the Skipper GUI to visualize the diagram, or continue building on it with additional CLI patch operations.