feat(cookidoo): add Cookidoo integration and configurable skills system#31
feat(cookidoo): add Cookidoo integration and configurable skills system#31using-system merged 42 commits intomainfrom
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Cookidoo-specific localization strings for email, password, test button, and error handling across English, French, German, and Spanish locales. Generated l10n artifacts with flutter gen-l10n. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a Cookidoo integration layer (client + repository + models) and wires it into the app via Settings (credentials) and LLM function-calling tools/skills to enable recipe search/detail.
Changes:
- Added Cookidoo feature module with HTTP client (OAuth2), repository, providers, and domain models.
- Registered new LLM tools (
search_recipes,get_recipe_detail) and added a newsearch-recipeskill asset. - Added Cookidoo credentials UI in Settings and localized strings across en/fr/de/es; added
httpdependency.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| pubspec.yaml | Adds http dependency and registers the new skill asset folder. |
| pubspec.lock | Records http as a direct dependency. |
| lib/l10n/app_en.arb | Adds Cookidoo Settings i18n keys + descriptions. |
| lib/l10n/app_fr.arb | Adds French Cookidoo Settings strings. |
| lib/l10n/app_de.arb | Adds German Cookidoo Settings strings. |
| lib/l10n/app_es.arb | Adds Spanish Cookidoo Settings strings. |
| lib/features/tools/providers.dart | Registers Cookidoo-backed tool handlers in the tool registry. |
| lib/features/tools/handlers/search_recipes_handler.dart | Adds search_recipes tool definition + execution wiring. |
| lib/features/tools/handlers/get_recipe_detail_handler.dart | Adds get_recipe_detail tool definition + execution wiring. |
| lib/features/skills/domain/skill_loader.dart | Loads the new search-recipe skill asset. |
| lib/features/settings/presentation/settings_page.dart | Inserts a new Cookidoo section and credentials tile in Settings. |
| lib/features/cookidoo/providers.dart | Adds Riverpod providers for credentials storage, client, and repository. |
| lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart | Adds UI for entering/testing/saving Cookidoo credentials. |
| lib/features/cookidoo/domain/models/cookidoo_recipe_overview.dart | Adds search result model parsing. |
| lib/features/cookidoo/domain/models/cookidoo_recipe_detail.dart | Adds recipe detail model parsing (ingredients/steps/nutrition). |
| lib/features/cookidoo/domain/models/cookidoo_exceptions.dart | Adds typed exceptions for auth/not-found/network errors. |
| lib/features/cookidoo/domain/models/cookidoo_credentials.dart | Adds credentials value object. |
| lib/features/cookidoo/domain/models/cookidoo_auth_token.dart | Adds in-memory auth token model + expiry logic. |
| lib/features/cookidoo/domain/cookidoo_repository.dart | Defines Cookidoo repository interface. |
| lib/features/cookidoo/data/cookidoo_repository_impl.dart | Implements repository behavior and credential gating for details. |
| lib/features/cookidoo/data/cookidoo_credentials_storage.dart | Persists credentials in SharedPreferences. |
| lib/features/cookidoo/data/cookidoo_client.dart | Implements Cookidoo API calls + token refresh/login. |
| assets/skills/search-recipe/SKILL.md | Adds LLM skill instructions for Cookidoo search/detail usage. |
| SPEC.md | Documents the new Cookidoo integration dependency. |
| docs/superpowers/specs/2026-04-20-cookidoo-search-design.md | Adds a design spec for the Cookidoo integration. |
| docs/superpowers/plans/2026-04-20-cookidoo-search.md | Adds an implementation plan for Cookidoo integration work. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The Test button was calling repo.isAuthenticated() which used the previously saved (empty) credentials instead of the ones typed in the dialog fields. Now tests directly with the input field values and logs errors for debugging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 26 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The repository now reads credentials at call time instead of at construction time. This breaks the ref.watch chain that was causing toolRegistryProvider to rebuild when credentials changed, which triggered a .depends.isEmpty assertion in conversation_page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ild crash Navigator.pop() must happen before setCredentials() to avoid the framework _dependents.isEmpty assertion. The provider state change was triggering a widget rebuild while the dialog was still disposing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… crash Use storage.write + ref.invalidate instead of notifier.setCredentials to prevent the provider state transition (loading→data) from triggering a widget rebuild while the dialog route is still animating out. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pletes The overlay GlobalKeys conflict when the provider rebuild happens while the dialog route is still animating out. Now we write to storage first, await the dialog close animation, then invalidate the provider. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 26 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ebuild crash Replace ConsumerWidget + ref.watch on async provider with a ConsumerStatefulWidget that manages credentials locally via setState. The dialog now returns the credentials via Navigator.pop result and the save happens after the dialog is fully closed — no provider invalidation, no rebuild cascade, no overlay GlobalKey conflict. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… crash The ConsumerState/ConsumerWidget creates InheritedWidget dependencies that conflict with dialog overlay disposal, causing _dependents.isEmpty assertion. Replaced with plain StatefulWidget using SharedPreferences directly — zero Riverpod involvement in this widget. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add explicit instructions in the base system prompt to always use search_recipes before answering and base recipes on search results. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 32 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `tools` field to SKILL.md frontmatter to declare associated tools - Add SkillPreferencesStorage for persisting skill enabled state - Add Skills section in Settings with SwitchListTile per skill - Filter tool registry to only load handlers for enabled skills - Revert system prompt to minimal (skill-specific instructions come from SKILL.md files only) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Defer _initModel to Future.microtask so the UI renders first - Add Future.delayed(Duration.zero) between history replay messages to keep the text input responsive during context loading - Yield to the event loop every ~50ms during token streaming so typing and scrolling are not blocked by inference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 38 out of 39 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
SkillsSection now invalidates skillRegistryProvider on toggle. ConversationPage listens to skillRegistryProvider and recreates the chat session when skills change, so enabled/disabled tools and instructions take effect without restarting the app. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows all skills with check/cross icon indicating enabled status. Tab inserted between Recipe and AI tabs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This instruction belongs to the recipe-format skill, not the base system prompt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 39 out of 40 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Tier 1 — Critical business logic (94 tests): - system_prompt_builder, cookidoo models (overview, detail, auth token, credentials, exceptions), cookidoo client, repository impl, search/detail/share handlers, skill preferences storage Tier 2 — Storage and config (41 tests): - recipe_config_storage, recipe_config, tm_version, unit_system, recipe_level, cookidoo_credentials_storage Tier 3 — Widget tests (14 tests): - tm_version/unit_system/portions/level/dietary_restrictions picker tiles, home_shell navigation bar Coverage: 41% → ~75% (309 tests total) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Display the current portion count in displayMedium bold text above the slider so the dialog feels less empty. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set inputClearMode to never while _chat is null so the user's message stays in the input field when they try to send before the model is ready. Once the model is loaded, clear mode reverts to always. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 64 out of 65 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
lib/features/recipe/presentation/dietary_restrictions_tile.dart:33
- This dialog creates a
TextEditingControllerbut no longer disposes it. Over time (or with repeated opens) this can leak resources. To avoid the Android crash while still disposing safely, consider moving the controller into a dedicatedStatefulWidgetused as the dialog content and dispose it in that widget’sdispose()method (instead of afterawait showDialog).
final controller = TextEditingController(text: current);
final result = await showDialog<String>(
context: context,
useRootNavigator: true,
builder: (ctx) => AlertDialog(
title: Text(l10n.settingsDietaryRestrictionsDialogTitle),
content: TextField(
controller: controller,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Remove unused import, unused local variable, unused class, and rename _fullJson to fullJson to fix no_leading_underscores_for_local warning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cast tool limit arg as num?.toInt() for JSON compat - URL-encode refresh token in token request body - Wrap HTTP calls in try/catch → typed exceptions - Gate sensitive debug logs behind kDebugMode - Use l10n key for hard-coded 'Cookidoo' strings - Make getRecipeDetail async, catch Exception in isAuthenticated - Add catch-all in GetRecipeDetailHandler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 64 out of 65 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
lib/features/cookidoo/) with OAuth2 auth, recipe search (no auth required), and recipe detail (auth required) via the Cookidoo mobile APIMessage.toolResponse, enabling the model to use Cookidoo search results as context for generating recipestoolsfield); new Settings UI section with on/off toggles per skill; disabled skills exclude both instructions and tools from the LLMChanges
Cookidoo service layer
CookidooRecipeOverview,CookidooRecipeDetail,CookidooAuthToken,CookidooCredentials, typed exceptionsCookidooClient: HTTP client with OAuth2 login/refresh, search, recipe detailCookidooRepository: abstract interface + implementation with lazy credentialsConfigurable skills
Skillmodel gainstoolsfield parsed from SKILL.md frontmatterSkillPreferencesStoragepersists on/off state per skill in SharedPreferencesSkillsSectionwidget in Settings withSwitchListTileper skill (default: off)toolRegistryProvideronly registers handlers for tools declared by enabled skillsTool response integration
ToolHandler.executereturnsMap<String, dynamic>?instead ofvoidToolRegistry.handlereturns tool name + resultconversation_page.dartsends results back viaMessage.toolResponse+ re-generates LLM responseTest plan
>>> SearchRecipesHandler+>>> Re-generatingflutter test→ all 166 tests passflutter analyze→ 0 errors🤖 Generated with Claude Code