# FF Orbit Command Reference Complete reference for all Orbit MCP commands. The primary MCP tool is `orbit()`: ``` orbit({ cmd: "", args?: {}, snapshot?: "", 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.