Skip to content

feat: inject user-invocable: false in ClaudeAdapter#4

Open
hugo-bluecorn wants to merge 4 commits intoserverpod:mainfrom
hugo-bluecorn:feature/claude-adapter-user-invocable
Open

feat: inject user-invocable: false in ClaudeAdapter#4
hugo-bluecorn wants to merge 4 commits intoserverpod:mainfrom
hugo-bluecorn:feature/claude-adapter-user-invocable

Conversation

@hugo-bluecorn
Copy link

Summary

Fixes #3

When ClaudeAdapter.installSkill() copies a SKILL.md into .claude/skills/, Claude Code defaults user-invocable to true when the field is absent from frontmatter. This causes every installed skill to appear as a slash command (22+ per Flutter project, 41+ for Serverpod).

This PR overrides installSkill() in ClaudeAdapter to inject user-invocable: false into the SKILL.md frontmatter when the field is absent. Existing values (true or false) are preserved.

Approach: Targeted text insertion before the closing --- — no SkillMetadata round-tripping, so nested YAML fields (e.g., metadata:) are preserved byte-for-byte.

Changes

  • lib/src/ide/adapters/claude_adapter.dart — override installSkill() with frontmatter injection
  • test/ide/claude_adapter_test.dart — 5 new tests, 1 updated existing test

Test plan

  • dart test — 130/130 passing
  • dart analyze — no issues
  • dart format — clean

Hugo Bluecorn and others added 4 commits March 21, 2026 09:16
…llSkill

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 Opus 4.6 (1M context) <noreply@anthropic.com>
Add four edge case tests for ClaudeAdapter.installSkill():
- Preserves explicit user-invocable: true
- No duplication when user-invocable: false already present
- Preserves nested frontmatter fields byte-for-byte
- Handles SKILL.md with no body

All tests pass against existing implementation, confirming
the guard logic correctly handles these edge cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hugo-bluecorn
Copy link
Author

Manual Verification

Verified end-to-end against a real Flutter project. The fix correctly injects user-invocable: false into installed SKILL.md frontmatter, and Claude Code no longer exposes registry skills as slash commands.

Environment: skills v0.2.1 (from feature branch), Dart 3.11.1, Flutter 3.41.4 (via fvm), Linux

Steps to reproduce

  1. Activate the CLI from the feature branch:

    dart pub global activate --source path <path-to-feature-branch>
    export PATH="$HOME/fvm/versions/stable/bin:$HOME/.pub-cache/bin:$PATH"
  2. Create a fresh Flutter project:

    cd /tmp
    flutter create --template=app test_skills_project
    cd test_skills_project
  3. Initialize the .claude/ directory so the CLI auto-detects Claude as a target IDE:

    mkdir -p .claude
  4. Run skills get to install registry skills:

    skills get

    Output:

    Cloning flutter/skills...
    Cloning serverpod/skills-registry...
      [claude] Installed flutter-working-with-databases
      [claude] Installed flutter-theming-apps
      [claude] Installed flutter-testing-apps
      [claude] Installed flutter-setting-up-on-windows
      [claude] Installed flutter-setting-up-on-macos
      [claude] Installed flutter-setting-up-on-linux
      [claude] Installed flutter-reducing-app-size
      [claude] Installed flutter-managing-state
      [claude] Installed flutter-localizing-apps
      [claude] Installed flutter-interoperating-with-native-apis
      [claude] Installed flutter-improving-accessibility
      [claude] Installed flutter-implementing-navigation-and-routing
      [claude] Installed flutter-handling-http-and-json
      [claude] Installed flutter-handling-concurrency
      [claude] Installed flutter-embedding-native-views
      [claude] Installed flutter-caching-data
      [claude] Installed flutter-building-plugins
      [claude] Installed flutter-building-layouts
      [claude] Installed flutter-building-forms
      [claude] Installed flutter-architecting-apps
      [claude] Installed flutter-animating-apps
      [claude] Installed flutter-adding-home-screen-widgets
    Installed 22 skill(s) for claude.
    
  5. Verify user-invocable: false was injected into the frontmatter:

    head -10 .claude/skills/flutter-managing-state/SKILL.md

    Output:

    ---
    name: "flutter-managing-state"
    description: "Manages application and ephemeral state in a Flutter app. ..."
    metadata:
      model: "models/gemini-3.1-pro-preview"
      last_modified: "Thu, 12 Mar 2026 22:18:06 GMT"
    
    user-invocable: false
    ---

    The user-invocable: false field is present in the frontmatter. The nested metadata: block with sub-keys is preserved byte-for-byte — confirming that the targeted text insertion approach does not corrupt complex YAML.

  6. Open Claude Code and verify skills are passive context, not slash commands:

    claude
    • Typed /fl — no autocomplete for flutter-* skills (previously all 22 autocompleted)
    • Ran show skills — flutter-* skills appear in the list as loaded passive context, which is the expected behavior
    • Only the user's own workflow commands (/update-config, /simplify, etc.) are invocable

Before vs After

Behavior Before (main) After (this PR)
user-invocable in installed SKILL.md Absent (defaults to true) false (injected)
Flutter skills in / autocomplete 22 slash commands None
Flutter skills as passive context Yes Yes (unchanged)
Nested YAML in frontmatter N/A Preserved byte-for-byte
Explicit user-invocable: true N/A Preserved (not overwritten)

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.

Claude Code: installed skills appear as slash commands instead of passive context

1 participant