Skip to content

feat(scanner): add plugin marketplace skill discovery (#80)#149

Merged
luongnv89 merged 7 commits intomainfrom
feat/80-plugin-marketplace-skills
Apr 12, 2026
Merged

feat(scanner): add plugin marketplace skill discovery (#80)#149
luongnv89 merged 7 commits intomainfrom
feat/80-plugin-marketplace-skills

Conversation

@luongnv89
Copy link
Copy Markdown
Owner

Closes #80

Summary

  • Adds scanPluginMarketplaces() in scanner.ts that recursively discovers skills installed via the Claude plugin marketplace
  • Integrates into scanAllSkills() for global and both scopes
  • Plugin-installed skills are visually distinguished by provider: "plugin" and providerLabel: "Plugin ({marketplace-name})"

Approach

Claude's plugin marketplace installs skills under ~/.claude/plugins/marketplaces/ with two known layout patterns:

  • User-installed: {marketplace}/skills/{skill}/SKILL.md
  • Official bundled: {marketplace}/plugins/{plugin}/skills/{skill}/SKILL.md

Rather than hard-coding path patterns, the implementation walks directories recursively until it finds a SKILL.md file, which cleanly handles both layouts and any future depth variations.

Changes

File Change
src/scanner.ts Add findSkillDirs() recursive helper, scanPluginMarketplaces() (exported), integrate into scanAllSkills()
src/utils/types.ts Add optional marketplace?: string field to SkillInfo
src/scanner.test.ts Add 7 tests for both marketplace path layouts, multi-marketplace discovery, and scope filtering

Test Results

44 scanner tests pass (37 existing + 7 new), 0 failures.

Note: pre-existing failures in publisher.test.ts and cli.test.ts are unrelated to this change (verified on main branch before branching).

Acceptance Criteria

  • ASM scans ~/.claude/plugins/marketplaces/ directories to discover plugin-installed skills
  • Plugin-installed skills appear in asm list output, distinguished from traditionally installed skills (provider: "plugin", providerLabel: "Plugin ({marketplace})")
  • asm info {skill} works for plugin-installed skills and shows the marketplace source (location: "global-plugin-{marketplace}" and marketplace field)
  • Both user-installed marketplace skills and official bundled plugins are detected (recursive finder handles variable nesting depths)
  • ASM correctly reads Claude's local config to determine which plugins are enabled vs disabled — ~/.claude/settings.local.json has no pluginSettings key; flagged as future enhancement

Scan ~/.claude/plugins/marketplaces/ for skills installed via the Claude
plugin marketplace system, alongside traditionally installed skills.

- Add scanPluginMarketplaces() with recursive SKILL.md finder that handles
  variable nesting depths (flat skills/ and nested plugins/.../skills/ layouts)
- Integrate into scanAllSkills() for global and both scopes
- Add optional marketplace field to SkillInfo for source attribution
- Skills appear with provider="plugin" and providerLabel="Plugin ({name})"
- Add 7 tests covering both path layouts, multi-marketplace, and scope filtering
- Rename misleading test title ("is included in scanAllSkills") to accurately
  describe what it tests (scanPluginMarketplaces metadata)
- Add pluginBaseDir param to scanAllSkills so tests can inject a temp dir
  instead of hitting the real ~/.claude/plugins/marketplaces path
- Add integration tests: scanAllSkills includes plugin skills (global scope),
  excludes them (project scope), and deduplicates by realPath when a skill
  appears in both a regular provider path and the plugin marketplace
- Parallelize provider scan and plugin marketplace scan in scanAllSkills
Use lstat() instead of stat() and skip symlinked entries to avoid
infinite recursion from symlink cycles in plugin marketplace dirs.
- scanPluginMarketplaces: remove unreachable lstat/readlink block;
  findSkillDirs() guarantees non-symlink paths so isSymlink is always
  false — inline the constants directly
- scanner.test.ts: rewrite dedup test to call scanAllSkills() end-to-end
  via customPaths + pluginBaseDir so the real dedup logic is exercised,
  not a manual reimplementation
)

Cover previously untested paths:
- symlink-skip safety (the cycle-prevention fix)
- non-directory entries at marketplace level
- multiple skills in a single marketplace
- dirName fallback when SKILL.md has no name field
- rich frontmatter fields (creator, license, compatibility, effort, allowed-tools)
- isSymlink/symlinkTarget are always false/null for marketplace skills
- empty marketplace directory
- scanAllSkills with both scope includes plugin skills
- path/originalPath/realPath values are set correctly
@luongnv89 luongnv89 merged commit a6bb765 into main Apr 12, 2026
10 checks passed
@luongnv89 luongnv89 deleted the feat/80-plugin-marketplace-skills branch April 12, 2026 07:44
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.

Add support for Claude plugin marketplace skills

1 participant