Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
baeb010
feat: ship agent skills for consumers of the package
DenisovAV Sep 10, 2026
8232d35
feat(skills): eight consumer skills, all in the core package
DenisovAV Sep 10, 2026
54c225b
chore(release): keep the shipped skills from going stale
DenisovAV Sep 10, 2026
9047050
chore(release): route the diff to the skills that need re-reading
DenisovAV Sep 10, 2026
a9168b7
fix(skills): rewrite against the source, and gate them by compiling
DenisovAV Sep 11, 2026
fc95044
docs(skills): second review round — core import, gate hole, platform …
DenisovAV Sep 11, 2026
38a9192
docs(readme): install the agent skills with --all
DenisovAV Sep 11, 2026
29d611a
docs(site): AI Assistant Skills — landing card and docs page
DenisovAV Sep 11, 2026
59f380e
docs(builtin-ai): Edge runs Phi-4-mini; web download needs a user ges…
DenisovAV Sep 12, 2026
c367593
Merge main: flush() took core 1.8.1, skills move to 1.8.2
DenisovAV Sep 12, 2026
7a79f7f
chore: example lockfiles follow core 1.8.2 and speech 0.5.0
DenisovAV Sep 12, 2026
76f58d3
docs(site): call the feature Package Skills, as dart.dev does
DenisovAV Sep 13, 2026
a94f7cc
docs: WAV header advice, the real pub.dev score, 1.8.2 pins, README news
DenisovAV Sep 13, 2026
4056f7f
ci(skills): add skills_lint, and run both skill gates in CI
DenisovAV Sep 13, 2026
c387cd3
fix(skills): third review round — what the code does, not what we ass…
DenisovAV Sep 13, 2026
4999d82
docs(release): skills are fixed in the PR that changes the API
DenisovAV Sep 13, 2026
ac7b4e1
docs(site): the landing snippet, and three sections that said the opp…
DenisovAV Sep 13, 2026
2b88ef9
docs(site): engine pages — ONNX web URL, web tool calls, Phi, LFM2.5,…
DenisovAV Sep 13, 2026
ca03f2e
docs(site): feature pages — the flags, the loops and the policies as …
DenisovAV Sep 13, 2026
a3416f8
docs(codelabs): the API floor .litertlm actually needs, and pins that…
DenisovAV Sep 13, 2026
1c642e5
docs: point every package and docs page at the skill that covers it
DenisovAV Sep 13, 2026
e9d0343
docs: the changelog line and the release skill used the bare skills c…
DenisovAV Sep 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 112 additions & 2 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ silently do the other thing.
3. **Reproduce this Definition-of-Done in your reply and mark every item**
(done / N/A + reason) before you publish. Do not publish off memory of the
skill — walk it as a literal checklist against the actual repo state.
4. **The skills are fixed in the PR that changes the API, not at release time.**
`packages/flutter_gemma/skills/**` ships inside core and is read by other
people's coding agents, so a stale sentence there becomes confident, broken
code in someone else's app. On any PR touching `packages/*/lib/**`, a native
build file (`android/`, `ios/`, `darwin/`, `macos/`, `windows/`, `hook/`), a
pinned CDN version or a `pubspec.yaml` floor:

```bash
bash tool/skills_review.sh origin/main # which skills the diff puts in doubt
dart tool/check_skills.dart # compiles every block; exit 0 required
dart run skills_lint@0.5.1 # file-level rules; exit 0 required
```

The two gates also run in CI (`skills` job), so a rename is caught without
you. What CI cannot catch is a symbol that survives while its MEANING moves —
`getActiveStt(language:)` went from "the language this recognizer was built
with" to "the default for its transcriptions" with no rename anywhere. That
is what reading the flagged skills is for. Step 12d is the release backstop,
not the first time this happens.

### Definition of Done (paste it; check 1a–12b before Step 10 publish; 12c is verified after merge)

Expand All @@ -44,14 +63,20 @@ silently do the other thing.
[ ] 5b manifest gate RUN and printed "N platform(s) compared" — N == number of tarballs
[ ] 1e core public API changed? → upgrade-genkit (realign + version), else N/A
[ ] 1f shared code duplicated across satellites patched everywhere (grep the pattern)
[ ] 1f-bis tool/check_macos_podfile_snippet.sh passes (5 copies of the macOS
post_install snippet identical) — RUN it, do not eyeball
[ ] 1f-bis tool/check_macos_podfile_snippet.sh passes (every copy of the macOS
post_install snippet byte-identical — 23 today: three example Podfiles,
the codelab step apps, README, desktop.md and the inference skill's
references/platform-setup.md) — RUN it, do not eyeball
[ ] 1g each changed satellite's flutter_gemma: floor >= the core version it now needs
[ ] 2 versions bumped: pubspec + podspec (if any) + CLAUDE.md Current-Version line
[ ] 7 CHANGELOG: one short line per package, every published package
[ ] 8 dart pub publish --dry-run → 0 warnings, every package
[ ] 12a website + README version pins bumped to the just-published versions
[ ] 12b new/changed public API + behavior documented (README + website) ← SAME PR
[ ] 12d skills/: `skills_review.sh <last-tag>` run, every flagged skill READ,
updated where the prose drifted, `dart tool/check_skills.dart` green and
`dart run skills_lint@0.5.1` green — backstop: rule 4 means the PRs in
this release already did it
[ ] 12c after merge: firebase-hosting-merge run == success (not just triggered)
```

Expand Down Expand Up @@ -615,6 +640,91 @@ Update each `^X.Y.Z` for the core packages (`flutter_gemma`, `flutter_gemma_lite
- **Breaking changes / migrations** → `migration.md`.
- **A bug class users hit** → `troubleshooting.md` (e.g. the #318 `maxTokens` vs `maxOutputTokens` confusion belongs here).

### 12d. Update the shipped agent skills — they are read by a MACHINE

`packages/flutter_gemma/skills/` holds eight `SKILL.md` files that ship inside
the core archive and are installed into users' coding agents by
`dart run skills@ get --all`. They are not a nice-to-have copy of the docs: an agent
follows them literally when writing code against this package.

That makes stale skills worse than stale docs. A human reading an outdated
README notices the mismatch; an agent does not — it writes confident, wrong code
against an API that moved, and the user blames the package.

**If this release changed public API or behaviour, the skills change with it.**
Map the change to the skill that covers it:

| Area | Skill |
|------|-------|
| registry, install, `ModelFileType`, `maxTokens`, sessions, chat, the `.litertlm` engine, backends, platform setup | `flutter-gemma-inference` (+ `references/platform-setup.md`) |
| function calling | `flutter-gemma-function-calling` |
| `.task`/`.bin`, MediaPipe web | `flutter-gemma-mediapipe` |
| ONNX / ORT-GenAI | `flutter-gemma-onnx` |
| the OS built-in model | `flutter-gemma-builtin-ai` |
| STT, TTS, `VoiceSession` | `flutter-gemma-speech` |
| embeddings, vector stores | `flutter-gemma-rag` |

**Do not go looking by hand.** Ask the diff which skills it puts in doubt:

```bash
bash tool/skills_review.sh <last-tag> # e.g. v1.8.0
```

For each skill it prints the symbols that skill NAMES and this release TOUCHED.
Run against the STT release it names `flutter-gemma-speech` with
`getActiveStt`, `language`, `SttModelType.whisper`; against the
`createChat`-tools fix it names the function-calling skill and leaves speech
alone.
That is the routing — a skill with hits gets opened, a skill without one gets
skipped with a clear conscience.

**Then open every flagged skill and read it against the change.** This is the
step, not the script. The script cannot tell whether the prose is still true;
it only says where to look.

Finally the mechanical gate:

```bash
dart tool/check_skills.dart # exit 0 required
```

It COMPILES the skills: every ```dart fence becomes a function body, every
inline `Type` and `Type.member` in the prose becomes a declaration, and
`dart analyze` runs over the result inside the example app, which depends on
every package. A misspelt parameter, a method that moved, a switch that is no
longer exhaustive — all fail. Read the count it prints, not just the exit code:
a run that extracted nothing exits 2 rather than reporting a pass.

It replaced a grep-based check that was green on four APIs that did not exist —
`gemma3` matched a model URL, `limit:` an unrelated argument. A text search
cannot tell "this name exists" from "this code is right".

And the file-level check, Google's linter for the Agent Skills format:

```bash
dart run skills_lint@0.5.1 # exit 0 required; config in skills_lint.yaml
```

It checks what compilation cannot: frontmatter keys the spec allows (these
skills install into eight different agents, and the reference validator rejects
anything outside its allowlist), a `name` that matches its directory, the
1024-character description budget, and every relative link resolving — that last
one is off upstream by default and an error here, so a renamed
`references/platform-setup.md` fails instead of handing an agent a dead pointer.

Both run in CI as the `skills` job (`.github/workflows/test.yml`), so a PR that
breaks either is red before it reaches this checklist.

**Why both.** `check_skills.dart` answers "does this code still compile" —
renames, deletions, signature changes. It stays green when a symbol survives and its MEANING moves,
which is the failure that actually happened here: `getActiveStt(language:)` went
from "the language this recognizer was built with" to "the default for its
transcriptions" with no rename anywhere. `skills_review.sh` is what puts that
change in front of your eyes; only reading closes it.

Skills live only in `flutter_gemma`, so a fix to any of them is one publish of
core. That is why they are all there rather than in the packages they describe.

### 12c. Deploy — it's automatic on merge to main

**You do NOT run a manual deploy.** `.github/workflows/firebase-hosting-merge.yml` auto-deploys to Firebase Hosting (`aichat-c0c27`, target `fluttergemma`, https://fluttergemma.dev → live channel) on every push to `main` that touches `website/**` or `packages/flutter_gemma/example/**`. So:
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,41 @@ jobs:
fail_ci_if_error: false
continue-on-error: true

# The skills shipped inside flutter_gemma are read by other people's coding
# agents, so a wrong name there becomes confident, broken code in someone
# else's app. Two gates, both fail-closed, and neither ran in CI before:
# check_skills.dart compiles every ```dart fence and every inline type in
# the prose against the real packages — it analyses inside the example app
# because that is the package depending on all of them;
# skills_lint checks the files themselves (frontmatter, name, description
# budget, relative links), with the rules set in skills_lint.yaml.
skills:
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
cache: true

- name: Install dependencies
run: flutter pub get

- name: Resolve the example app
run: flutter pub get
working-directory: packages/flutter_gemma/example

- name: Compile every code block in the skills
run: dart tool/check_skills.dart

- name: Lint the skill files
run: dart run skills_lint@0.5.1

build-example-android:
runs-on: ubuntu-latest
needs: analyze-and-test
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Core has NO pigeon (dropped at the 1.0 cut; its value types are hand-written in
- **LiteRT-LM**: native libs from `native-v0.16.0` GitHub Release (LiteRT-LM pin `924e79c9`, LiteRT pin `0ff28117`). Android tarball bundles the Qualcomm QNN dispatch stack and Windows tarball bundles Intel NPU dispatch (`LiteRtDispatch.dll` + OpenVino runtime + TBB) for `PreferredBackend.npu` (Qualcomm Snapdragon / Intel LunarLake/PantherLake) — both dispatch libs are **rebuilt from the pin every release**; carrying them forward is what silently broke NPU on both platforms (see the `build-native` skill). v0.16.0: fixes the Android OpenCL per-turn memory leak (LiteRT-LM #2699, #348/#402); v0.15.0 **broke the stream-callback ABI** (4-arg → 2-arg chunk object) with no compat path, handled by a runtime probe in `stream_proxy.c`. Windows discrete GPU works again — the crash was our own dead `litert_link_capi_so` Bazel define, not an upstream regression (#2957 retracted).
- **sqlite-vec**: `flutter_gemma_rag_sqlite` fetches the per-platform `vec0` loadable from the `native-sqlite-vec-v<X>` GitHub Release (`sqlite-vec-<target>.tar.gz` + `checksums_sqlite_vec.txt`), SHA256-verified by its `hook/build.dart`. `<X>` names the **upstream sqlite-vec release** the bytes were built from; a letter suffix (`0.1.9-a`) is only for RE-releasing changed bytes under an already-published number. The loadables are NOT committed — `native/sqlite_vec/prebuilt/` is a maintainer override produced by `build_local.sh`, gitignored and `.pubignore`d.
- **large_file_handler**: `^0.5.0` (core dep; 0.5.0 declares all 6 platforms — needed for pana platform support + the dart2wasm-clean web graph)
- **Current Version**: core `flutter_gemma` `1.8.1`, `flutter_gemma_rag_sqlite` `1.3.2`, `flutter_gemma_rag_qdrant` `1.3.1`; `flutter_gemma_litertlm` `1.6.3`, `flutter_gemma_mediapipe` `1.0.5`, `flutter_gemma_embeddings` `2.1.1`, `flutter_gemma_speech` `0.5.0`; `flutter_gemma_agent` `0.2.5`, `flutter_gemma_builtin_ai` `0.2.1`, `flutter_gemma_onnx` `0.3.3`; `genkit_flutter_gemma` `0.6.1`, `genkit_hybrid` `0.2.1`
- **Current Version**: core `flutter_gemma` `1.8.2`, `flutter_gemma_rag_sqlite` `1.3.2`, `flutter_gemma_rag_qdrant` `1.3.1`; `flutter_gemma_litertlm` `1.6.3`, `flutter_gemma_mediapipe` `1.0.5`, `flutter_gemma_embeddings` `2.1.1`, `flutter_gemma_speech` `0.5.0`; `flutter_gemma_agent` `0.2.5`, `flutter_gemma_builtin_ai` `0.2.1`, `flutter_gemma_onnx` `0.3.3`; `genkit_flutter_gemma` `0.6.1`, `genkit_hybrid` `0.2.1`
- **0.15.2**: embedding unified on LiteRT C API via Dart FFI on all native platforms (Android + iOS + Desktop). Drops `localagents-rag` JVM dep on Android and the separate TFLite C 0.12.7 tarball on Desktop; `TensorFlowLiteC` pod no longer needed on iOS. Single source of truth for `TaskType.prefix` in Dart, fixes cross-platform embedding drift (#264).

## Platform-Specific Setup
Expand Down Expand Up @@ -311,7 +311,7 @@ flutter analyze && dart format . && tool/test_all.sh
| `hook/build.dart` | Native Assets hook — fetches the per-platform `vec0` loadable extension |
| `web/rag/sqlite3.wasm` | custom `sqlite3.wasm` with `sqlite-vec`/`vec0` statically linked (app copies to its web root) |

**`packages/flutter_gemma_builtin_ai/` (OS built-in AI; Gemini Nano on Android, Apple Foundation Models on iOS/macOS; no web/desktop):**
**`packages/flutter_gemma_builtin_ai/` (OS built-in AI; Gemini Nano on Android and desktop Chrome via the Prompt API, Apple Foundation Models on iOS/macOS; no Windows/Linux):**

| File | Purpose |
|------|---------|
Expand All @@ -324,7 +324,7 @@ flutter analyze && dart format . && tool/test_all.sh
| `android/src/.../` | Android ML Kit GenAI (AICore) native layer; declares `minSdk 26` |
| `darwin/Classes/` (shared iOS+macOS source via `sharedDarwinSource: true`) | Apple Foundation Models native layer |

**`packages/flutter_gemma_onnx/` (ONNX Runtime — ORT-GenAI inference + plain-ORT embeddings; macOS arm64 only in v1, no web):**
**`packages/flutter_gemma_onnx/` (ONNX Runtime — ORT-GenAI inference + plain-ORT embeddings on macOS arm64 / Linux x64 / Windows x64 / Android arm64 / iOS arm64; web via Transformers.js + onnxruntime-web):**

| File | Purpose |
|------|---------|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ android {
applicationId = "dev.fluttergemma.quickstart"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
// libLiteRtLm.so needs API 30+ Bionic (pthread_cond_clockwait,
// sem_clockwait). Below 30 the app installs and then fails at the first
// model load with a dlopen error.
minSdk = 30
targetSdk = flutter.targetSdkVersion
// Uses the version code from pubspec.yaml. When using split APKs, 1000 * ABI_VERSION
// is added automatically by Flutter. (https://developer.android.com/studio/build/configure-apk-splits#configure-APK-versions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import FlutterMacOS
import Foundation

import connectivity_plus
import flutter_gemma
import large_file_handler
import shared_preferences_foundation

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FlutterGemmaPlugin.register(with: registry.registrar(forPlugin: "FlutterGemmaPlugin"))
LargeFileHandlerPlugin.register(with: registry.registrar(forPlugin: "LargeFileHandlerPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
Expand Down
4 changes: 2 additions & 2 deletions codelabs/getting-started-flutter-gemma/complete/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ dependencies:
cupertino_icons: ^1.0.8

# The engine-agnostic core: registry, install/runtime API, chat.
flutter_gemma: ^1.7.1
flutter_gemma: ^1.8.1
# The .litertlm inference engine. Engines are opt-in — core
# registers none, so this package must be added explicitly.
flutter_gemma_litertlm: ^1.6.2
flutter_gemma_litertlm: ^1.6.3

dev_dependencies:
flutter_test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

#include "generated_plugin_registrant.h"

#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <flutter_gemma/flutter_gemma_plugin.h>

void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterGemmaPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterGemmaPlugin"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
flutter_gemma
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ android {
applicationId = "dev.fluttergemma.quickstart"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
// libLiteRtLm.so needs API 30+ Bionic (pthread_cond_clockwait,
// sem_clockwait). Below 30 the app installs and then fails at the first
// model load with a dlopen error.
minSdk = 30
targetSdk = flutter.targetSdkVersion
// Uses the version code from pubspec.yaml. When using split APKs, 1000 * ABI_VERSION
// is added automatically by Flutter. (https://developer.android.com/studio/build/configure-apk-splits#configure-APK-versions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import FlutterMacOS
import Foundation

import connectivity_plus
import flutter_gemma
import large_file_handler
import shared_preferences_foundation

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FlutterGemmaPlugin.register(with: registry.registrar(forPlugin: "FlutterGemmaPlugin"))
LargeFileHandlerPlugin.register(with: registry.registrar(forPlugin: "LargeFileHandlerPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ dependencies:
cupertino_icons: ^1.0.8

# The engine-agnostic core: registry, install/runtime API, chat.
flutter_gemma: ^1.7.1
flutter_gemma: ^1.8.1
# The .litertlm inference engine. Engines are opt-in — core
# registers none, so this package must be added explicitly.
flutter_gemma_litertlm: ^1.6.2
flutter_gemma_litertlm: ^1.6.3

dev_dependencies:
flutter_test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

#include "generated_plugin_registrant.h"

#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <flutter_gemma/flutter_gemma_plugin.h>

void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterGemmaPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterGemmaPlugin"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
flutter_gemma
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ android {
applicationId = "dev.fluttergemma.quickstart"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
// libLiteRtLm.so needs API 30+ Bionic (pthread_cond_clockwait,
// sem_clockwait). Below 30 the app installs and then fails at the first
// model load with a dlopen error.
minSdk = 30
targetSdk = flutter.targetSdkVersion
// Uses the version code from pubspec.yaml. When using split APKs, 1000 * ABI_VERSION
// is added automatically by Flutter. (https://developer.android.com/studio/build/configure-apk-splits#configure-APK-versions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import FlutterMacOS
import Foundation

import connectivity_plus
import flutter_gemma
import large_file_handler
import shared_preferences_foundation

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FlutterGemmaPlugin.register(with: registry.registrar(forPlugin: "FlutterGemmaPlugin"))
LargeFileHandlerPlugin.register(with: registry.registrar(forPlugin: "LargeFileHandlerPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ dependencies:
cupertino_icons: ^1.0.8

# The engine-agnostic core: registry, install/runtime API, chat.
flutter_gemma: ^1.7.1
flutter_gemma: ^1.8.1
# The .litertlm inference engine. Engines are opt-in — core
# registers none, so this package must be added explicitly.
flutter_gemma_litertlm: ^1.6.2
flutter_gemma_litertlm: ^1.6.3

dev_dependencies:
flutter_test:
Expand Down
Loading
Loading