From f7f512e47be7d8307c3f198f4d9a56cebe52910d Mon Sep 17 00:00:00 2001 From: John Higgins <77738205+johnbhiggins7@users.noreply.github.com> Date: Fri, 25 Sep 2026 17:17:35 -0500 Subject: [PATCH] Track latest flutterflow_cli; refresh build skill for SDK 0.0.41 SDK 0.0.41 requires flutterflow_cli >= 0.0.41, so the pinned 0.0.38 made `flutterflow ai init` fail for every new plugin user. The SessionStart hook now installs the latest CLI and, at most every 6h, upgrades an older install to pub.dev's latest (forward-only, skips path/git activations, silent when offline). Adds hook tests with pub.dev and dart faked. Build skill: remove the non-existent `projects --json` step, add CLI-too-old recovery, the 0.0.41 page-file edit workflow and new commands, and gotchas for the Supabase list/detail validation gap (#1) and MCP userPrompts. README documents the upgrade behavior and what the CLI sends. Bump to 0.1.7. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 38 +++++++++ README.md | 26 +++++- .../flutterflow/.claude-plugin/plugin.json | 2 +- plugins/flutterflow/hooks/session-start.sh | 84 +++++++++++++++++-- .../flutterflow/hooks/session-start.test.sh | 65 +++++++++++++- plugins/flutterflow/skills/build/SKILL.md | 69 ++++++++++----- 6 files changed, 250 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e95e59..d9d4718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,44 @@ commit a version number refers to. ## [Unreleased] +## [0.1.7] — 2026-09-25 + +Catches the plugin up with `flutterflow_cli` / FlutterFlow AI SDK 0.0.41. + +### Fixed +- **`flutterflow ai init` failed for new users.** The hook pinned + `flutterflow_cli` 0.0.38, but `init` always downloads the latest SDK, and SDK + 0.0.41 (released 2026-09-23) requires CLI ≥ 0.0.41 — so fresh installs stopped + with *"This FlutterFlow AI snapshot requires flutterflow_cli >= 0.0.41"*. + +### Changed +- **The hook now tracks the latest `flutterflow_cli` instead of a pin**, so it + can't fall behind the SDK again. Fresh installs get the latest release. For + existing installs, the hook reads the installed version from the pub-global + lockfile and, at most once every 6h, checks pub.dev (3s timeout, silent when + offline); if a newer release exists it runs + `dart pub global activate flutterflow_cli `. Forward-only (never + downgrades), skipped for path/git activations, logged to + `~/.cache/flutterflow-claude/upgrade.log`. Covered by new hook tests with + pub.dev and `dart` faked. +- **Build skill: removed the `flutterflow ai projects --json` step** added in + 0.1.6 — no released CLI/SDK (through 0.0.41 and beta 0.0.41+1) ships that + command. The skill asks for the project URL instead. +- Build skill refreshed against CLI/SDK 0.0.41: + - New "CLI is too old" recovery for the minimum-CLI error. + - Page-file editing workflow (`init --page-dsl`, `orient` → edit → + `run --dry-run` → `run`, `scaffold edit`), with the workspace `CLAUDE.md` as + the authority. + - Orient gains `orient` and `review`; other surfaces gain `assets` and + `scaffold`, with a note that media must be uploaded, not pushed in the DSL. + - Re-running `init` in an existing workspace refreshes managed guidance. + - Gotchas: the known Supabase/Postgres list/detail validation gap + ([#1](https://github.com/FlutterFlow/flutterflow-claude/issues/1)) and its + workaround, and that MCP commit tools require `userPrompts` (stored + server-side — never include secrets). +- README: documents the CLI auto-upgrade behavior and what the CLI sends + (installation id with its opt-out, AI commit attribution, MCP user prompts). + ## [0.1.6] — 2026-07-15 ### Changed diff --git a/README.md b/README.md index d966458..e378275 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,9 @@ as possible. It ships one plugin, **`flutterflow`**, which: - **Auto-installs the FlutterFlow CLI** (`dart pub global activate flutterflow_cli`) - on session start — and **handles a missing Dart SDK** by pointing the user at an - install instead of failing silently. + on session start and keeps it at the latest release (the FlutterFlow AI SDK + requires a minimum CLI) — and **handles a missing Dart SDK** by pointing the + user at an install instead of failing silently. - **Prompts for the API token** at enable time and stores it securely. - Adds a **guided build skill** (workspace `init`, then orient → validate → apply) that drives `flutterflow ai` over the Bash tool (which reads your shell profile), @@ -70,6 +71,13 @@ session start the CLI installs itself; after that the hook is a fast pass that j refreshes your PATH and the key file (no install work) — so a rotated key is picked up next session. +**CLI version.** The hook tracks the latest `flutterflow_cli` on pub.dev. At most +once every 6 hours it checks pub.dev (3-second timeout, silent when offline) and +upgrades an older install (log at `~/.cache/flutterflow-claude/upgrade.log`). It +never downgrades and leaves path/git activations alone. Staying current matters +because `flutterflow ai init` always downloads the latest FlutterFlow AI SDK, and an +SDK build refuses to run under a CLI older than its minimum. + ## Using it Ask Claude to build something in FlutterFlow — the **build** skill @@ -124,6 +132,18 @@ flutterflow ai logout --all # clears ~/.flutterflow/credentials.json (bare `lo prompt or passed via `--api-key`; keys provided through the env file are never persisted, so `logout --all` may report nothing to clear.) +## What the FlutterFlow CLI sends + +These are FlutterFlow CLI/SDK behaviors, not something this plugin adds: + +- **Installation id** (CLI ≥ 0.0.40): each API call carries a random per-machine + id stored at `~/.flutterflow/install_id` (no hostname, MAC, or user name). Set + `FLUTTERFLOW_NO_TELEMETRY=1` or `DO_NOT_TRACK=1` to turn it off. +- **AI attribution on commits**: commits made through the CLI/MCP show an AI badge + in FlutterFlow's version history; the API-key owner stays the author. The MCP + commit tools also send the user prompt(s) behind each change, stored server-side + and not visible to collaborators. + ## Native MCP — registered automatically per workspace There's nothing to wire up by hand: `flutterflow ai init` (and @@ -189,7 +209,7 @@ claude plugin validate ./plugins/flutterflow --strict Run the SessionStart hook's security-property tests (token file perms, safe `%q` quoting, cleanup when the token is cleared, refusal to follow a symlinked -config dir). These also run in CI on every push and PR: +config dir, CLI upgrade checks with pub.dev faked). These also run in CI on every push and PR: ```bash bash plugins/flutterflow/hooks/session-start.test.sh diff --git a/plugins/flutterflow/.claude-plugin/plugin.json b/plugins/flutterflow/.claude-plugin/plugin.json index ebd22cf..a8386cc 100644 --- a/plugins/flutterflow/.claude-plugin/plugin.json +++ b/plugins/flutterflow/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "flutterflow", "displayName": "FlutterFlow", - "version": "0.1.6", + "version": "0.1.7", "description": "Agentic building with FlutterFlow. Auto-installs the FlutterFlow CLI (handles a missing Dart SDK gracefully) and provides a guided orient → validate → apply workflow over `flutterflow ai`.", "author": { "name": "FlutterFlow", diff --git a/plugins/flutterflow/hooks/session-start.sh b/plugins/flutterflow/hooks/session-start.sh index f37c6b8..da9d15c 100755 --- a/plugins/flutterflow/hooks/session-start.sh +++ b/plugins/flutterflow/hooks/session-start.sh @@ -3,7 +3,7 @@ # # Goal: make the `flutterflow` CLI available with zero user effort, and fail # soft no matter what. Runs on every session start/resume; it is a fast no-op -# once the CLI is installed. +# once the CLI is installed (plus a pub.dev version check at most every 6h). # # It never exits non-zero (a failing SessionStart hook must not block a session). @@ -20,9 +20,11 @@ if [ -z "${HOME:-}" ]; then exit 0 fi -# Pin the CLI version so this auto-run hook never floats to an unreviewed -# 'latest' from pub.dev. Bump deliberately alongside the plugin version. -FF_CLI_VERSION="0.0.38" +# Track the latest flutterflow_cli on pub.dev rather than a pin. The CLI +# downloads the latest FlutterFlow AI SDK at `init`/`upgrade`, and each SDK +# build declares a minimum CLI version — a pinned CLI that falls behind breaks +# `init` for everyone, so the hook keeps the CLI current instead. +PUB_API_URL="https://pub.dev/api/packages/flutterflow_cli" # ----------------------------------------------------------------------------- # 1. Make common Dart / Flutter / pub-cache bin dirs visible. @@ -110,14 +112,80 @@ else fi fi +STAMP_DIR="$HOME/.cache/flutterflow-claude" + +# Version of the pub-global flutterflow_cli, read from its lockfile (cheap — no +# CLI launch). Prints nothing for path/git activations (a developer's local +# checkout) or when the CLI didn't come from `dart pub global activate`. +installed_cli_version() { + local lock="${PUB_CACHE:-$HOME/.pub-cache}/global_packages/flutterflow_cli/pubspec.lock" + [ -f "$lock" ] || return 0 + awk ' + /^ flutterflow_cli:/ { f = 1; next } + f && /^ [^ ]/ { exit } + f && /^ source:/ { src = $2 } + f && /^ version:/ { v = $2; gsub(/"/, "", v) } + END { if (src == "hosted" && v != "") print v } + ' "$lock" 2>/dev/null +} + +# version_lt A B — true when A < B, comparing MAJOR.MINOR.PATCH numerically +# (any -prerelease / +build suffix is ignored). +version_lt() { + local a="${1%%[-+]*}" b="${2%%[-+]*}" i x y + for i in 1 2 3; do + x=$(printf '%s' "$a" | cut -d. -f"$i"); y=$(printf '%s' "$b" | cut -d. -f"$i") + x=${x:-0}; y=${y:-0} + case "$x$y" in *[!0-9]*) return 1 ;; esac + [ "$x" -lt "$y" ] && return 0 + [ "$x" -gt "$y" ] && return 1 + done + return 1 +} + +# Latest published flutterflow_cli version from pub.dev, or nothing on any +# failure (offline, no curl, slow network, unexpected response). Only a plain +# MAJOR.MINOR.PATCH is accepted, since it's passed to `dart pub global activate`. +latest_cli_version() { + command -v curl >/dev/null 2>&1 || return 0 + local v + v=$(curl -fsS --max-time 3 "$PUB_API_URL" 2>/dev/null \ + | grep -o '"latest":{"version":"[^"]*"' | head -1 | sed 's/.*"version":"//; s/"$//') + case "$v" in + *[!0-9.]* | "" | .* | *. | *..*) return 0 ;; + *.*.*) printf '%s' "$v" ;; + esac +} + # ----------------------------------------------------------------------------- -# 3. Already installed? Nothing more to do. +# 3. Already installed? Upgrade to the latest release if it's older, else done. +# Checks pub.dev at most once / 6h (3s timeout, silent when offline). +# Forward-only (never downgrades) and skipped for path/git activations. # ----------------------------------------------------------------------------- if command -v flutterflow >/dev/null 2>&1; then + CURRENT=$(installed_cli_version) + if [ -n "$CURRENT" ] && command -v dart >/dev/null 2>&1; then + mkdir -p "$STAMP_DIR" 2>/dev/null + STAMP="$STAMP_DIR/last-update-check" + if [ -z "$(find "$STAMP" -mmin -360 2>/dev/null)" ]; then + # Stamp before the network call so an offline machine isn't re-checked + # (and delayed by the timeout) on every session start. + : > "$STAMP" + LATEST=$(latest_cli_version) + if [ -n "$LATEST" ] && version_lt "$CURRENT" "$LATEST"; then + log "Upgrading the FlutterFlow CLI ($CURRENT → $LATEST)…" + if dart pub global activate flutterflow_cli "$LATEST" >"$STAMP_DIR/upgrade.log" 2>&1; then + log "✓ FlutterFlow CLI upgraded to $LATEST. Existing workspaces refresh their SDK on the next command." + else + log "✗ CLI upgrade failed. See the log: $STAMP_DIR/upgrade.log" + log " Upgrade manually with: dart pub global activate flutterflow_cli" + fi + fi + fi + fi exit 0 fi -STAMP_DIR="$HOME/.cache/flutterflow-claude" mkdir -p "$STAMP_DIR" 2>/dev/null # ----------------------------------------------------------------------------- @@ -134,8 +202,8 @@ if command -v dart >/dev/null 2>&1; then exit 0 fi - log "Installing the FlutterFlow CLI (flutterflow_cli $FF_CLI_VERSION)…" - if dart pub global activate flutterflow_cli "$FF_CLI_VERSION" >"$STAMP_DIR/activate.log" 2>&1; then + log "Installing the FlutterFlow CLI (latest flutterflow_cli)…" + if dart pub global activate flutterflow_cli >"$STAMP_DIR/activate.log" 2>&1; then log "✓ FlutterFlow CLI installed — you're ready for agentic building." if ! command -v flutterflow >/dev/null 2>&1; then log "One more step: add Dart's pub-cache bin to your PATH, then restart:" diff --git a/plugins/flutterflow/hooks/session-start.test.sh b/plugins/flutterflow/hooks/session-start.test.sh index 46e3eb0..715686a 100755 --- a/plugins/flutterflow/hooks/session-start.test.sh +++ b/plugins/flutterflow/hooks/session-start.test.sh @@ -27,7 +27,9 @@ printf '#!/bin/sh\nexit 0\n' > "$WORK/bin/flutterflow" chmod +x "$WORK/bin/flutterflow" # run -run() { HOME="$1" PATH="$WORK/bin:$PATH" CLAUDE_PLUGIN_OPTION_API_TOKEN="$2" bash "$HOOK" 2>"$3"; } +# PUB_CACHE points into the throwaway HOME (no lockfile there), so the hook's +# upgrade check never sees the runner's real CLI and never runs a real `dart`. +run() { HOME="$1" PUB_CACHE="$1/.pub-cache" PATH="$WORK/bin:$PATH" CLAUDE_PLUGIN_OPTION_API_TOKEN="$2" bash "$HOOK" 2>"$3"; } echo "== A: token set -> file 600, dir 700, both exports present ==" H="$WORK/A"; mkdir -p "$H" @@ -104,6 +106,67 @@ run "$H" '' "$WORK/g2.log" grep -q 'app.flutterflow.io/account' "$WORK/g2.log" \ && fail "notice repeated within throttle window" || pass "notice throttled on the next run" +echo +echo "== U: installed CLI older than pub.dev latest -> upgrade, throttled; never downgrade ==" +# Fakes: `dart` records its arguments; `curl` serves a pub.dev-shaped response +# for $FAKE_LATEST (or fails when it's empty) — no network, no real installs. +mkdir -p "$WORK/ubin" +cp "$WORK/bin/flutterflow" "$WORK/ubin/flutterflow" +printf '#!/bin/sh\necho "$*" >> "$DART_LOG"\nexit 0\n' > "$WORK/ubin/dart" +cat > "$WORK/ubin/curl" <<'SH' +#!/bin/sh +echo x >> "$CURL_LOG" +[ -n "$FAKE_LATEST" ] || exit 7 +printf '{"name":"flutterflow_cli","latest":{"version":"%s","pubspec":{}},"versions":[{"version":"0.0.1"}]}' "$FAKE_LATEST" +SH +chmod +x "$WORK/ubin/dart" "$WORK/ubin/curl" +# seed_lock — fake a pub-global lockfile entry. +seed_lock() { + mkdir -p "$1/.pub-cache/global_packages/flutterflow_cli" + printf 'packages:\n flutterflow_cli:\n dependency: "direct main"\n source: %s\n version: "%s"\n glob:\n version: "9.9.9"\n' \ + "$3" "$2" > "$1/.pub-cache/global_packages/flutterflow_cli/pubspec.lock" +} +# urun — run the hook with the fakes on PATH. +urun() { + HOME="$1" PUB_CACHE="$1/.pub-cache" PATH="$WORK/ubin:$PATH" DART_LOG="$1/dart.log" \ + CURL_LOG="$1/curl.log" FAKE_LATEST="$2" CLAUDE_PLUGIN_OPTION_API_TOKEN=x \ + bash "$HOOK" 2>"$1/hook.log" +} + +H="$WORK/U1"; mkdir -p "$H"; seed_lock "$H" "0.0.1" hosted +urun "$H" "0.0.41" +grep -qx "pub global activate flutterflow_cli 0.0.41" "$H/dart.log" 2>/dev/null \ + && pass "older hosted CLI upgraded to pub.dev latest" || fail "older CLI not upgraded to latest" +urun "$H" "0.0.41" +{ [ "$(wc -l < "$H/dart.log")" -eq 1 ] && [ "$(wc -l < "$H/curl.log")" -eq 1 ]; } \ + && pass "version check throttled on the next run" || fail "re-checked within the throttle window" + +H="$WORK/U2"; mkdir -p "$H"; seed_lock "$H" "0.0.41" hosted +urun "$H" "0.0.41" +[ ! -e "$H/dart.log" ] && pass "CLI already at latest left alone" || fail "dart invoked for an up-to-date CLI" + +H="$WORK/U3"; mkdir -p "$H"; seed_lock "$H" "99.0.0" hosted +urun "$H" "0.0.41" +[ ! -e "$H/dart.log" ] && pass "newer CLI never downgraded" || fail "dart invoked for a newer CLI" + +H="$WORK/U4"; mkdir -p "$H"; seed_lock "$H" "0.0.1" path +urun "$H" "0.0.41" +{ [ ! -e "$H/dart.log" ] && [ ! -e "$H/curl.log" ]; } \ + && pass "path-activated (dev checkout) CLI left alone, no network" || fail "path activation was checked or upgraded" + +H="$WORK/U5"; mkdir -p "$H" +urun "$H" "0.0.41" +{ [ ! -e "$H/dart.log" ] && [ ! -e "$H/curl.log" ]; } \ + && pass "no lockfile (non-pub install) -> no check, no upgrade" || fail "checked or upgraded without a lockfile" + +H="$WORK/U6"; mkdir -p "$H"; seed_lock "$H" "0.0.1" hosted +if urun "$H" ""; then pass "offline (curl fails) -> exit 0"; else fail "non-zero exit when offline"; fi +[ ! -e "$H/dart.log" ] && pass "offline -> no upgrade attempted" || fail "dart invoked while offline" + +H="$WORK/U7"; mkdir -p "$H"; seed_lock "$H" "0.0.1" hosted +urun "$H" '0.0.99;touch pwned' +[ ! -e "$H/dart.log" ] && pass "malformed latest version rejected" || fail "dart invoked with a malformed version" + echo echo "== F: HOME unset -> clean exit 0, no crash, no writes ==" if env -u HOME PATH="$WORK/bin:$PATH" CLAUDE_PLUGIN_OPTION_API_TOKEN=x bash "$HOOK" 2>/dev/null; then diff --git a/plugins/flutterflow/skills/build/SKILL.md b/plugins/flutterflow/skills/build/SKILL.md index 2d5b219..c25c6c3 100644 --- a/plugins/flutterflow/skills/build/SKILL.md +++ b/plugins/flutterflow/skills/build/SKILL.md @@ -43,6 +43,14 @@ command -v flutterflow >/dev/null && echo "cli: ok" || echo "cli: MISSING" - `dart` NOT available → Dart (bundled with Flutter) must be installed first: https://docs.flutter.dev/get-started/install. Don't install Flutter unasked. +### If a command says the CLI is too old +The CLI downloads the latest FlutterFlow AI SDK at `init`/`upgrade`, and each SDK +build requires a minimum CLI. An outdated CLI fails with *"This FlutterFlow AI +snapshot requires flutterflow_cli >= X, but you are running Y"*. Fix it with +`dart pub global activate flutterflow_cli`, then retry — on CLI ≥ 0.0.41 the first +command in an existing workspace refreshes its SDK automatically. (The plugin's +SessionStart hook also keeps the CLI at the latest release, checking at most every 6h.) + ### If `key: MISSING` `flutterflow ai` authenticates with **`FF_API_KEY`**. If the user doesn't have a key yet, point them to **https://app.flutterflow.io/account** to create one. @@ -136,26 +144,16 @@ pass the key on the command line (e.g. `--api-key`) — that puts the secret int invocation and the model context; rely on `FF_API_KEY` from the sourced env file. `init` writes `.flutterflow/.env` with `FF_API_KEY` and `FF_DSL_PROJECT_ID`, so later commands in this workspace are authenticated and -project-scoped. See `flutterflow ai init --help` for more (`--env`, `--sdk-path`, -`--pre-release`, `--no-save`, `--yes`). - -**Existing project, but no id?** Newer CLIs (> 0.0.38) list the account's projects -non-interactively — names and ids aren't secrets, so this is safe to show in chat: - -```bash -flutterflow ai projects --json [--match ] -``` +project-scoped. See `flutterflow ai init --help` for more (`--page-dsl`, `--env`, +`--sdk-path`, `--pre-release`, `--no-save`, `--yes`). -stdout is exactly one JSON array of `{id, name, isLibrary, lastEditMillis}`, -most recently edited first (notices go to stderr). Offer the top few as an -AskUserQuestion — one option per project (name + id in the description, since -names can repeat) plus a "create a new app" option — and let Other capture a -name or id to fuzzy-match against the full list, re-asking only when ambiguous. -If the command fails (older CLI — outside a workspace it exits with "No -FlutterFlow AI workspace found"), fall back to asking for the project URL. +**Existing project, but no id?** Ask for the project URL — the id is the last path +segment. There is no non-interactive project-listing command as of CLI/SDK 0.0.41; +if `flutterflow ai --help` lists `projects` in a future release, use it instead. `init` also scaffolds guidance **inside the workspace** — `CLAUDE.md`, `AGENTS.md`, -`references/`, `patterns/` (managed files; `refresh-workspace` overwrites them) — +`references/`, `patterns/` (managed files; `refresh-workspace` overwrites them, and +re-running `init` in an existing workspace refreshes them while keeping user files) — and auto-registers the FlutterFlow MCP server with detected agents, including a project-scoped `.mcp.json` for Claude Code. A session rooted in the workspace may therefore offer `flutterflow_ai` MCP tools alongside this skill; both drive the @@ -171,6 +169,11 @@ The read commands take the project id as a positional argument: - `flutterflow ai inspect [--page |--component ] [--outline] [--tree] [--dsl-json] [--max-depth ] [--output ]` — structure of the project or a specific page/component. (Scope is chosen with flags, not a positional.) - `flutterflow ai resources [--library ] [--match ]` — list pages/components/types. - `flutterflow ai search --query ` — find by name or visible text (`-q` works too). +- `flutterflow ai orient [--page |--component |--node ]` — one + bounded orientation card for the workspace's bound project (no id needed); with + `--page-dsl` it names the editable file for that scope. +- `flutterflow ai review []` — wiring review of the project; defaults to + the workspace's bound project. - `flutterflow ai doctor` / `flutterflow ai context-check` — local diagnostics. - `flutterflow ai docs [topic]` — DSL and command docs from the terminal. - `flutterflow ai upgrade --check` — SDK freshness as `key: value` lines (last line @@ -183,12 +186,22 @@ If `context-check` reports STALE: `flutterflow ai refresh-context `. Changes are declarative Dart (DSL) files. Learn the DSL with `flutterflow ai docs`. -- **Validate (dry run):** `flutterflow ai validate ` — checks the change without applying it. +- **Validate (dry run):** `flutterflow ai validate ` or + `flutterflow ai run --dry-run` — checks the change without applying it. - **Apply:** `flutterflow ai run [--commit-message ""] [--find-or-create]`. Both take the DSL file as a positional. Always validate before run, and show the user the output; don't apply blind. +**Editing existing pages (CLI ≥ 0.0.41).** Workspaces initialized with `--page-dsl` +get one editable Dart file per page/component under `lib/flutterflow_project/`: +`orient` → edit that file → `run --dry-run` → `run `. Edits merge by +stable key and preserve anything the DSL can't express — keep existing `key:` +values, and don't touch `.g.dart` files. For bulk, cross-page, or schema/custom-code +changes, `flutterflow ai scaffold edit` creates `dsl/edit.dart` (never overwrites). +Details: `flutterflow ai docs edit-apis`. The workspace's own `CLAUDE.md` is the +authority on which path it uses. + ## 4. Audit / record - `flutterflow ai plan save ` | `plan save --content ""` | `plan show` | `plan clear` — stores/echoes YOUR intent in `.flutterflow/plan.md` (it does not generate a plan). @@ -214,9 +227,11 @@ target it automatically. `merge commit -m ""` (refuses while drops exist unless `--accept-drops`), `merge abort`. -Other surfaces exist too (`codegen`, `test`, `test-pilot`, `issue`, `support`, -`upgrade`, `refresh-workspace`, `precache`, `create-project`, `mcp`, `logout`) — -use `--help`. +Other surfaces exist too (`codegen`, `test`, `test-pilot`, `assets`, `scaffold`, +`issue`, `support`, `upgrade`, `refresh-workspace`, `precache`, `create-project`, +`mcp`, `logout`) — use `--help`. Media (images, fonts, Lottie/Rive) can't be pushed +in the DSL: upload with `flutterflow ai assets upload` first, then reference the +returned path. ## Gotchas - **Write project URLs with the scheme.** When telling the user where a project @@ -234,6 +249,18 @@ use `--help`. preamble or add it to `~/.zshrc`. - **GUI vs shell env:** running via Bash (this skill) reads your shell profile and the token file; a GUI-launched MCP server may not — which is why this skill drives the CLI. +- **Supabase/Postgres lists and detail views don't validate yet** (known SDK gap, + still present in 0.0.41 — + [issue #1](https://github.com/FlutterFlow/flutterflow-claude/issues/1)). Write + actions (`PostgresCreate`/`Update`/`Delete`) work, but loading rows into page + state and showing them (`PostgresQuery` → `SetState(ActionOutput(...))` → + `ListView(source: State(...))`) is rejected with errors like "table not found" or + `VALIDATION_GENERATOR_VARIABLE`. Tell the user before building a Supabase-backed + list: build the layout and write actions with the DSL, then have them bind the + list's Supabase query in the FlutterFlow editor. +- **MCP commit tools need the user's prompt.** The MCP `run`/`patch`/merge-commit + tools require `userPrompts` (verbatim user message(s) behind the change). They're + stored server-side with the commit, so never include keys or other secrets in them. - **Credential cache:** `flutterflow ai init` caches the key in `~/.flutterflow/credentials.json` only when it was typed at the prompt or passed via `--api-key`; keys from `FF_API_KEY` (this skill's path) are never persisted.