Skip to content

feat(cookidoo): add Cookidoo integration and configurable skills system#31

Merged
using-system merged 42 commits intomainfrom
feat/cookidoo-search
Apr 21, 2026
Merged

feat(cookidoo): add Cookidoo integration and configurable skills system#31
using-system merged 42 commits intomainfrom
feat/cookidoo-search

Conversation

@using-system
Copy link
Copy Markdown
Owner

@using-system using-system commented Apr 20, 2026

Summary

  • Cookidoo integration — reusable service layer (lib/features/cookidoo/) with OAuth2 auth, recipe search (no auth required), and recipe detail (auth required) via the Cookidoo mobile API
  • Tool response flow — tool handlers now return results to the LLM via Message.toolResponse, enabling the model to use Cookidoo search results as context for generating recipes
  • Configurable skills system — each skill declares its associated tools in SKILL.md frontmatter (tools field); new Settings UI section with on/off toggles per skill; disabled skills exclude both instructions and tools from the LLM
  • Cookidoo credentials settings — email/password fields with test connection button in Settings
  • i18n — all new strings in 4 locales (en, fr, de, es)
  • Bug fixes — TextEditingController disposal crash in dialogs under StatefulShellRoute on Android

Changes

Cookidoo service layer

  • Domain models: CookidooRecipeOverview, CookidooRecipeDetail, CookidooAuthToken, CookidooCredentials, typed exceptions
  • CookidooClient: HTTP client with OAuth2 login/refresh, search, recipe detail
  • CookidooRepository: abstract interface + implementation with lazy credentials
  • Riverpod providers for the full feature

Configurable skills

  • Skill model gains tools field parsed from SKILL.md frontmatter
  • SkillPreferencesStorage persists on/off state per skill in SharedPreferences
  • SkillsSection widget in Settings with SwitchListTile per skill (default: off)
  • toolRegistryProvider only registers handlers for tools declared by enabled skills
  • System prompt reverted to minimal — skill-specific behavior lives in SKILL.md only

Tool response integration

  • ToolHandler.execute returns Map<String, dynamic>? instead of void
  • ToolRegistry.handle returns tool name + result
  • conversation_page.dart sends results back via Message.toolResponse + re-generates LLM response

Test plan

  • Settings → Skills section shows 3 skills (all off by default)
  • Enable share-recipe → share tool available in chat
  • Enable search-recipe → search_recipes + get_recipe_detail tools available
  • Disable a skill → its tools are not registered
  • Settings → Cookidoo → enter credentials → Test → success snackbar
  • Ask for a recipe with search-recipe enabled → logs show >>> SearchRecipesHandler + >>> Re-generating
  • flutter test → all 166 tests pass
  • flutter analyze → 0 errors

🤖 Generated with Claude Code

using-system and others added 11 commits April 20, 2026 20:10
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>
Copilot AI review requested due to automatic review settings April 20, 2026 18:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 new search-recipe skill asset.
  • Added Cookidoo credentials UI in Settings and localized strings across en/fr/de/es; added http dependency.

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.

Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/cookidoo/data/cookidoo_client.dart Outdated
Comment thread lib/features/tools/handlers/get_recipe_detail_handler.dart Outdated
Comment thread assets/skills/search-recipe/SKILL.md Outdated
Comment thread lib/features/cookidoo/domain/models/cookidoo_recipe_detail.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/cookidoo/providers.dart
Comment thread lib/features/tools/handlers/search_recipes_handler.dart Outdated
Comment thread lib/features/cookidoo/data/cookidoo_credentials_storage.dart
using-system and others added 2 commits April 20, 2026 21:10
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>
Copilot AI review requested due to automatic review settings April 20, 2026 19:12
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread lib/features/cookidoo/data/cookidoo_credentials_storage.dart
Comment thread lib/features/cookidoo/data/cookidoo_client.dart Outdated
Comment thread lib/features/cookidoo/data/cookidoo_client.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/tools/handlers/search_recipes_handler.dart Outdated
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/cookidoo/data/cookidoo_client.dart Outdated
Comment thread lib/features/cookidoo/data/cookidoo_client.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
using-system and others added 2 commits April 20, 2026 21:21
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>
Copilot AI review requested due to automatic review settings April 20, 2026 19:23
using-system and others added 2 commits April 20, 2026 21:25
… 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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart Outdated
Comment thread lib/features/tools/handlers/search_recipes_handler.dart Outdated
Comment thread lib/features/tools/handlers/search_recipes_handler.dart
using-system and others added 2 commits April 20, 2026 21:30
…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>
Copilot AI review requested due to automatic review settings April 20, 2026 19:36
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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread lib/features/tools/tool_registry.dart
Comment thread lib/features/chat/presentation/conversation_page.dart
Comment thread lib/features/recipe/presentation/dietary_restrictions_tile.dart
Comment thread assets/skills/search-recipe/SKILL.md
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart
Comment thread lib/features/cookidoo/data/cookidoo_client.dart Outdated
Comment thread lib/features/cookidoo/data/cookidoo_client.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart
using-system and others added 2 commits April 21, 2026 08:52
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>
Copilot AI review requested due to automatic review settings April 21, 2026 06:55
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@using-system using-system changed the title feat(cookidoo): add Cookidoo recipe search integration feat(cookidoo): add Cookidoo integration and configurable skills system Apr 21, 2026
- 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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread lib/features/skills/presentation/skills_section.dart
Comment thread lib/features/skills/presentation/skills_section.dart
Comment thread lib/features/skills/data/skill_preferences_storage.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart
using-system and others added 2 commits April 21, 2026 09:10
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>
Copilot AI review requested due to automatic review settings April 21, 2026 07:11
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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread lib/l10n/app_fr.arb
Comment thread lib/l10n/app_de.arb
Comment thread lib/l10n/app_es.arb
Comment thread lib/l10n/app_es.arb
Comment thread lib/features/chat/presentation/conversation_page.dart
Comment thread lib/features/skills/presentation/skills_section.dart
Comment thread lib/features/chat/presentation/conversation_page.dart
Comment thread lib/l10n/app_de.arb
Comment thread test/features/tools/tool_registry_test.dart
Comment thread lib/l10n/app_fr.arb
using-system and others added 2 commits April 21, 2026 09:26
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>
Copilot AI review requested due to automatic review settings April 21, 2026 07:28
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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TextEditingController but 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 dedicated StatefulWidget used as the dialog content and dispose it in that widget’s dispose() method (instead of after await 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.

Comment thread lib/features/cookidoo/data/cookidoo_client.dart Outdated
Comment thread test/features/skills/data/skill_preferences_storage_test.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart
Comment thread lib/features/chat/presentation/conversation_page.dart
Comment thread lib/l10n/app_fr.arb
Comment thread test/features/tools/handlers/search_recipes_handler_test.dart
using-system and others added 2 commits April 21, 2026 09:36
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>
Copilot AI review requested due to automatic review settings April 21, 2026 07:42
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart
Comment thread lib/features/tools/handlers/search_recipes_handler.dart
Comment thread lib/features/tools/handlers/get_recipe_detail_handler.dart
Comment thread lib/features/skills/presentation/skills_section.dart
Comment thread lib/l10n/app_fr.arb
Comment thread lib/features/chat/presentation/conversation_page.dart
Comment thread test/features/skills/data/skill_preferences_storage_test.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart
Comment thread lib/features/cookidoo/presentation/cookidoo_credentials_tile.dart
@using-system using-system merged commit 69cf26f into main Apr 21, 2026
7 checks passed
@using-system using-system deleted the feat/cookidoo-search branch April 21, 2026 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants