skills/flutterflow-designer/orbit-commands.md

287 lines
11 KiB
Markdown

# FF Orbit Command Reference
Complete reference for all Orbit MCP commands. The primary MCP tool is `orbit()`:
```
orbit({ cmd: "<command>", args?: {}, snapshot?: "<id>", format?: "json"|"explain" })
```
Supporting tools: `orbit_policy_get()`, `orbit_policy_set()`, `orbit_export_changeset()`
---
## Architecture
Orbit is snapshot-first:
- **Snapshot**: Local point-in-time copy of project files (cached in SQLite)
- **Index**: Symbols and edges extracted from snapshot for fast search
- **Changeset**: Draft edits with preview, validation, and apply lifecycle
Prefer deterministic first-class commands over exploratory probing. Use `help` when command shape is unclear.
---
## Discovery Commands
| Command | Purpose | Example Args |
|---------|---------|-------------|
| `help` | List all commands or get help for specific command | `{ cmd: "widget.create" }` |
| `api.capabilities` | Check FlutterFlow API connectivity | — |
| `projects.list` | List accessible FlutterFlow projects | — |
---
## Snapshot Commands
| Command | Purpose | Key Args |
|---------|---------|----------|
| `snapshots.create` | Create snapshot from a project | `{ projectId: "..." }` |
| `snapshots.refresh` | Update snapshot with latest files | `{ mode, fetchStrategy, maxFetch, concurrency, sleepMs }` |
| `snapshots.refreshSlow` | Throttled refresh for rate-limited scenarios | `{ passes, maxFetch, concurrency, sleepMs, pauseMs }` |
| `snapshots.ensureFresh` | Refresh only if older than threshold | — |
| `snapshots.info` | Show snapshot metadata | — |
| `snapshots.ls` | List all snapshots | — |
### Refresh Strategies
| Scenario | Args |
|----------|------|
| Normal incremental | `{ mode: "incremental", fetchStrategy: "auto", maxFetch: 25, concurrency: 1, sleepMs: 250 }` |
| Heavy rate limiting | `{ mode: "incremental", fetchStrategy: "file", maxFetch: 5, concurrency: 1, sleepMs: 1000 }` |
| Full sync | `{ mode: "full", fetchStrategy: "bulk" }` |
| Chunked full fallback | `{ mode: "full", fetchStrategy: "file", maxFetch: 5 }` |
---
## Page Commands
| Command | Purpose | Key Args |
|---------|---------|----------|
| `pages.list` | List all pages | `{ includeDeleted?, strictSnapshot? }` |
| `page.get` | Get full page details (widget tree) | `{ nameOrId }` |
| `page.create` | Create a new blank page | `{ name, apply? }` |
| `page.scaffold` | Generate page from built-in recipe | `{ recipe, name, apply? }` |
| `page.update` | Update page properties | `{ nameOrId, key, value, apply? }` |
| `page.clone` | Duplicate a page | `{ nameOrId, newName, apply? }` |
| `page.preflightDelete` | Check what would break before deleting | `{ nameOrId }` |
| `page.remove` | Safe delete with checks | `{ nameOrId, apply? }` |
| `page.delete` | Low-level delete (no safety checks) | `{ nameOrId, apply? }` |
### Built-In Page Recipes
| Recipe | Description |
|--------|-------------|
| `auth.login` | Login page with email/password fields |
| `auth.signup` | Signup page with registration fields |
| `settings.basic` | Settings page with toggles and list tiles |
| `list.cards.search` | List with search bar and card items |
| `detail.basic` | Detail page with header and content |
### Examples
```
# Create page
orbit({ cmd: "page.create", args: { name: "SplashScreen", apply: true } })
# Rename page
orbit({ cmd: "page.update", args: { nameOrId: "SplashScreen", key: "name", value: "Splash", apply: true } })
# Safe delete
orbit({ cmd: "page.remove", args: { nameOrId: "OldPage", apply: true } })
# Scaffold from recipe
orbit({ cmd: "page.scaffold", args: { recipe: "auth.login", name: "LoginPage", apply: true } })
```
---
## Widget Commands — Read
| Command | Purpose | Key Args |
|---------|---------|----------|
| `widgets.list` | List widgets on a page | `{ nameOrId, type?, include? }` |
| `widgets.find` | Search by type, text, or properties | `{ nameOrId, ... }` |
| `widgets.findText` | Search widgets containing text | `{ nameOrId, text }` |
| `textfields.list` | List all text field widgets | `{ nameOrId }` |
| `widget.get` | Get single widget by node ID | `{ nameOrId, nodeId }` |
| `widget.getMany` | Get multiple widgets | `{ nameOrId, nodeIds: [...] }` |
---
## Widget Commands — Write
| Command | Purpose | Key Args |
|---------|---------|----------|
| `widget.create` | Create a new widget | `{ nameOrId, type, parentNodeId?, apply? }` |
| `widget.insert` | Insert at specific position | `{ nameOrId, type, parentNodeId, beforeNodeId?, afterNodeId?, apply? }` |
| `widget.set` | Update widget properties | `{ nameOrId, nodeId, text?, props?, apply? }` |
| `widget.delete` | Delete a widget | `{ nameOrId, nodeId, apply? }` |
| `widget.deleteSubtree` | Delete widget and all children | `{ nameOrId, nodeId, apply? }` |
| `widget.duplicate` | Duplicate a widget | `{ nameOrId, nodeId, count?, apply? }` |
---
## Widget Commands — Structure
| Command | Purpose | Key Args |
|---------|---------|----------|
| `widget.wrap` | Wrap widget in new parent | `{ nameOrId, nodeId, wrapperType, apply? }` |
| `widget.unwrap` | Remove wrapper, promote children | `{ nameOrId, nodeId, apply? }` |
| `widget.move` | Move to new parent | `{ nameOrId, nodeId, newParentNodeId, apply? }` |
| `widget.moveMany` | Move multiple widgets | `{ nameOrId, nodeIds, newParentNodeId, apply? }` |
| `widget.reorder` | Change position among siblings | `{ nameOrId, nodeId, position, apply? }` |
| `widget.replaceType` | Change widget type in place | `{ nameOrId, nodeId, newType, apply? }` |
| `widget.removeChildren` | Remove all children | `{ nameOrId, nodeId, apply? }` |
---
## Widget Commands — Batch
| Command | Purpose | Key Args |
|---------|---------|----------|
| `widgets.updateMany` | Batch update matching widgets | `{ nameOrId, filter, updates, apply? }` |
| `widgets.copyPaste` | Copy widgets between locations | `{ ... }` |
---
## Widget Actions & Data Binding
| Command | Purpose | Key Args |
|---------|---------|----------|
| `widget.action.list` | List actions on a widget | `{ nameOrId, nodeId }` |
| `widget.action.get` | Get specific action by trigger | `{ nameOrId, nodeId, trigger }` |
| `widget.bindAction` | Bind action to widget trigger | `{ nameOrId, nodeId, trigger, action, apply? }` |
| `widget.bindData` | Bind data to widget property | `{ nameOrId, nodeId, property, binding, apply? }` |
---
## Component Commands
| Command | Purpose | Key Args |
|---------|---------|----------|
| `component.get` | Get component details | `{ nameOrId }` |
| `component.instance.insert` | Insert component instance | `{ nameOrId, componentId, parentNodeId, apply? }` |
| `component.extractFromWidget` | Extract widget into component | `{ nameOrId, nodeId, componentName, apply? }` |
---
## Widget Tree Commands
| Command | Purpose | Key Args |
|---------|---------|----------|
| `tree.locate` | Find widget position in tree | `{ nameOrId, nodeId }` |
| `tree.subtree` | Get subtree rooted at widget | `{ nameOrId, nodeId }` |
| `tree.find` | Search tree (alias for locate) | `{ nameOrId, ... }` |
| `tree.validate` | Check tree integrity | `{ nameOrId }` |
| `tree.repair` | Fix tree issues | `{ nameOrId, apply? }` |
---
## Navigation & Routes
| Command | Purpose | Key Args |
|---------|---------|----------|
| `routes.list` | List all routes | — |
| `routes.listByPage` | Routes for a specific page | `{ nameOrId }` |
| `routes.validate` | Validate routes | `{ nameOrId }` |
| `routes.upsert` | Create or update route | `{ from, to, trigger?, apply? }` |
| `routes.delete` | Delete a route | `{ routeId, apply? }` |
| `graph.nav` | Query navigation graph | `{ ... }` |
| `graph.usage` | Query component usage graph | `{ ... }` |
---
## Search & Utility
| Command | Purpose | Key Args |
|---------|---------|----------|
| `search` | Full-text search across snapshot | `{ query }` |
| `selection.get` | Get current sticky selection | — |
| `selection.clear` | Clear sticky selection | — |
| `intent.run` | Natural language → command | `{ text: "..." }` |
| `settings.get` | Get project settings | — |
---
## Summaries
| Command | Purpose | Key Args |
|---------|---------|----------|
| `summarize.page` | Human-readable page summary | `{ nameOrId }` |
| `summarize.component` | Component summary | `{ nameOrId }` |
| `summarize.project` | Project overview | — |
---
## Changeset Commands
The changeset pipeline ensures safe writes:
| Command | Purpose | Key Args |
|---------|---------|----------|
| `changeset.new` | Create draft changeset | — |
| `changeset.add` | Add patch to changeset | `{ changesetId, patches }` |
| `changeset.preview` | Generate diffs and risk score | `{ changesetId }` |
| `changeset.validate` | Validate YAML + policy | `{ changesetId }` |
| `changeset.apply` | Apply changeset to FlutterFlow | `{ changesetId, confirm }` |
| `changeset.applySafe` | Apply with rate-limit retry | `{ changesetId, confirm, rateLimitRetries?, rateLimitBaseMs?, rateLimitMaxWaitMs? }` |
| `changeset.rollback` | Undo most recent changeset | `{ changesetId }` |
| `changeset.revert` | Revert snapshot to pre-changeset | `{ changesetId }` |
| `changeset.drop` | Discard draft changeset | `{ changesetId }` |
### Safe Write Workflow
```
1. orbit({ cmd: "changeset.new" }) → get changesetId
2. orbit({ cmd: "changeset.add", args: { changesetId, patches: [...] } })
3. orbit({ cmd: "changeset.preview", args: { changesetId } }) → review diffs
4. orbit({ cmd: "changeset.validate", args: { changesetId } }) → check structure
5. orbit({ cmd: "changeset.applySafe", args: { changesetId, confirm: true } })
```
### Rate-Limited Apply
```
orbit({ cmd: "changeset.applySafe", args: {
changesetId: "chg_...",
confirm: true,
rateLimitRetries: 3,
rateLimitBaseMs: 1500,
rateLimitMaxWaitMs: 90000
}})
```
---
## Schema Knowledge Base
| Command | Purpose | Key Args |
|---------|---------|----------|
| `schema.search` | Search built-in docs and snippets | `{ query }` |
| `schema.read` | Read specific doc by ID | `{ id }` |
| `schema.snippet` | Get code snippet by ID | `{ id }` |
Use only for unfamiliar patterns or advanced debugging — not for routine operations.
---
## Prompt-to-Command Quick Map
| User Says | Command |
|-----------|---------|
| "List pages" | `pages.list` |
| "Show widgets on login" | `widgets.list { nameOrId: "login" }` |
| "All text fields on login" | `textfields.list { nameOrId: "login" }` |
| "Get this widget" | `widget.get { nameOrId, nodeId }` |
| "Change text on widget" | `widget.set { nameOrId, nodeId, text: "..." }` |
| "Create a button under container" | `widget.insert { nameOrId, type, parentNodeId }` |
| "Remove widget" | `widget.delete { nameOrId, nodeId }` |
| "Rename page X to Y" | `page.update { nameOrId, key: "name", value: "Y" }` |
| "Refresh from FlutterFlow" | `snapshots.refresh { mode: "incremental" }` |
| "Wrap in column" | `widget.wrap { nameOrId, nodeId, wrapperType: "Column" }` |
| "Move widget under X" | `widget.move { nameOrId, nodeId, newParentNodeId }` |
| "Duplicate widget" | `widget.duplicate { nameOrId, nodeId }` |
| "Safe apply" | `changeset.applySafe { changesetId, confirm: true }` |
Always return resolved IDs (`snapshotId`, `pageId`, `nodeId`, `fileKey`) in responses.