{"owner":"BasedHardware","repo":"omi","hasSkills":true,"totalSkillsCount":90,"totalTokensCount":37092,"categories":["claude-rule","root-instruction","anthropic-skill","cursor-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md","AGENTS.md","app/e2e/SKILL.md",".github/AGENTS.md",".cursor/rules/arc.md",".cursor/rules/backend-imports.mdc",".cursor/rules/backend-testing.mdc",".cursor/rules/common-mistakes.mdc",".cursor/rules/flutter-localization.mdc",".cursor/rules/formatting.mdc","plugins/chatgpt/gpt_schema.example.yaml","plugins/composio/README.md","plugins/hume-ai/README.md","plugins/hume-ai/emotion_config.json","plugins/hume-ai/render.yaml","plugins/omi-arxiv-app/README.md","plugins/omi-citybikes-app/README.md","plugins/omi-clickup-app/README.md","plugins/omi-coingecko-crypto-app/README.md","plugins/omi-crossref-app/README.md","plugins/omi-frankfurter-app/README.md","plugins/omi-github-app/COMPARISON.md","plugins/omi-github-app/QUICKSTART.md","plugins/omi-github-app/README.md","plugins/omi-github-app/SETUP_COMPLETE.md","plugins/omi-github-app/TEST_CHANGE.md","plugins/omi-github-app/THEME_UPDATE.md","plugins/omi-google-calendar-app/README.md","plugins/omi-hacker-news-app/README.md","plugins/omi-hermes-agent-app/README.md","plugins/omi-hive-app/README.md","plugins/omi-launch-library-app/README.md","plugins/omi-linear-app/README.md","plugins/omi-ms365-app/README.md","plugins/omi-ms365-app/omi-tools.json","plugins/omi-nobel-prize-app/README.md","plugins/omi-notion-app/README.md","plugins/omi-open-library-app/README.md","plugins/omi-open-meteo-app/README.md","plugins/omi-openfoodfacts-app/README.md","plugins/omi-plugin-sdk/README.md","plugins/omi-public-holidays-app/README.md","plugins/omi-pubmed-app/README.md","plugins/omi-semantic-scholar-app/README.md","plugins/omi-shipbob-app/README.md","plugins/omi-shopify-app/README.md","plugins/omi-slack-app/OMI_SETUP.md","plugins/omi-slack-app/README.md","plugins/omi-slack-app/SETUP.md","plugins/omi-slack-app/TOKEN_DEBUG.md","plugins/omi-stack-overflow-app/README.md","plugins/omi-twitter-app/README.md","plugins/omi-twitter-chat-tools-app/README.md","plugins/omi-usgs-earthquake-app/README.md","plugins/omi-whoop-app/README.md","plugins/omi-wikipedia-app/README.md","plugins/uber_call/README.md","backend/evals/dream_context/README.md","backend/evals/dream_reasoning/README.md","backend/evals/dream_reasoning/fixture.json","backend/evals/dream_reasoning/guard-fixtures.json","backend/evals/dream_reasoning/quality-fixtures.json","backend/evals/dream_reasoning/quality-results.json","backend/evals/dream_reasoning/results.json","backend/evals/dream_triage/README.md","backend/evals/dream_triage/fixtures.json","backend/evals/dream_triage/quality-results.json","backend/evals/dream_triage/results.json","backend/evals/dream_verifier/LABELING.v2.md","backend/evals/dream_verifier/README.md","backend/evals/dream_verifier/manifest.json","backend/evals/dream_verifier/manifest.v2.json","plugins/import/manual-import/README.md","plugins/instructions/SOS/README.md","plugins/instructions/ahda/README.md","plugins/instructions/bookwatch/README.md","plugins/instructions/gcal/README.md","plugins/instructions/gdrive/README.md","plugins/instructions/heavenorhell/README.md","plugins/instructions/map-notes/README.md","plugins/instructions/mentor_01/README.md","plugins/instructions/multion-amazon/README.md","plugins/instructions/notion-crm/README.md","plugins/instructions/reflect/README.md","plugins/instructions/zapier/README.md","plugins/zapier/omi-zap/package-lock.json","plugins/zapier/omi-zap/package.json","web/packages/note-shared/README.md","web/packages/note-shared/package.json","desktop/macos/agent/evals/realtime-routing-cases.json"],"skills":{"CLAUDE.md":"# Omi — Agent Instructions\n<!-- Official guidance for writing these files:\n     CLAUDE.md: https://docs.anthropic.com/en/docs/claude-code/memory\n     AGENTS.md: https://developers.openai.com/codex/guides/agents-md\n     Format spec: https://agents.md -->\n\n**Treat `AGENTS.md` as this repository's instruction file.** Every rule for every agent\n(Claude Code, Codex, and any other) lives there. This file exists only because some tools\nlook for `CLAUDE.md` by name — it is the one such pointer in the repo.\n\n- Start with the root [`AGENTS.md`](./AGENTS.md): cross-component rules plus an index.\n- When working inside a directory, also read the **nearest `AGENTS.md` at or above it** —\n  `backend/`, `app/`, `desktop/macos/`, `.github/`, `web/admin/`, `omi/firmware/`.\n  Those carry the detail the root file deliberately omits.\n- Add or change rules in the relevant `AGENTS.md`. **Never add instructions here** — this\n  file stays a pointer so there is only ever one source of truth to maintain.\n\n@AGENTS.md\n","AGENTS.md":"# Omi agent guide\n\nRead the guide for the area you are changing. `CLAUDE.md` points here.\n\n## Routes\n\n| Working on | Read |\n|---|---|\n| Backend | `backend/AGENTS.md` |\n| Flutter app | `app/AGENTS.md` |\n| macOS desktop | `desktop/macos/AGENTS.md` |\n| Windows/Linux desktop | `desktop/windows/AGENTS.md` |\n| Web app / admin | `web/app/AGENTS.md` / `web/admin/AGENTS.md` |\n| Firmware | `omi/firmware/AGENTS.md` |\n| CI / deployment workflows | `.github/AGENTS.md` |\n| Public documentation | `docs/AGENTS.md` |\n| Product behavior / UI | `PRODUCT.md`, `product/invariants/`; macOS UI: `desktop/macos/docs/ux-contract.md` |\n| Shared client behavior | `contracts/parity/README.md` |\n| Released-client compatibility | `contracts/client-compat/` |\n| Fallbacks / fail-open paths | `.github/agent-docs/fallback-telemetry.md` |\n| Plan catalog | `.github/agent-docs/plan-catalog.md` |\n| App E2E | `app/e2e/SKILL.md`, `desktop/macos/e2e/SKILL.md` |\n| Cursor Cloud environment | `.cursor/cloud-agent-environment.md` |\n| Contributions / PR requirements | `docs/doc/developer/Contribution.mdx` |\n| Failure-class classification | `product/failure-classes.md`, `scripts/failure-class` |\n| Formatting | `.github/agent-docs/formatting.md` |\n| Agent documentation / new checks | `.github/agent-docs/doc-maintenance.md` |\n\n## Repository conventions\n\n- Use a task worktree based on current `origin/main`. `make setup` installs the repository hooks, including staged-file formatting. Keep Git identity in the user's configuration; temporary test identities use `git -c`.\n- Upstream `main` changes land through PRs with merge commits, never squash or direct pushes.\n- macOS development uses dev or `omi-*` named bundles. Do not stop or replace the production Omi or Omi Beta apps; bundle details and commands live in the macOS guide.\n- Migrate in-tree callers together rather than preserving retired APIs with compatibility wrappers. Released clients follow the compatibility contracts above.\n- Run the component's documented verification and `make preflight`. For PR metadata, start with `scripts/pr-preflight --suggest`, then validate the draft with `scripts/pr-preflight --pr-body-file <file>`. These tools identify required invariant citations and failure-class declarations.\n\nKeep this file a router. Add repository-specific facts at their owning guide; omit generic agent behavior and duplicated procedures. Size budgets live in `.github/scripts/check_agents_md_lean.py`.\n","app/e2e/SKILL.md":"---\nname: mobile-app-flows\ndescription: \"Understand and explore the Omi Flutter mobile app's UI flows, navigation patterns, and widget architecture. Use when developing features, fixing bugs, or verifying changes in app/lib/ Dart files. Provides agent-flutter commands to explore the live app, understand how screens connect, and verify your work.\"\nallowed-tools: Bash, Read, Glob, Grep\n---\n\n# Omi Mobile App — Flows & Exploration\n\nThis skill teaches you the Omi Flutter mobile app's navigation structure, screen architecture, and widget patterns. Use it when developing features (to understand how the app works), fixing bugs (to navigate to the affected screen), or verifying changes (to confirm your code works in the live app).\n\n> **Verifying changes (agents and contributors):** this skill drives a *live*\n> app on an emulator/device and assumes a manually-authenticated session\n> against a real or local backend — it is an exploration and physical-device\n> tool. The canonical fast verification path is the seeded local lane with\n> synthetic auth: `make mobile-verify ARGS=\"fast --paths <changed-file>\"` —\n> no device, no OAuth, loopback fixtures. See\n> [`scripts/dev-harness/MOBILE_VERIFY.md`](../../scripts/dev-harness/MOBILE_VERIFY.md)\n> and [`MOBILE_SESSIONS.md`](../../scripts/dev-harness/MOBILE_SESSIONS.md).\n> Physical-device evidence remains a separately reported lane (SCA-491).\n\n## How to Explore the App\n\nYou can interact with the running app via `agent-flutter` — a CLI that taps widgets, reads the widget tree, and captures screenshots through Flutter's Marionette debug protocol.\n\n### Setup\n```bash\n# 1. Emulator must be running\nadb devices                          # should show emulator-5554\n# If not: sg kvm -c \"$ANDROID_HOME/emulator/emulator -avd omi-dev -no-window -gpu swiftshader_indirect -no-audio -no-boot-anim &\"\n\n# 2. Set system language to English (REQUIRED — non-English IME breaks text input)\nadb shell \"settings put system system_locales en-US\"\nadb shell \"setprop persist.sys.locale en-US\"\n\n# 3. App must be running in debug mode with flutter run stdout captured\ncd app && flutter run -d emulator-5554 --flavor dev > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"VM Service\" line to appear in the log\n\n# 4. Connect agent-flutter (AGENT_FLUTTER_LOG must point to flutter run stdout, NOT logcat)\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json    # see what's on screen\n```\n\n**Prerequisites:**\n- AVD name: `omi-dev` (check: `$ANDROID_HOME/emulator/emulator -list-avds`)\n- KVM access required: user must be in `kvm` group (`sg kvm -c \"...\"` if not in current session)\n- App package: `com.friend.ios.dev` (dev flavor)\n- **System language must be English** — non-English IME breaks `fill` commands\n- **App must be authenticated and connected to the correct backend** (local, dev, or prod — depends on the task)\n- Marionette already integrated: `marionette_flutter: ^0.3.0` in pubspec.yaml\n\n### Setup (iOS physical device)\n\nFull iOS physical-device playbook (setup, driving, verification, troubleshooting): [`IOS_DEVICE_TESTING.md`](./IOS_DEVICE_TESTING.md).\n\nVerified 2026-07-11 on an iPhone XR (iOS 18.7.9), app 1.0.543, prod-flavor debug build, agent-flutter CLI + marionette MCP. iOS Simulator has no BLE — use a physical device for anything beyond onboarding/UI checks (see also iOS Simulator Known Limitations below).\n\n```bash\n# 1. Get the physical device id\nflutter devices\n\n# 2. Run in debug mode with stdout captured — prod flavor (dev flavor can fail codesigning on a\n#    fresh worktree: \"provisioning profile doesn't support the App Groups capability\")\ncd app && flutter run -d <device-id> --flavor prod > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"A Dart VM Service ... is available\" in the log\n\n# 3. Connect agent-flutter — auto-detects the ws URI from the log\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json\n```\n\n**Gotchas:**\n- **Fresh git worktrees need real prod config seeded from the primary checkout** before building the `prod` flavor: copy `app/.env`, `app/lib/firebase_options_prod.dart`, `app/ios/Config/Prod/GoogleService-Info.plist`, `app/lib/env/prod_env.g.dart`. The `test.sh` bootstrap seeds dev-placeholder versions that point at the wrong Firebase project.\n- **adb-backed commands do NOT work on iOS**: `back`, `press x y`, `dismiss`, `text --press`, `text --fill`. Use in-app back buttons (find the top-left `IconButton` ref) and marionette ref presses only.\n- **`fill @ref` can silently no-op on keyless `TextField`s on iOS** — it reports success but the controller stays empty. Verify via the marionette MCP `get_interactive_elements` (shows the `TextEditingController` contents). Durable fix: add a `ValueKey` to the field, hot reload (sheet state survives), then enter text by key.\n- **`snapshot` labels are empty with `marionette_flutter` 0.3.0** on iOS. Primary orientation/assertion tool is `agent-flutter text` (semantic text dump) — assert outcomes by text presence (e.g. the copy snackbar text). Target elements by type + bounds from `snapshot -i`.\n- `agent-flutter screenshot` output path must be under `/tmp`.\n- Check behavior via the run log: `grep -iE 'exception|error' /tmp/omi-flutter.log` after each flow. A `PlatformException` 4001 (Intercom push token, notifications not granted) is benign.\n- iOS terminates the debug connection if the app is backgrounded/locked too long (\"The OS has terminated the Flutter debug connection for being inactive\") — keep the device unlocked; reconnecting requires relaunching `flutter run`.\n- General key guidance (same as Android): prefer `find key \"name\"`; when a control can't be targeted, add a `ValueKey` in source + hot reload rather than fighting coordinates.\n\n### Commands\n\n| Command | Purpose | Example |\n|---------|---------|---------|\n| `snapshot -i --json` | See all interactive widgets with refs, types, bounds | `agent-flutter snapshot -i --json` |\n| `press @ref` | Tap a widget by ref | `agent-flutter press @e3` |\n| `press x y` | Tap by coordinates (ADB input tap) | `agent-flutter press 540 1200` |\n| `press @ref --adb` | Tap by ref using ADB (for stale refs) | `agent-flutter press @e3 --adb` |\n| `dismiss` | Dismiss system dialogs (location, permissions) | `agent-flutter dismiss` |\n| `find type X press` | Find widget by type and tap | `agent-flutter find type button press` |\n| `find text \"X\" press` | Find by visible text and tap | `agent-flutter find text \"Settings\" press` |\n| `find type X --index N press` | Tap Nth match (0-indexed) | `agent-flutter find type switch --index 0 press` |\n| `fill @ref \"text\"` | Type into text field | `agent-flutter fill @e7 \"search\"` |\n| `scroll down/up` | Scroll current view | `agent-flutter scroll down` |\n| `back` | Android back button | `agent-flutter back` |\n| `screenshot PATH` | Capture current screen | `agent-flutter screenshot /tmp/screen.png` |\n\n**Key rules:**\n- Refs go stale frequently (Flutter rebuilds widget tree aggressively) — always re-snapshot before every interaction, not just after mutations.\n- `find type X` is more stable than hardcoded `@ref` numbers.\n- `AGENT_FLUTTER_LOG` must point to `flutter run` stdout (not logcat).\n- After hot restart: `disconnect` → wait 3s → `connect`.\n- Widget text labels are often null — use `type`, `flutterType`, or `bounds` to identify.\n- `back`, `press x y`, `dismiss`, and the `--adb`/`--press`/`--fill` text flags are ADB-backed — Android only. On a physical iOS device use marionette ref-based commands instead (see Setup (iOS physical device) above).\n\n### Recovery\n```bash\n# \"No isolate with Marionette\" → bring app to foreground + reconnect\nadb -s emulator-5554 shell am start -n com.friend.ios.dev/com.friend.ios.MainActivity\nagent-flutter disconnect && agent-flutter connect\n\n# Unhealthy widget tree → hot restart\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## App Navigation Architecture\n\n### Screen Map\n```\nOnboarding (wrapper.dart) — step wizard\n├── 0: Auth (auth.dart) — Google/Apple sign-in\n├── 1: AI Consent\n├── 2: Name (name_widget.dart)\n├── 3: Primary Language (primary_language_widget.dart)\n├── 4: Found Omi (found_omi_widget.dart)\n├── 5: Permissions (permissions_widget.dart)\n├── 6: User Review (user_review_page.dart)\n├── 9: Speech Profile (speech_profile_widget.dart)\n├── 10: Knowledge Graph (knowledge_graph_step.dart)\n└── 11: Complete (complete_screen.dart) → Home\n\nHome (home/page.dart) — main app after auth, 4-slot bottom nav\n├── [\"Ask Omi\" input bar] → Chat (chat/page.dart) — full-width bar above bottom nav, not a tab\n│   ├── Message history, \"Ask anything\" field, AI responses\n│   └── AI-message action row: Copy (\"✨ Message copied to clipboard\" snackbar), thumbs up, thumbs down, Share\n├── [mic in the bar] → Chat with voice auto-start\n├── [battery/record widget, top left] (battery_info_widget.dart)\n│   ├── Device connected → battery pill → Connected Device (home/device.dart); phone icon → Phone Calls\n│   └── No device → Connect → Connect Device page; Record pill → Conversation Capturing (episodes/live_episode_page.dart)\n│       └── Chevron → record options sheet (Phone Mic record / Phone Call)\n├── [settings gear, top right] → Settings sheet (settings_drawer.dart) — present on every slot\n│\n├── [slot 0] Home (home/home_content.dart)\n│   ├── Conversation capture widget, today's tasks widget\n│   ├── Daily Recaps → Daily Summary Detail; \"View All\" → Conversations\n│   ├── Mind Map → Memory Graph (memory_graph_page.dart) (only with ≥3 conversations)\n│   └── Get-started tiles (only with <3 conversations)\n│\n├── [slot 1] Conversations (conversations_page.dart)\n│   ├── Folder tabs (All, Starred, custom folders)\n│   ├── Top-bar extras on this slot: sync icon (when paired/pending), search, calendar date filter\n│   └── Conversation item (GestureDetector row) → Detail (conversation_detail/page.dart)\n│       ├── Transcript, Summary, Action Items tabs, share, audio (search hidden on Action Items tab)\n│       ├── Pull-down menu: Copy Transcript / Copy Summary / Copy ID / Share Audio (if audio) / Link Event / Test Prompt\n│       └── Back via in-app top-left IconButton — not the OS/adb back gesture\n│\n├── [slot 2] Tasks (action_items_page.dart)\n│   ├── Categories: Today, Tomorrow, Later, No Deadline, Overdue\n│   ├── FAB → Create task sheet (action_item_form_sheet.dart)\n│   ├── Task checkboxes, drag-drop reorder, task → goal linking\n│   └── Top-bar extras on this slot: export → Task Integrations, completed toggle\n│\n└── [slot 3] Apps (apps/page.dart) — \"Search apps\" / \"Featured\" (explore_install_page.dart)\n    ├── Popular apps (horizontal scroll)\n    ├── Category sections → Category apps page\n    ├── App item → App Detail (app_detail/app_detail.dart)\n    │   └── Reviews, capabilities, install/enable, Chat button → Chat\n    └── Top-bar \"+\" on this slot → Add App / Add MCP Server\n\nSettings sheet (settings_drawer.dart) — search + close header, then five visual groups: Account ·\nPlan & Usage, Referral Program · Device … Data & Privacy · Help & About, Feedback · Developer Settings.\nEvery top-level row has a ValueKey (`settings_account`, `settings_group_<group>`, or\n`settings_row_<SettingsDestination>` for the direct rows Plan, Referral and Feedback); each page has a\nScaffold key (`settings_page_<page>`); rows on group/Account pages are `settings_row_<SettingsDestination>`\n(plus `settings_row_voiceResponseMode`, `settings_row_transcribeLater`, `settings_row_backgroundMode`,\n`settings_row_name`, `settings_row_email`, `settings_row_userId`, `settings_row_version`).\n├── Account [settings_account] — shows the name, email as subtitle → Account page (profile.dart, ProfilePage)\n│   ├── Name → Change name dialog; Email (read-only)\n│   ├── User ID (tap copies)\n│   └── Sign Out → Confirmation dialog; Delete Account (delete_account.dart)\n├── Plan & Usage [settings_row_planAndUsage] (usage_page.dart) — \"Pro\" value when paid\n├── Referral Program [settings_row_referral] (referral_page.dart) — NEW tag\n├── Device [settings_group_device] → settings_groups.dart\n│   ├── Device Settings (device_settings.dart) (only when a device is connected)\n│   ├── Offline Sync (sync_page.dart / auto_sync_page.dart)\n│   ├── Phone Calls (phone_call_settings_page.dart)\n│   └── Permissions (permissions_page.dart) — microphone, Bluetooth, notifications\n├── Recording & Transcription [settings_group_recording] → settings_groups.dart\n│   ├── Transcription (transcription_settings_page.dart) — provider value; Language; Custom Vocabulary\n│   ├── Voice Profile → guided introduction (onboarding/speech_profile_widget.dart); Identifying Others (people.dart)\n│   ├── Voice Response (picker sheet); Conversation Timeout (picker)\n│   └── Recording (BETA): Transcribe Later switch; Background Mode switch (Android only)\n├── Notifications & Display [settings_group_notifications] → settings_groups.dart\n│   └── Notifications (notifications_settings_page.dart); Home Screen; Conversation Display\n├── Integrations [settings_group_integrations] (integrations_page.dart) — BETA; also opens from conversation detail\n├── Data & Privacy [settings_group_privacy] → settings_groups.dart\n│   ├── Data Protection (data_privacy_page.dart); Memories (memories/page.dart)\n│   └── Export All Data (spinner while running); Import Data (import_history_page.dart)\n├── Help & About [settings_group_help] → settings_groups.dart\n│   ├── Help Center → help.omi.me (Intercom platforms only)\n│   ├── What's New → Changelog sheet\n│   └── Version + copy button (iOS/Android)\n├── Feedback / Report a bug [settings_row_feedback] → feedback.omi.me (Intercom platforms only)\n└── Developer Settings [settings_group_developer] (developer.dart)\n(Settings search finds every row above and opens the page that holds it.)\n\nTranscription Settings (transcription_settings_page.dart) — not in settings drawer; reached from\nPlan & Usage, Developer Settings, or the Plans sheet\n├── Source toggle: Omi Cloud vs Custom STT\n├── Provider selector, API key, model config\n└── Advanced JSON editors, logs viewer\n\nPhone Calls (phone_calls_page.dart) — from battery-widget phone icon, record options, or get-started tile\n├── Phone Setup Intro when no verified numbers\n├── Contacts / Keypad tabs; call → Active Call page\n└── Gear → Phone Call Settings (phone_call_settings_page.dart) — verified numbers list, delete button\n\nPlans sheet (plans_sheet.dart) — from Plan & Usage, chat quota-exceeded, phone-calls upsell\n\nPersona Profile (persona_profile.dart) — AI clone management\n├── Avatar (100x100), name with verified badge\n├── Share Public Link button\n├── Make Public toggle\n└── 10 social link rows (omi, Twitter active; others Coming Soon)\n    └── Twitter → Social Handle Entry → Verify Identity → Clone Success\n\nConnected Device (home/device.dart) — requires BLE\n├── Device name, connection status, battery\n├── Actions: Firmware Update, SD Card Sync, Disconnect, Unpair\n└── Device info: Product, Model, Manufacturer, Firmware, ID, Serial\n\nVoice Profile — guided introduction (onboarding/speech_profile_widget.dart, #14514)\n├── Four sentence starters, phone mic; Start Speaking or Skip (whole step), then Next / Pause / Skip Question while recording\n└── Review: edit or uncheck answers, then Save and Finish (voice, memories, goal)\n```\n\n### Widget Patterns\n\n**Bottom navigation bar:**\n- Android: 4 `InkWell` widgets at `bounds.y > 780`, sorted left-to-right by `bounds.x`\n- Detect with: `snapshot -i --json` → filter `flutterType == 'InkWell'` and `bounds.y > 780`\n- Navigate home: press the leftmost one\n- iOS (verified 2026-07-11, iPhone XR, 414pt-wide screen): 4 slots at y≈816, x=20/114/207/300, each w=94.\n  Left to right: slot 0 = Home, slot 1 = Conversations (folder tabs All/Starred/…), slot 2 = Tasks,\n  slot 3 = Apps marketplace (\"Search apps\" / \"Featured\")\n\n**Chat entry point (not a bottom-nav tab):**\n- Open chat by tapping the \"Ask Omi\" input bar on the home screen — a full-width gesture\n  element directly above the bottom nav (~y=756, w≈382 on a 414pt-wide screen; verified iOS 2026-07-11)\n\n**Settings gear:**\n- Android: rightmost `button` widget in the top bar; detect by sorting buttons by `bounds.x` descending, take first\n- iOS (verified 2026-07-11): single top-right icon on home at ~x=362, y=58 → Settings sheet (Account,\n  Plan & Usage, Referral Program, Device, Recording & Transcription, Notifications & Display,\n  Integrations, Data & Privacy, Help & About, Feedback, Developer Settings)\n\n**Settings rows:**\n- `gesture` widgets with `bounds.width > 300`\n- Prefer the ValueKeys above (`settings_account`, `settings_group_*`, `settings_row_*`) over positions\n\n**Switch toggles:**\n- Type `switch` in snapshots\n- Press to toggle ON/OFF (no separate ON/OFF actions)\n\n**Bottom sheet pickers:**\n- Open when you press a settings row\n- Language items appear as `gesture` rows with `bounds.y > 380`\n- Many items — use scroll if needed\n\n**Conversation feed rows (iOS, verified 2026-07-11):**\n- `GestureDetector` widgets, h≈84–96; detail opens on tap\n- Go back with the in-app top-left `IconButton` (x=8, y=56, w=40) — not the OS/adb back gesture\n\n**Chat AI-message actions (iOS, verified 2026-07-11):**\n- Four small `InkWell`s (w≈12–14) under each AI message at x≈22/54/88/122; the first is copy —\n  pressing it shows the \"✨ Message copied to clipboard\" snackbar\n\n### Changing Locale\n```bash\nDEVICE=emulator-5554; APP_PKG=com.friend.ios.dev\n\n# Read current\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" | grep app_locale\n\n# Change to Spanish, hot restart to apply\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" > /tmp/prefs.xml\nsed -i 's|flutter.app_locale\">[^<]*|flutter.app_locale\">es|' /tmp/prefs.xml\nadb -s $DEVICE push /tmp/prefs.xml /data/local/tmp/FlutterSharedPreferences.xml\nadb -s $DEVICE shell \"run-as $APP_PKG cp /data/local/tmp/FlutterSharedPreferences.xml shared_prefs/FlutterSharedPreferences.xml\"\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## Prerequisites Reference\n\nEvery flow lists `prerequisites:` — conditions that MUST be true before running. These describe the **real user state** — no bypasses, no shortcuts. Test the app the way users experience it.\n\n| Prerequisite | What it means | How to achieve (Android) | How to achieve (iOS) |\n|-------------|---------------|--------------------------|----------------------|\n| `auth_ready` | User completed sign-in (Google or Apple), app shows home screen | Run `bash setup.sh android` → launch app → complete Google Sign-In flow → complete onboarding | Run `bash setup.sh ios` → launch on simulator or device → complete Google/Apple Sign-In → complete onboarding |\n| `signed_out` | Fresh app, user NOT signed in, shows Get Started screen | Uninstall + reinstall, or clear app data via Settings → Apps → Omi → Clear Data | Delete app from simulator/device and reinstall |\n| `microphone_permission` | App has mic permission granted | When app requests mic permission during use, tap \"Allow\". Or pre-grant: `adb shell pm grant com.friend.ios.dev android.permission.RECORD_AUDIO` | When app requests mic permission, tap \"Allow\" in the iOS permission dialog |\n| `ble_on` | Bluetooth enabled on device | Enable Bluetooth in device Settings → Connected Devices. **Emulators/simulators do not support BLE** — requires physical device | Enable Bluetooth in device Settings. **iOS Simulator has no BLE** — requires physical iPhone |\n| `omi_device_connected` | Omi hardware paired and connected via BLE | Power on Omi device within BLE range → app auto-discovers on home screen → tap Connect. **Physical device only** | Same — power on Omi, app discovers it. **Physical iPhone only** |\n| `phone_number_verified` | Phone number added and verified in settings | Settings → Phone Calls → add phone number → receive SMS → enter code. Requires real phone number | Same flow — requires real phone number that receives SMS |\n| `developer_settings_enabled` | Developer Settings screen is open | Settings drawer → scroll down → tap \"Developer Settings\" (visible to all users) | Same navigation path |\n| `adb_access` | Shell access for locale/prefs manipulation (Android only) | Debug build + `adb` in PATH. Verify: `adb shell run-as com.friend.ios.dev ls shared_prefs/` | Not applicable — iOS equivalent uses `xcrun simctl` for simulator or Xcode for device |\n\n### Prerequisite dependency chain\n```\nsigned_out ─── (fresh install, no prior state)\n\nauth_ready ─── launch app → sign in with Google/Apple → complete onboarding\n  │             (this is the REAL user flow — no bypasses)\n  ├── microphone_permission  (grant when prompted, or pre-grant via platform tools)\n  ├── developer_settings_enabled  (navigate in-app to Settings → Developer Settings)\n  ├── phone_number_verified  (in-app SMS verification — manual step)\n  ├── ble_on + omi_device_connected  (physical device + physical Omi hardware)\n  └── adb_access  (Android debug builds only — for locale manipulation)\n```\n\n### Platform setup\n```bash\n# Android: setup + build + launch\ncd app && bash setup.sh android\n# → completes: keystore, Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google when app launches, complete onboarding\n\n# iOS: setup + build + launch\ncd app && bash setup.sh ios\n# → completes: Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google/Apple when app launches, complete onboarding\n```\n\n**Important:** Both platforms require completing the real sign-in and onboarding flows. Never bypass auth or onboarding — these are user-facing flows that must work correctly.\n\n## YAML Flow Schema (v2)\n\nEach flow file uses schema v2:\n```yaml\nversion: 2\nname: string          # Flow identifier\ndescription: string   # What this flow covers\napp: com.friend.ios.dev\nevidence:\n  video: true\ncovers: [string]      # Source files this flow exercises\npreconditions: [string]  # Conditions required — see Prerequisites Reference above\nsteps: [Step]         # Ordered list of actions\n```\n\nEach step has these fields:\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `id` | yes | Step ID (S1, S2, ...) |\n| `name` | yes | Short description |\n| `do` | yes | Instructions for executing |\n| `verify` | no | `true` = must be verified even during snapshot replay |\n| `expect` | no | Assertions (text_visible, interactive_count) |\n| `evidence` | no | Screenshot filename (`.webp` preferred) |\n| `note` | no | Edge case notes |\n\nSupported `expect` kinds:\n\n| Kind | Fields | Example |\n|------|--------|---------|\n| `text_visible` | `values: [string]` | Verify specific text appears |\n| `interactive_count` | `min: number` | Verify minimum interactive widgets |\n\n## Navigation Graph (flow-walker verified)\n\nReal navigation edges verified by flow-walker run11 on Pixel 7a (26 screens, 44 edges, depth 3). Screen names mapped from fingerprint IDs to semantic names.\n\nNote: run11 predates the current bottom nav — its tab labels below reflect the old layout (Conversations/Action Items/Memories/Apps as tabs). Today slot 0 is Home, Tasks is slot 2, Apps is slot 3, and Memories lives under Settings — see Screen Map above. The per-screen element counts and edges remain useful.\n\n```\nHome (24 elements: 17 gesture, 3 icon, 4 inkwell)\n├── [tab 0] Conversations (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   ├── Conversation Detail (27 el: 18 gesture, 4 icon, 4 inkwell, 1 textformfield)\n│   │   └── Notifications Settings (14 el: 10 gesture, 1 icon, 1 inkwell, 1 switch, 1 textbutton)\n│   ├── Settings Drawer (13 el: 13 gesture)\n│   │   ├── Language Settings (13 el: 10 gesture, 1 icon, 1 switch, 1 textbutton)\n│   │   │   ├── Sub-detail (5 el: 4 gesture, 1 icon)\n│   │   │   ├── Sub-settings (7 el: 1 elevated, 5 gesture, 1 icon)\n│   │   │   │   ├── Settings Confirmation (3 el: 1 gesture, 2 textbutton)\n│   │   │   │   └── Settings Form (16 el: 1 elevated, 9 gesture, 3 icon, 1 inkwell, 2 textfield)\n│   │   │   └── Filter Sheet (13 el: 6 gesture, 5 inkwell, 2 textbutton)\n│   │   └── Back → Home\n│   ├── Sub-detail (2 el: gesture, icon)\n│   ├── Sub-page (3 el: 2 gesture, 1 icon)\n│   └── Back → Home\n├── [tab 1] Action Items (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   └── Notifications Settings (same as above)\n├── [tab 2] Memories (25 el: 15 gesture, 4 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Memory Detail (9 el: 5 gesture, 2 icon, 2 inkwell)\n│   ├── Category Page (7 el: 4 gesture, 3 inkwell)\n│   └── Memory Search (18 el: 12 gesture, 4 icon, 1 inkwell, 1 textfield)\n├── [tab 3] Apps (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Confirmation Dialog (4 el: 1 elevated, 1 gesture, 2 icon)\n│   ├── Apps Sub-page (14 el: 3 elevated, 6 gesture, 1 icon, 4 inkwell)\n│   ├── App Form (5 el: 1 elevated, 2 gesture, 1 icon, 1 textfield)\n│   └── Back → Home\n├── [Ask Omi] Chat (22 el: 19 gesture, 1 textbutton, 2 textfield)\n│   ├── Delete Account (6 el: 1 checkbox, 4 gesture, 1 textfield)\n│   └── Back → Home\n├── [record] Capturing (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n├── [settings] Settings Drawer (13 el: 13 gesture)\n├── [gesture] Sign Up (5 el: 1 elevated, 1 gesture, 3 textfield)\n├── [gesture] Confirmation (2 el: gesture, textbutton)\n└── [gesture] Conversations Alt (17 el: 10 gesture, 2 icon, 4 inkwell, 1 textfield)\n```\n\n### Screen Fingerprint Mapping\n\nFor flow-walker compatibility — maps auto-generated fingerprint names to semantic flow names.\n\n| Fingerprint ID | Element Count | Semantic Name | Flow File |\n|----------------|---------------|---------------|-----------|\n| `92a58e321064` | 24 | Home | home-navigation, tab-navigation |\n| `113cc0d4f097` | 17 | Conversations (FAB) | conversations |\n| `59c8c3dba1aa` | 17 | Conversations Alt | conversations |\n| `2108e83364c2` | 19 | Apps | apps |\n| `2b9f6e0d087b` | 25 | Memories | memories |\n| `13f37b3018c5` | 22 | Chat | chat |\n| `49f053d00b0f` | 27 | Conversation Detail | conversation-detail |\n| `011f78c61152` | 14 | Notifications Settings | settings-notifications |\n| `1b40a175a5fd` | 13 | Language Settings | language-change |\n| `4b666d4ec8e3` | 13 | Settings Drawer | home-navigation |\n| `0f852433acc6` | 6 | Delete Account | delete-account |\n| `a74411638a9c` | 5 | Sign Up Form | onboarding |\n| `0ba3aef7f00f` | 2 | Confirmation Dialog | — |\n| `8407c23c9698` | 2 | Sub-detail | — |\n| `9580b20fb6e0` | 3 | Sub-page | — |\n| `dacfbda46c10` | 5 | Sub-detail (settings) | — |\n| `e8ea54a72f46` | 7 | Sub-settings | — |\n| `0766ec584fb4` | 13 | Filter Sheet | apps |\n| `f94e0140ed7e` | 4 | Confirmation Dialog | — |\n| `88992c72a759` | 14 | Apps Sub-page | app-detail |\n| `f757010c13b3` | 5 | App Form | apps |\n| `485a8ce57ebc` | 9 | Memory Detail | memories |\n| `a6983264d893` | 7 | Category Page | memories |\n| `4450e0d3044e` | 18 | Memory Search | memories |\n| `35c0d8ed0daa` | 3 | Settings Confirmation | settings-profile |\n| `a864d8b70edf` | 16 | Settings Form | settings-profile |\n\n## Verified Flows (v2 flow-walker)\n\nOnly flows that have been executed, verified, and pushed through the v2 flow-walker pipeline are listed here. Each has a `.snapshot.json` for fast replay.\n\n| Flow | Steps | Prerequisites | What it tests | Reports |\n|------|-------|--------------|---------------|---------|\n| `flows/login.yaml` | 5 | signed_out | Auth screen → Google Sign-In → consent → OAuth → home | [normal](https://flow-walker.beastoin.workers.dev/runs/hao5vbBsBI.html), [replay](https://flow-walker.beastoin.workers.dev/runs/-aeMGlV88w.html) |\n| `flows/onboarding.yaml` | 9 | signed_out + auth | Name → Language → Found Omi → Permissions → Review → Speech → Knowledge → Complete → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/UHqa8Fysj2.html), [replay](https://flow-walker.beastoin.workers.dev/runs/B9Py6uoJaO.html) |\n| `flows/logout.yaml` | 5 | auth_ready | Home → Settings → Sign Out → Confirm → Auth screen | [normal](https://flow-walker.beastoin.workers.dev/runs/SH4MXH9gkv.html), [replay](https://flow-walker.beastoin.workers.dev/runs/NcZlCUW0W7.html) |\n| `flows/ask-omi-chat.yaml` | 9 | auth_ready | Home → Ask Omi → Chat page → Type message → AI response → Chat apps drawer → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/O5h8bR6izW.html) |\n\nWhen you modify a Dart file, check if any flow's `covers:` includes it. If so, that flow describes the user journey your change affects — use it to understand context and verify your work.\n\n## Verification & Evidence\n\nAfter making changes, verify them in the live app:\n1. Navigate to the affected screen using the commands above\n2. Check that your changes appear (snapshot, screenshot)\n3. Test interactions (press buttons, fill fields, scroll)\n4. Capture evidence: `agent-flutter screenshot /tmp/evidence.png`\n5. Generate video: `ffmpeg -framerate 1 -pattern_type glob -i '/tmp/e2e-*.png' -vf \"scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1\" -c:v libx264 -pix_fmt yuv420p /tmp/report.mp4`\n\n## Visual audit (before/after screenshots without a device)\n\n`app/scripts/visual_audit.sh` screenshots registered screen states at two revisions and pairs them\nin one gallery. It pumps **production widgets** in `flutter-tester` with synthetic local state: the\napp's own fonts (FontManifest plus Roboto from the pinned SDK), `buildOmiTheme()`, 390×844 logical\npx at 2×, and a loopback-only network guard over the hermetic fixture backend. No login, simulator,\nsigning, customer data or external API.\n\n**Use it** for layout, copy, grouping and visual-regression review of a UI change, and for the\nbefore/after images a UI PR description needs. **Use agent-flutter / flow-walker (above) instead**\nfor anything a headless capture cannot show: native keyboard, status bar and safe areas, system\npermission dialogs, BLE, audio, real network timing, screen readers, or a claim about the installed\napp. The captures are evidence of a widget rendering, not of device behavior.\n\n```bash\napp/scripts/visual_audit.sh --list                                  # scenario ids, per suite\napp/scripts/visual_audit.sh --base origin/main --head HEAD          # every scenario, both sides\napp/scripts/visual_audit.sh --base <sha> --head <sha> --only settings-sheet,settings-device\napp/scripts/visual_audit.sh --head WORKTREE --only settings-help    # this checkout, uncommitted, one side\n```\n\nEach revision is checked out in a temporary detached worktree under `$OMI_WORKTREES` (default: the\nsystem temp dir), prepared like CI (generated env files, `pub get`, `build_runner`) and removed on\nexit. The harness and scenarios always come from the checkout you run the command in, copied over\nboth sides. The current suite (`registry.dart`) compiles against current main; a revision from\nbefore a large UI change is captured with a compat suite (`compat/<name>/`, chosen by its `UNTIL`\ncommit; see `compat/README.md`) that pumps the equivalent old page under the same id, so the old\nProfile page pairs with Account. A page only one side has is shown as \"did not exist\" there. The\noutput directory (`--out`,\ndefault a new temp dir; it must be empty and outside the repository) holds `before/` and `after/`\nPNGs named `<id>.png` or `<id>-<step>.png`, per-side `capture.log`, `INDEX.md` with both SHAs and\ncapture times, and a static `gallery.html`. A scenario that throws on one side is reported as failed\nthere and the rest still capture. **Never commit screenshots.**\n\n**Add a scenario** in `app/integration_test/visual_audit/scenarios/<area>.dart` (new areas are\nspread into `registry.dart`). Put `id` first; it is lowercase-hyphenated, starts with the area, and\nnames the PNGs:\n\n```dart\nAuditScenario(\n  id: 'settings-device',\n  title: 'Device group, device connected',\n  page: 'lib/pages/settings/settings_groups.dart (DeviceGroupPage)',\n  state: 'Signed-in fixture account; a device connected',\n  run: (a) async {\n    await a.pump(const DeviceGroupPage(), providers: [\n      ChangeNotifierProvider<DeviceProvider>.value(value: AuditDeviceProvider(connected: true)),\n    ]);\n    await a.scrollSeries('Open Device from the Settings sheet');\n  },\n),\n```\n\n`AuditRun` (`a`) gives `pump` (the production theme and a broad inert provider roster from\n`fakes.dart`; your `providers` win), `pumpHost` (opens a sheet or dialog from a neutral host),\n`tap`/`longPress`/`enterText` (each settles), `shot(action, step:)`, `scrollSeries(action)` (top,\nevery 700 px, exact bottom) and `server` (the running fixture backend: seed conversations, set\n`assistantReplyText`, `failNext`). Seed state through providers and the fixture backend, find\nwidgets by stable `Key`s, and keep `expect(...)` checks that prove the intended state rendered.\n`app/test/visual_audit/visual_audit_smoke_test.dart` renders every registered scenario in the\nordinary `app/test.sh` suite without writing images, so a scenario that breaks fails CI.\n\n## Decision Tree\n\n| Problem | Solution |\n|---------|----------|\n| Widget not found | Re-snapshot, try scrolling, check if on wrong screen, match by bounds position |\n| Ref expired between commands | Use `press x y` with coordinates from last snapshot bounds, or `press @ref --adb` |\n| System dialog blocking (location, permissions) | `agent-flutter dismiss` — detects and dismisses via ADB |\n| \"No isolate with Marionette\" | ADB foreground + disconnect + reconnect |\n| Snapshot returns 0 interactive elements | Marionette lost widget tree — `disconnect` then `connect` to re-attach |\n| Bottom nav tabs not detected | `back` until nav bar appears, filter InkWell y > 780 |\n| Hot restart breaks connection | Wait 3s → disconnect → connect |\n| Text labels null | Match by `type`, `flutterType`, or `bounds` — Marionette doesn't extract child text |\n| Non-English IME breaks text input | Set system locale to English: `adb shell \"settings put system system_locales en-US\"` |\n\n## iOS Simulator Known Limitations\n\nFor a physical iOS device (has BLE, avoids most limitations below), see **Setup (iOS physical device)** under How to Explore the App.\n\n| Issue | Workaround |\n|-------|-----------|\n| ASWebAuthenticationSession blocks Google Sign-In (system dialog, not automatable) | Use `FirebaseAuth.instance.signInWithCustomToken(token)` via VM Service evaluate |\n| `agent-flutter scroll` fails (uses ADB/transport.swipe, Simulator window disconnected) | Scroll via VM Service Dart evaluation: `pos.jumpTo()` on Scrollable widgets |\n| Simulator.app window shows Home Screen (not app) when booted via SSH user | Use `simctl screenshot` for true device state; use Marionette/VM Service for interaction |\n| No `simctl` touch/swipe API | VM Service evaluate for Flutter-level interaction; cliclick only works if Simulator window is synced |\n| iOS keychain persists through app uninstall | `xcrun simctl erase <UDID>` for full reset |\n| iOS onboarding is shorter than Android (language only → home) | Mark skipped steps as pass with platform note |\n\n**VM Service scroll expression** (target app root library):\n```dart\n(() { var count = 0; void visit(Element el) { if (el.widget is Scrollable) { try { final s = (el as StatefulElement).state as ScrollableState; final p = s.position; if (p.maxScrollExtent > 0 && p.pixels < p.maxScrollExtent) { p.jumpTo((p.pixels + 500).clamp(0.0, p.maxScrollExtent)); count++; } } catch (_) {} } el.visitChildren(visit); } WidgetsBinding.instance.rootElement?.visitChildren(visit); return count; })()\n```\n\n**iOS simulator auth** (dev Firebase tokens rejected by prod API — needs local backend):\n```bash\n# .dev.env must point to a local/dev backend\nAPI_BASE_URL=http://<your-backend-host>:<port>/\nUSE_WEB_AUTH=false\nUSE_AUTH_CUSTOM_TOKEN=true\n# Regenerate envied after .dev.env change, rebuild app\n```\n\n## Guard Conditions\n\n**NEVER:**\n- Use development env vars to bypass auth — test with real auth flows\n- Set `hasCompletedOnboarding` to skip onboarding — test the real flow\n- Modify source code to make tests pass — report the failure instead\n- Commit screenshots to git — use GCS upload for PR evidence\n",".github/AGENTS.md":"# GitHub Workflow Agent Guide\n\nThese rules apply to GitHub Actions workflows and custom actions under `.github/`.\n\n## CI/CD Deploy Safety\n\n- Every workflow that mutates a persistent Cloud Run service/job, GKE Helm\n  release, or traffic/promotion state must use a workflow-level concurrency\n  group scoped to the exact target and logical environment. Manual and\n  automatic entry points for the same target must resolve to the same group.\n- Deployment group names are a cross-workflow API. Keep them aligned with\n  `.github/scripts/check-deployment-concurrency.py`; use\n  `cancel-in-progress: false` so a newer run cannot interrupt a remote mutation\n  or a staged validation/traffic promotion.\n- `deploy-backend-stack-<environment>` intentionally covers the four backend\n  Cloud Run services, traffic repair, backend-listen, LLM gateway, and\n  backend-secrets. Those paths share mutable releases, while unrelated services\n  retain their own groups and may deploy in parallel.\n- GitHub concurrency is serialization, not a FIFO queue: only one pending run is\n  retained and ordering is not guaranteed. Deploy workflows must not assume\n  that every intermediate commit will run.\n- Use immutable image tags for deploys. Build and push the short SHA tag, then deploy that exact tag.\n- Development desktop-backend acceptance signs Firebase probe tokens via IAM `signJwt` on `vars.FIREBASE_PROBE_SIGNER_SERVICE_ACCOUNT` (a `@based-hardware` SA matching `FIREBASE_AUTH_PROJECT_ID`); the deploy identity needs `roles/iam.serviceAccountTokenCreator` on it. Never stage service-account key files in runners or deploy them as runtime configuration.\n- Do not deploy Cloud Run services from an untagged image path; use `image:...:${SHORT_SHA}` so revisions show the source commit.\n- Do not let Helm chart-only deploys reset GKE workloads to `latest`. Preserve the currently deployed immutable tag or require an explicit tag input.\n- Every GKE Deployment deploy must wait for rollout completion with `kubectl rollout status ... --timeout=...` and fail on timeout.\n- For chart-only backend-listen deploys, fail if the current deployed image tag is missing or `latest`; use the backend deploy workflow to establish an immutable tag first.\n- Keep rollout checks close to the deploy step they verify, especially for `backend-listen`, `pusher`, `llm-gateway`, `parakeet`, `diarizer`, and `vad`.\n- Use `backend/scripts/deploy_status_report.py` as a strict gate on success paths; use it with `|| true` only after a primary rollout/traffic command already failed.\n- Full backend deploys must derive one immutable Cloud Run plus `backend-listen` release vector and run `backend/scripts/verify_backend_release_vector.py` after traffic promotion; a mixed or partially applied serving vector must fail the workflow and emit evidence for a retry.\n- Before restarting GKE workloads that depend on `backend-secrets`, wait for ExternalSecret Ready and run `backend/scripts/verify_k8s_secret_keys.py`; never print secret values.\n- Before any pusher Helm mutation, verify `${ENV}-omi-backend-config` exists so a missing shared runtime ConfigMap cannot replace the healthy replica.\n- Backend deploy workflows may only run Firestore index readiness through the shared `.github/actions/firestore-readiness` composite, staged from immutable `github.workflow_sha` into .github/firestore-workflow after the runtime-source checkout and bound to the exact checked source SHA before any image build, push, or rollout. It `--check-only`s against `RUNTIME_GCP_PROJECT_ID` with `GCP_FIRESTORE_READONLY_CREDENTIALS` (set in both `development` and `prod` Environments) in an isolated gcloud config; JIT QA lanes use `env.QA_PROJECT`/`env.QA_FIRESTORE_DATABASE` instead. `gcp_backend_listen_helm.yml` runs the gate only when its explicit-tag path resolves `env.BACKEND_LISTEN_SOURCE_SHA`; kept-tag and rollback lanes replay an image already served in the same environment, the index pipeline is create-only, and rollback must not gain new failure modes. A failed gate may upload only a locally revalidated, bounded, redacted proposal. Index writes belong solely to `gcp_firestore_indexes.yml`, which keeps its own `firestore-schema-${environment}` lock and the typed `APPLY_FIRESTORE_INDEXES` confirmation. Detail: `agent-docs/firestore-queries-and-indexes.md`.\n- When editing workflows, keep `actionlint` coverage in CI so YAML and GitHub expression mistakes fail before merge.\n",".cursor/rules/arc.md":"# Rules Skill\n\nPath: `.cursor/rules/arc.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/arc.md)",".cursor/rules/backend-imports.mdc":"# Cursor Rule: backend-imports\n\nPath: `.cursor/rules/backend-imports.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-imports.mdc)",".cursor/rules/backend-testing.mdc":"# Cursor Rule: backend-testing\n\nPath: `.cursor/rules/backend-testing.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-testing.mdc)",".cursor/rules/common-mistakes.mdc":"# Cursor Rule: common-mistakes\n\nPath: `.cursor/rules/common-mistakes.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/common-mistakes.mdc)",".cursor/rules/flutter-localization.mdc":"# Cursor Rule: flutter-localization\n\nPath: `.cursor/rules/flutter-localization.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/flutter-localization.mdc)",".cursor/rules/formatting.mdc":"# Cursor Rule: formatting\n\nPath: `.cursor/rules/formatting.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/formatting.mdc)","plugins/chatgpt/gpt_schema.example.yaml":"# Chatgpt Skill\n\nPath: `plugins/chatgpt/gpt_schema.example.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/chatgpt/gpt_schema.example.yaml)","plugins/composio/README.md":"# composio Documentation\n\nPath: `plugins/composio/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/composio/README.md)","plugins/hume-ai/README.md":"# hume-ai Documentation\n\nPath: `plugins/hume-ai/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/README.md)","plugins/hume-ai/emotion_config.json":"# Hume-ai Skill\n\nPath: `plugins/hume-ai/emotion_config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/emotion_config.json)","plugins/hume-ai/render.yaml":"# Hume-ai Skill\n\nPath: `plugins/hume-ai/render.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/render.yaml)","plugins/omi-arxiv-app/README.md":"# omi-arxiv-app Documentation\n\nPath: `plugins/omi-arxiv-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-arxiv-app/README.md)","plugins/omi-citybikes-app/README.md":"# omi-citybikes-app Documentation\n\nPath: `plugins/omi-citybikes-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-citybikes-app/README.md)","plugins/omi-clickup-app/README.md":"# omi-clickup-app Documentation\n\nPath: `plugins/omi-clickup-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-clickup-app/README.md)","plugins/omi-coingecko-crypto-app/README.md":"# omi-coingecko-crypto-app Documentation\n\nPath: `plugins/omi-coingecko-crypto-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-coingecko-crypto-app/README.md)","plugins/omi-crossref-app/README.md":"# omi-crossref-app Documentation\n\nPath: `plugins/omi-crossref-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-crossref-app/README.md)","plugins/omi-frankfurter-app/README.md":"# omi-frankfurter-app Documentation\n\nPath: `plugins/omi-frankfurter-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-frankfurter-app/README.md)","plugins/omi-github-app/COMPARISON.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/COMPARISON.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/COMPARISON.md)","plugins/omi-github-app/QUICKSTART.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/QUICKSTART.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/QUICKSTART.md)","plugins/omi-github-app/README.md":"# omi-github-app Documentation\n\nPath: `plugins/omi-github-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/README.md)","plugins/omi-github-app/SETUP_COMPLETE.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/SETUP_COMPLETE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/SETUP_COMPLETE.md)","plugins/omi-github-app/TEST_CHANGE.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/TEST_CHANGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/TEST_CHANGE.md)","plugins/omi-github-app/THEME_UPDATE.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/THEME_UPDATE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/THEME_UPDATE.md)","plugins/omi-google-calendar-app/README.md":"# omi-google-calendar-app Documentation\n\nPath: `plugins/omi-google-calendar-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-google-calendar-app/README.md)","plugins/omi-hacker-news-app/README.md":"# omi-hacker-news-app Documentation\n\nPath: `plugins/omi-hacker-news-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hacker-news-app/README.md)","plugins/omi-hermes-agent-app/README.md":"# omi-hermes-agent-app Documentation\n\nPath: `plugins/omi-hermes-agent-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hermes-agent-app/README.md)","plugins/omi-hive-app/README.md":"# omi-hive-app Documentation\n\nPath: `plugins/omi-hive-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hive-app/README.md)","plugins/omi-launch-library-app/README.md":"# omi-launch-library-app Documentation\n\nPath: `plugins/omi-launch-library-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-launch-library-app/README.md)","plugins/omi-linear-app/README.md":"# omi-linear-app Documentation\n\nPath: `plugins/omi-linear-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-linear-app/README.md)","plugins/omi-ms365-app/README.md":"# omi-ms365-app Documentation\n\nPath: `plugins/omi-ms365-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/README.md)","plugins/omi-ms365-app/omi-tools.json":"# Omi-ms365-app Skill\n\nPath: `plugins/omi-ms365-app/omi-tools.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/omi-tools.json)","plugins/omi-nobel-prize-app/README.md":"# omi-nobel-prize-app Documentation\n\nPath: `plugins/omi-nobel-prize-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-nobel-prize-app/README.md)","plugins/omi-notion-app/README.md":"# omi-notion-app Documentation\n\nPath: `plugins/omi-notion-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-notion-app/README.md)","plugins/omi-open-library-app/README.md":"# omi-open-library-app Documentation\n\nPath: `plugins/omi-open-library-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-library-app/README.md)","plugins/omi-open-meteo-app/README.md":"# omi-open-meteo-app Documentation\n\nPath: `plugins/omi-open-meteo-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-meteo-app/README.md)","plugins/omi-openfoodfacts-app/README.md":"# omi-openfoodfacts-app Documentation\n\nPath: `plugins/omi-openfoodfacts-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-openfoodfacts-app/README.md)","plugins/omi-plugin-sdk/README.md":"# omi-plugin-sdk Documentation\n\nPath: `plugins/omi-plugin-sdk/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-plugin-sdk/README.md)","plugins/omi-public-holidays-app/README.md":"# omi-public-holidays-app Documentation\n\nPath: `plugins/omi-public-holidays-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-public-holidays-app/README.md)","plugins/omi-pubmed-app/README.md":"# omi-pubmed-app Documentation\n\nPath: `plugins/omi-pubmed-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-pubmed-app/README.md)","plugins/omi-semantic-scholar-app/README.md":"# omi-semantic-scholar-app Documentation\n\nPath: `plugins/omi-semantic-scholar-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-semantic-scholar-app/README.md)","plugins/omi-shipbob-app/README.md":"# omi-shipbob-app Documentation\n\nPath: `plugins/omi-shipbob-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shipbob-app/README.md)","plugins/omi-shopify-app/README.md":"# omi-shopify-app Documentation\n\nPath: `plugins/omi-shopify-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shopify-app/README.md)","plugins/omi-slack-app/OMI_SETUP.md":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/OMI_SETUP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/OMI_SETUP.md)","plugins/omi-slack-app/README.md":"# omi-slack-app Documentation\n\nPath: `plugins/omi-slack-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/README.md)","plugins/omi-slack-app/SETUP.md":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/SETUP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/SETUP.md)","plugins/omi-slack-app/TOKEN_DEBUG.md":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/TOKEN_DEBUG.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/TOKEN_DEBUG.md)","plugins/omi-stack-overflow-app/README.md":"# omi-stack-overflow-app Documentation\n\nPath: `plugins/omi-stack-overflow-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-stack-overflow-app/README.md)","plugins/omi-twitter-app/README.md":"# omi-twitter-app Documentation\n\nPath: `plugins/omi-twitter-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-app/README.md)","plugins/omi-twitter-chat-tools-app/README.md":"# omi-twitter-chat-tools-app Documentation\n\nPath: `plugins/omi-twitter-chat-tools-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-chat-tools-app/README.md)","plugins/omi-usgs-earthquake-app/README.md":"# omi-usgs-earthquake-app Documentation\n\nPath: `plugins/omi-usgs-earthquake-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-usgs-earthquake-app/README.md)","plugins/omi-whoop-app/README.md":"# omi-whoop-app Documentation\n\nPath: `plugins/omi-whoop-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-whoop-app/README.md)","plugins/omi-wikipedia-app/README.md":"# omi-wikipedia-app Documentation\n\nPath: `plugins/omi-wikipedia-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-wikipedia-app/README.md)","plugins/uber_call/README.md":"# uber_call Documentation\n\nPath: `plugins/uber_call/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/uber_call/README.md)","backend/evals/dream_context/README.md":"# Dream_context Skill\n\nPath: `backend/evals/dream_context/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_context/README.md)","backend/evals/dream_reasoning/README.md":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/README.md)","backend/evals/dream_reasoning/fixture.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/fixture.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/fixture.json)","backend/evals/dream_reasoning/guard-fixtures.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/guard-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/guard-fixtures.json)","backend/evals/dream_reasoning/quality-fixtures.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-fixtures.json)","backend/evals/dream_reasoning/quality-results.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-results.json)","backend/evals/dream_reasoning/results.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/results.json)","backend/evals/dream_triage/README.md":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/README.md)","backend/evals/dream_triage/fixtures.json":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/fixtures.json)","backend/evals/dream_triage/quality-results.json":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/quality-results.json)","backend/evals/dream_triage/results.json":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/results.json)","backend/evals/dream_verifier/LABELING.v2.md":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/LABELING.v2.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/LABELING.v2.md)","backend/evals/dream_verifier/README.md":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/README.md)","backend/evals/dream_verifier/manifest.json":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.json)","backend/evals/dream_verifier/manifest.v2.json":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.v2.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.v2.json)","plugins/import/manual-import/README.md":"# manual-import Documentation\n\nPath: `plugins/import/manual-import/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/import/manual-import/README.md)","plugins/instructions/SOS/README.md":"# SOS Documentation\n\nPath: `plugins/instructions/SOS/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/SOS/README.md)","plugins/instructions/ahda/README.md":"# ahda Documentation\n\nPath: `plugins/instructions/ahda/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/ahda/README.md)","plugins/instructions/bookwatch/README.md":"# bookwatch Documentation\n\nPath: `plugins/instructions/bookwatch/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/bookwatch/README.md)","plugins/instructions/gcal/README.md":"# gcal Documentation\n\nPath: `plugins/instructions/gcal/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gcal/README.md)","plugins/instructions/gdrive/README.md":"# gdrive Documentation\n\nPath: `plugins/instructions/gdrive/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gdrive/README.md)","plugins/instructions/heavenorhell/README.md":"# heavenorhell Documentation\n\nPath: `plugins/instructions/heavenorhell/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/heavenorhell/README.md)","plugins/instructions/map-notes/README.md":"# map-notes Documentation\n\nPath: `plugins/instructions/map-notes/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/map-notes/README.md)","plugins/instructions/mentor_01/README.md":"# mentor_01 Documentation\n\nPath: `plugins/instructions/mentor_01/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/mentor_01/README.md)","plugins/instructions/multion-amazon/README.md":"# multion-amazon Documentation\n\nPath: `plugins/instructions/multion-amazon/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/multion-amazon/README.md)","plugins/instructions/notion-crm/README.md":"# notion-crm Documentation\n\nPath: `plugins/instructions/notion-crm/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/notion-crm/README.md)","plugins/instructions/reflect/README.md":"# reflect Documentation\n\nPath: `plugins/instructions/reflect/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/reflect/README.md)","plugins/instructions/zapier/README.md":"# zapier Documentation\n\nPath: `plugins/instructions/zapier/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/zapier/README.md)","plugins/zapier/omi-zap/package-lock.json":"# Omi-zap Skill\n\nPath: `plugins/zapier/omi-zap/package-lock.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package-lock.json)","plugins/zapier/omi-zap/package.json":"# Omi-zap Skill\n\nPath: `plugins/zapier/omi-zap/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package.json)","web/packages/note-shared/README.md":"# note-shared Documentation\n\nPath: `web/packages/note-shared/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/README.md)","web/packages/note-shared/package.json":"# Note-shared Skill\n\nPath: `web/packages/note-shared/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/package.json)","desktop/macos/agent/evals/realtime-routing-cases.json":"# Evals Skill\n\nPath: `desktop/macos/agent/evals/realtime-routing-cases.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/desktop/macos/agent/evals/realtime-routing-cases.json)"},"files":{"CLAUDE.md":"# Omi — Agent Instructions\n<!-- Official guidance for writing these files:\n     CLAUDE.md: https://docs.anthropic.com/en/docs/claude-code/memory\n     AGENTS.md: https://developers.openai.com/codex/guides/agents-md\n     Format spec: https://agents.md -->\n\n**Treat `AGENTS.md` as this repository's instruction file.** Every rule for every agent\n(Claude Code, Codex, and any other) lives there. This file exists only because some tools\nlook for `CLAUDE.md` by name — it is the one such pointer in the repo.\n\n- Start with the root [`AGENTS.md`](./AGENTS.md): cross-component rules plus an index.\n- When working inside a directory, also read the **nearest `AGENTS.md` at or above it** —\n  `backend/`, `app/`, `desktop/macos/`, `.github/`, `web/admin/`, `omi/firmware/`.\n  Those carry the detail the root file deliberately omits.\n- Add or change rules in the relevant `AGENTS.md`. **Never add instructions here** — this\n  file stays a pointer so there is only ever one source of truth to maintain.\n\n@AGENTS.md\n","AGENTS.md":"# Omi agent guide\n\nRead the guide for the area you are changing. `CLAUDE.md` points here.\n\n## Routes\n\n| Working on | Read |\n|---|---|\n| Backend | `backend/AGENTS.md` |\n| Flutter app | `app/AGENTS.md` |\n| macOS desktop | `desktop/macos/AGENTS.md` |\n| Windows/Linux desktop | `desktop/windows/AGENTS.md` |\n| Web app / admin | `web/app/AGENTS.md` / `web/admin/AGENTS.md` |\n| Firmware | `omi/firmware/AGENTS.md` |\n| CI / deployment workflows | `.github/AGENTS.md` |\n| Public documentation | `docs/AGENTS.md` |\n| Product behavior / UI | `PRODUCT.md`, `product/invariants/`; macOS UI: `desktop/macos/docs/ux-contract.md` |\n| Shared client behavior | `contracts/parity/README.md` |\n| Released-client compatibility | `contracts/client-compat/` |\n| Fallbacks / fail-open paths | `.github/agent-docs/fallback-telemetry.md` |\n| Plan catalog | `.github/agent-docs/plan-catalog.md` |\n| App E2E | `app/e2e/SKILL.md`, `desktop/macos/e2e/SKILL.md` |\n| Cursor Cloud environment | `.cursor/cloud-agent-environment.md` |\n| Contributions / PR requirements | `docs/doc/developer/Contribution.mdx` |\n| Failure-class classification | `product/failure-classes.md`, `scripts/failure-class` |\n| Formatting | `.github/agent-docs/formatting.md` |\n| Agent documentation / new checks | `.github/agent-docs/doc-maintenance.md` |\n\n## Repository conventions\n\n- Use a task worktree based on current `origin/main`. `make setup` installs the repository hooks, including staged-file formatting. Keep Git identity in the user's configuration; temporary test identities use `git -c`.\n- Upstream `main` changes land through PRs with merge commits, never squash or direct pushes.\n- macOS development uses dev or `omi-*` named bundles. Do not stop or replace the production Omi or Omi Beta apps; bundle details and commands live in the macOS guide.\n- Migrate in-tree callers together rather than preserving retired APIs with compatibility wrappers. Released clients follow the compatibility contracts above.\n- Run the component's documented verification and `make preflight`. For PR metadata, start with `scripts/pr-preflight --suggest`, then validate the draft with `scripts/pr-preflight --pr-body-file <file>`. These tools identify required invariant citations and failure-class declarations.\n\nKeep this file a router. Add repository-specific facts at their owning guide; omit generic agent behavior and duplicated procedures. Size budgets live in `.github/scripts/check_agents_md_lean.py`.\n","app/e2e/SKILL.md":"---\nname: mobile-app-flows\ndescription: \"Understand and explore the Omi Flutter mobile app's UI flows, navigation patterns, and widget architecture. Use when developing features, fixing bugs, or verifying changes in app/lib/ Dart files. Provides agent-flutter commands to explore the live app, understand how screens connect, and verify your work.\"\nallowed-tools: Bash, Read, Glob, Grep\n---\n\n# Omi Mobile App — Flows & Exploration\n\nThis skill teaches you the Omi Flutter mobile app's navigation structure, screen architecture, and widget patterns. Use it when developing features (to understand how the app works), fixing bugs (to navigate to the affected screen), or verifying changes (to confirm your code works in the live app).\n\n> **Verifying changes (agents and contributors):** this skill drives a *live*\n> app on an emulator/device and assumes a manually-authenticated session\n> against a real or local backend — it is an exploration and physical-device\n> tool. The canonical fast verification path is the seeded local lane with\n> synthetic auth: `make mobile-verify ARGS=\"fast --paths <changed-file>\"` —\n> no device, no OAuth, loopback fixtures. See\n> [`scripts/dev-harness/MOBILE_VERIFY.md`](../../scripts/dev-harness/MOBILE_VERIFY.md)\n> and [`MOBILE_SESSIONS.md`](../../scripts/dev-harness/MOBILE_SESSIONS.md).\n> Physical-device evidence remains a separately reported lane (SCA-491).\n\n## How to Explore the App\n\nYou can interact with the running app via `agent-flutter` — a CLI that taps widgets, reads the widget tree, and captures screenshots through Flutter's Marionette debug protocol.\n\n### Setup\n```bash\n# 1. Emulator must be running\nadb devices                          # should show emulator-5554\n# If not: sg kvm -c \"$ANDROID_HOME/emulator/emulator -avd omi-dev -no-window -gpu swiftshader_indirect -no-audio -no-boot-anim &\"\n\n# 2. Set system language to English (REQUIRED — non-English IME breaks text input)\nadb shell \"settings put system system_locales en-US\"\nadb shell \"setprop persist.sys.locale en-US\"\n\n# 3. App must be running in debug mode with flutter run stdout captured\ncd app && flutter run -d emulator-5554 --flavor dev > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"VM Service\" line to appear in the log\n\n# 4. Connect agent-flutter (AGENT_FLUTTER_LOG must point to flutter run stdout, NOT logcat)\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json    # see what's on screen\n```\n\n**Prerequisites:**\n- AVD name: `omi-dev` (check: `$ANDROID_HOME/emulator/emulator -list-avds`)\n- KVM access required: user must be in `kvm` group (`sg kvm -c \"...\"` if not in current session)\n- App package: `com.friend.ios.dev` (dev flavor)\n- **System language must be English** — non-English IME breaks `fill` commands\n- **App must be authenticated and connected to the correct backend** (local, dev, or prod — depends on the task)\n- Marionette already integrated: `marionette_flutter: ^0.3.0` in pubspec.yaml\n\n### Setup (iOS physical device)\n\nFull iOS physical-device playbook (setup, driving, verification, troubleshooting): [`IOS_DEVICE_TESTING.md`](./IOS_DEVICE_TESTING.md).\n\nVerified 2026-07-11 on an iPhone XR (iOS 18.7.9), app 1.0.543, prod-flavor debug build, agent-flutter CLI + marionette MCP. iOS Simulator has no BLE — use a physical device for anything beyond onboarding/UI checks (see also iOS Simulator Known Limitations below).\n\n```bash\n# 1. Get the physical device id\nflutter devices\n\n# 2. Run in debug mode with stdout captured — prod flavor (dev flavor can fail codesigning on a\n#    fresh worktree: \"provisioning profile doesn't support the App Groups capability\")\ncd app && flutter run -d <device-id> --flavor prod > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"A Dart VM Service ... is available\" in the log\n\n# 3. Connect agent-flutter — auto-detects the ws URI from the log\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json\n```\n\n**Gotchas:**\n- **Fresh git worktrees need real prod config seeded from the primary checkout** before building the `prod` flavor: copy `app/.env`, `app/lib/firebase_options_prod.dart`, `app/ios/Config/Prod/GoogleService-Info.plist`, `app/lib/env/prod_env.g.dart`. The `test.sh` bootstrap seeds dev-placeholder versions that point at the wrong Firebase project.\n- **adb-backed commands do NOT work on iOS**: `back`, `press x y`, `dismiss`, `text --press`, `text --fill`. Use in-app back buttons (find the top-left `IconButton` ref) and marionette ref presses only.\n- **`fill @ref` can silently no-op on keyless `TextField`s on iOS** — it reports success but the controller stays empty. Verify via the marionette MCP `get_interactive_elements` (shows the `TextEditingController` contents). Durable fix: add a `ValueKey` to the field, hot reload (sheet state survives), then enter text by key.\n- **`snapshot` labels are empty with `marionette_flutter` 0.3.0** on iOS. Primary orientation/assertion tool is `agent-flutter text` (semantic text dump) — assert outcomes by text presence (e.g. the copy snackbar text). Target elements by type + bounds from `snapshot -i`.\n- `agent-flutter screenshot` output path must be under `/tmp`.\n- Check behavior via the run log: `grep -iE 'exception|error' /tmp/omi-flutter.log` after each flow. A `PlatformException` 4001 (Intercom push token, notifications not granted) is benign.\n- iOS terminates the debug connection if the app is backgrounded/locked too long (\"The OS has terminated the Flutter debug connection for being inactive\") — keep the device unlocked; reconnecting requires relaunching `flutter run`.\n- General key guidance (same as Android): prefer `find key \"name\"`; when a control can't be targeted, add a `ValueKey` in source + hot reload rather than fighting coordinates.\n\n### Commands\n\n| Command | Purpose | Example |\n|---------|---------|---------|\n| `snapshot -i --json` | See all interactive widgets with refs, types, bounds | `agent-flutter snapshot -i --json` |\n| `press @ref` | Tap a widget by ref | `agent-flutter press @e3` |\n| `press x y` | Tap by coordinates (ADB input tap) | `agent-flutter press 540 1200` |\n| `press @ref --adb` | Tap by ref using ADB (for stale refs) | `agent-flutter press @e3 --adb` |\n| `dismiss` | Dismiss system dialogs (location, permissions) | `agent-flutter dismiss` |\n| `find type X press` | Find widget by type and tap | `agent-flutter find type button press` |\n| `find text \"X\" press` | Find by visible text and tap | `agent-flutter find text \"Settings\" press` |\n| `find type X --index N press` | Tap Nth match (0-indexed) | `agent-flutter find type switch --index 0 press` |\n| `fill @ref \"text\"` | Type into text field | `agent-flutter fill @e7 \"search\"` |\n| `scroll down/up` | Scroll current view | `agent-flutter scroll down` |\n| `back` | Android back button | `agent-flutter back` |\n| `screenshot PATH` | Capture current screen | `agent-flutter screenshot /tmp/screen.png` |\n\n**Key rules:**\n- Refs go stale frequently (Flutter rebuilds widget tree aggressively) — always re-snapshot before every interaction, not just after mutations.\n- `find type X` is more stable than hardcoded `@ref` numbers.\n- `AGENT_FLUTTER_LOG` must point to `flutter run` stdout (not logcat).\n- After hot restart: `disconnect` → wait 3s → `connect`.\n- Widget text labels are often null — use `type`, `flutterType`, or `bounds` to identify.\n- `back`, `press x y`, `dismiss`, and the `--adb`/`--press`/`--fill` text flags are ADB-backed — Android only. On a physical iOS device use marionette ref-based commands instead (see Setup (iOS physical device) above).\n\n### Recovery\n```bash\n# \"No isolate with Marionette\" → bring app to foreground + reconnect\nadb -s emulator-5554 shell am start -n com.friend.ios.dev/com.friend.ios.MainActivity\nagent-flutter disconnect && agent-flutter connect\n\n# Unhealthy widget tree → hot restart\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## App Navigation Architecture\n\n### Screen Map\n```\nOnboarding (wrapper.dart) — step wizard\n├── 0: Auth (auth.dart) — Google/Apple sign-in\n├── 1: AI Consent\n├── 2: Name (name_widget.dart)\n├── 3: Primary Language (primary_language_widget.dart)\n├── 4: Found Omi (found_omi_widget.dart)\n├── 5: Permissions (permissions_widget.dart)\n├── 6: User Review (user_review_page.dart)\n├── 9: Speech Profile (speech_profile_widget.dart)\n├── 10: Knowledge Graph (knowledge_graph_step.dart)\n└── 11: Complete (complete_screen.dart) → Home\n\nHome (home/page.dart) — main app after auth, 4-slot bottom nav\n├── [\"Ask Omi\" input bar] → Chat (chat/page.dart) — full-width bar above bottom nav, not a tab\n│   ├── Message history, \"Ask anything\" field, AI responses\n│   └── AI-message action row: Copy (\"✨ Message copied to clipboard\" snackbar), thumbs up, thumbs down, Share\n├── [mic in the bar] → Chat with voice auto-start\n├── [battery/record widget, top left] (battery_info_widget.dart)\n│   ├── Device connected → battery pill → Connected Device (home/device.dart); phone icon → Phone Calls\n│   └── No device → Connect → Connect Device page; Record pill → Conversation Capturing (episodes/live_episode_page.dart)\n│       └── Chevron → record options sheet (Phone Mic record / Phone Call)\n├── [settings gear, top right] → Settings sheet (settings_drawer.dart) — present on every slot\n│\n├── [slot 0] Home (home/home_content.dart)\n│   ├── Conversation capture widget, today's tasks widget\n│   ├── Daily Recaps → Daily Summary Detail; \"View All\" → Conversations\n│   ├── Mind Map → Memory Graph (memory_graph_page.dart) (only with ≥3 conversations)\n│   └── Get-started tiles (only with <3 conversations)\n│\n├── [slot 1] Conversations (conversations_page.dart)\n│   ├── Folder tabs (All, Starred, custom folders)\n│   ├── Top-bar extras on this slot: sync icon (when paired/pending), search, calendar date filter\n│   └── Conversation item (GestureDetector row) → Detail (conversation_detail/page.dart)\n│       ├── Transcript, Summary, Action Items tabs, share, audio (search hidden on Action Items tab)\n│       ├── Pull-down menu: Copy Transcript / Copy Summary / Copy ID / Share Audio (if audio) / Link Event / Test Prompt\n│       └── Back via in-app top-left IconButton — not the OS/adb back gesture\n│\n├── [slot 2] Tasks (action_items_page.dart)\n│   ├── Categories: Today, Tomorrow, Later, No Deadline, Overdue\n│   ├── FAB → Create task sheet (action_item_form_sheet.dart)\n│   ├── Task checkboxes, drag-drop reorder, task → goal linking\n│   └── Top-bar extras on this slot: export → Task Integrations, completed toggle\n│\n└── [slot 3] Apps (apps/page.dart) — \"Search apps\" / \"Featured\" (explore_install_page.dart)\n    ├── Popular apps (horizontal scroll)\n    ├── Category sections → Category apps page\n    ├── App item → App Detail (app_detail/app_detail.dart)\n    │   └── Reviews, capabilities, install/enable, Chat button → Chat\n    └── Top-bar \"+\" on this slot → Add App / Add MCP Server\n\nSettings sheet (settings_drawer.dart) — search + close header, then five visual groups: Account ·\nPlan & Usage, Referral Program · Device … Data & Privacy · Help & About, Feedback · Developer Settings.\nEvery top-level row has a ValueKey (`settings_account`, `settings_group_<group>`, or\n`settings_row_<SettingsDestination>` for the direct rows Plan, Referral and Feedback); each page has a\nScaffold key (`settings_page_<page>`); rows on group/Account pages are `settings_row_<SettingsDestination>`\n(plus `settings_row_voiceResponseMode`, `settings_row_transcribeLater`, `settings_row_backgroundMode`,\n`settings_row_name`, `settings_row_email`, `settings_row_userId`, `settings_row_version`).\n├── Account [settings_account] — shows the name, email as subtitle → Account page (profile.dart, ProfilePage)\n│   ├── Name → Change name dialog; Email (read-only)\n│   ├── User ID (tap copies)\n│   └── Sign Out → Confirmation dialog; Delete Account (delete_account.dart)\n├── Plan & Usage [settings_row_planAndUsage] (usage_page.dart) — \"Pro\" value when paid\n├── Referral Program [settings_row_referral] (referral_page.dart) — NEW tag\n├── Device [settings_group_device] → settings_groups.dart\n│   ├── Device Settings (device_settings.dart) (only when a device is connected)\n│   ├── Offline Sync (sync_page.dart / auto_sync_page.dart)\n│   ├── Phone Calls (phone_call_settings_page.dart)\n│   └── Permissions (permissions_page.dart) — microphone, Bluetooth, notifications\n├── Recording & Transcription [settings_group_recording] → settings_groups.dart\n│   ├── Transcription (transcription_settings_page.dart) — provider value; Language; Custom Vocabulary\n│   ├── Voice Profile → guided introduction (onboarding/speech_profile_widget.dart); Identifying Others (people.dart)\n│   ├── Voice Response (picker sheet); Conversation Timeout (picker)\n│   └── Recording (BETA): Transcribe Later switch; Background Mode switch (Android only)\n├── Notifications & Display [settings_group_notifications] → settings_groups.dart\n│   └── Notifications (notifications_settings_page.dart); Home Screen; Conversation Display\n├── Integrations [settings_group_integrations] (integrations_page.dart) — BETA; also opens from conversation detail\n├── Data & Privacy [settings_group_privacy] → settings_groups.dart\n│   ├── Data Protection (data_privacy_page.dart); Memories (memories/page.dart)\n│   └── Export All Data (spinner while running); Import Data (import_history_page.dart)\n├── Help & About [settings_group_help] → settings_groups.dart\n│   ├── Help Center → help.omi.me (Intercom platforms only)\n│   ├── What's New → Changelog sheet\n│   └── Version + copy button (iOS/Android)\n├── Feedback / Report a bug [settings_row_feedback] → feedback.omi.me (Intercom platforms only)\n└── Developer Settings [settings_group_developer] (developer.dart)\n(Settings search finds every row above and opens the page that holds it.)\n\nTranscription Settings (transcription_settings_page.dart) — not in settings drawer; reached from\nPlan & Usage, Developer Settings, or the Plans sheet\n├── Source toggle: Omi Cloud vs Custom STT\n├── Provider selector, API key, model config\n└── Advanced JSON editors, logs viewer\n\nPhone Calls (phone_calls_page.dart) — from battery-widget phone icon, record options, or get-started tile\n├── Phone Setup Intro when no verified numbers\n├── Contacts / Keypad tabs; call → Active Call page\n└── Gear → Phone Call Settings (phone_call_settings_page.dart) — verified numbers list, delete button\n\nPlans sheet (plans_sheet.dart) — from Plan & Usage, chat quota-exceeded, phone-calls upsell\n\nPersona Profile (persona_profile.dart) — AI clone management\n├── Avatar (100x100), name with verified badge\n├── Share Public Link button\n├── Make Public toggle\n└── 10 social link rows (omi, Twitter active; others Coming Soon)\n    └── Twitter → Social Handle Entry → Verify Identity → Clone Success\n\nConnected Device (home/device.dart) — requires BLE\n├── Device name, connection status, battery\n├── Actions: Firmware Update, SD Card Sync, Disconnect, Unpair\n└── Device info: Product, Model, Manufacturer, Firmware, ID, Serial\n\nVoice Profile — guided introduction (onboarding/speech_profile_widget.dart, #14514)\n├── Four sentence starters, phone mic; Start Speaking or Skip (whole step), then Next / Pause / Skip Question while recording\n└── Review: edit or uncheck answers, then Save and Finish (voice, memories, goal)\n```\n\n### Widget Patterns\n\n**Bottom navigation bar:**\n- Android: 4 `InkWell` widgets at `bounds.y > 780`, sorted left-to-right by `bounds.x`\n- Detect with: `snapshot -i --json` → filter `flutterType == 'InkWell'` and `bounds.y > 780`\n- Navigate home: press the leftmost one\n- iOS (verified 2026-07-11, iPhone XR, 414pt-wide screen): 4 slots at y≈816, x=20/114/207/300, each w=94.\n  Left to right: slot 0 = Home, slot 1 = Conversations (folder tabs All/Starred/…), slot 2 = Tasks,\n  slot 3 = Apps marketplace (\"Search apps\" / \"Featured\")\n\n**Chat entry point (not a bottom-nav tab):**\n- Open chat by tapping the \"Ask Omi\" input bar on the home screen — a full-width gesture\n  element directly above the bottom nav (~y=756, w≈382 on a 414pt-wide screen; verified iOS 2026-07-11)\n\n**Settings gear:**\n- Android: rightmost `button` widget in the top bar; detect by sorting buttons by `bounds.x` descending, take first\n- iOS (verified 2026-07-11): single top-right icon on home at ~x=362, y=58 → Settings sheet (Account,\n  Plan & Usage, Referral Program, Device, Recording & Transcription, Notifications & Display,\n  Integrations, Data & Privacy, Help & About, Feedback, Developer Settings)\n\n**Settings rows:**\n- `gesture` widgets with `bounds.width > 300`\n- Prefer the ValueKeys above (`settings_account`, `settings_group_*`, `settings_row_*`) over positions\n\n**Switch toggles:**\n- Type `switch` in snapshots\n- Press to toggle ON/OFF (no separate ON/OFF actions)\n\n**Bottom sheet pickers:**\n- Open when you press a settings row\n- Language items appear as `gesture` rows with `bounds.y > 380`\n- Many items — use scroll if needed\n\n**Conversation feed rows (iOS, verified 2026-07-11):**\n- `GestureDetector` widgets, h≈84–96; detail opens on tap\n- Go back with the in-app top-left `IconButton` (x=8, y=56, w=40) — not the OS/adb back gesture\n\n**Chat AI-message actions (iOS, verified 2026-07-11):**\n- Four small `InkWell`s (w≈12–14) under each AI message at x≈22/54/88/122; the first is copy —\n  pressing it shows the \"✨ Message copied to clipboard\" snackbar\n\n### Changing Locale\n```bash\nDEVICE=emulator-5554; APP_PKG=com.friend.ios.dev\n\n# Read current\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" | grep app_locale\n\n# Change to Spanish, hot restart to apply\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" > /tmp/prefs.xml\nsed -i 's|flutter.app_locale\">[^<]*|flutter.app_locale\">es|' /tmp/prefs.xml\nadb -s $DEVICE push /tmp/prefs.xml /data/local/tmp/FlutterSharedPreferences.xml\nadb -s $DEVICE shell \"run-as $APP_PKG cp /data/local/tmp/FlutterSharedPreferences.xml shared_prefs/FlutterSharedPreferences.xml\"\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## Prerequisites Reference\n\nEvery flow lists `prerequisites:` — conditions that MUST be true before running. These describe the **real user state** — no bypasses, no shortcuts. Test the app the way users experience it.\n\n| Prerequisite | What it means | How to achieve (Android) | How to achieve (iOS) |\n|-------------|---------------|--------------------------|----------------------|\n| `auth_ready` | User completed sign-in (Google or Apple), app shows home screen | Run `bash setup.sh android` → launch app → complete Google Sign-In flow → complete onboarding | Run `bash setup.sh ios` → launch on simulator or device → complete Google/Apple Sign-In → complete onboarding |\n| `signed_out` | Fresh app, user NOT signed in, shows Get Started screen | Uninstall + reinstall, or clear app data via Settings → Apps → Omi → Clear Data | Delete app from simulator/device and reinstall |\n| `microphone_permission` | App has mic permission granted | When app requests mic permission during use, tap \"Allow\". Or pre-grant: `adb shell pm grant com.friend.ios.dev android.permission.RECORD_AUDIO` | When app requests mic permission, tap \"Allow\" in the iOS permission dialog |\n| `ble_on` | Bluetooth enabled on device | Enable Bluetooth in device Settings → Connected Devices. **Emulators/simulators do not support BLE** — requires physical device | Enable Bluetooth in device Settings. **iOS Simulator has no BLE** — requires physical iPhone |\n| `omi_device_connected` | Omi hardware paired and connected via BLE | Power on Omi device within BLE range → app auto-discovers on home screen → tap Connect. **Physical device only** | Same — power on Omi, app discovers it. **Physical iPhone only** |\n| `phone_number_verified` | Phone number added and verified in settings | Settings → Phone Calls → add phone number → receive SMS → enter code. Requires real phone number | Same flow — requires real phone number that receives SMS |\n| `developer_settings_enabled` | Developer Settings screen is open | Settings drawer → scroll down → tap \"Developer Settings\" (visible to all users) | Same navigation path |\n| `adb_access` | Shell access for locale/prefs manipulation (Android only) | Debug build + `adb` in PATH. Verify: `adb shell run-as com.friend.ios.dev ls shared_prefs/` | Not applicable — iOS equivalent uses `xcrun simctl` for simulator or Xcode for device |\n\n### Prerequisite dependency chain\n```\nsigned_out ─── (fresh install, no prior state)\n\nauth_ready ─── launch app → sign in with Google/Apple → complete onboarding\n  │             (this is the REAL user flow — no bypasses)\n  ├── microphone_permission  (grant when prompted, or pre-grant via platform tools)\n  ├── developer_settings_enabled  (navigate in-app to Settings → Developer Settings)\n  ├── phone_number_verified  (in-app SMS verification — manual step)\n  ├── ble_on + omi_device_connected  (physical device + physical Omi hardware)\n  └── adb_access  (Android debug builds only — for locale manipulation)\n```\n\n### Platform setup\n```bash\n# Android: setup + build + launch\ncd app && bash setup.sh android\n# → completes: keystore, Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google when app launches, complete onboarding\n\n# iOS: setup + build + launch\ncd app && bash setup.sh ios\n# → completes: Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google/Apple when app launches, complete onboarding\n```\n\n**Important:** Both platforms require completing the real sign-in and onboarding flows. Never bypass auth or onboarding — these are user-facing flows that must work correctly.\n\n## YAML Flow Schema (v2)\n\nEach flow file uses schema v2:\n```yaml\nversion: 2\nname: string          # Flow identifier\ndescription: string   # What this flow covers\napp: com.friend.ios.dev\nevidence:\n  video: true\ncovers: [string]      # Source files this flow exercises\npreconditions: [string]  # Conditions required — see Prerequisites Reference above\nsteps: [Step]         # Ordered list of actions\n```\n\nEach step has these fields:\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `id` | yes | Step ID (S1, S2, ...) |\n| `name` | yes | Short description |\n| `do` | yes | Instructions for executing |\n| `verify` | no | `true` = must be verified even during snapshot replay |\n| `expect` | no | Assertions (text_visible, interactive_count) |\n| `evidence` | no | Screenshot filename (`.webp` preferred) |\n| `note` | no | Edge case notes |\n\nSupported `expect` kinds:\n\n| Kind | Fields | Example |\n|------|--------|---------|\n| `text_visible` | `values: [string]` | Verify specific text appears |\n| `interactive_count` | `min: number` | Verify minimum interactive widgets |\n\n## Navigation Graph (flow-walker verified)\n\nReal navigation edges verified by flow-walker run11 on Pixel 7a (26 screens, 44 edges, depth 3). Screen names mapped from fingerprint IDs to semantic names.\n\nNote: run11 predates the current bottom nav — its tab labels below reflect the old layout (Conversations/Action Items/Memories/Apps as tabs). Today slot 0 is Home, Tasks is slot 2, Apps is slot 3, and Memories lives under Settings — see Screen Map above. The per-screen element counts and edges remain useful.\n\n```\nHome (24 elements: 17 gesture, 3 icon, 4 inkwell)\n├── [tab 0] Conversations (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   ├── Conversation Detail (27 el: 18 gesture, 4 icon, 4 inkwell, 1 textformfield)\n│   │   └── Notifications Settings (14 el: 10 gesture, 1 icon, 1 inkwell, 1 switch, 1 textbutton)\n│   ├── Settings Drawer (13 el: 13 gesture)\n│   │   ├── Language Settings (13 el: 10 gesture, 1 icon, 1 switch, 1 textbutton)\n│   │   │   ├── Sub-detail (5 el: 4 gesture, 1 icon)\n│   │   │   ├── Sub-settings (7 el: 1 elevated, 5 gesture, 1 icon)\n│   │   │   │   ├── Settings Confirmation (3 el: 1 gesture, 2 textbutton)\n│   │   │   │   └── Settings Form (16 el: 1 elevated, 9 gesture, 3 icon, 1 inkwell, 2 textfield)\n│   │   │   └── Filter Sheet (13 el: 6 gesture, 5 inkwell, 2 textbutton)\n│   │   └── Back → Home\n│   ├── Sub-detail (2 el: gesture, icon)\n│   ├── Sub-page (3 el: 2 gesture, 1 icon)\n│   └── Back → Home\n├── [tab 1] Action Items (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   └── Notifications Settings (same as above)\n├── [tab 2] Memories (25 el: 15 gesture, 4 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Memory Detail (9 el: 5 gesture, 2 icon, 2 inkwell)\n│   ├── Category Page (7 el: 4 gesture, 3 inkwell)\n│   └── Memory Search (18 el: 12 gesture, 4 icon, 1 inkwell, 1 textfield)\n├── [tab 3] Apps (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Confirmation Dialog (4 el: 1 elevated, 1 gesture, 2 icon)\n│   ├── Apps Sub-page (14 el: 3 elevated, 6 gesture, 1 icon, 4 inkwell)\n│   ├── App Form (5 el: 1 elevated, 2 gesture, 1 icon, 1 textfield)\n│   └── Back → Home\n├── [Ask Omi] Chat (22 el: 19 gesture, 1 textbutton, 2 textfield)\n│   ├── Delete Account (6 el: 1 checkbox, 4 gesture, 1 textfield)\n│   └── Back → Home\n├── [record] Capturing (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n├── [settings] Settings Drawer (13 el: 13 gesture)\n├── [gesture] Sign Up (5 el: 1 elevated, 1 gesture, 3 textfield)\n├── [gesture] Confirmation (2 el: gesture, textbutton)\n└── [gesture] Conversations Alt (17 el: 10 gesture, 2 icon, 4 inkwell, 1 textfield)\n```\n\n### Screen Fingerprint Mapping\n\nFor flow-walker compatibility — maps auto-generated fingerprint names to semantic flow names.\n\n| Fingerprint ID | Element Count | Semantic Name | Flow File |\n|----------------|---------------|---------------|-----------|\n| `92a58e321064` | 24 | Home | home-navigation, tab-navigation |\n| `113cc0d4f097` | 17 | Conversations (FAB) | conversations |\n| `59c8c3dba1aa` | 17 | Conversations Alt | conversations |\n| `2108e83364c2` | 19 | Apps | apps |\n| `2b9f6e0d087b` | 25 | Memories | memories |\n| `13f37b3018c5` | 22 | Chat | chat |\n| `49f053d00b0f` | 27 | Conversation Detail | conversation-detail |\n| `011f78c61152` | 14 | Notifications Settings | settings-notifications |\n| `1b40a175a5fd` | 13 | Language Settings | language-change |\n| `4b666d4ec8e3` | 13 | Settings Drawer | home-navigation |\n| `0f852433acc6` | 6 | Delete Account | delete-account |\n| `a74411638a9c` | 5 | Sign Up Form | onboarding |\n| `0ba3aef7f00f` | 2 | Confirmation Dialog | — |\n| `8407c23c9698` | 2 | Sub-detail | — |\n| `9580b20fb6e0` | 3 | Sub-page | — |\n| `dacfbda46c10` | 5 | Sub-detail (settings) | — |\n| `e8ea54a72f46` | 7 | Sub-settings | — |\n| `0766ec584fb4` | 13 | Filter Sheet | apps |\n| `f94e0140ed7e` | 4 | Confirmation Dialog | — |\n| `88992c72a759` | 14 | Apps Sub-page | app-detail |\n| `f757010c13b3` | 5 | App Form | apps |\n| `485a8ce57ebc` | 9 | Memory Detail | memories |\n| `a6983264d893` | 7 | Category Page | memories |\n| `4450e0d3044e` | 18 | Memory Search | memories |\n| `35c0d8ed0daa` | 3 | Settings Confirmation | settings-profile |\n| `a864d8b70edf` | 16 | Settings Form | settings-profile |\n\n## Verified Flows (v2 flow-walker)\n\nOnly flows that have been executed, verified, and pushed through the v2 flow-walker pipeline are listed here. Each has a `.snapshot.json` for fast replay.\n\n| Flow | Steps | Prerequisites | What it tests | Reports |\n|------|-------|--------------|---------------|---------|\n| `flows/login.yaml` | 5 | signed_out | Auth screen → Google Sign-In → consent → OAuth → home | [normal](https://flow-walker.beastoin.workers.dev/runs/hao5vbBsBI.html), [replay](https://flow-walker.beastoin.workers.dev/runs/-aeMGlV88w.html) |\n| `flows/onboarding.yaml` | 9 | signed_out + auth | Name → Language → Found Omi → Permissions → Review → Speech → Knowledge → Complete → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/UHqa8Fysj2.html), [replay](https://flow-walker.beastoin.workers.dev/runs/B9Py6uoJaO.html) |\n| `flows/logout.yaml` | 5 | auth_ready | Home → Settings → Sign Out → Confirm → Auth screen | [normal](https://flow-walker.beastoin.workers.dev/runs/SH4MXH9gkv.html), [replay](https://flow-walker.beastoin.workers.dev/runs/NcZlCUW0W7.html) |\n| `flows/ask-omi-chat.yaml` | 9 | auth_ready | Home → Ask Omi → Chat page → Type message → AI response → Chat apps drawer → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/O5h8bR6izW.html) |\n\nWhen you modify a Dart file, check if any flow's `covers:` includes it. If so, that flow describes the user journey your change affects — use it to understand context and verify your work.\n\n## Verification & Evidence\n\nAfter making changes, verify them in the live app:\n1. Navigate to the affected screen using the commands above\n2. Check that your changes appear (snapshot, screenshot)\n3. Test interactions (press buttons, fill fields, scroll)\n4. Capture evidence: `agent-flutter screenshot /tmp/evidence.png`\n5. Generate video: `ffmpeg -framerate 1 -pattern_type glob -i '/tmp/e2e-*.png' -vf \"scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1\" -c:v libx264 -pix_fmt yuv420p /tmp/report.mp4`\n\n## Visual audit (before/after screenshots without a device)\n\n`app/scripts/visual_audit.sh` screenshots registered screen states at two revisions and pairs them\nin one gallery. It pumps **production widgets** in `flutter-tester` with synthetic local state: the\napp's own fonts (FontManifest plus Roboto from the pinned SDK), `buildOmiTheme()`, 390×844 logical\npx at 2×, and a loopback-only network guard over the hermetic fixture backend. No login, simulator,\nsigning, customer data or external API.\n\n**Use it** for layout, copy, grouping and visual-regression review of a UI change, and for the\nbefore/after images a UI PR description needs. **Use agent-flutter / flow-walker (above) instead**\nfor anything a headless capture cannot show: native keyboard, status bar and safe areas, system\npermission dialogs, BLE, audio, real network timing, screen readers, or a claim about the installed\napp. The captures are evidence of a widget rendering, not of device behavior.\n\n```bash\napp/scripts/visual_audit.sh --list                                  # scenario ids, per suite\napp/scripts/visual_audit.sh --base origin/main --head HEAD          # every scenario, both sides\napp/scripts/visual_audit.sh --base <sha> --head <sha> --only settings-sheet,settings-device\napp/scripts/visual_audit.sh --head WORKTREE --only settings-help    # this checkout, uncommitted, one side\n```\n\nEach revision is checked out in a temporary detached worktree under `$OMI_WORKTREES` (default: the\nsystem temp dir), prepared like CI (generated env files, `pub get`, `build_runner`) and removed on\nexit. The harness and scenarios always come from the checkout you run the command in, copied over\nboth sides. The current suite (`registry.dart`) compiles against current main; a revision from\nbefore a large UI change is captured with a compat suite (`compat/<name>/`, chosen by its `UNTIL`\ncommit; see `compat/README.md`) that pumps the equivalent old page under the same id, so the old\nProfile page pairs with Account. A page only one side has is shown as \"did not exist\" there. The\noutput directory (`--out`,\ndefault a new temp dir; it must be empty and outside the repository) holds `before/` and `after/`\nPNGs named `<id>.png` or `<id>-<step>.png`, per-side `capture.log`, `INDEX.md` with both SHAs and\ncapture times, and a static `gallery.html`. A scenario that throws on one side is reported as failed\nthere and the rest still capture. **Never commit screenshots.**\n\n**Add a scenario** in `app/integration_test/visual_audit/scenarios/<area>.dart` (new areas are\nspread into `registry.dart`). Put `id` first; it is lowercase-hyphenated, starts with the area, and\nnames the PNGs:\n\n```dart\nAuditScenario(\n  id: 'settings-device',\n  title: 'Device group, device connected',\n  page: 'lib/pages/settings/settings_groups.dart (DeviceGroupPage)',\n  state: 'Signed-in fixture account; a device connected',\n  run: (a) async {\n    await a.pump(const DeviceGroupPage(), providers: [\n      ChangeNotifierProvider<DeviceProvider>.value(value: AuditDeviceProvider(connected: true)),\n    ]);\n    await a.scrollSeries('Open Device from the Settings sheet');\n  },\n),\n```\n\n`AuditRun` (`a`) gives `pump` (the production theme and a broad inert provider roster from\n`fakes.dart`; your `providers` win), `pumpHost` (opens a sheet or dialog from a neutral host),\n`tap`/`longPress`/`enterText` (each settles), `shot(action, step:)`, `scrollSeries(action)` (top,\nevery 700 px, exact bottom) and `server` (the running fixture backend: seed conversations, set\n`assistantReplyText`, `failNext`). Seed state through providers and the fixture backend, find\nwidgets by stable `Key`s, and keep `expect(...)` checks that prove the intended state rendered.\n`app/test/visual_audit/visual_audit_smoke_test.dart` renders every registered scenario in the\nordinary `app/test.sh` suite without writing images, so a scenario that breaks fails CI.\n\n## Decision Tree\n\n| Problem | Solution |\n|---------|----------|\n| Widget not found | Re-snapshot, try scrolling, check if on wrong screen, match by bounds position |\n| Ref expired between commands | Use `press x y` with coordinates from last snapshot bounds, or `press @ref --adb` |\n| System dialog blocking (location, permissions) | `agent-flutter dismiss` — detects and dismisses via ADB |\n| \"No isolate with Marionette\" | ADB foreground + disconnect + reconnect |\n| Snapshot returns 0 interactive elements | Marionette lost widget tree — `disconnect` then `connect` to re-attach |\n| Bottom nav tabs not detected | `back` until nav bar appears, filter InkWell y > 780 |\n| Hot restart breaks connection | Wait 3s → disconnect → connect |\n| Text labels null | Match by `type`, `flutterType`, or `bounds` — Marionette doesn't extract child text |\n| Non-English IME breaks text input | Set system locale to English: `adb shell \"settings put system system_locales en-US\"` |\n\n## iOS Simulator Known Limitations\n\nFor a physical iOS device (has BLE, avoids most limitations below), see **Setup (iOS physical device)** under How to Explore the App.\n\n| Issue | Workaround |\n|-------|-----------|\n| ASWebAuthenticationSession blocks Google Sign-In (system dialog, not automatable) | Use `FirebaseAuth.instance.signInWithCustomToken(token)` via VM Service evaluate |\n| `agent-flutter scroll` fails (uses ADB/transport.swipe, Simulator window disconnected) | Scroll via VM Service Dart evaluation: `pos.jumpTo()` on Scrollable widgets |\n| Simulator.app window shows Home Screen (not app) when booted via SSH user | Use `simctl screenshot` for true device state; use Marionette/VM Service for interaction |\n| No `simctl` touch/swipe API | VM Service evaluate for Flutter-level interaction; cliclick only works if Simulator window is synced |\n| iOS keychain persists through app uninstall | `xcrun simctl erase <UDID>` for full reset |\n| iOS onboarding is shorter than Android (language only → home) | Mark skipped steps as pass with platform note |\n\n**VM Service scroll expression** (target app root library):\n```dart\n(() { var count = 0; void visit(Element el) { if (el.widget is Scrollable) { try { final s = (el as StatefulElement).state as ScrollableState; final p = s.position; if (p.maxScrollExtent > 0 && p.pixels < p.maxScrollExtent) { p.jumpTo((p.pixels + 500).clamp(0.0, p.maxScrollExtent)); count++; } } catch (_) {} } el.visitChildren(visit); } WidgetsBinding.instance.rootElement?.visitChildren(visit); return count; })()\n```\n\n**iOS simulator auth** (dev Firebase tokens rejected by prod API — needs local backend):\n```bash\n# .dev.env must point to a local/dev backend\nAPI_BASE_URL=http://<your-backend-host>:<port>/\nUSE_WEB_AUTH=false\nUSE_AUTH_CUSTOM_TOKEN=true\n# Regenerate envied after .dev.env change, rebuild app\n```\n\n## Guard Conditions\n\n**NEVER:**\n- Use development env vars to bypass auth — test with real auth flows\n- Set `hasCompletedOnboarding` to skip onboarding — test the real flow\n- Modify source code to make tests pass — report the failure instead\n- Commit screenshots to git — use GCS upload for PR evidence\n",".github/AGENTS.md":"# GitHub Workflow Agent Guide\n\nThese rules apply to GitHub Actions workflows and custom actions under `.github/`.\n\n## CI/CD Deploy Safety\n\n- Every workflow that mutates a persistent Cloud Run service/job, GKE Helm\n  release, or traffic/promotion state must use a workflow-level concurrency\n  group scoped to the exact target and logical environment. Manual and\n  automatic entry points for the same target must resolve to the same group.\n- Deployment group names are a cross-workflow API. Keep them aligned with\n  `.github/scripts/check-deployment-concurrency.py`; use\n  `cancel-in-progress: false` so a newer run cannot interrupt a remote mutation\n  or a staged validation/traffic promotion.\n- `deploy-backend-stack-<environment>` intentionally covers the four backend\n  Cloud Run services, traffic repair, backend-listen, LLM gateway, and\n  backend-secrets. Those paths share mutable releases, while unrelated services\n  retain their own groups and may deploy in parallel.\n- GitHub concurrency is serialization, not a FIFO queue: only one pending run is\n  retained and ordering is not guaranteed. Deploy workflows must not assume\n  that every intermediate commit will run.\n- Use immutable image tags for deploys. Build and push the short SHA tag, then deploy that exact tag.\n- Development desktop-backend acceptance signs Firebase probe tokens via IAM `signJwt` on `vars.FIREBASE_PROBE_SIGNER_SERVICE_ACCOUNT` (a `@based-hardware` SA matching `FIREBASE_AUTH_PROJECT_ID`); the deploy identity needs `roles/iam.serviceAccountTokenCreator` on it. Never stage service-account key files in runners or deploy them as runtime configuration.\n- Do not deploy Cloud Run services from an untagged image path; use `image:...:${SHORT_SHA}` so revisions show the source commit.\n- Do not let Helm chart-only deploys reset GKE workloads to `latest`. Preserve the currently deployed immutable tag or require an explicit tag input.\n- Every GKE Deployment deploy must wait for rollout completion with `kubectl rollout status ... --timeout=...` and fail on timeout.\n- For chart-only backend-listen deploys, fail if the current deployed image tag is missing or `latest`; use the backend deploy workflow to establish an immutable tag first.\n- Keep rollout checks close to the deploy step they verify, especially for `backend-listen`, `pusher`, `llm-gateway`, `parakeet`, `diarizer`, and `vad`.\n- Use `backend/scripts/deploy_status_report.py` as a strict gate on success paths; use it with `|| true` only after a primary rollout/traffic command already failed.\n- Full backend deploys must derive one immutable Cloud Run plus `backend-listen` release vector and run `backend/scripts/verify_backend_release_vector.py` after traffic promotion; a mixed or partially applied serving vector must fail the workflow and emit evidence for a retry.\n- Before restarting GKE workloads that depend on `backend-secrets`, wait for ExternalSecret Ready and run `backend/scripts/verify_k8s_secret_keys.py`; never print secret values.\n- Before any pusher Helm mutation, verify `${ENV}-omi-backend-config` exists so a missing shared runtime ConfigMap cannot replace the healthy replica.\n- Backend deploy workflows may only run Firestore index readiness through the shared `.github/actions/firestore-readiness` composite, staged from immutable `github.workflow_sha` into .github/firestore-workflow after the runtime-source checkout and bound to the exact checked source SHA before any image build, push, or rollout. It `--check-only`s against `RUNTIME_GCP_PROJECT_ID` with `GCP_FIRESTORE_READONLY_CREDENTIALS` (set in both `development` and `prod` Environments) in an isolated gcloud config; JIT QA lanes use `env.QA_PROJECT`/`env.QA_FIRESTORE_DATABASE` instead. `gcp_backend_listen_helm.yml` runs the gate only when its explicit-tag path resolves `env.BACKEND_LISTEN_SOURCE_SHA`; kept-tag and rollback lanes replay an image already served in the same environment, the index pipeline is create-only, and rollback must not gain new failure modes. A failed gate may upload only a locally revalidated, bounded, redacted proposal. Index writes belong solely to `gcp_firestore_indexes.yml`, which keeps its own `firestore-schema-${environment}` lock and the typed `APPLY_FIRESTORE_INDEXES` confirmation. Detail: `agent-docs/firestore-queries-and-indexes.md`.\n- When editing workflows, keep `actionlint` coverage in CI so YAML and GitHub expression mistakes fail before merge.\n",".cursor/rules/arc.md":"# Rules Skill\n\nPath: `.cursor/rules/arc.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/arc.md)",".cursor/rules/backend-imports.mdc":"# Cursor Rule: backend-imports\n\nPath: `.cursor/rules/backend-imports.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-imports.mdc)",".cursor/rules/backend-testing.mdc":"# Cursor Rule: backend-testing\n\nPath: `.cursor/rules/backend-testing.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-testing.mdc)",".cursor/rules/common-mistakes.mdc":"# Cursor Rule: common-mistakes\n\nPath: `.cursor/rules/common-mistakes.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/common-mistakes.mdc)",".cursor/rules/flutter-localization.mdc":"# Cursor Rule: flutter-localization\n\nPath: `.cursor/rules/flutter-localization.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/flutter-localization.mdc)",".cursor/rules/formatting.mdc":"# Cursor Rule: formatting\n\nPath: `.cursor/rules/formatting.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/formatting.mdc)","plugins/chatgpt/gpt_schema.example.yaml":"# Chatgpt Skill\n\nPath: `plugins/chatgpt/gpt_schema.example.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/chatgpt/gpt_schema.example.yaml)","plugins/composio/README.md":"# composio Documentation\n\nPath: `plugins/composio/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/composio/README.md)","plugins/hume-ai/README.md":"# hume-ai Documentation\n\nPath: `plugins/hume-ai/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/README.md)","plugins/hume-ai/emotion_config.json":"# Hume-ai Skill\n\nPath: `plugins/hume-ai/emotion_config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/emotion_config.json)","plugins/hume-ai/render.yaml":"# Hume-ai Skill\n\nPath: `plugins/hume-ai/render.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/render.yaml)","plugins/omi-arxiv-app/README.md":"# omi-arxiv-app Documentation\n\nPath: `plugins/omi-arxiv-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-arxiv-app/README.md)","plugins/omi-citybikes-app/README.md":"# omi-citybikes-app Documentation\n\nPath: `plugins/omi-citybikes-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-citybikes-app/README.md)","plugins/omi-clickup-app/README.md":"# omi-clickup-app Documentation\n\nPath: `plugins/omi-clickup-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-clickup-app/README.md)","plugins/omi-coingecko-crypto-app/README.md":"# omi-coingecko-crypto-app Documentation\n\nPath: `plugins/omi-coingecko-crypto-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-coingecko-crypto-app/README.md)","plugins/omi-crossref-app/README.md":"# omi-crossref-app Documentation\n\nPath: `plugins/omi-crossref-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-crossref-app/README.md)","plugins/omi-frankfurter-app/README.md":"# omi-frankfurter-app Documentation\n\nPath: `plugins/omi-frankfurter-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-frankfurter-app/README.md)","plugins/omi-github-app/COMPARISON.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/COMPARISON.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/COMPARISON.md)","plugins/omi-github-app/QUICKSTART.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/QUICKSTART.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/QUICKSTART.md)","plugins/omi-github-app/README.md":"# omi-github-app Documentation\n\nPath: `plugins/omi-github-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/README.md)","plugins/omi-github-app/SETUP_COMPLETE.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/SETUP_COMPLETE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/SETUP_COMPLETE.md)","plugins/omi-github-app/TEST_CHANGE.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/TEST_CHANGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/TEST_CHANGE.md)","plugins/omi-github-app/THEME_UPDATE.md":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/THEME_UPDATE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/THEME_UPDATE.md)","plugins/omi-google-calendar-app/README.md":"# omi-google-calendar-app Documentation\n\nPath: `plugins/omi-google-calendar-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-google-calendar-app/README.md)","plugins/omi-hacker-news-app/README.md":"# omi-hacker-news-app Documentation\n\nPath: `plugins/omi-hacker-news-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hacker-news-app/README.md)","plugins/omi-hermes-agent-app/README.md":"# omi-hermes-agent-app Documentation\n\nPath: `plugins/omi-hermes-agent-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hermes-agent-app/README.md)","plugins/omi-hive-app/README.md":"# omi-hive-app Documentation\n\nPath: `plugins/omi-hive-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hive-app/README.md)","plugins/omi-launch-library-app/README.md":"# omi-launch-library-app Documentation\n\nPath: `plugins/omi-launch-library-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-launch-library-app/README.md)","plugins/omi-linear-app/README.md":"# omi-linear-app Documentation\n\nPath: `plugins/omi-linear-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-linear-app/README.md)","plugins/omi-ms365-app/README.md":"# omi-ms365-app Documentation\n\nPath: `plugins/omi-ms365-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/README.md)","plugins/omi-ms365-app/omi-tools.json":"# Omi-ms365-app Skill\n\nPath: `plugins/omi-ms365-app/omi-tools.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/omi-tools.json)","plugins/omi-nobel-prize-app/README.md":"# omi-nobel-prize-app Documentation\n\nPath: `plugins/omi-nobel-prize-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-nobel-prize-app/README.md)","plugins/omi-notion-app/README.md":"# omi-notion-app Documentation\n\nPath: `plugins/omi-notion-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-notion-app/README.md)","plugins/omi-open-library-app/README.md":"# omi-open-library-app Documentation\n\nPath: `plugins/omi-open-library-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-library-app/README.md)","plugins/omi-open-meteo-app/README.md":"# omi-open-meteo-app Documentation\n\nPath: `plugins/omi-open-meteo-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-meteo-app/README.md)","plugins/omi-openfoodfacts-app/README.md":"# omi-openfoodfacts-app Documentation\n\nPath: `plugins/omi-openfoodfacts-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-openfoodfacts-app/README.md)","plugins/omi-plugin-sdk/README.md":"# omi-plugin-sdk Documentation\n\nPath: `plugins/omi-plugin-sdk/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-plugin-sdk/README.md)","plugins/omi-public-holidays-app/README.md":"# omi-public-holidays-app Documentation\n\nPath: `plugins/omi-public-holidays-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-public-holidays-app/README.md)","plugins/omi-pubmed-app/README.md":"# omi-pubmed-app Documentation\n\nPath: `plugins/omi-pubmed-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-pubmed-app/README.md)","plugins/omi-semantic-scholar-app/README.md":"# omi-semantic-scholar-app Documentation\n\nPath: `plugins/omi-semantic-scholar-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-semantic-scholar-app/README.md)","plugins/omi-shipbob-app/README.md":"# omi-shipbob-app Documentation\n\nPath: `plugins/omi-shipbob-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shipbob-app/README.md)","plugins/omi-shopify-app/README.md":"# omi-shopify-app Documentation\n\nPath: `plugins/omi-shopify-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shopify-app/README.md)","plugins/omi-slack-app/OMI_SETUP.md":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/OMI_SETUP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/OMI_SETUP.md)","plugins/omi-slack-app/README.md":"# omi-slack-app Documentation\n\nPath: `plugins/omi-slack-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/README.md)","plugins/omi-slack-app/SETUP.md":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/SETUP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/SETUP.md)","plugins/omi-slack-app/TOKEN_DEBUG.md":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/TOKEN_DEBUG.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/TOKEN_DEBUG.md)","plugins/omi-stack-overflow-app/README.md":"# omi-stack-overflow-app Documentation\n\nPath: `plugins/omi-stack-overflow-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-stack-overflow-app/README.md)","plugins/omi-twitter-app/README.md":"# omi-twitter-app Documentation\n\nPath: `plugins/omi-twitter-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-app/README.md)","plugins/omi-twitter-chat-tools-app/README.md":"# omi-twitter-chat-tools-app Documentation\n\nPath: `plugins/omi-twitter-chat-tools-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-chat-tools-app/README.md)","plugins/omi-usgs-earthquake-app/README.md":"# omi-usgs-earthquake-app Documentation\n\nPath: `plugins/omi-usgs-earthquake-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-usgs-earthquake-app/README.md)","plugins/omi-whoop-app/README.md":"# omi-whoop-app Documentation\n\nPath: `plugins/omi-whoop-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-whoop-app/README.md)","plugins/omi-wikipedia-app/README.md":"# omi-wikipedia-app Documentation\n\nPath: `plugins/omi-wikipedia-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-wikipedia-app/README.md)","plugins/uber_call/README.md":"# uber_call Documentation\n\nPath: `plugins/uber_call/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/uber_call/README.md)","backend/evals/dream_context/README.md":"# Dream_context Skill\n\nPath: `backend/evals/dream_context/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_context/README.md)","backend/evals/dream_reasoning/README.md":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/README.md)","backend/evals/dream_reasoning/fixture.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/fixture.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/fixture.json)","backend/evals/dream_reasoning/guard-fixtures.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/guard-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/guard-fixtures.json)","backend/evals/dream_reasoning/quality-fixtures.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-fixtures.json)","backend/evals/dream_reasoning/quality-results.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-results.json)","backend/evals/dream_reasoning/results.json":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/results.json)","backend/evals/dream_triage/README.md":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/README.md)","backend/evals/dream_triage/fixtures.json":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/fixtures.json)","backend/evals/dream_triage/quality-results.json":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/quality-results.json)","backend/evals/dream_triage/results.json":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/results.json)","backend/evals/dream_verifier/LABELING.v2.md":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/LABELING.v2.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/LABELING.v2.md)","backend/evals/dream_verifier/README.md":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/README.md)","backend/evals/dream_verifier/manifest.json":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.json)","backend/evals/dream_verifier/manifest.v2.json":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.v2.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.v2.json)","plugins/import/manual-import/README.md":"# manual-import Documentation\n\nPath: `plugins/import/manual-import/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/import/manual-import/README.md)","plugins/instructions/SOS/README.md":"# SOS Documentation\n\nPath: `plugins/instructions/SOS/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/SOS/README.md)","plugins/instructions/ahda/README.md":"# ahda Documentation\n\nPath: `plugins/instructions/ahda/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/ahda/README.md)","plugins/instructions/bookwatch/README.md":"# bookwatch Documentation\n\nPath: `plugins/instructions/bookwatch/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/bookwatch/README.md)","plugins/instructions/gcal/README.md":"# gcal Documentation\n\nPath: `plugins/instructions/gcal/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gcal/README.md)","plugins/instructions/gdrive/README.md":"# gdrive Documentation\n\nPath: `plugins/instructions/gdrive/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gdrive/README.md)","plugins/instructions/heavenorhell/README.md":"# heavenorhell Documentation\n\nPath: `plugins/instructions/heavenorhell/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/heavenorhell/README.md)","plugins/instructions/map-notes/README.md":"# map-notes Documentation\n\nPath: `plugins/instructions/map-notes/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/map-notes/README.md)","plugins/instructions/mentor_01/README.md":"# mentor_01 Documentation\n\nPath: `plugins/instructions/mentor_01/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/mentor_01/README.md)","plugins/instructions/multion-amazon/README.md":"# multion-amazon Documentation\n\nPath: `plugins/instructions/multion-amazon/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/multion-amazon/README.md)","plugins/instructions/notion-crm/README.md":"# notion-crm Documentation\n\nPath: `plugins/instructions/notion-crm/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/notion-crm/README.md)","plugins/instructions/reflect/README.md":"# reflect Documentation\n\nPath: `plugins/instructions/reflect/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/reflect/README.md)","plugins/instructions/zapier/README.md":"# zapier Documentation\n\nPath: `plugins/instructions/zapier/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/zapier/README.md)","plugins/zapier/omi-zap/package-lock.json":"# Omi-zap Skill\n\nPath: `plugins/zapier/omi-zap/package-lock.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package-lock.json)","plugins/zapier/omi-zap/package.json":"# Omi-zap Skill\n\nPath: `plugins/zapier/omi-zap/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package.json)","web/packages/note-shared/README.md":"# note-shared Documentation\n\nPath: `web/packages/note-shared/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/README.md)","web/packages/note-shared/package.json":"# Note-shared Skill\n\nPath: `web/packages/note-shared/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/package.json)","desktop/macos/agent/evals/realtime-routing-cases.json":"# Evals Skill\n\nPath: `desktop/macos/agent/evals/realtime-routing-cases.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/desktop/macos/agent/evals/realtime-routing-cases.json)"},"items":[{"name":"AGENTS.md","path":".github/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.github/AGENTS.md","title":".github - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# GitHub Workflow Agent Guide\n\nThese rules apply to GitHub Actions workflows and custom actions under `.github/`.\n\n## CI/CD Deploy Safety\n\n- Every workflow that mutates a persistent Cloud Run service/job, GKE Helm\n  release, or traffic/promotion state must use a workflow-level concurrency\n  group scoped to the exact target and logical environment. Manual and\n  automatic entry points for the same target must resolve to the same group.\n- Deployment group names are a cross-workflow API. Keep them aligned with\n  `.github/scripts/check-deployment-concurrency.py`; use\n  `cancel-in-progress: false` so a newer run cannot interrupt a remote mutation\n  or a staged validation/traffic promotion.\n- `deploy-backend-stack-<environment>` intentionally covers the four backend\n  Cloud Run services, traffic repair, backend-listen, LLM gateway, and\n  backend-secrets. Those paths share mutable releases, while unrelated services\n  retain their own groups and may deploy in parallel.\n- GitHub concurrency is serialization, not a FIFO queue: only one pending run is\n  retained and ordering is not guaranteed. Deploy workflows must not assume\n  that every intermediate commit will run.\n- Use immutable image tags for deploys. Build and push the short SHA tag, then deploy that exact tag.\n- Development desktop-backend acceptance signs Firebase probe tokens via IAM `signJwt` on `vars.FIREBASE_PROBE_SIGNER_SERVICE_ACCOUNT` (a `@based-hardware` SA matching `FIREBASE_AUTH_PROJECT_ID`); the deploy identity needs `roles/iam.serviceAccountTokenCreator` on it. Never stage service-account key files in runners or deploy them as runtime configuration.\n- Do not deploy Cloud Run services from an untagged image path; use `image:...:${SHORT_SHA}` so revisions show the source commit.\n- Do not let Helm chart-only deploys reset GKE workloads to `latest`. Preserve the currently deployed immutable tag or require an explicit tag input.\n- Every GKE Deployment deploy must wait for rollout completion with `kubectl rollout status ... --timeout=...` and fail on timeout.\n- For chart-only backend-listen deploys, fail if the current deployed image tag is missing or `latest`; use the backend deploy workflow to establish an immutable tag first.\n- Keep rollout checks close to the deploy step they verify, especially for `backend-listen`, `pusher`, `llm-gateway`, `parakeet`, `diarizer`, and `vad`.\n- Use `backend/scripts/deploy_status_report.py` as a strict gate on success paths; use it with `|| true` only after a primary rollout/traffic command already failed.\n- Full backend deploys must derive one immutable Cloud Run plus `backend-listen` release vector and run `backend/scripts/verify_backend_release_vector.py` after traffic promotion; a mixed or partially applied serving vector must fail the workflow and emit evidence for a retry.\n- Before restarting GKE workloads that depend on `backend-secrets`, wait for ExternalSecret Ready and run `backend/scripts/verify_k8s_secret_keys.py`; never print secret values.\n- Before any pusher Helm mutation, verify `${ENV}-omi-backend-config` exists so a missing shared runtime ConfigMap cannot replace the healthy replica.\n- Backend deploy workflows may only run Firestore index readiness through the shared `.github/actions/firestore-readiness` composite, staged from immutable `github.workflow_sha` into .github/firestore-workflow after the runtime-source checkout and bound to the exact checked source SHA before any image build, push, or rollout. It `--check-only`s against `RUNTIME_GCP_PROJECT_ID` with `GCP_FIRESTORE_READONLY_CREDENTIALS` (set in both `development` and `prod` Environments) in an isolated gcloud config; JIT QA lanes use `env.QA_PROJECT`/`env.QA_FIRESTORE_DATABASE` instead. `gcp_backend_listen_helm.yml` runs the gate only when its explicit-tag path resolves `env.BACKEND_LISTEN_SOURCE_SHA`; kept-tag and rollback lanes replay an image already served in the same environment, the index pipeline is create-only, and rollback must not gain new failure modes. A failed gate may upload only a locally revalidated, bounded, redacted proposal. Index writes belong solely to `gcp_firestore_indexes.yml`, which keeps its own `firestore-schema-${environment}` lock and the typed `APPLY_FIRESTORE_INDEXES` confirmation. Detail: `agent-docs/firestore-queries-and-indexes.md`.\n- When editing workflows, keep `actionlint` coverage in CI so YAML and GitHub expression mistakes fail before merge.\n","isInternal":false,"tokens":951,"sizeBytes":4430},{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Omi agent guide\n\nRead the guide for the area you are changing. `CLAUDE.md` points here.\n\n## Routes\n\n| Working on | Read |\n|---|---|\n| Backend | `backend/AGENTS.md` |\n| Flutter app | `app/AGENTS.md` |\n| macOS desktop | `desktop/macos/AGENTS.md` |\n| Windows/Linux desktop | `desktop/windows/AGENTS.md` |\n| Web app / admin | `web/app/AGENTS.md` / `web/admin/AGENTS.md` |\n| Firmware | `omi/firmware/AGENTS.md` |\n| CI / deployment workflows | `.github/AGENTS.md` |\n| Public documentation | `docs/AGENTS.md` |\n| Product behavior / UI | `PRODUCT.md`, `product/invariants/`; macOS UI: `desktop/macos/docs/ux-contract.md` |\n| Shared client behavior | `contracts/parity/README.md` |\n| Released-client compatibility | `contracts/client-compat/` |\n| Fallbacks / fail-open paths | `.github/agent-docs/fallback-telemetry.md` |\n| Plan catalog | `.github/agent-docs/plan-catalog.md` |\n| App E2E | `app/e2e/SKILL.md`, `desktop/macos/e2e/SKILL.md` |\n| Cursor Cloud environment | `.cursor/cloud-agent-environment.md` |\n| Contributions / PR requirements | `docs/doc/developer/Contribution.mdx` |\n| Failure-class classification | `product/failure-classes.md`, `scripts/failure-class` |\n| Formatting | `.github/agent-docs/formatting.md` |\n| Agent documentation / new checks | `.github/agent-docs/doc-maintenance.md` |\n\n## Repository conventions\n\n- Use a task worktree based on current `origin/main`. `make setup` installs the repository hooks, including staged-file formatting. Keep Git identity in the user's configuration; temporary test identities use `git -c`.\n- Upstream `main` changes land through PRs with merge commits, never squash or direct pushes.\n- macOS development uses dev or `omi-*` named bundles. Do not stop or replace the production Omi or Omi Beta apps; bundle details and commands live in the macOS guide.\n- Migrate in-tree callers together rather than preserving retired APIs with compatibility wrappers. Released clients follow the compatibility contracts above.\n- Run the component's documented verification and `make preflight`. For PR metadata, start with `scripts/pr-preflight --suggest`, then validate the draft with `scripts/pr-preflight --pr-body-file <file>`. These tools identify required invariant citations and failure-class declarations.\n\nKeep this file a router. Add repository-specific facts at their owning guide; omit generic agent behavior and duplicated procedures. Size budgets live in `.github/scripts/check_agents_md_lean.py`.\n","isInternal":false,"tokens":591,"sizeBytes":2451},{"name":"arc.md","path":".cursor/rules/arc.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/arc.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.cursor/rules/arc.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/arc.md)","tokens":300,"sizeBytes":152},{"name":"SKILL.md","path":"app/e2e/SKILL.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/app/e2e/SKILL.md","title":"mobile-app-flows","category":"anthropic-skill","format":"markdown","content":"---\nname: mobile-app-flows\ndescription: \"Understand and explore the Omi Flutter mobile app's UI flows, navigation patterns, and widget architecture. Use when developing features, fixing bugs, or verifying changes in app/lib/ Dart files. Provides agent-flutter commands to explore the live app, understand how screens connect, and verify your work.\"\nallowed-tools: Bash, Read, Glob, Grep\n---\n\n# Omi Mobile App — Flows & Exploration\n\nThis skill teaches you the Omi Flutter mobile app's navigation structure, screen architecture, and widget patterns. Use it when developing features (to understand how the app works), fixing bugs (to navigate to the affected screen), or verifying changes (to confirm your code works in the live app).\n\n> **Verifying changes (agents and contributors):** this skill drives a *live*\n> app on an emulator/device and assumes a manually-authenticated session\n> against a real or local backend — it is an exploration and physical-device\n> tool. The canonical fast verification path is the seeded local lane with\n> synthetic auth: `make mobile-verify ARGS=\"fast --paths <changed-file>\"` —\n> no device, no OAuth, loopback fixtures. See\n> [`scripts/dev-harness/MOBILE_VERIFY.md`](../../scripts/dev-harness/MOBILE_VERIFY.md)\n> and [`MOBILE_SESSIONS.md`](../../scripts/dev-harness/MOBILE_SESSIONS.md).\n> Physical-device evidence remains a separately reported lane (SCA-491).\n\n## How to Explore the App\n\nYou can interact with the running app via `agent-flutter` — a CLI that taps widgets, reads the widget tree, and captures screenshots through Flutter's Marionette debug protocol.\n\n### Setup\n```bash\n# 1. Emulator must be running\nadb devices                          # should show emulator-5554\n# If not: sg kvm -c \"$ANDROID_HOME/emulator/emulator -avd omi-dev -no-window -gpu swiftshader_indirect -no-audio -no-boot-anim &\"\n\n# 2. Set system language to English (REQUIRED — non-English IME breaks text input)\nadb shell \"settings put system system_locales en-US\"\nadb shell \"setprop persist.sys.locale en-US\"\n\n# 3. App must be running in debug mode with flutter run stdout captured\ncd app && flutter run -d emulator-5554 --flavor dev > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"VM Service\" line to appear in the log\n\n# 4. Connect agent-flutter (AGENT_FLUTTER_LOG must point to flutter run stdout, NOT logcat)\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json    # see what's on screen\n```\n\n**Prerequisites:**\n- AVD name: `omi-dev` (check: `$ANDROID_HOME/emulator/emulator -list-avds`)\n- KVM access required: user must be in `kvm` group (`sg kvm -c \"...\"` if not in current session)\n- App package: `com.friend.ios.dev` (dev flavor)\n- **System language must be English** — non-English IME breaks `fill` commands\n- **App must be authenticated and connected to the correct backend** (local, dev, or prod — depends on the task)\n- Marionette already integrated: `marionette_flutter: ^0.3.0` in pubspec.yaml\n\n### Setup (iOS physical device)\n\nFull iOS physical-device playbook (setup, driving, verification, troubleshooting): [`IOS_DEVICE_TESTING.md`](./IOS_DEVICE_TESTING.md).\n\nVerified 2026-07-11 on an iPhone XR (iOS 18.7.9), app 1.0.543, prod-flavor debug build, agent-flutter CLI + marionette MCP. iOS Simulator has no BLE — use a physical device for anything beyond onboarding/UI checks (see also iOS Simulator Known Limitations below).\n\n```bash\n# 1. Get the physical device id\nflutter devices\n\n# 2. Run in debug mode with stdout captured — prod flavor (dev flavor can fail codesigning on a\n#    fresh worktree: \"provisioning profile doesn't support the App Groups capability\")\ncd app && flutter run -d <device-id> --flavor prod > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"A Dart VM Service ... is available\" in the log\n\n# 3. Connect agent-flutter — auto-detects the ws URI from the log\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json\n```\n\n**Gotchas:**\n- **Fresh git worktrees need real prod config seeded from the primary checkout** before building the `prod` flavor: copy `app/.env`, `app/lib/firebase_options_prod.dart`, `app/ios/Config/Prod/GoogleService-Info.plist`, `app/lib/env/prod_env.g.dart`. The `test.sh` bootstrap seeds dev-placeholder versions that point at the wrong Firebase project.\n- **adb-backed commands do NOT work on iOS**: `back`, `press x y`, `dismiss`, `text --press`, `text --fill`. Use in-app back buttons (find the top-left `IconButton` ref) and marionette ref presses only.\n- **`fill @ref` can silently no-op on keyless `TextField`s on iOS** — it reports success but the controller stays empty. Verify via the marionette MCP `get_interactive_elements` (shows the `TextEditingController` contents). Durable fix: add a `ValueKey` to the field, hot reload (sheet state survives), then enter text by key.\n- **`snapshot` labels are empty with `marionette_flutter` 0.3.0** on iOS. Primary orientation/assertion tool is `agent-flutter text` (semantic text dump) — assert outcomes by text presence (e.g. the copy snackbar text). Target elements by type + bounds from `snapshot -i`.\n- `agent-flutter screenshot` output path must be under `/tmp`.\n- Check behavior via the run log: `grep -iE 'exception|error' /tmp/omi-flutter.log` after each flow. A `PlatformException` 4001 (Intercom push token, notifications not granted) is benign.\n- iOS terminates the debug connection if the app is backgrounded/locked too long (\"The OS has terminated the Flutter debug connection for being inactive\") — keep the device unlocked; reconnecting requires relaunching `flutter run`.\n- General key guidance (same as Android): prefer `find key \"name\"`; when a control can't be targeted, add a `ValueKey` in source + hot reload rather than fighting coordinates.\n\n### Commands\n\n| Command | Purpose | Example |\n|---------|---------|---------|\n| `snapshot -i --json` | See all interactive widgets with refs, types, bounds | `agent-flutter snapshot -i --json` |\n| `press @ref` | Tap a widget by ref | `agent-flutter press @e3` |\n| `press x y` | Tap by coordinates (ADB input tap) | `agent-flutter press 540 1200` |\n| `press @ref --adb` | Tap by ref using ADB (for stale refs) | `agent-flutter press @e3 --adb` |\n| `dismiss` | Dismiss system dialogs (location, permissions) | `agent-flutter dismiss` |\n| `find type X press` | Find widget by type and tap | `agent-flutter find type button press` |\n| `find text \"X\" press` | Find by visible text and tap | `agent-flutter find text \"Settings\" press` |\n| `find type X --index N press` | Tap Nth match (0-indexed) | `agent-flutter find type switch --index 0 press` |\n| `fill @ref \"text\"` | Type into text field | `agent-flutter fill @e7 \"search\"` |\n| `scroll down/up` | Scroll current view | `agent-flutter scroll down` |\n| `back` | Android back button | `agent-flutter back` |\n| `screenshot PATH` | Capture current screen | `agent-flutter screenshot /tmp/screen.png` |\n\n**Key rules:**\n- Refs go stale frequently (Flutter rebuilds widget tree aggressively) — always re-snapshot before every interaction, not just after mutations.\n- `find type X` is more stable than hardcoded `@ref` numbers.\n- `AGENT_FLUTTER_LOG` must point to `flutter run` stdout (not logcat).\n- After hot restart: `disconnect` → wait 3s → `connect`.\n- Widget text labels are often null — use `type`, `flutterType`, or `bounds` to identify.\n- `back`, `press x y`, `dismiss`, and the `--adb`/`--press`/`--fill` text flags are ADB-backed — Android only. On a physical iOS device use marionette ref-based commands instead (see Setup (iOS physical device) above).\n\n### Recovery\n```bash\n# \"No isolate with Marionette\" → bring app to foreground + reconnect\nadb -s emulator-5554 shell am start -n com.friend.ios.dev/com.friend.ios.MainActivity\nagent-flutter disconnect && agent-flutter connect\n\n# Unhealthy widget tree → hot restart\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## App Navigation Architecture\n\n### Screen Map\n```\nOnboarding (wrapper.dart) — step wizard\n├── 0: Auth (auth.dart) — Google/Apple sign-in\n├── 1: AI Consent\n├── 2: Name (name_widget.dart)\n├── 3: Primary Language (primary_language_widget.dart)\n├── 4: Found Omi (found_omi_widget.dart)\n├── 5: Permissions (permissions_widget.dart)\n├── 6: User Review (user_review_page.dart)\n├── 9: Speech Profile (speech_profile_widget.dart)\n├── 10: Knowledge Graph (knowledge_graph_step.dart)\n└── 11: Complete (complete_screen.dart) → Home\n\nHome (home/page.dart) — main app after auth, 4-slot bottom nav\n├── [\"Ask Omi\" input bar] → Chat (chat/page.dart) — full-width bar above bottom nav, not a tab\n│   ├── Message history, \"Ask anything\" field, AI responses\n│   └── AI-message action row: Copy (\"✨ Message copied to clipboard\" snackbar), thumbs up, thumbs down, Share\n├── [mic in the bar] → Chat with voice auto-start\n├── [battery/record widget, top left] (battery_info_widget.dart)\n│   ├── Device connected → battery pill → Connected Device (home/device.dart); phone icon → Phone Calls\n│   └── No device → Connect → Connect Device page; Record pill → Conversation Capturing (episodes/live_episode_page.dart)\n│       └── Chevron → record options sheet (Phone Mic record / Phone Call)\n├── [settings gear, top right] → Settings sheet (settings_drawer.dart) — present on every slot\n│\n├── [slot 0] Home (home/home_content.dart)\n│   ├── Conversation capture widget, today's tasks widget\n│   ├── Daily Recaps → Daily Summary Detail; \"View All\" → Conversations\n│   ├── Mind Map → Memory Graph (memory_graph_page.dart) (only with ≥3 conversations)\n│   └── Get-started tiles (only with <3 conversations)\n│\n├── [slot 1] Conversations (conversations_page.dart)\n│   ├── Folder tabs (All, Starred, custom folders)\n│   ├── Top-bar extras on this slot: sync icon (when paired/pending), search, calendar date filter\n│   └── Conversation item (GestureDetector row) → Detail (conversation_detail/page.dart)\n│       ├── Transcript, Summary, Action Items tabs, share, audio (search hidden on Action Items tab)\n│       ├── Pull-down menu: Copy Transcript / Copy Summary / Copy ID / Share Audio (if audio) / Link Event / Test Prompt\n│       └── Back via in-app top-left IconButton — not the OS/adb back gesture\n│\n├── [slot 2] Tasks (action_items_page.dart)\n│   ├── Categories: Today, Tomorrow, Later, No Deadline, Overdue\n│   ├── FAB → Create task sheet (action_item_form_sheet.dart)\n│   ├── Task checkboxes, drag-drop reorder, task → goal linking\n│   └── Top-bar extras on this slot: export → Task Integrations, completed toggle\n│\n└── [slot 3] Apps (apps/page.dart) — \"Search apps\" / \"Featured\" (explore_install_page.dart)\n    ├── Popular apps (horizontal scroll)\n    ├── Category sections → Category apps page\n    ├── App item → App Detail (app_detail/app_detail.dart)\n    │   └── Reviews, capabilities, install/enable, Chat button → Chat\n    └── Top-bar \"+\" on this slot → Add App / Add MCP Server\n\nSettings sheet (settings_drawer.dart) — search + close header, then five visual groups: Account ·\nPlan & Usage, Referral Program · Device … Data & Privacy · Help & About, Feedback · Developer Settings.\nEvery top-level row has a ValueKey (`settings_account`, `settings_group_<group>`, or\n`settings_row_<SettingsDestination>` for the direct rows Plan, Referral and Feedback); each page has a\nScaffold key (`settings_page_<page>`); rows on group/Account pages are `settings_row_<SettingsDestination>`\n(plus `settings_row_voiceResponseMode`, `settings_row_transcribeLater`, `settings_row_backgroundMode`,\n`settings_row_name`, `settings_row_email`, `settings_row_userId`, `settings_row_version`).\n├── Account [settings_account] — shows the name, email as subtitle → Account page (profile.dart, ProfilePage)\n│   ├── Name → Change name dialog; Email (read-only)\n│   ├── User ID (tap copies)\n│   └── Sign Out → Confirmation dialog; Delete Account (delete_account.dart)\n├── Plan & Usage [settings_row_planAndUsage] (usage_page.dart) — \"Pro\" value when paid\n├── Referral Program [settings_row_referral] (referral_page.dart) — NEW tag\n├── Device [settings_group_device] → settings_groups.dart\n│   ├── Device Settings (device_settings.dart) (only when a device is connected)\n│   ├── Offline Sync (sync_page.dart / auto_sync_page.dart)\n│   ├── Phone Calls (phone_call_settings_page.dart)\n│   └── Permissions (permissions_page.dart) — microphone, Bluetooth, notifications\n├── Recording & Transcription [settings_group_recording] → settings_groups.dart\n│   ├── Transcription (transcription_settings_page.dart) — provider value; Language; Custom Vocabulary\n│   ├── Voice Profile → guided introduction (onboarding/speech_profile_widget.dart); Identifying Others (people.dart)\n│   ├── Voice Response (picker sheet); Conversation Timeout (picker)\n│   └── Recording (BETA): Transcribe Later switch; Background Mode switch (Android only)\n├── Notifications & Display [settings_group_notifications] → settings_groups.dart\n│   └── Notifications (notifications_settings_page.dart); Home Screen; Conversation Display\n├── Integrations [settings_group_integrations] (integrations_page.dart) — BETA; also opens from conversation detail\n├── Data & Privacy [settings_group_privacy] → settings_groups.dart\n│   ├── Data Protection (data_privacy_page.dart); Memories (memories/page.dart)\n│   └── Export All Data (spinner while running); Import Data (import_history_page.dart)\n├── Help & About [settings_group_help] → settings_groups.dart\n│   ├── Help Center → help.omi.me (Intercom platforms only)\n│   ├── What's New → Changelog sheet\n│   └── Version + copy button (iOS/Android)\n├── Feedback / Report a bug [settings_row_feedback] → feedback.omi.me (Intercom platforms only)\n└── Developer Settings [settings_group_developer] (developer.dart)\n(Settings search finds every row above and opens the page that holds it.)\n\nTranscription Settings (transcription_settings_page.dart) — not in settings drawer; reached from\nPlan & Usage, Developer Settings, or the Plans sheet\n├── Source toggle: Omi Cloud vs Custom STT\n├── Provider selector, API key, model config\n└── Advanced JSON editors, logs viewer\n\nPhone Calls (phone_calls_page.dart) — from battery-widget phone icon, record options, or get-started tile\n├── Phone Setup Intro when no verified numbers\n├── Contacts / Keypad tabs; call → Active Call page\n└── Gear → Phone Call Settings (phone_call_settings_page.dart) — verified numbers list, delete button\n\nPlans sheet (plans_sheet.dart) — from Plan & Usage, chat quota-exceeded, phone-calls upsell\n\nPersona Profile (persona_profile.dart) — AI clone management\n├── Avatar (100x100), name with verified badge\n├── Share Public Link button\n├── Make Public toggle\n└── 10 social link rows (omi, Twitter active; others Coming Soon)\n    └── Twitter → Social Handle Entry → Verify Identity → Clone Success\n\nConnected Device (home/device.dart) — requires BLE\n├── Device name, connection status, battery\n├── Actions: Firmware Update, SD Card Sync, Disconnect, Unpair\n└── Device info: Product, Model, Manufacturer, Firmware, ID, Serial\n\nVoice Profile — guided introduction (onboarding/speech_profile_widget.dart, #14514)\n├── Four sentence starters, phone mic; Start Speaking or Skip (whole step), then Next / Pause / Skip Question while recording\n└── Review: edit or uncheck answers, then Save and Finish (voice, memories, goal)\n```\n\n### Widget Patterns\n\n**Bottom navigation bar:**\n- Android: 4 `InkWell` widgets at `bounds.y > 780`, sorted left-to-right by `bounds.x`\n- Detect with: `snapshot -i --json` → filter `flutterType == 'InkWell'` and `bounds.y > 780`\n- Navigate home: press the leftmost one\n- iOS (verified 2026-07-11, iPhone XR, 414pt-wide screen): 4 slots at y≈816, x=20/114/207/300, each w=94.\n  Left to right: slot 0 = Home, slot 1 = Conversations (folder tabs All/Starred/…), slot 2 = Tasks,\n  slot 3 = Apps marketplace (\"Search apps\" / \"Featured\")\n\n**Chat entry point (not a bottom-nav tab):**\n- Open chat by tapping the \"Ask Omi\" input bar on the home screen — a full-width gesture\n  element directly above the bottom nav (~y=756, w≈382 on a 414pt-wide screen; verified iOS 2026-07-11)\n\n**Settings gear:**\n- Android: rightmost `button` widget in the top bar; detect by sorting buttons by `bounds.x` descending, take first\n- iOS (verified 2026-07-11): single top-right icon on home at ~x=362, y=58 → Settings sheet (Account,\n  Plan & Usage, Referral Program, Device, Recording & Transcription, Notifications & Display,\n  Integrations, Data & Privacy, Help & About, Feedback, Developer Settings)\n\n**Settings rows:**\n- `gesture` widgets with `bounds.width > 300`\n- Prefer the ValueKeys above (`settings_account`, `settings_group_*`, `settings_row_*`) over positions\n\n**Switch toggles:**\n- Type `switch` in snapshots\n- Press to toggle ON/OFF (no separate ON/OFF actions)\n\n**Bottom sheet pickers:**\n- Open when you press a settings row\n- Language items appear as `gesture` rows with `bounds.y > 380`\n- Many items — use scroll if needed\n\n**Conversation feed rows (iOS, verified 2026-07-11):**\n- `GestureDetector` widgets, h≈84–96; detail opens on tap\n- Go back with the in-app top-left `IconButton` (x=8, y=56, w=40) — not the OS/adb back gesture\n\n**Chat AI-message actions (iOS, verified 2026-07-11):**\n- Four small `InkWell`s (w≈12–14) under each AI message at x≈22/54/88/122; the first is copy —\n  pressing it shows the \"✨ Message copied to clipboard\" snackbar\n\n### Changing Locale\n```bash\nDEVICE=emulator-5554; APP_PKG=com.friend.ios.dev\n\n# Read current\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" | grep app_locale\n\n# Change to Spanish, hot restart to apply\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" > /tmp/prefs.xml\nsed -i 's|flutter.app_locale\">[^<]*|flutter.app_locale\">es|' /tmp/prefs.xml\nadb -s $DEVICE push /tmp/prefs.xml /data/local/tmp/FlutterSharedPreferences.xml\nadb -s $DEVICE shell \"run-as $APP_PKG cp /data/local/tmp/FlutterSharedPreferences.xml shared_prefs/FlutterSharedPreferences.xml\"\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## Prerequisites Reference\n\nEvery flow lists `prerequisites:` — conditions that MUST be true before running. These describe the **real user state** — no bypasses, no shortcuts. Test the app the way users experience it.\n\n| Prerequisite | What it means | How to achieve (Android) | How to achieve (iOS) |\n|-------------|---------------|--------------------------|----------------------|\n| `auth_ready` | User completed sign-in (Google or Apple), app shows home screen | Run `bash setup.sh android` → launch app → complete Google Sign-In flow → complete onboarding | Run `bash setup.sh ios` → launch on simulator or device → complete Google/Apple Sign-In → complete onboarding |\n| `signed_out` | Fresh app, user NOT signed in, shows Get Started screen | Uninstall + reinstall, or clear app data via Settings → Apps → Omi → Clear Data | Delete app from simulator/device and reinstall |\n| `microphone_permission` | App has mic permission granted | When app requests mic permission during use, tap \"Allow\". Or pre-grant: `adb shell pm grant com.friend.ios.dev android.permission.RECORD_AUDIO` | When app requests mic permission, tap \"Allow\" in the iOS permission dialog |\n| `ble_on` | Bluetooth enabled on device | Enable Bluetooth in device Settings → Connected Devices. **Emulators/simulators do not support BLE** — requires physical device | Enable Bluetooth in device Settings. **iOS Simulator has no BLE** — requires physical iPhone |\n| `omi_device_connected` | Omi hardware paired and connected via BLE | Power on Omi device within BLE range → app auto-discovers on home screen → tap Connect. **Physical device only** | Same — power on Omi, app discovers it. **Physical iPhone only** |\n| `phone_number_verified` | Phone number added and verified in settings | Settings → Phone Calls → add phone number → receive SMS → enter code. Requires real phone number | Same flow — requires real phone number that receives SMS |\n| `developer_settings_enabled` | Developer Settings screen is open | Settings drawer → scroll down → tap \"Developer Settings\" (visible to all users) | Same navigation path |\n| `adb_access` | Shell access for locale/prefs manipulation (Android only) | Debug build + `adb` in PATH. Verify: `adb shell run-as com.friend.ios.dev ls shared_prefs/` | Not applicable — iOS equivalent uses `xcrun simctl` for simulator or Xcode for device |\n\n### Prerequisite dependency chain\n```\nsigned_out ─── (fresh install, no prior state)\n\nauth_ready ─── launch app → sign in with Google/Apple → complete onboarding\n  │             (this is the REAL user flow — no bypasses)\n  ├── microphone_permission  (grant when prompted, or pre-grant via platform tools)\n  ├── developer_settings_enabled  (navigate in-app to Settings → Developer Settings)\n  ├── phone_number_verified  (in-app SMS verification — manual step)\n  ├── ble_on + omi_device_connected  (physical device + physical Omi hardware)\n  └── adb_access  (Android debug builds only — for locale manipulation)\n```\n\n### Platform setup\n```bash\n# Android: setup + build + launch\ncd app && bash setup.sh android\n# → completes: keystore, Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google when app launches, complete onboarding\n\n# iOS: setup + build + launch\ncd app && bash setup.sh ios\n# → completes: Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google/Apple when app launches, complete onboarding\n```\n\n**Important:** Both platforms require completing the real sign-in and onboarding flows. Never bypass auth or onboarding — these are user-facing flows that must work correctly.\n\n## YAML Flow Schema (v2)\n\nEach flow file uses schema v2:\n```yaml\nversion: 2\nname: string          # Flow identifier\ndescription: string   # What this flow covers\napp: com.friend.ios.dev\nevidence:\n  video: true\ncovers: [string]      # Source files this flow exercises\npreconditions: [string]  # Conditions required — see Prerequisites Reference above\nsteps: [Step]         # Ordered list of actions\n```\n\nEach step has these fields:\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `id` | yes | Step ID (S1, S2, ...) |\n| `name` | yes | Short description |\n| `do` | yes | Instructions for executing |\n| `verify` | no | `true` = must be verified even during snapshot replay |\n| `expect` | no | Assertions (text_visible, interactive_count) |\n| `evidence` | no | Screenshot filename (`.webp` preferred) |\n| `note` | no | Edge case notes |\n\nSupported `expect` kinds:\n\n| Kind | Fields | Example |\n|------|--------|---------|\n| `text_visible` | `values: [string]` | Verify specific text appears |\n| `interactive_count` | `min: number` | Verify minimum interactive widgets |\n\n## Navigation Graph (flow-walker verified)\n\nReal navigation edges verified by flow-walker run11 on Pixel 7a (26 screens, 44 edges, depth 3). Screen names mapped from fingerprint IDs to semantic names.\n\nNote: run11 predates the current bottom nav — its tab labels below reflect the old layout (Conversations/Action Items/Memories/Apps as tabs). Today slot 0 is Home, Tasks is slot 2, Apps is slot 3, and Memories lives under Settings — see Screen Map above. The per-screen element counts and edges remain useful.\n\n```\nHome (24 elements: 17 gesture, 3 icon, 4 inkwell)\n├── [tab 0] Conversations (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   ├── Conversation Detail (27 el: 18 gesture, 4 icon, 4 inkwell, 1 textformfield)\n│   │   └── Notifications Settings (14 el: 10 gesture, 1 icon, 1 inkwell, 1 switch, 1 textbutton)\n│   ├── Settings Drawer (13 el: 13 gesture)\n│   │   ├── Language Settings (13 el: 10 gesture, 1 icon, 1 switch, 1 textbutton)\n│   │   │   ├── Sub-detail (5 el: 4 gesture, 1 icon)\n│   │   │   ├── Sub-settings (7 el: 1 elevated, 5 gesture, 1 icon)\n│   │   │   │   ├── Settings Confirmation (3 el: 1 gesture, 2 textbutton)\n│   │   │   │   └── Settings Form (16 el: 1 elevated, 9 gesture, 3 icon, 1 inkwell, 2 textfield)\n│   │   │   └── Filter Sheet (13 el: 6 gesture, 5 inkwell, 2 textbutton)\n│   │   └── Back → Home\n│   ├── Sub-detail (2 el: gesture, icon)\n│   ├── Sub-page (3 el: 2 gesture, 1 icon)\n│   └── Back → Home\n├── [tab 1] Action Items (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   └── Notifications Settings (same as above)\n├── [tab 2] Memories (25 el: 15 gesture, 4 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Memory Detail (9 el: 5 gesture, 2 icon, 2 inkwell)\n│   ├── Category Page (7 el: 4 gesture, 3 inkwell)\n│   └── Memory Search (18 el: 12 gesture, 4 icon, 1 inkwell, 1 textfield)\n├── [tab 3] Apps (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Confirmation Dialog (4 el: 1 elevated, 1 gesture, 2 icon)\n│   ├── Apps Sub-page (14 el: 3 elevated, 6 gesture, 1 icon, 4 inkwell)\n│   ├── App Form (5 el: 1 elevated, 2 gesture, 1 icon, 1 textfield)\n│   └── Back → Home\n├── [Ask Omi] Chat (22 el: 19 gesture, 1 textbutton, 2 textfield)\n│   ├── Delete Account (6 el: 1 checkbox, 4 gesture, 1 textfield)\n│   └── Back → Home\n├── [record] Capturing (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n├── [settings] Settings Drawer (13 el: 13 gesture)\n├── [gesture] Sign Up (5 el: 1 elevated, 1 gesture, 3 textfield)\n├── [gesture] Confirmation (2 el: gesture, textbutton)\n└── [gesture] Conversations Alt (17 el: 10 gesture, 2 icon, 4 inkwell, 1 textfield)\n```\n\n### Screen Fingerprint Mapping\n\nFor flow-walker compatibility — maps auto-generated fingerprint names to semantic flow names.\n\n| Fingerprint ID | Element Count | Semantic Name | Flow File |\n|----------------|---------------|---------------|-----------|\n| `92a58e321064` | 24 | Home | home-navigation, tab-navigation |\n| `113cc0d4f097` | 17 | Conversations (FAB) | conversations |\n| `59c8c3dba1aa` | 17 | Conversations Alt | conversations |\n| `2108e83364c2` | 19 | Apps | apps |\n| `2b9f6e0d087b` | 25 | Memories | memories |\n| `13f37b3018c5` | 22 | Chat | chat |\n| `49f053d00b0f` | 27 | Conversation Detail | conversation-detail |\n| `011f78c61152` | 14 | Notifications Settings | settings-notifications |\n| `1b40a175a5fd` | 13 | Language Settings | language-change |\n| `4b666d4ec8e3` | 13 | Settings Drawer | home-navigation |\n| `0f852433acc6` | 6 | Delete Account | delete-account |\n| `a74411638a9c` | 5 | Sign Up Form | onboarding |\n| `0ba3aef7f00f` | 2 | Confirmation Dialog | — |\n| `8407c23c9698` | 2 | Sub-detail | — |\n| `9580b20fb6e0` | 3 | Sub-page | — |\n| `dacfbda46c10` | 5 | Sub-detail (settings) | — |\n| `e8ea54a72f46` | 7 | Sub-settings | — |\n| `0766ec584fb4` | 13 | Filter Sheet | apps |\n| `f94e0140ed7e` | 4 | Confirmation Dialog | — |\n| `88992c72a759` | 14 | Apps Sub-page | app-detail |\n| `f757010c13b3` | 5 | App Form | apps |\n| `485a8ce57ebc` | 9 | Memory Detail | memories |\n| `a6983264d893` | 7 | Category Page | memories |\n| `4450e0d3044e` | 18 | Memory Search | memories |\n| `35c0d8ed0daa` | 3 | Settings Confirmation | settings-profile |\n| `a864d8b70edf` | 16 | Settings Form | settings-profile |\n\n## Verified Flows (v2 flow-walker)\n\nOnly flows that have been executed, verified, and pushed through the v2 flow-walker pipeline are listed here. Each has a `.snapshot.json` for fast replay.\n\n| Flow | Steps | Prerequisites | What it tests | Reports |\n|------|-------|--------------|---------------|---------|\n| `flows/login.yaml` | 5 | signed_out | Auth screen → Google Sign-In → consent → OAuth → home | [normal](https://flow-walker.beastoin.workers.dev/runs/hao5vbBsBI.html), [replay](https://flow-walker.beastoin.workers.dev/runs/-aeMGlV88w.html) |\n| `flows/onboarding.yaml` | 9 | signed_out + auth | Name → Language → Found Omi → Permissions → Review → Speech → Knowledge → Complete → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/UHqa8Fysj2.html), [replay](https://flow-walker.beastoin.workers.dev/runs/B9Py6uoJaO.html) |\n| `flows/logout.yaml` | 5 | auth_ready | Home → Settings → Sign Out → Confirm → Auth screen | [normal](https://flow-walker.beastoin.workers.dev/runs/SH4MXH9gkv.html), [replay](https://flow-walker.beastoin.workers.dev/runs/NcZlCUW0W7.html) |\n| `flows/ask-omi-chat.yaml` | 9 | auth_ready | Home → Ask Omi → Chat page → Type message → AI response → Chat apps drawer → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/O5h8bR6izW.html) |\n\nWhen you modify a Dart file, check if any flow's `covers:` includes it. If so, that flow describes the user journey your change affects — use it to understand context and verify your work.\n\n## Verification & Evidence\n\nAfter making changes, verify them in the live app:\n1. Navigate to the affected screen using the commands above\n2. Check that your changes appear (snapshot, screenshot)\n3. Test interactions (press buttons, fill fields, scroll)\n4. Capture evidence: `agent-flutter screenshot /tmp/evidence.png`\n5. Generate video: `ffmpeg -framerate 1 -pattern_type glob -i '/tmp/e2e-*.png' -vf \"scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1\" -c:v libx264 -pix_fmt yuv420p /tmp/report.mp4`\n\n## Visual audit (before/after screenshots without a device)\n\n`app/scripts/visual_audit.sh` screenshots registered screen states at two revisions and pairs them\nin one gallery. It pumps **production widgets** in `flutter-tester` with synthetic local state: the\napp's own fonts (FontManifest plus Roboto from the pinned SDK), `buildOmiTheme()`, 390×844 logical\npx at 2×, and a loopback-only network guard over the hermetic fixture backend. No login, simulator,\nsigning, customer data or external API.\n\n**Use it** for layout, copy, grouping and visual-regression review of a UI change, and for the\nbefore/after images a UI PR description needs. **Use agent-flutter / flow-walker (above) instead**\nfor anything a headless capture cannot show: native keyboard, status bar and safe areas, system\npermission dialogs, BLE, audio, real network timing, screen readers, or a claim about the installed\napp. The captures are evidence of a widget rendering, not of device behavior.\n\n```bash\napp/scripts/visual_audit.sh --list                                  # scenario ids, per suite\napp/scripts/visual_audit.sh --base origin/main --head HEAD          # every scenario, both sides\napp/scripts/visual_audit.sh --base <sha> --head <sha> --only settings-sheet,settings-device\napp/scripts/visual_audit.sh --head WORKTREE --only settings-help    # this checkout, uncommitted, one side\n```\n\nEach revision is checked out in a temporary detached worktree under `$OMI_WORKTREES` (default: the\nsystem temp dir), prepared like CI (generated env files, `pub get`, `build_runner`) and removed on\nexit. The harness and scenarios always come from the checkout you run the command in, copied over\nboth sides. The current suite (`registry.dart`) compiles against current main; a revision from\nbefore a large UI change is captured with a compat suite (`compat/<name>/`, chosen by its `UNTIL`\ncommit; see `compat/README.md`) that pumps the equivalent old page under the same id, so the old\nProfile page pairs with Account. A page only one side has is shown as \"did not exist\" there. The\noutput directory (`--out`,\ndefault a new temp dir; it must be empty and outside the repository) holds `before/` and `after/`\nPNGs named `<id>.png` or `<id>-<step>.png`, per-side `capture.log`, `INDEX.md` with both SHAs and\ncapture times, and a static `gallery.html`. A scenario that throws on one side is reported as failed\nthere and the rest still capture. **Never commit screenshots.**\n\n**Add a scenario** in `app/integration_test/visual_audit/scenarios/<area>.dart` (new areas are\nspread into `registry.dart`). Put `id` first; it is lowercase-hyphenated, starts with the area, and\nnames the PNGs:\n\n```dart\nAuditScenario(\n  id: 'settings-device',\n  title: 'Device group, device connected',\n  page: 'lib/pages/settings/settings_groups.dart (DeviceGroupPage)',\n  state: 'Signed-in fixture account; a device connected',\n  run: (a) async {\n    await a.pump(const DeviceGroupPage(), providers: [\n      ChangeNotifierProvider<DeviceProvider>.value(value: AuditDeviceProvider(connected: true)),\n    ]);\n    await a.scrollSeries('Open Device from the Settings sheet');\n  },\n),\n```\n\n`AuditRun` (`a`) gives `pump` (the production theme and a broad inert provider roster from\n`fakes.dart`; your `providers` win), `pumpHost` (opens a sheet or dialog from a neutral host),\n`tap`/`longPress`/`enterText` (each settles), `shot(action, step:)`, `scrollSeries(action)` (top,\nevery 700 px, exact bottom) and `server` (the running fixture backend: seed conversations, set\n`assistantReplyText`, `failNext`). Seed state through providers and the fixture backend, find\nwidgets by stable `Key`s, and keep `expect(...)` checks that prove the intended state rendered.\n`app/test/visual_audit/visual_audit_smoke_test.dart` renders every registered scenario in the\nordinary `app/test.sh` suite without writing images, so a scenario that breaks fails CI.\n\n## Decision Tree\n\n| Problem | Solution |\n|---------|----------|\n| Widget not found | Re-snapshot, try scrolling, check if on wrong screen, match by bounds position |\n| Ref expired between commands | Use `press x y` with coordinates from last snapshot bounds, or `press @ref --adb` |\n| System dialog blocking (location, permissions) | `agent-flutter dismiss` — detects and dismisses via ADB |\n| \"No isolate with Marionette\" | ADB foreground + disconnect + reconnect |\n| Snapshot returns 0 interactive elements | Marionette lost widget tree — `disconnect` then `connect` to re-attach |\n| Bottom nav tabs not detected | `back` until nav bar appears, filter InkWell y > 780 |\n| Hot restart breaks connection | Wait 3s → disconnect → connect |\n| Text labels null | Match by `type`, `flutterType`, or `bounds` — Marionette doesn't extract child text |\n| Non-English IME breaks text input | Set system locale to English: `adb shell \"settings put system system_locales en-US\"` |\n\n## iOS Simulator Known Limitations\n\nFor a physical iOS device (has BLE, avoids most limitations below), see **Setup (iOS physical device)** under How to Explore the App.\n\n| Issue | Workaround |\n|-------|-----------|\n| ASWebAuthenticationSession blocks Google Sign-In (system dialog, not automatable) | Use `FirebaseAuth.instance.signInWithCustomToken(token)` via VM Service evaluate |\n| `agent-flutter scroll` fails (uses ADB/transport.swipe, Simulator window disconnected) | Scroll via VM Service Dart evaluation: `pos.jumpTo()` on Scrollable widgets |\n| Simulator.app window shows Home Screen (not app) when booted via SSH user | Use `simctl screenshot` for true device state; use Marionette/VM Service for interaction |\n| No `simctl` touch/swipe API | VM Service evaluate for Flutter-level interaction; cliclick only works if Simulator window is synced |\n| iOS keychain persists through app uninstall | `xcrun simctl erase <UDID>` for full reset |\n| iOS onboarding is shorter than Android (language only → home) | Mark skipped steps as pass with platform note |\n\n**VM Service scroll expression** (target app root library):\n```dart\n(() { var count = 0; void visit(Element el) { if (el.widget is Scrollable) { try { final s = (el as StatefulElement).state as ScrollableState; final p = s.position; if (p.maxScrollExtent > 0 && p.pixels < p.maxScrollExtent) { p.jumpTo((p.pixels + 500).clamp(0.0, p.maxScrollExtent)); count++; } } catch (_) {} } el.visitChildren(visit); } WidgetsBinding.instance.rootElement?.visitChildren(visit); return count; })()\n```\n\n**iOS simulator auth** (dev Firebase tokens rejected by prod API — needs local backend):\n```bash\n# .dev.env must point to a local/dev backend\nAPI_BASE_URL=http://<your-backend-host>:<port>/\nUSE_WEB_AUTH=false\nUSE_AUTH_CUSTOM_TOKEN=true\n# Regenerate envied after .dev.env change, rebuild app\n```\n\n## Guard Conditions\n\n**NEVER:**\n- Use development env vars to bypass auth — test with real auth flows\n- Set `hasCompletedOnboarding` to skip onboarding — test the real flow\n- Modify source code to make tests pass — report the failure instead\n- Commit screenshots to git — use GCS upload for PR evidence\n","frontmatter":{"name":"mobile-app-flows","description":"Understand and explore the Omi Flutter mobile app's UI flows, navigation patterns, and widget architecture. Use when developing features, fixing bugs, or verifying changes in app/lib/ Dart files. Provides agent-flutter commands to explore the live app, understand how screens connect, and verify your work.","allowed-tools":"Bash, Read, Glob, Grep","allowedTools":["Bash,","Read,","Glob,","Grep"]},"allowedTools":["Bash,","Read,","Glob,","Grep"],"isInternal":false,"tokens":9486,"sizeBytes":37394},{"name":"README.md","path":"backend/evals/dream_context/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_context/README.md","title":"Dream_context Skill","category":"anthropic-skill","format":"markdown","content":"# Dream_context Skill\n\nPath: `backend/evals/dream_context/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_context/README.md)","tokens":300,"sizeBytes":194},{"name":"fixture.json","path":"backend/evals/dream_reasoning/fixture.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/fixture.json","title":"Dream_reasoning Skill","category":"anthropic-skill","format":"json","content":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/fixture.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/fixture.json)","tokens":300,"sizeBytes":206},{"name":"guard-fixtures.json","path":"backend/evals/dream_reasoning/guard-fixtures.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/guard-fixtures.json","title":"Dream_reasoning Skill","category":"anthropic-skill","format":"json","content":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/guard-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/guard-fixtures.json)","tokens":300,"sizeBytes":220},{"name":"quality-fixtures.json","path":"backend/evals/dream_reasoning/quality-fixtures.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-fixtures.json","title":"Dream_reasoning Skill","category":"anthropic-skill","format":"json","content":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-fixtures.json)","tokens":300,"sizeBytes":224},{"name":"quality-results.json","path":"backend/evals/dream_reasoning/quality-results.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-results.json","title":"Dream_reasoning Skill","category":"anthropic-skill","format":"json","content":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-results.json)","tokens":300,"sizeBytes":222},{"name":"README.md","path":"backend/evals/dream_reasoning/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/README.md","title":"Dream_reasoning Skill","category":"anthropic-skill","format":"markdown","content":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/README.md)","tokens":300,"sizeBytes":200},{"name":"results.json","path":"backend/evals/dream_reasoning/results.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/results.json","title":"Dream_reasoning Skill","category":"anthropic-skill","format":"json","content":"# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/results.json)","tokens":300,"sizeBytes":206},{"name":"fixtures.json","path":"backend/evals/dream_triage/fixtures.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/fixtures.json","title":"Dream_triage Skill","category":"anthropic-skill","format":"json","content":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/fixtures.json)","tokens":300,"sizeBytes":199},{"name":"quality-results.json","path":"backend/evals/dream_triage/quality-results.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/quality-results.json","title":"Dream_triage Skill","category":"anthropic-skill","format":"json","content":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/quality-results.json)","tokens":300,"sizeBytes":213},{"name":"README.md","path":"backend/evals/dream_triage/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/README.md","title":"Dream_triage Skill","category":"anthropic-skill","format":"markdown","content":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/README.md)","tokens":300,"sizeBytes":191},{"name":"results.json","path":"backend/evals/dream_triage/results.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/results.json","title":"Dream_triage Skill","category":"anthropic-skill","format":"json","content":"# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/results.json)","tokens":300,"sizeBytes":197},{"name":"LABELING.v2.md","path":"backend/evals/dream_verifier/LABELING.v2.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/LABELING.v2.md","title":"Dream_verifier Skill","category":"anthropic-skill","format":"markdown","content":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/LABELING.v2.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/LABELING.v2.md)","tokens":300,"sizeBytes":207},{"name":"manifest.json","path":"backend/evals/dream_verifier/manifest.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.json","title":"Dream_verifier Skill","category":"anthropic-skill","format":"json","content":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.json)","tokens":300,"sizeBytes":205},{"name":"manifest.v2.json","path":"backend/evals/dream_verifier/manifest.v2.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.v2.json","title":"Dream_verifier Skill","category":"anthropic-skill","format":"json","content":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.v2.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.v2.json)","tokens":300,"sizeBytes":211},{"name":"README.md","path":"backend/evals/dream_verifier/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/README.md","title":"Dream_verifier Skill","category":"anthropic-skill","format":"markdown","content":"# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/README.md)","tokens":300,"sizeBytes":197},{"name":"realtime-routing-cases.json","path":"desktop/macos/agent/evals/realtime-routing-cases.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/desktop/macos/agent/evals/realtime-routing-cases.json","title":"Evals Skill","category":"anthropic-skill","format":"json","content":"# Evals Skill\n\nPath: `desktop/macos/agent/evals/realtime-routing-cases.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/desktop/macos/agent/evals/realtime-routing-cases.json)","tokens":300,"sizeBytes":218},{"name":"gpt_schema.example.yaml","path":"plugins/chatgpt/gpt_schema.example.yaml","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/chatgpt/gpt_schema.example.yaml","title":"Chatgpt Skill","category":"anthropic-skill","format":"yaml","content":"# Chatgpt Skill\n\nPath: `plugins/chatgpt/gpt_schema.example.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/chatgpt/gpt_schema.example.yaml)","tokens":300,"sizeBytes":192},{"name":"emotion_config.json","path":"plugins/hume-ai/emotion_config.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/emotion_config.json","title":"Hume-ai Skill","category":"anthropic-skill","format":"json","content":"# Hume-ai Skill\n\nPath: `plugins/hume-ai/emotion_config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/emotion_config.json)","tokens":300,"sizeBytes":184},{"name":"render.yaml","path":"plugins/hume-ai/render.yaml","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/render.yaml","title":"Hume-ai Skill","category":"anthropic-skill","format":"yaml","content":"# Hume-ai Skill\n\nPath: `plugins/hume-ai/render.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/render.yaml)","tokens":300,"sizeBytes":168},{"name":"COMPARISON.md","path":"plugins/omi-github-app/COMPARISON.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/COMPARISON.md","title":"Omi-github-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/COMPARISON.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/COMPARISON.md)","tokens":300,"sizeBytes":193},{"name":"QUICKSTART.md","path":"plugins/omi-github-app/QUICKSTART.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/QUICKSTART.md","title":"Omi-github-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/QUICKSTART.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/QUICKSTART.md)","tokens":300,"sizeBytes":193},{"name":"SETUP_COMPLETE.md","path":"plugins/omi-github-app/SETUP_COMPLETE.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/SETUP_COMPLETE.md","title":"Omi-github-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/SETUP_COMPLETE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/SETUP_COMPLETE.md)","tokens":300,"sizeBytes":201},{"name":"TEST_CHANGE.md","path":"plugins/omi-github-app/TEST_CHANGE.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/TEST_CHANGE.md","title":"Omi-github-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/TEST_CHANGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/TEST_CHANGE.md)","tokens":300,"sizeBytes":195},{"name":"THEME_UPDATE.md","path":"plugins/omi-github-app/THEME_UPDATE.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/THEME_UPDATE.md","title":"Omi-github-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/THEME_UPDATE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/THEME_UPDATE.md)","tokens":300,"sizeBytes":197},{"name":"omi-tools.json","path":"plugins/omi-ms365-app/omi-tools.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/omi-tools.json","title":"Omi-ms365-app Skill","category":"anthropic-skill","format":"json","content":"# Omi-ms365-app Skill\n\nPath: `plugins/omi-ms365-app/omi-tools.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/omi-tools.json)","tokens":300,"sizeBytes":192},{"name":"OMI_SETUP.md","path":"plugins/omi-slack-app/OMI_SETUP.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/OMI_SETUP.md","title":"Omi-slack-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/OMI_SETUP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/OMI_SETUP.md)","tokens":300,"sizeBytes":188},{"name":"SETUP.md","path":"plugins/omi-slack-app/SETUP.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/SETUP.md","title":"Omi-slack-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/SETUP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/SETUP.md)","tokens":300,"sizeBytes":180},{"name":"TOKEN_DEBUG.md","path":"plugins/omi-slack-app/TOKEN_DEBUG.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/TOKEN_DEBUG.md","title":"Omi-slack-app Skill","category":"anthropic-skill","format":"markdown","content":"# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/TOKEN_DEBUG.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/TOKEN_DEBUG.md)","tokens":300,"sizeBytes":192},{"name":"package-lock.json","path":"plugins/zapier/omi-zap/package-lock.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package-lock.json","title":"Omi-zap Skill","category":"anthropic-skill","format":"json","content":"# Omi-zap Skill\n\nPath: `plugins/zapier/omi-zap/package-lock.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package-lock.json)","tokens":300,"sizeBytes":194},{"name":"package.json","path":"plugins/zapier/omi-zap/package.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package.json","title":"Omi-zap Skill","category":"anthropic-skill","format":"json","content":"# Omi-zap Skill\n\nPath: `plugins/zapier/omi-zap/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/zapier/omi-zap/package.json)","tokens":300,"sizeBytes":184},{"name":"package.json","path":"web/packages/note-shared/package.json","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/package.json","title":"Note-shared Skill","category":"anthropic-skill","format":"json","content":"# Note-shared Skill\n\nPath: `web/packages/note-shared/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/package.json)","tokens":300,"sizeBytes":192},{"name":"backend-imports.mdc","path":".cursor/rules/backend-imports.mdc","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-imports.mdc","title":"Cursor Rule: backend-imports","category":"cursor-rule","format":"mdc","content":"# Cursor Rule: backend-imports\n\nPath: `.cursor/rules/backend-imports.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-imports.mdc)","tokens":300,"sizeBytes":195},{"name":"backend-testing.mdc","path":".cursor/rules/backend-testing.mdc","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-testing.mdc","title":"Cursor Rule: backend-testing","category":"cursor-rule","format":"mdc","content":"# Cursor Rule: backend-testing\n\nPath: `.cursor/rules/backend-testing.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/backend-testing.mdc)","tokens":300,"sizeBytes":195},{"name":"common-mistakes.mdc","path":".cursor/rules/common-mistakes.mdc","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/common-mistakes.mdc","title":"Cursor Rule: common-mistakes","category":"cursor-rule","format":"mdc","content":"# Cursor Rule: common-mistakes\n\nPath: `.cursor/rules/common-mistakes.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/common-mistakes.mdc)","tokens":300,"sizeBytes":195},{"name":"flutter-localization.mdc","path":".cursor/rules/flutter-localization.mdc","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/flutter-localization.mdc","title":"Cursor Rule: flutter-localization","category":"cursor-rule","format":"mdc","content":"# Cursor Rule: flutter-localization\n\nPath: `.cursor/rules/flutter-localization.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/flutter-localization.mdc)","tokens":300,"sizeBytes":210},{"name":"formatting.mdc","path":".cursor/rules/formatting.mdc","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/formatting.mdc","title":"Cursor Rule: formatting","category":"cursor-rule","format":"mdc","content":"# Cursor Rule: formatting\n\nPath: `.cursor/rules/formatting.mdc`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/formatting.mdc)","tokens":300,"sizeBytes":180},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Omi — Agent Instructions\n<!-- Official guidance for writing these files:\n     CLAUDE.md: https://docs.anthropic.com/en/docs/claude-code/memory\n     AGENTS.md: https://developers.openai.com/codex/guides/agents-md\n     Format spec: https://agents.md -->\n\n**Treat `AGENTS.md` as this repository's instruction file.** Every rule for every agent\n(Claude Code, Codex, and any other) lives there. This file exists only because some tools\nlook for `CLAUDE.md` by name — it is the one such pointer in the repo.\n\n- Start with the root [`AGENTS.md`](./AGENTS.md): cross-component rules plus an index.\n- When working inside a directory, also read the **nearest `AGENTS.md` at or above it** —\n  `backend/`, `app/`, `desktop/macos/`, `.github/`, `web/admin/`, `omi/firmware/`.\n  Those carry the detail the root file deliberately omits.\n- Add or change rules in the relevant `AGENTS.md`. **Never add instructions here** — this\n  file stays a pointer so there is only ever one source of truth to maintain.\n\n@AGENTS.md\n","isInternal":false,"tokens":264,"sizeBytes":1012},{"name":"README.md","path":"plugins/composio/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/composio/README.md","title":"composio Documentation","category":"plugin-manifest","format":"markdown","content":"# composio Documentation\n\nPath: `plugins/composio/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/composio/README.md)","tokens":300,"sizeBytes":175},{"name":"README.md","path":"plugins/hume-ai/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/README.md","title":"hume-ai Documentation","category":"plugin-manifest","format":"markdown","content":"# hume-ai Documentation\n\nPath: `plugins/hume-ai/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/README.md)","tokens":300,"sizeBytes":172},{"name":"README.md","path":"plugins/import/manual-import/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/import/manual-import/README.md","title":"manual-import Documentation","category":"plugin-manifest","format":"markdown","content":"# manual-import Documentation\n\nPath: `plugins/import/manual-import/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/import/manual-import/README.md)","tokens":300,"sizeBytes":204},{"name":"README.md","path":"plugins/instructions/ahda/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/ahda/README.md","title":"ahda Documentation","category":"plugin-manifest","format":"markdown","content":"# ahda Documentation\n\nPath: `plugins/instructions/ahda/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/ahda/README.md)","tokens":300,"sizeBytes":189},{"name":"README.md","path":"plugins/instructions/bookwatch/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/bookwatch/README.md","title":"bookwatch Documentation","category":"plugin-manifest","format":"markdown","content":"# bookwatch Documentation\n\nPath: `plugins/instructions/bookwatch/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/bookwatch/README.md)","tokens":300,"sizeBytes":204},{"name":"README.md","path":"plugins/instructions/gcal/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gcal/README.md","title":"gcal Documentation","category":"plugin-manifest","format":"markdown","content":"# gcal Documentation\n\nPath: `plugins/instructions/gcal/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gcal/README.md)","tokens":300,"sizeBytes":189},{"name":"README.md","path":"plugins/instructions/gdrive/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gdrive/README.md","title":"gdrive Documentation","category":"plugin-manifest","format":"markdown","content":"# gdrive Documentation\n\nPath: `plugins/instructions/gdrive/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/gdrive/README.md)","tokens":300,"sizeBytes":195},{"name":"README.md","path":"plugins/instructions/heavenorhell/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/heavenorhell/README.md","title":"heavenorhell Documentation","category":"plugin-manifest","format":"markdown","content":"# heavenorhell Documentation\n\nPath: `plugins/instructions/heavenorhell/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/heavenorhell/README.md)","tokens":300,"sizeBytes":213},{"name":"README.md","path":"plugins/instructions/map-notes/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/map-notes/README.md","title":"map-notes Documentation","category":"plugin-manifest","format":"markdown","content":"# map-notes Documentation\n\nPath: `plugins/instructions/map-notes/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/map-notes/README.md)","tokens":300,"sizeBytes":204},{"name":"README.md","path":"plugins/instructions/mentor_01/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/mentor_01/README.md","title":"mentor_01 Documentation","category":"plugin-manifest","format":"markdown","content":"# mentor_01 Documentation\n\nPath: `plugins/instructions/mentor_01/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/mentor_01/README.md)","tokens":300,"sizeBytes":204},{"name":"README.md","path":"plugins/instructions/multion-amazon/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/multion-amazon/README.md","title":"multion-amazon Documentation","category":"plugin-manifest","format":"markdown","content":"# multion-amazon Documentation\n\nPath: `plugins/instructions/multion-amazon/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/multion-amazon/README.md)","tokens":300,"sizeBytes":219},{"name":"README.md","path":"plugins/instructions/notion-crm/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/notion-crm/README.md","title":"notion-crm Documentation","category":"plugin-manifest","format":"markdown","content":"# notion-crm Documentation\n\nPath: `plugins/instructions/notion-crm/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/notion-crm/README.md)","tokens":300,"sizeBytes":207},{"name":"README.md","path":"plugins/instructions/reflect/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/reflect/README.md","title":"reflect Documentation","category":"plugin-manifest","format":"markdown","content":"# reflect Documentation\n\nPath: `plugins/instructions/reflect/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/reflect/README.md)","tokens":300,"sizeBytes":198},{"name":"README.md","path":"plugins/instructions/SOS/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/SOS/README.md","title":"SOS Documentation","category":"plugin-manifest","format":"markdown","content":"# SOS Documentation\n\nPath: `plugins/instructions/SOS/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/SOS/README.md)","tokens":300,"sizeBytes":186},{"name":"README.md","path":"plugins/instructions/zapier/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/zapier/README.md","title":"zapier Documentation","category":"plugin-manifest","format":"markdown","content":"# zapier Documentation\n\nPath: `plugins/instructions/zapier/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/instructions/zapier/README.md)","tokens":300,"sizeBytes":195},{"name":"README.md","path":"plugins/omi-arxiv-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-arxiv-app/README.md","title":"omi-arxiv-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-arxiv-app Documentation\n\nPath: `plugins/omi-arxiv-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-arxiv-app/README.md)","tokens":300,"sizeBytes":190},{"name":"README.md","path":"plugins/omi-citybikes-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-citybikes-app/README.md","title":"omi-citybikes-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-citybikes-app Documentation\n\nPath: `plugins/omi-citybikes-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-citybikes-app/README.md)","tokens":300,"sizeBytes":202},{"name":"README.md","path":"plugins/omi-clickup-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-clickup-app/README.md","title":"omi-clickup-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-clickup-app Documentation\n\nPath: `plugins/omi-clickup-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-clickup-app/README.md)","tokens":300,"sizeBytes":196},{"name":"README.md","path":"plugins/omi-coingecko-crypto-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-coingecko-crypto-app/README.md","title":"omi-coingecko-crypto-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-coingecko-crypto-app Documentation\n\nPath: `plugins/omi-coingecko-crypto-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-coingecko-crypto-app/README.md)","tokens":300,"sizeBytes":223},{"name":"README.md","path":"plugins/omi-crossref-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-crossref-app/README.md","title":"omi-crossref-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-crossref-app Documentation\n\nPath: `plugins/omi-crossref-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-crossref-app/README.md)","tokens":300,"sizeBytes":199},{"name":"README.md","path":"plugins/omi-frankfurter-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-frankfurter-app/README.md","title":"omi-frankfurter-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-frankfurter-app Documentation\n\nPath: `plugins/omi-frankfurter-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-frankfurter-app/README.md)","tokens":300,"sizeBytes":208},{"name":"README.md","path":"plugins/omi-github-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/README.md","title":"omi-github-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-github-app Documentation\n\nPath: `plugins/omi-github-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/README.md)","tokens":300,"sizeBytes":193},{"name":"README.md","path":"plugins/omi-google-calendar-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-google-calendar-app/README.md","title":"omi-google-calendar-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-google-calendar-app Documentation\n\nPath: `plugins/omi-google-calendar-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-google-calendar-app/README.md)","tokens":300,"sizeBytes":220},{"name":"README.md","path":"plugins/omi-hacker-news-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hacker-news-app/README.md","title":"omi-hacker-news-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-hacker-news-app Documentation\n\nPath: `plugins/omi-hacker-news-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hacker-news-app/README.md)","tokens":300,"sizeBytes":208},{"name":"README.md","path":"plugins/omi-hermes-agent-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hermes-agent-app/README.md","title":"omi-hermes-agent-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-hermes-agent-app Documentation\n\nPath: `plugins/omi-hermes-agent-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hermes-agent-app/README.md)","tokens":300,"sizeBytes":211},{"name":"README.md","path":"plugins/omi-hive-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hive-app/README.md","title":"omi-hive-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-hive-app Documentation\n\nPath: `plugins/omi-hive-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-hive-app/README.md)","tokens":300,"sizeBytes":187},{"name":"README.md","path":"plugins/omi-launch-library-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-launch-library-app/README.md","title":"omi-launch-library-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-launch-library-app Documentation\n\nPath: `plugins/omi-launch-library-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-launch-library-app/README.md)","tokens":300,"sizeBytes":217},{"name":"README.md","path":"plugins/omi-linear-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-linear-app/README.md","title":"omi-linear-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-linear-app Documentation\n\nPath: `plugins/omi-linear-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-linear-app/README.md)","tokens":300,"sizeBytes":193},{"name":"README.md","path":"plugins/omi-ms365-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/README.md","title":"omi-ms365-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-ms365-app Documentation\n\nPath: `plugins/omi-ms365-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/README.md)","tokens":300,"sizeBytes":190},{"name":"README.md","path":"plugins/omi-nobel-prize-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-nobel-prize-app/README.md","title":"omi-nobel-prize-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-nobel-prize-app Documentation\n\nPath: `plugins/omi-nobel-prize-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-nobel-prize-app/README.md)","tokens":300,"sizeBytes":208},{"name":"README.md","path":"plugins/omi-notion-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-notion-app/README.md","title":"omi-notion-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-notion-app Documentation\n\nPath: `plugins/omi-notion-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-notion-app/README.md)","tokens":300,"sizeBytes":193},{"name":"README.md","path":"plugins/omi-open-library-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-library-app/README.md","title":"omi-open-library-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-open-library-app Documentation\n\nPath: `plugins/omi-open-library-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-library-app/README.md)","tokens":300,"sizeBytes":211},{"name":"README.md","path":"plugins/omi-open-meteo-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-meteo-app/README.md","title":"omi-open-meteo-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-open-meteo-app Documentation\n\nPath: `plugins/omi-open-meteo-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-open-meteo-app/README.md)","tokens":300,"sizeBytes":205},{"name":"README.md","path":"plugins/omi-openfoodfacts-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-openfoodfacts-app/README.md","title":"omi-openfoodfacts-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-openfoodfacts-app Documentation\n\nPath: `plugins/omi-openfoodfacts-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-openfoodfacts-app/README.md)","tokens":300,"sizeBytes":214},{"name":"README.md","path":"plugins/omi-plugin-sdk/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-plugin-sdk/README.md","title":"omi-plugin-sdk Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-plugin-sdk Documentation\n\nPath: `plugins/omi-plugin-sdk/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-plugin-sdk/README.md)","tokens":300,"sizeBytes":193},{"name":"README.md","path":"plugins/omi-public-holidays-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-public-holidays-app/README.md","title":"omi-public-holidays-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-public-holidays-app Documentation\n\nPath: `plugins/omi-public-holidays-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-public-holidays-app/README.md)","tokens":300,"sizeBytes":220},{"name":"README.md","path":"plugins/omi-pubmed-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-pubmed-app/README.md","title":"omi-pubmed-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-pubmed-app Documentation\n\nPath: `plugins/omi-pubmed-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-pubmed-app/README.md)","tokens":300,"sizeBytes":193},{"name":"README.md","path":"plugins/omi-semantic-scholar-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-semantic-scholar-app/README.md","title":"omi-semantic-scholar-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-semantic-scholar-app Documentation\n\nPath: `plugins/omi-semantic-scholar-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-semantic-scholar-app/README.md)","tokens":300,"sizeBytes":223},{"name":"README.md","path":"plugins/omi-shipbob-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shipbob-app/README.md","title":"omi-shipbob-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-shipbob-app Documentation\n\nPath: `plugins/omi-shipbob-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shipbob-app/README.md)","tokens":300,"sizeBytes":196},{"name":"README.md","path":"plugins/omi-shopify-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shopify-app/README.md","title":"omi-shopify-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-shopify-app Documentation\n\nPath: `plugins/omi-shopify-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-shopify-app/README.md)","tokens":300,"sizeBytes":196},{"name":"README.md","path":"plugins/omi-slack-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/README.md","title":"omi-slack-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-slack-app Documentation\n\nPath: `plugins/omi-slack-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/README.md)","tokens":300,"sizeBytes":190},{"name":"README.md","path":"plugins/omi-stack-overflow-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-stack-overflow-app/README.md","title":"omi-stack-overflow-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-stack-overflow-app Documentation\n\nPath: `plugins/omi-stack-overflow-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-stack-overflow-app/README.md)","tokens":300,"sizeBytes":217},{"name":"README.md","path":"plugins/omi-twitter-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-app/README.md","title":"omi-twitter-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-twitter-app Documentation\n\nPath: `plugins/omi-twitter-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-app/README.md)","tokens":300,"sizeBytes":196},{"name":"README.md","path":"plugins/omi-twitter-chat-tools-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-chat-tools-app/README.md","title":"omi-twitter-chat-tools-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-twitter-chat-tools-app Documentation\n\nPath: `plugins/omi-twitter-chat-tools-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-twitter-chat-tools-app/README.md)","tokens":300,"sizeBytes":229},{"name":"README.md","path":"plugins/omi-usgs-earthquake-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-usgs-earthquake-app/README.md","title":"omi-usgs-earthquake-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-usgs-earthquake-app Documentation\n\nPath: `plugins/omi-usgs-earthquake-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-usgs-earthquake-app/README.md)","tokens":300,"sizeBytes":220},{"name":"README.md","path":"plugins/omi-whoop-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-whoop-app/README.md","title":"omi-whoop-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-whoop-app Documentation\n\nPath: `plugins/omi-whoop-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-whoop-app/README.md)","tokens":300,"sizeBytes":190},{"name":"README.md","path":"plugins/omi-wikipedia-app/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-wikipedia-app/README.md","title":"omi-wikipedia-app Documentation","category":"plugin-manifest","format":"markdown","content":"# omi-wikipedia-app Documentation\n\nPath: `plugins/omi-wikipedia-app/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-wikipedia-app/README.md)","tokens":300,"sizeBytes":202},{"name":"README.md","path":"plugins/uber_call/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/uber_call/README.md","title":"uber_call Documentation","category":"plugin-manifest","format":"markdown","content":"# uber_call Documentation\n\nPath: `plugins/uber_call/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/uber_call/README.md)","tokens":300,"sizeBytes":178},{"name":"README.md","path":"web/packages/note-shared/README.md","rawUrl":"https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/README.md","title":"note-shared Documentation","category":"plugin-manifest","format":"markdown","content":"# note-shared Documentation\n\nPath: `web/packages/note-shared/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/web/packages/note-shared/README.md)","tokens":300,"sizeBytes":194}],"systemPromptSnippet":"<yakaai_skills repo=\"BasedHardware/omi\">\n<!-- File: .github/AGENTS.md (Tokens: ~951 | Category: root-instruction) -->\n# GitHub Workflow Agent Guide\n\nThese rules apply to GitHub Actions workflows and custom actions under `.github/`.\n\n## CI/CD Deploy Safety\n\n- Every workflow that mutates a persistent Cloud Run service/job, GKE Helm\n  release, or traffic/promotion state must use a workflow-level concurrency\n  group scoped to the exact target and logical environment. Manual and\n  automatic entry points for the same target must resolve to the same group.\n- Deployment group names are a cross-workflow API. Keep them aligned with\n  `.github/scripts/check-deployment-concurrency.py`; use\n  `cancel-in-progress: false` so a newer run cannot interrupt a remote mutation\n  or a staged validation/traffic promotion.\n- `deploy-backend-stack-<environment>` intentionally covers the four backend\n  Cloud Run services, traffic repair, backend-listen, LLM gateway, and\n  backend-secrets. Those paths share mutable releases, while unrelated services\n  retain their own groups and may deploy in parallel.\n- GitHub concurrency is serialization, not a FIFO queue: only one pending run is\n  retained and ordering is not guaranteed. Deploy workflows must not assume\n  that every intermediate commit will run.\n- Use immutable image tags for deploys. Build and push the short SHA tag, then deploy that exact tag.\n- Development desktop-backend acceptance signs Firebase probe tokens via IAM `signJwt` on `vars.FIREBASE_PROBE_SIGNER_SERVICE_ACCOUNT` (a `@based-hardware` SA matching `FIREBASE_AUTH_PROJECT_ID`); the deploy identity needs `roles/iam.serviceAccountTokenCreator` on it. Never stage service-account key files in runners or deploy them as runtime configuration.\n- Do not deploy Cloud Run services from an untagged image path; use `image:...:${SHORT_SHA}` so revisions show the source commit.\n- Do not let Helm chart-only deploys reset GKE workloads to `latest`. Preserve the currently deployed immutable tag or require an explicit tag input.\n- Every GKE Deployment deploy must wait for rollout completion with `kubectl rollout status ... --timeout=...` and fail on timeout.\n- For chart-only backend-listen deploys, fail if the current deployed image tag is missing or `latest`; use the backend deploy workflow to establish an immutable tag first.\n- Keep rollout checks close to the deploy step they verify, especially for `backend-listen`, `pusher`, `llm-gateway`, `parakeet`, `diarizer`, and `vad`.\n- Use `backend/scripts/deploy_status_report.py` as a strict gate on success paths; use it with `|| true` only after a primary rollout/traffic command already failed.\n- Full backend deploys must derive one immutable Cloud Run plus `backend-listen` release vector and run `backend/scripts/verify_backend_release_vector.py` after traffic promotion; a mixed or partially applied serving vector must fail the workflow and emit evidence for a retry.\n- Before restarting GKE workloads that depend on `backend-secrets`, wait for ExternalSecret Ready and run `backend/scripts/verify_k8s_secret_keys.py`; never print secret values.\n- Before any pusher Helm mutation, verify `${ENV}-omi-backend-config` exists so a missing shared runtime ConfigMap cannot replace the healthy replica.\n- Backend deploy workflows may only run Firestore index readiness through the shared `.github/actions/firestore-readiness` composite, staged from immutable `github.workflow_sha` into .github/firestore-workflow after the runtime-source checkout and bound to the exact checked source SHA before any image build, push, or rollout. It `--check-only`s against `RUNTIME_GCP_PROJECT_ID` with `GCP_FIRESTORE_READONLY_CREDENTIALS` (set in both `development` and `prod` Environments) in an isolated gcloud config; JIT QA lanes use `env.QA_PROJECT`/`env.QA_FIRESTORE_DATABASE` instead. `gcp_backend_listen_helm.yml` runs the gate only when its explicit-tag path resolves `env.BACKEND_LISTEN_SOURCE_SHA`; kept-tag and rollback lanes replay an image already served in the same environment, the index pipeline is create-only, and rollback must not gain new failure modes. A failed gate may upload only a locally revalidated, bounded, redacted proposal. Index writes belong solely to `gcp_firestore_indexes.yml`, which keeps its own `firestore-schema-${environment}` lock and the typed `APPLY_FIRESTORE_INDEXES` confirmation. Detail: `agent-docs/firestore-queries-and-indexes.md`.\n- When editing workflows, keep `actionlint` coverage in CI so YAML and GitHub expression mistakes fail before merge.\n\n<!-- File: AGENTS.md (Tokens: ~591 | Category: root-instruction) -->\n# Omi agent guide\n\nRead the guide for the area you are changing. `CLAUDE.md` points here.\n\n## Routes\n\n| Working on | Read |\n|---|---|\n| Backend | `backend/AGENTS.md` |\n| Flutter app | `app/AGENTS.md` |\n| macOS desktop | `desktop/macos/AGENTS.md` |\n| Windows/Linux desktop | `desktop/windows/AGENTS.md` |\n| Web app / admin | `web/app/AGENTS.md` / `web/admin/AGENTS.md` |\n| Firmware | `omi/firmware/AGENTS.md` |\n| CI / deployment workflows | `.github/AGENTS.md` |\n| Public documentation | `docs/AGENTS.md` |\n| Product behavior / UI | `PRODUCT.md`, `product/invariants/`; macOS UI: `desktop/macos/docs/ux-contract.md` |\n| Shared client behavior | `contracts/parity/README.md` |\n| Released-client compatibility | `contracts/client-compat/` |\n| Fallbacks / fail-open paths | `.github/agent-docs/fallback-telemetry.md` |\n| Plan catalog | `.github/agent-docs/plan-catalog.md` |\n| App E2E | `app/e2e/SKILL.md`, `desktop/macos/e2e/SKILL.md` |\n| Cursor Cloud environment | `.cursor/cloud-agent-environment.md` |\n| Contributions / PR requirements | `docs/doc/developer/Contribution.mdx` |\n| Failure-class classification | `product/failure-classes.md`, `scripts/failure-class` |\n| Formatting | `.github/agent-docs/formatting.md` |\n| Agent documentation / new checks | `.github/agent-docs/doc-maintenance.md` |\n\n## Repository conventions\n\n- Use a task worktree based on current `origin/main`. `make setup` installs the repository hooks, including staged-file formatting. Keep Git identity in the user's configuration; temporary test identities use `git -c`.\n- Upstream `main` changes land through PRs with merge commits, never squash or direct pushes.\n- macOS development uses dev or `omi-*` named bundles. Do not stop or replace the production Omi or Omi Beta apps; bundle details and commands live in the macOS guide.\n- Migrate in-tree callers together rather than preserving retired APIs with compatibility wrappers. Released clients follow the compatibility contracts above.\n- Run the component's documented verification and `make preflight`. For PR metadata, start with `scripts/pr-preflight --suggest`, then validate the draft with `scripts/pr-preflight --pr-body-file <file>`. These tools identify required invariant citations and failure-class declarations.\n\nKeep this file a router. Add repository-specific facts at their owning guide; omit generic agent behavior and duplicated procedures. Size budgets live in `.github/scripts/check_agents_md_lean.py`.\n\n<!-- File: .cursor/rules/arc.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.cursor/rules/arc.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/.cursor/rules/arc.md)\n\n<!-- File: app/e2e/SKILL.md (Tokens: ~9486 | Category: anthropic-skill) -->\n---\nname: mobile-app-flows\ndescription: \"Understand and explore the Omi Flutter mobile app's UI flows, navigation patterns, and widget architecture. Use when developing features, fixing bugs, or verifying changes in app/lib/ Dart files. Provides agent-flutter commands to explore the live app, understand how screens connect, and verify your work.\"\nallowed-tools: Bash, Read, Glob, Grep\n---\n\n# Omi Mobile App — Flows & Exploration\n\nThis skill teaches you the Omi Flutter mobile app's navigation structure, screen architecture, and widget patterns. Use it when developing features (to understand how the app works), fixing bugs (to navigate to the affected screen), or verifying changes (to confirm your code works in the live app).\n\n> **Verifying changes (agents and contributors):** this skill drives a *live*\n> app on an emulator/device and assumes a manually-authenticated session\n> against a real or local backend — it is an exploration and physical-device\n> tool. The canonical fast verification path is the seeded local lane with\n> synthetic auth: `make mobile-verify ARGS=\"fast --paths <changed-file>\"` —\n> no device, no OAuth, loopback fixtures. See\n> [`scripts/dev-harness/MOBILE_VERIFY.md`](../../scripts/dev-harness/MOBILE_VERIFY.md)\n> and [`MOBILE_SESSIONS.md`](../../scripts/dev-harness/MOBILE_SESSIONS.md).\n> Physical-device evidence remains a separately reported lane (SCA-491).\n\n## How to Explore the App\n\nYou can interact with the running app via `agent-flutter` — a CLI that taps widgets, reads the widget tree, and captures screenshots through Flutter's Marionette debug protocol.\n\n### Setup\n```bash\n# 1. Emulator must be running\nadb devices                          # should show emulator-5554\n# If not: sg kvm -c \"$ANDROID_HOME/emulator/emulator -avd omi-dev -no-window -gpu swiftshader_indirect -no-audio -no-boot-anim &\"\n\n# 2. Set system language to English (REQUIRED — non-English IME breaks text input)\nadb shell \"settings put system system_locales en-US\"\nadb shell \"setprop persist.sys.locale en-US\"\n\n# 3. App must be running in debug mode with flutter run stdout captured\ncd app && flutter run -d emulator-5554 --flavor dev > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"VM Service\" line to appear in the log\n\n# 4. Connect agent-flutter (AGENT_FLUTTER_LOG must point to flutter run stdout, NOT logcat)\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json    # see what's on screen\n```\n\n**Prerequisites:**\n- AVD name: `omi-dev` (check: `$ANDROID_HOME/emulator/emulator -list-avds`)\n- KVM access required: user must be in `kvm` group (`sg kvm -c \"...\"` if not in current session)\n- App package: `com.friend.ios.dev` (dev flavor)\n- **System language must be English** — non-English IME breaks `fill` commands\n- **App must be authenticated and connected to the correct backend** (local, dev, or prod — depends on the task)\n- Marionette already integrated: `marionette_flutter: ^0.3.0` in pubspec.yaml\n\n### Setup (iOS physical device)\n\nFull iOS physical-device playbook (setup, driving, verification, troubleshooting): [`IOS_DEVICE_TESTING.md`](./IOS_DEVICE_TESTING.md).\n\nVerified 2026-07-11 on an iPhone XR (iOS 18.7.9), app 1.0.543, prod-flavor debug build, agent-flutter CLI + marionette MCP. iOS Simulator has no BLE — use a physical device for anything beyond onboarding/UI checks (see also iOS Simulator Known Limitations below).\n\n```bash\n# 1. Get the physical device id\nflutter devices\n\n# 2. Run in debug mode with stdout captured — prod flavor (dev flavor can fail codesigning on a\n#    fresh worktree: \"provisioning profile doesn't support the App Groups capability\")\ncd app && flutter run -d <device-id> --flavor prod > /tmp/omi-flutter.log 2>&1 &\n# Wait for \"A Dart VM Service ... is available\" in the log\n\n# 3. Connect agent-flutter — auto-detects the ws URI from the log\nAGENT_FLUTTER_LOG=/tmp/omi-flutter.log agent-flutter connect\nagent-flutter snapshot -i --json\n```\n\n**Gotchas:**\n- **Fresh git worktrees need real prod config seeded from the primary checkout** before building the `prod` flavor: copy `app/.env`, `app/lib/firebase_options_prod.dart`, `app/ios/Config/Prod/GoogleService-Info.plist`, `app/lib/env/prod_env.g.dart`. The `test.sh` bootstrap seeds dev-placeholder versions that point at the wrong Firebase project.\n- **adb-backed commands do NOT work on iOS**: `back`, `press x y`, `dismiss`, `text --press`, `text --fill`. Use in-app back buttons (find the top-left `IconButton` ref) and marionette ref presses only.\n- **`fill @ref` can silently no-op on keyless `TextField`s on iOS** — it reports success but the controller stays empty. Verify via the marionette MCP `get_interactive_elements` (shows the `TextEditingController` contents). Durable fix: add a `ValueKey` to the field, hot reload (sheet state survives), then enter text by key.\n- **`snapshot` labels are empty with `marionette_flutter` 0.3.0** on iOS. Primary orientation/assertion tool is `agent-flutter text` (semantic text dump) — assert outcomes by text presence (e.g. the copy snackbar text). Target elements by type + bounds from `snapshot -i`.\n- `agent-flutter screenshot` output path must be under `/tmp`.\n- Check behavior via the run log: `grep -iE 'exception|error' /tmp/omi-flutter.log` after each flow. A `PlatformException` 4001 (Intercom push token, notifications not granted) is benign.\n- iOS terminates the debug connection if the app is backgrounded/locked too long (\"The OS has terminated the Flutter debug connection for being inactive\") — keep the device unlocked; reconnecting requires relaunching `flutter run`.\n- General key guidance (same as Android): prefer `find key \"name\"`; when a control can't be targeted, add a `ValueKey` in source + hot reload rather than fighting coordinates.\n\n### Commands\n\n| Command | Purpose | Example |\n|---------|---------|---------|\n| `snapshot -i --json` | See all interactive widgets with refs, types, bounds | `agent-flutter snapshot -i --json` |\n| `press @ref` | Tap a widget by ref | `agent-flutter press @e3` |\n| `press x y` | Tap by coordinates (ADB input tap) | `agent-flutter press 540 1200` |\n| `press @ref --adb` | Tap by ref using ADB (for stale refs) | `agent-flutter press @e3 --adb` |\n| `dismiss` | Dismiss system dialogs (location, permissions) | `agent-flutter dismiss` |\n| `find type X press` | Find widget by type and tap | `agent-flutter find type button press` |\n| `find text \"X\" press` | Find by visible text and tap | `agent-flutter find text \"Settings\" press` |\n| `find type X --index N press` | Tap Nth match (0-indexed) | `agent-flutter find type switch --index 0 press` |\n| `fill @ref \"text\"` | Type into text field | `agent-flutter fill @e7 \"search\"` |\n| `scroll down/up` | Scroll current view | `agent-flutter scroll down` |\n| `back` | Android back button | `agent-flutter back` |\n| `screenshot PATH` | Capture current screen | `agent-flutter screenshot /tmp/screen.png` |\n\n**Key rules:**\n- Refs go stale frequently (Flutter rebuilds widget tree aggressively) — always re-snapshot before every interaction, not just after mutations.\n- `find type X` is more stable than hardcoded `@ref` numbers.\n- `AGENT_FLUTTER_LOG` must point to `flutter run` stdout (not logcat).\n- After hot restart: `disconnect` → wait 3s → `connect`.\n- Widget text labels are often null — use `type`, `flutterType`, or `bounds` to identify.\n- `back`, `press x y`, `dismiss`, and the `--adb`/`--press`/`--fill` text flags are ADB-backed — Android only. On a physical iOS device use marionette ref-based commands instead (see Setup (iOS physical device) above).\n\n### Recovery\n```bash\n# \"No isolate with Marionette\" → bring app to foreground + reconnect\nadb -s emulator-5554 shell am start -n com.friend.ios.dev/com.friend.ios.MainActivity\nagent-flutter disconnect && agent-flutter connect\n\n# Unhealthy widget tree → hot restart\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## App Navigation Architecture\n\n### Screen Map\n```\nOnboarding (wrapper.dart) — step wizard\n├── 0: Auth (auth.dart) — Google/Apple sign-in\n├── 1: AI Consent\n├── 2: Name (name_widget.dart)\n├── 3: Primary Language (primary_language_widget.dart)\n├── 4: Found Omi (found_omi_widget.dart)\n├── 5: Permissions (permissions_widget.dart)\n├── 6: User Review (user_review_page.dart)\n├── 9: Speech Profile (speech_profile_widget.dart)\n├── 10: Knowledge Graph (knowledge_graph_step.dart)\n└── 11: Complete (complete_screen.dart) → Home\n\nHome (home/page.dart) — main app after auth, 4-slot bottom nav\n├── [\"Ask Omi\" input bar] → Chat (chat/page.dart) — full-width bar above bottom nav, not a tab\n│   ├── Message history, \"Ask anything\" field, AI responses\n│   └── AI-message action row: Copy (\"✨ Message copied to clipboard\" snackbar), thumbs up, thumbs down, Share\n├── [mic in the bar] → Chat with voice auto-start\n├── [battery/record widget, top left] (battery_info_widget.dart)\n│   ├── Device connected → battery pill → Connected Device (home/device.dart); phone icon → Phone Calls\n│   └── No device → Connect → Connect Device page; Record pill → Conversation Capturing (episodes/live_episode_page.dart)\n│       └── Chevron → record options sheet (Phone Mic record / Phone Call)\n├── [settings gear, top right] → Settings sheet (settings_drawer.dart) — present on every slot\n│\n├── [slot 0] Home (home/home_content.dart)\n│   ├── Conversation capture widget, today's tasks widget\n│   ├── Daily Recaps → Daily Summary Detail; \"View All\" → Conversations\n│   ├── Mind Map → Memory Graph (memory_graph_page.dart) (only with ≥3 conversations)\n│   └── Get-started tiles (only with <3 conversations)\n│\n├── [slot 1] Conversations (conversations_page.dart)\n│   ├── Folder tabs (All, Starred, custom folders)\n│   ├── Top-bar extras on this slot: sync icon (when paired/pending), search, calendar date filter\n│   └── Conversation item (GestureDetector row) → Detail (conversation_detail/page.dart)\n│       ├── Transcript, Summary, Action Items tabs, share, audio (search hidden on Action Items tab)\n│       ├── Pull-down menu: Copy Transcript / Copy Summary / Copy ID / Share Audio (if audio) / Link Event / Test Prompt\n│       └── Back via in-app top-left IconButton — not the OS/adb back gesture\n│\n├── [slot 2] Tasks (action_items_page.dart)\n│   ├── Categories: Today, Tomorrow, Later, No Deadline, Overdue\n│   ├── FAB → Create task sheet (action_item_form_sheet.dart)\n│   ├── Task checkboxes, drag-drop reorder, task → goal linking\n│   └── Top-bar extras on this slot: export → Task Integrations, completed toggle\n│\n└── [slot 3] Apps (apps/page.dart) — \"Search apps\" / \"Featured\" (explore_install_page.dart)\n    ├── Popular apps (horizontal scroll)\n    ├── Category sections → Category apps page\n    ├── App item → App Detail (app_detail/app_detail.dart)\n    │   └── Reviews, capabilities, install/enable, Chat button → Chat\n    └── Top-bar \"+\" on this slot → Add App / Add MCP Server\n\nSettings sheet (settings_drawer.dart) — search + close header, then five visual groups: Account ·\nPlan & Usage, Referral Program · Device … Data & Privacy · Help & About, Feedback · Developer Settings.\nEvery top-level row has a ValueKey (`settings_account`, `settings_group_<group>`, or\n`settings_row_<SettingsDestination>` for the direct rows Plan, Referral and Feedback); each page has a\nScaffold key (`settings_page_<page>`); rows on group/Account pages are `settings_row_<SettingsDestination>`\n(plus `settings_row_voiceResponseMode`, `settings_row_transcribeLater`, `settings_row_backgroundMode`,\n`settings_row_name`, `settings_row_email`, `settings_row_userId`, `settings_row_version`).\n├── Account [settings_account] — shows the name, email as subtitle → Account page (profile.dart, ProfilePage)\n│   ├── Name → Change name dialog; Email (read-only)\n│   ├── User ID (tap copies)\n│   └── Sign Out → Confirmation dialog; Delete Account (delete_account.dart)\n├── Plan & Usage [settings_row_planAndUsage] (usage_page.dart) — \"Pro\" value when paid\n├── Referral Program [settings_row_referral] (referral_page.dart) — NEW tag\n├── Device [settings_group_device] → settings_groups.dart\n│   ├── Device Settings (device_settings.dart) (only when a device is connected)\n│   ├── Offline Sync (sync_page.dart / auto_sync_page.dart)\n│   ├── Phone Calls (phone_call_settings_page.dart)\n│   └── Permissions (permissions_page.dart) — microphone, Bluetooth, notifications\n├── Recording & Transcription [settings_group_recording] → settings_groups.dart\n│   ├── Transcription (transcription_settings_page.dart) — provider value; Language; Custom Vocabulary\n│   ├── Voice Profile → guided introduction (onboarding/speech_profile_widget.dart); Identifying Others (people.dart)\n│   ├── Voice Response (picker sheet); Conversation Timeout (picker)\n│   └── Recording (BETA): Transcribe Later switch; Background Mode switch (Android only)\n├── Notifications & Display [settings_group_notifications] → settings_groups.dart\n│   └── Notifications (notifications_settings_page.dart); Home Screen; Conversation Display\n├── Integrations [settings_group_integrations] (integrations_page.dart) — BETA; also opens from conversation detail\n├── Data & Privacy [settings_group_privacy] → settings_groups.dart\n│   ├── Data Protection (data_privacy_page.dart); Memories (memories/page.dart)\n│   └── Export All Data (spinner while running); Import Data (import_history_page.dart)\n├── Help & About [settings_group_help] → settings_groups.dart\n│   ├── Help Center → help.omi.me (Intercom platforms only)\n│   ├── What's New → Changelog sheet\n│   └── Version + copy button (iOS/Android)\n├── Feedback / Report a bug [settings_row_feedback] → feedback.omi.me (Intercom platforms only)\n└── Developer Settings [settings_group_developer] (developer.dart)\n(Settings search finds every row above and opens the page that holds it.)\n\nTranscription Settings (transcription_settings_page.dart) — not in settings drawer; reached from\nPlan & Usage, Developer Settings, or the Plans sheet\n├── Source toggle: Omi Cloud vs Custom STT\n├── Provider selector, API key, model config\n└── Advanced JSON editors, logs viewer\n\nPhone Calls (phone_calls_page.dart) — from battery-widget phone icon, record options, or get-started tile\n├── Phone Setup Intro when no verified numbers\n├── Contacts / Keypad tabs; call → Active Call page\n└── Gear → Phone Call Settings (phone_call_settings_page.dart) — verified numbers list, delete button\n\nPlans sheet (plans_sheet.dart) — from Plan & Usage, chat quota-exceeded, phone-calls upsell\n\nPersona Profile (persona_profile.dart) — AI clone management\n├── Avatar (100x100), name with verified badge\n├── Share Public Link button\n├── Make Public toggle\n└── 10 social link rows (omi, Twitter active; others Coming Soon)\n    └── Twitter → Social Handle Entry → Verify Identity → Clone Success\n\nConnected Device (home/device.dart) — requires BLE\n├── Device name, connection status, battery\n├── Actions: Firmware Update, SD Card Sync, Disconnect, Unpair\n└── Device info: Product, Model, Manufacturer, Firmware, ID, Serial\n\nVoice Profile — guided introduction (onboarding/speech_profile_widget.dart, #14514)\n├── Four sentence starters, phone mic; Start Speaking or Skip (whole step), then Next / Pause / Skip Question while recording\n└── Review: edit or uncheck answers, then Save and Finish (voice, memories, goal)\n```\n\n### Widget Patterns\n\n**Bottom navigation bar:**\n- Android: 4 `InkWell` widgets at `bounds.y > 780`, sorted left-to-right by `bounds.x`\n- Detect with: `snapshot -i --json` → filter `flutterType == 'InkWell'` and `bounds.y > 780`\n- Navigate home: press the leftmost one\n- iOS (verified 2026-07-11, iPhone XR, 414pt-wide screen): 4 slots at y≈816, x=20/114/207/300, each w=94.\n  Left to right: slot 0 = Home, slot 1 = Conversations (folder tabs All/Starred/…), slot 2 = Tasks,\n  slot 3 = Apps marketplace (\"Search apps\" / \"Featured\")\n\n**Chat entry point (not a bottom-nav tab):**\n- Open chat by tapping the \"Ask Omi\" input bar on the home screen — a full-width gesture\n  element directly above the bottom nav (~y=756, w≈382 on a 414pt-wide screen; verified iOS 2026-07-11)\n\n**Settings gear:**\n- Android: rightmost `button` widget in the top bar; detect by sorting buttons by `bounds.x` descending, take first\n- iOS (verified 2026-07-11): single top-right icon on home at ~x=362, y=58 → Settings sheet (Account,\n  Plan & Usage, Referral Program, Device, Recording & Transcription, Notifications & Display,\n  Integrations, Data & Privacy, Help & About, Feedback, Developer Settings)\n\n**Settings rows:**\n- `gesture` widgets with `bounds.width > 300`\n- Prefer the ValueKeys above (`settings_account`, `settings_group_*`, `settings_row_*`) over positions\n\n**Switch toggles:**\n- Type `switch` in snapshots\n- Press to toggle ON/OFF (no separate ON/OFF actions)\n\n**Bottom sheet pickers:**\n- Open when you press a settings row\n- Language items appear as `gesture` rows with `bounds.y > 380`\n- Many items — use scroll if needed\n\n**Conversation feed rows (iOS, verified 2026-07-11):**\n- `GestureDetector` widgets, h≈84–96; detail opens on tap\n- Go back with the in-app top-left `IconButton` (x=8, y=56, w=40) — not the OS/adb back gesture\n\n**Chat AI-message actions (iOS, verified 2026-07-11):**\n- Four small `InkWell`s (w≈12–14) under each AI message at x≈22/54/88/122; the first is copy —\n  pressing it shows the \"✨ Message copied to clipboard\" snackbar\n\n### Changing Locale\n```bash\nDEVICE=emulator-5554; APP_PKG=com.friend.ios.dev\n\n# Read current\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" | grep app_locale\n\n# Change to Spanish, hot restart to apply\nadb -s $DEVICE shell \"run-as $APP_PKG cat shared_prefs/FlutterSharedPreferences.xml\" > /tmp/prefs.xml\nsed -i 's|flutter.app_locale\">[^<]*|flutter.app_locale\">es|' /tmp/prefs.xml\nadb -s $DEVICE push /tmp/prefs.xml /data/local/tmp/FlutterSharedPreferences.xml\nadb -s $DEVICE shell \"run-as $APP_PKG cp /data/local/tmp/FlutterSharedPreferences.xml shared_prefs/FlutterSharedPreferences.xml\"\nkill -SIGUSR2 $(pgrep -f \"flutter_tools.*run\" | head -1)\nsleep 3 && agent-flutter disconnect && agent-flutter connect\n```\n\n## Prerequisites Reference\n\nEvery flow lists `prerequisites:` — conditions that MUST be true before running. These describe the **real user state** — no bypasses, no shortcuts. Test the app the way users experience it.\n\n| Prerequisite | What it means | How to achieve (Android) | How to achieve (iOS) |\n|-------------|---------------|--------------------------|----------------------|\n| `auth_ready` | User completed sign-in (Google or Apple), app shows home screen | Run `bash setup.sh android` → launch app → complete Google Sign-In flow → complete onboarding | Run `bash setup.sh ios` → launch on simulator or device → complete Google/Apple Sign-In → complete onboarding |\n| `signed_out` | Fresh app, user NOT signed in, shows Get Started screen | Uninstall + reinstall, or clear app data via Settings → Apps → Omi → Clear Data | Delete app from simulator/device and reinstall |\n| `microphone_permission` | App has mic permission granted | When app requests mic permission during use, tap \"Allow\". Or pre-grant: `adb shell pm grant com.friend.ios.dev android.permission.RECORD_AUDIO` | When app requests mic permission, tap \"Allow\" in the iOS permission dialog |\n| `ble_on` | Bluetooth enabled on device | Enable Bluetooth in device Settings → Connected Devices. **Emulators/simulators do not support BLE** — requires physical device | Enable Bluetooth in device Settings. **iOS Simulator has no BLE** — requires physical iPhone |\n| `omi_device_connected` | Omi hardware paired and connected via BLE | Power on Omi device within BLE range → app auto-discovers on home screen → tap Connect. **Physical device only** | Same — power on Omi, app discovers it. **Physical iPhone only** |\n| `phone_number_verified` | Phone number added and verified in settings | Settings → Phone Calls → add phone number → receive SMS → enter code. Requires real phone number | Same flow — requires real phone number that receives SMS |\n| `developer_settings_enabled` | Developer Settings screen is open | Settings drawer → scroll down → tap \"Developer Settings\" (visible to all users) | Same navigation path |\n| `adb_access` | Shell access for locale/prefs manipulation (Android only) | Debug build + `adb` in PATH. Verify: `adb shell run-as com.friend.ios.dev ls shared_prefs/` | Not applicable — iOS equivalent uses `xcrun simctl` for simulator or Xcode for device |\n\n### Prerequisite dependency chain\n```\nsigned_out ─── (fresh install, no prior state)\n\nauth_ready ─── launch app → sign in with Google/Apple → complete onboarding\n  │             (this is the REAL user flow — no bypasses)\n  ├── microphone_permission  (grant when prompted, or pre-grant via platform tools)\n  ├── developer_settings_enabled  (navigate in-app to Settings → Developer Settings)\n  ├── phone_number_verified  (in-app SMS verification — manual step)\n  ├── ble_on + omi_device_connected  (physical device + physical Omi hardware)\n  └── adb_access  (Android debug builds only — for locale manipulation)\n```\n\n### Platform setup\n```bash\n# Android: setup + build + launch\ncd app && bash setup.sh android\n# → completes: keystore, Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google when app launches, complete onboarding\n\n# iOS: setup + build + launch\ncd app && bash setup.sh ios\n# → completes: Firebase config, .dev.env, flutter run --flavor dev\n# → sign in with Google/Apple when app launches, complete onboarding\n```\n\n**Important:** Both platforms require completing the real sign-in and onboarding flows. Never bypass auth or onboarding — these are user-facing flows that must work correctly.\n\n## YAML Flow Schema (v2)\n\nEach flow file uses schema v2:\n```yaml\nversion: 2\nname: string          # Flow identifier\ndescription: string   # What this flow covers\napp: com.friend.ios.dev\nevidence:\n  video: true\ncovers: [string]      # Source files this flow exercises\npreconditions: [string]  # Conditions required — see Prerequisites Reference above\nsteps: [Step]         # Ordered list of actions\n```\n\nEach step has these fields:\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `id` | yes | Step ID (S1, S2, ...) |\n| `name` | yes | Short description |\n| `do` | yes | Instructions for executing |\n| `verify` | no | `true` = must be verified even during snapshot replay |\n| `expect` | no | Assertions (text_visible, interactive_count) |\n| `evidence` | no | Screenshot filename (`.webp` preferred) |\n| `note` | no | Edge case notes |\n\nSupported `expect` kinds:\n\n| Kind | Fields | Example |\n|------|--------|---------|\n| `text_visible` | `values: [string]` | Verify specific text appears |\n| `interactive_count` | `min: number` | Verify minimum interactive widgets |\n\n## Navigation Graph (flow-walker verified)\n\nReal navigation edges verified by flow-walker run11 on Pixel 7a (26 screens, 44 edges, depth 3). Screen names mapped from fingerprint IDs to semantic names.\n\nNote: run11 predates the current bottom nav — its tab labels below reflect the old layout (Conversations/Action Items/Memories/Apps as tabs). Today slot 0 is Home, Tasks is slot 2, Apps is slot 3, and Memories lives under Settings — see Screen Map above. The per-screen element counts and edges remain useful.\n\n```\nHome (24 elements: 17 gesture, 3 icon, 4 inkwell)\n├── [tab 0] Conversations (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   ├── Conversation Detail (27 el: 18 gesture, 4 icon, 4 inkwell, 1 textformfield)\n│   │   └── Notifications Settings (14 el: 10 gesture, 1 icon, 1 inkwell, 1 switch, 1 textbutton)\n│   ├── Settings Drawer (13 el: 13 gesture)\n│   │   ├── Language Settings (13 el: 10 gesture, 1 icon, 1 switch, 1 textbutton)\n│   │   │   ├── Sub-detail (5 el: 4 gesture, 1 icon)\n│   │   │   ├── Sub-settings (7 el: 1 elevated, 5 gesture, 1 icon)\n│   │   │   │   ├── Settings Confirmation (3 el: 1 gesture, 2 textbutton)\n│   │   │   │   └── Settings Form (16 el: 1 elevated, 9 gesture, 3 icon, 1 inkwell, 2 textfield)\n│   │   │   └── Filter Sheet (13 el: 6 gesture, 5 inkwell, 2 textbutton)\n│   │   └── Back → Home\n│   ├── Sub-detail (2 el: gesture, icon)\n│   ├── Sub-page (3 el: 2 gesture, 1 icon)\n│   └── Back → Home\n├── [tab 1] Action Items (17 el: 1 FAB, 9 gesture, 3 icon, 4 inkwell)\n│   └── Notifications Settings (same as above)\n├── [tab 2] Memories (25 el: 15 gesture, 4 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Memory Detail (9 el: 5 gesture, 2 icon, 2 inkwell)\n│   ├── Category Page (7 el: 4 gesture, 3 inkwell)\n│   └── Memory Search (18 el: 12 gesture, 4 icon, 1 inkwell, 1 textfield)\n├── [tab 3] Apps (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n│   ├── Notifications Settings\n│   ├── Confirmation Dialog (4 el: 1 elevated, 1 gesture, 2 icon)\n│   ├── Apps Sub-page (14 el: 3 elevated, 6 gesture, 1 icon, 4 inkwell)\n│   ├── App Form (5 el: 1 elevated, 2 gesture, 1 icon, 1 textfield)\n│   └── Back → Home\n├── [Ask Omi] Chat (22 el: 19 gesture, 1 textbutton, 2 textfield)\n│   ├── Delete Account (6 el: 1 checkbox, 4 gesture, 1 textfield)\n│   └── Back → Home\n├── [record] Capturing (19 el: 2 elevated, 1 FAB, 9 gesture, 1 icon, 5 inkwell, 1 textfield)\n├── [settings] Settings Drawer (13 el: 13 gesture)\n├── [gesture] Sign Up (5 el: 1 elevated, 1 gesture, 3 textfield)\n├── [gesture] Confirmation (2 el: gesture, textbutton)\n└── [gesture] Conversations Alt (17 el: 10 gesture, 2 icon, 4 inkwell, 1 textfield)\n```\n\n### Screen Fingerprint Mapping\n\nFor flow-walker compatibility — maps auto-generated fingerprint names to semantic flow names.\n\n| Fingerprint ID | Element Count | Semantic Name | Flow File |\n|----------------|---------------|---------------|-----------|\n| `92a58e321064` | 24 | Home | home-navigation, tab-navigation |\n| `113cc0d4f097` | 17 | Conversations (FAB) | conversations |\n| `59c8c3dba1aa` | 17 | Conversations Alt | conversations |\n| `2108e83364c2` | 19 | Apps | apps |\n| `2b9f6e0d087b` | 25 | Memories | memories |\n| `13f37b3018c5` | 22 | Chat | chat |\n| `49f053d00b0f` | 27 | Conversation Detail | conversation-detail |\n| `011f78c61152` | 14 | Notifications Settings | settings-notifications |\n| `1b40a175a5fd` | 13 | Language Settings | language-change |\n| `4b666d4ec8e3` | 13 | Settings Drawer | home-navigation |\n| `0f852433acc6` | 6 | Delete Account | delete-account |\n| `a74411638a9c` | 5 | Sign Up Form | onboarding |\n| `0ba3aef7f00f` | 2 | Confirmation Dialog | — |\n| `8407c23c9698` | 2 | Sub-detail | — |\n| `9580b20fb6e0` | 3 | Sub-page | — |\n| `dacfbda46c10` | 5 | Sub-detail (settings) | — |\n| `e8ea54a72f46` | 7 | Sub-settings | — |\n| `0766ec584fb4` | 13 | Filter Sheet | apps |\n| `f94e0140ed7e` | 4 | Confirmation Dialog | — |\n| `88992c72a759` | 14 | Apps Sub-page | app-detail |\n| `f757010c13b3` | 5 | App Form | apps |\n| `485a8ce57ebc` | 9 | Memory Detail | memories |\n| `a6983264d893` | 7 | Category Page | memories |\n| `4450e0d3044e` | 18 | Memory Search | memories |\n| `35c0d8ed0daa` | 3 | Settings Confirmation | settings-profile |\n| `a864d8b70edf` | 16 | Settings Form | settings-profile |\n\n## Verified Flows (v2 flow-walker)\n\nOnly flows that have been executed, verified, and pushed through the v2 flow-walker pipeline are listed here. Each has a `.snapshot.json` for fast replay.\n\n| Flow | Steps | Prerequisites | What it tests | Reports |\n|------|-------|--------------|---------------|---------|\n| `flows/login.yaml` | 5 | signed_out | Auth screen → Google Sign-In → consent → OAuth → home | [normal](https://flow-walker.beastoin.workers.dev/runs/hao5vbBsBI.html), [replay](https://flow-walker.beastoin.workers.dev/runs/-aeMGlV88w.html) |\n| `flows/onboarding.yaml` | 9 | signed_out + auth | Name → Language → Found Omi → Permissions → Review → Speech → Knowledge → Complete → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/UHqa8Fysj2.html), [replay](https://flow-walker.beastoin.workers.dev/runs/B9Py6uoJaO.html) |\n| `flows/logout.yaml` | 5 | auth_ready | Home → Settings → Sign Out → Confirm → Auth screen | [normal](https://flow-walker.beastoin.workers.dev/runs/SH4MXH9gkv.html), [replay](https://flow-walker.beastoin.workers.dev/runs/NcZlCUW0W7.html) |\n| `flows/ask-omi-chat.yaml` | 9 | auth_ready | Home → Ask Omi → Chat page → Type message → AI response → Chat apps drawer → Home | [normal](https://flow-walker.beastoin.workers.dev/runs/O5h8bR6izW.html) |\n\nWhen you modify a Dart file, check if any flow's `covers:` includes it. If so, that flow describes the user journey your change affects — use it to understand context and verify your work.\n\n## Verification & Evidence\n\nAfter making changes, verify them in the live app:\n1. Navigate to the affected screen using the commands above\n2. Check that your changes appear (snapshot, screenshot)\n3. Test interactions (press buttons, fill fields, scroll)\n4. Capture evidence: `agent-flutter screenshot /tmp/evidence.png`\n5. Generate video: `ffmpeg -framerate 1 -pattern_type glob -i '/tmp/e2e-*.png' -vf \"scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1\" -c:v libx264 -pix_fmt yuv420p /tmp/report.mp4`\n\n## Visual audit (before/after screenshots without a device)\n\n`app/scripts/visual_audit.sh` screenshots registered screen states at two revisions and pairs them\nin one gallery. It pumps **production widgets** in `flutter-tester` with synthetic local state: the\napp's own fonts (FontManifest plus Roboto from the pinned SDK), `buildOmiTheme()`, 390×844 logical\npx at 2×, and a loopback-only network guard over the hermetic fixture backend. No login, simulator,\nsigning, customer data or external API.\n\n**Use it** for layout, copy, grouping and visual-regression review of a UI change, and for the\nbefore/after images a UI PR description needs. **Use agent-flutter / flow-walker (above) instead**\nfor anything a headless capture cannot show: native keyboard, status bar and safe areas, system\npermission dialogs, BLE, audio, real network timing, screen readers, or a claim about the installed\napp. The captures are evidence of a widget rendering, not of device behavior.\n\n```bash\napp/scripts/visual_audit.sh --list                                  # scenario ids, per suite\napp/scripts/visual_audit.sh --base origin/main --head HEAD          # every scenario, both sides\napp/scripts/visual_audit.sh --base <sha> --head <sha> --only settings-sheet,settings-device\napp/scripts/visual_audit.sh --head WORKTREE --only settings-help    # this checkout, uncommitted, one side\n```\n\nEach revision is checked out in a temporary detached worktree under `$OMI_WORKTREES` (default: the\nsystem temp dir), prepared like CI (generated env files, `pub get`, `build_runner`) and removed on\nexit. The harness and scenarios always come from the checkout you run the command in, copied over\nboth sides. The current suite (`registry.dart`) compiles against current main; a revision from\nbefore a large UI change is captured with a compat suite (`compat/<name>/`, chosen by its `UNTIL`\ncommit; see `compat/README.md`) that pumps the equivalent old page under the same id, so the old\nProfile page pairs with Account. A page only one side has is shown as \"did not exist\" there. The\noutput directory (`--out`,\ndefault a new temp dir; it must be empty and outside the repository) holds `before/` and `after/`\nPNGs named `<id>.png` or `<id>-<step>.png`, per-side `capture.log`, `INDEX.md` with both SHAs and\ncapture times, and a static `gallery.html`. A scenario that throws on one side is reported as failed\nthere and the rest still capture. **Never commit screenshots.**\n\n**Add a scenario** in `app/integration_test/visual_audit/scenarios/<area>.dart` (new areas are\nspread into `registry.dart`). Put `id` first; it is lowercase-hyphenated, starts with the area, and\nnames the PNGs:\n\n```dart\nAuditScenario(\n  id: 'settings-device',\n  title: 'Device group, device connected',\n  page: 'lib/pages/settings/settings_groups.dart (DeviceGroupPage)',\n  state: 'Signed-in fixture account; a device connected',\n  run: (a) async {\n    await a.pump(const DeviceGroupPage(), providers: [\n      ChangeNotifierProvider<DeviceProvider>.value(value: AuditDeviceProvider(connected: true)),\n    ]);\n    await a.scrollSeries('Open Device from the Settings sheet');\n  },\n),\n```\n\n`AuditRun` (`a`) gives `pump` (the production theme and a broad inert provider roster from\n`fakes.dart`; your `providers` win), `pumpHost` (opens a sheet or dialog from a neutral host),\n`tap`/`longPress`/`enterText` (each settles), `shot(action, step:)`, `scrollSeries(action)` (top,\nevery 700 px, exact bottom) and `server` (the running fixture backend: seed conversations, set\n`assistantReplyText`, `failNext`). Seed state through providers and the fixture backend, find\nwidgets by stable `Key`s, and keep `expect(...)` checks that prove the intended state rendered.\n`app/test/visual_audit/visual_audit_smoke_test.dart` renders every registered scenario in the\nordinary `app/test.sh` suite without writing images, so a scenario that breaks fails CI.\n\n## Decision Tree\n\n| Problem | Solution |\n|---------|----------|\n| Widget not found | Re-snapshot, try scrolling, check if on wrong screen, match by bounds position |\n| Ref expired between commands | Use `press x y` with coordinates from last snapshot bounds, or `press @ref --adb` |\n| System dialog blocking (location, permissions) | `agent-flutter dismiss` — detects and dismisses via ADB |\n| \"No isolate with Marionette\" | ADB foreground + disconnect + reconnect |\n| Snapshot returns 0 interactive elements | Marionette lost widget tree — `disconnect` then `connect` to re-attach |\n| Bottom nav tabs not detected | `back` until nav bar appears, filter InkWell y > 780 |\n| Hot restart breaks connection | Wait 3s → disconnect → connect |\n| Text labels null | Match by `type`, `flutterType`, or `bounds` — Marionette doesn't extract child text |\n| Non-English IME breaks text input | Set system locale to English: `adb shell \"settings put system system_locales en-US\"` |\n\n## iOS Simulator Known Limitations\n\nFor a physical iOS device (has BLE, avoids most limitations below), see **Setup (iOS physical device)** under How to Explore the App.\n\n| Issue | Workaround |\n|-------|-----------|\n| ASWebAuthenticationSession blocks Google Sign-In (system dialog, not automatable) | Use `FirebaseAuth.instance.signInWithCustomToken(token)` via VM Service evaluate |\n| `agent-flutter scroll` fails (uses ADB/transport.swipe, Simulator window disconnected) | Scroll via VM Service Dart evaluation: `pos.jumpTo()` on Scrollable widgets |\n| Simulator.app window shows Home Screen (not app) when booted via SSH user | Use `simctl screenshot` for true device state; use Marionette/VM Service for interaction |\n| No `simctl` touch/swipe API | VM Service evaluate for Flutter-level interaction; cliclick only works if Simulator window is synced |\n| iOS keychain persists through app uninstall | `xcrun simctl erase <UDID>` for full reset |\n| iOS onboarding is shorter than Android (language only → home) | Mark skipped steps as pass with platform note |\n\n**VM Service scroll expression** (target app root library):\n```dart\n(() { var count = 0; void visit(Element el) { if (el.widget is Scrollable) { try { final s = (el as StatefulElement).state as ScrollableState; final p = s.position; if (p.maxScrollExtent > 0 && p.pixels < p.maxScrollExtent) { p.jumpTo((p.pixels + 500).clamp(0.0, p.maxScrollExtent)); count++; } } catch (_) {} } el.visitChildren(visit); } WidgetsBinding.instance.rootElement?.visitChildren(visit); return count; })()\n```\n\n**iOS simulator auth** (dev Firebase tokens rejected by prod API — needs local backend):\n```bash\n# .dev.env must point to a local/dev backend\nAPI_BASE_URL=http://<your-backend-host>:<port>/\nUSE_WEB_AUTH=false\nUSE_AUTH_CUSTOM_TOKEN=true\n# Regenerate envied after .dev.env change, rebuild app\n```\n\n## Guard Conditions\n\n**NEVER:**\n- Use development env vars to bypass auth — test with real auth flows\n- Set `hasCompletedOnboarding` to skip onboarding — test the real flow\n- Modify source code to make tests pass — report the failure instead\n- Commit screenshots to git — use GCS upload for PR evidence\n\n<!-- File: backend/evals/dream_context/README.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_context Skill\n\nPath: `backend/evals/dream_context/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_context/README.md)\n\n<!-- File: backend/evals/dream_reasoning/fixture.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/fixture.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/fixture.json)\n\n<!-- File: backend/evals/dream_reasoning/guard-fixtures.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/guard-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/guard-fixtures.json)\n\n<!-- File: backend/evals/dream_reasoning/quality-fixtures.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-fixtures.json)\n\n<!-- File: backend/evals/dream_reasoning/quality-results.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/quality-results.json)\n\n<!-- File: backend/evals/dream_reasoning/README.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/README.md)\n\n<!-- File: backend/evals/dream_reasoning/results.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_reasoning Skill\n\nPath: `backend/evals/dream_reasoning/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_reasoning/results.json)\n\n<!-- File: backend/evals/dream_triage/fixtures.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/fixtures.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/fixtures.json)\n\n<!-- File: backend/evals/dream_triage/quality-results.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/quality-results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/quality-results.json)\n\n<!-- File: backend/evals/dream_triage/README.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/README.md)\n\n<!-- File: backend/evals/dream_triage/results.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_triage Skill\n\nPath: `backend/evals/dream_triage/results.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_triage/results.json)\n\n<!-- File: backend/evals/dream_verifier/LABELING.v2.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/LABELING.v2.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/LABELING.v2.md)\n\n<!-- File: backend/evals/dream_verifier/manifest.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.json)\n\n<!-- File: backend/evals/dream_verifier/manifest.v2.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/manifest.v2.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/manifest.v2.json)\n\n<!-- File: backend/evals/dream_verifier/README.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Dream_verifier Skill\n\nPath: `backend/evals/dream_verifier/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/backend/evals/dream_verifier/README.md)\n\n<!-- File: desktop/macos/agent/evals/realtime-routing-cases.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Evals Skill\n\nPath: `desktop/macos/agent/evals/realtime-routing-cases.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/desktop/macos/agent/evals/realtime-routing-cases.json)\n\n<!-- File: plugins/chatgpt/gpt_schema.example.yaml (Tokens: ~300 | Category: anthropic-skill) -->\n# Chatgpt Skill\n\nPath: `plugins/chatgpt/gpt_schema.example.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/chatgpt/gpt_schema.example.yaml)\n\n<!-- File: plugins/hume-ai/emotion_config.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Hume-ai Skill\n\nPath: `plugins/hume-ai/emotion_config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/emotion_config.json)\n\n<!-- File: plugins/hume-ai/render.yaml (Tokens: ~300 | Category: anthropic-skill) -->\n# Hume-ai Skill\n\nPath: `plugins/hume-ai/render.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/hume-ai/render.yaml)\n\n<!-- File: plugins/omi-github-app/COMPARISON.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/COMPARISON.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/COMPARISON.md)\n\n<!-- File: plugins/omi-github-app/QUICKSTART.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/QUICKSTART.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/QUICKSTART.md)\n\n<!-- File: plugins/omi-github-app/SETUP_COMPLETE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/SETUP_COMPLETE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/SETUP_COMPLETE.md)\n\n<!-- File: plugins/omi-github-app/TEST_CHANGE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/TEST_CHANGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/TEST_CHANGE.md)\n\n<!-- File: plugins/omi-github-app/THEME_UPDATE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Omi-github-app Skill\n\nPath: `plugins/omi-github-app/THEME_UPDATE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-github-app/THEME_UPDATE.md)\n\n<!-- File: plugins/omi-ms365-app/omi-tools.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Omi-ms365-app Skill\n\nPath: `plugins/omi-ms365-app/omi-tools.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-ms365-app/omi-tools.json)\n\n<!-- File: plugins/omi-slack-app/OMI_SETUP.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Omi-slack-app Skill\n\nPath: `plugins/omi-slack-app/OMI_SETUP.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/BasedHardware/omi/HEAD/plugins/omi-slack-app/OMI_SETUP.md)\n\n<!-- ... +60 more modular skills available via /api/skills/BasedHardware/omi?path={path} -->\n</yakaai_skills>"}