utils:find-claude-plugin-root

This skill should be used when the user needs to locate a plugin's installation path, when ${CLAUDE_PLUGIN_ROOT} doesn't expand in markdown files, or when invoked via /utils:find-claude-plugin-root. Generates a CPR resolver script at /tmp/cpr.py.

16 stars

Best use case

utils:find-claude-plugin-root is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

This skill should be used when the user needs to locate a plugin's installation path, when ${CLAUDE_PLUGIN_ROOT} doesn't expand in markdown files, or when invoked via /utils:find-claude-plugin-root. Generates a CPR resolver script at /tmp/cpr.py.

Teams using utils:find-claude-plugin-root should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/utils-find-claude-plugin-root/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/ai-agents/utils-find-claude-plugin-root/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/utils-find-claude-plugin-root/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How utils:find-claude-plugin-root Compares

Feature / Agentutils:find-claude-plugin-rootStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

This skill should be used when the user needs to locate a plugin's installation path, when ${CLAUDE_PLUGIN_ROOT} doesn't expand in markdown files, or when invoked via /utils:find-claude-plugin-root. Generates a CPR resolver script at /tmp/cpr.py.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Find Claude Plugin Root

This skill generates a Python resolver script at `/tmp/cpr.py` that locates a plugin's installation directory by reading `~/.claude/plugins/installed_plugins.json`.

## Problem It Solves

`${CLAUDE_PLUGIN_ROOT}` environment variable doesn't expand in markdown command files, making it impossible to reference plugin scripts and resources. This is a known issue: https://github.com/anthropics/claude-code/issues/9354

## Solution

Generate a Python script that:
1. Accepts plugin name as argument
2. Tries `${CLAUDE_PLUGIN_ROOT}` first (backwards compatible)
3. Reads installed_plugins.json and searches for exact match
4. If no exact match, finds similar plugin names (fuzzy matching)
5. Outputs the plugin installation path to stdout
6. Saves to `/tmp/cpr.py` (ephemeral, no project pollution)

## Usage

Invoke this skill before executing plugin scripts:

```bash
# Generate the resolver
Skill(skill="utils:find-claude-plugin-root")

# Use it to find a plugin and execute its scripts
PLUGIN_ROOT=$(python3 /tmp/cpr.py readme-and-co)
python "$PLUGIN_ROOT/scripts/populate_license.py" --license MIT
```

## Implementation

### Step 1: Create the CPR resolver Python script

```bash
cat > /tmp/cpr.py << 'CPREOF'
#!/usr/bin/env python3
"""
Claude Plugin Root (CPR) Resolver

Usage: python3 /tmp/cpr.py <plugin-name>
Returns: absolute path to plugin installation directory

Searches for plugins in ~/.claude/plugins/installed_plugins.json with fuzzy matching.
"""

import json
import os
import sys
from pathlib import Path
from difflib import SequenceMatcher


def similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a.lower(), b.lower()).ratio()


def find_plugin_root(plugin_name):
    """
    Find plugin installation directory.

    Returns: (plugin_root_path, match_type)
        match_type: 'env_var', 'exact', 'fuzzy', or None
    """
    # Try CLAUDE_PLUGIN_ROOT first (backwards compatible)
    env_root = os.environ.get('CLAUDE_PLUGIN_ROOT')
    if env_root and os.path.isdir(env_root):
        return env_root.rstrip('/'), 'env_var'

    # Read installed_plugins.json
    plugins_file = Path.home() / '.claude' / 'plugins' / 'installed_plugins.json'

    if not plugins_file.exists():
        return None, None

    try:
        with open(plugins_file, 'r') as f:
            data = json.load(f)
            plugins = data.get('plugins', {})
    except (OSError, json.JSONDecodeError):
        return None, None

    # Try exact match first (case-insensitive)
    for key, value in plugins.items():
        if plugin_name.lower() in key.lower():
            # Handle list or dict value
            if isinstance(value, list) and len(value) > 0:
                value = value[0]
            install_path = value.get('installPath', '').rstrip('/')
            if install_path and os.path.isdir(install_path):
                return install_path, 'exact'

    # Try fuzzy matching if no exact match
    matches = []
    for key, value in plugins.items():
        # Extract just the plugin name from key (e.g., "owner/plugin-name" -> "plugin-name")
        key_parts = key.split('/')
        plugin_part = key_parts[-1] if key_parts else key
        # Also handle @ separator (e.g., "plugin-name@plugin-name")
        plugin_part = plugin_part.split('@')[0]

        ratio = similarity(plugin_name, plugin_part)
        if ratio > 0.6:  # 60% similarity threshold
            # Handle list or dict value
            if isinstance(value, list) and len(value) > 0:
                value = value[0]
            install_path = value.get('installPath', '').rstrip('/')
            if install_path and os.path.isdir(install_path):
                matches.append((ratio, install_path, key))

    if matches:
        # Return best match
        matches.sort(reverse=True, key=lambda x: x[0])
        best_match = matches[0]
        return best_match[1], 'fuzzy'

    return None, None


def main():
    if len(sys.argv) < 2:
        print("Usage: python3 /tmp/cpr.py <plugin-name>", file=sys.stderr)
        print("Example: python3 /tmp/cpr.py readme-and-co", file=sys.stderr)
        sys.exit(1)

    plugin_name = sys.argv[1]
    plugin_root, match_type = find_plugin_root(plugin_name)

    if plugin_root:
        # Output just the path to stdout (for command substitution)
        print(plugin_root)
        sys.exit(0)
    else:
        print(f"Error: Could not locate plugin '{plugin_name}'", file=sys.stderr)
        print(f"Checked: $CLAUDE_PLUGIN_ROOT, ~/.claude/plugins/installed_plugins.json", file=sys.stderr)
        sys.exit(1)


if __name__ == '__main__':
    main()
CPREOF

chmod +x /tmp/cpr.py
```

### Step 2: Verify the script works

```bash
# Test finding the utils plugin itself
if PLUGIN_ROOT=$(python3 /tmp/cpr.py utils); then
  echo "✓ CPR resolver created at /tmp/cpr.py"
  echo "✓ Test lookup succeeded: $PLUGIN_ROOT"
else
  echo "❌ CPR resolver test failed" >&2
  exit 1
fi
```

## Examples

### Find and use readme-and-co plugin

```bash
# Invoke this skill first
Skill(skill="utils:find-claude-plugin-root")

# Then use the resolver - run script and capture output
PLUGIN_ROOT=$(python3 /tmp/cpr.py readme-and-co)
python "$PLUGIN_ROOT/scripts/detect_project_info.py"
```

### Find and use any plugin

```bash
# The resolver works for any plugin
PLUGIN_ROOT=$(python3 /tmp/cpr.py my-plugin)
node "$PLUGIN_ROOT/tools/analyzer.js"
```

## Benefits

- **No project pollution** - Script saved to /tmp, not in project
- **Backwards compatible** - Tries ${CLAUDE_PLUGIN_ROOT} first
- **Fuzzy matching** - Finds plugins even if name doesn't exactly match
- **Pure Python** - No external dependencies (jq not needed)
- **Reusable** - One skill for all plugins
- **Ephemeral** - /tmp/cpr.py cleaned up on reboot

## Limitations

- Recreated on each system reboot (since /tmp is ephemeral)
- Requires Python 3 (standard on all modern systems)

Related Skills

llm-icon-finder

16
from diegosouzapw/awesome-omni-skill

Finding and accessing AI/LLM model brand icons from lobe-icons library. Use when users need icon URLs, want to download brand logos for AI models/providers/applications (Claude, GPT, Gemini, etc.), or request icons in SVG/PNG/WEBP formats.

findymail-automation

16
from diegosouzapw/awesome-omni-skill

Automate Findymail tasks via Rube MCP (Composio). Always search tools first for current schemas.

find-skills

16
from diegosouzapw/awesome-omni-skill

Find and install agent skills with `npx playbooks find skill` and `npx playbooks add skill`. Use whenever a skill needs to be discovered or installed.

clawdhub-find-skills

16
from diegosouzapw/awesome-omni-skill

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. Uses reskill as the package manager.

claude-player

16
from diegosouzapw/awesome-omni-skill

An AI-powered Game Boy emulator agent that uses Claude's vision and reasoning to autonomously play Game Boy games.

claude-opus-4-5-migration

16
from diegosouzapw/awesome-omni-skill

Migrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5. Use when the user wants to update their codebase, prompts, or API calls to use Opus 4.5. Handles model string updates and prompt adjustments for known Opus 4.5 behavioral differences. Does NOT migrate Haiku 4.5.

claude-config-management

16
from diegosouzapw/awesome-omni-skill

Claude Code設定(リポジトリルート)の構成管理ガイド。ファイルレベルsymlinkによる設定管理、管理対象の追加・削除、Taskfileタスクの実行方法を提供する。「設定ファイルを追加して」「新しいスキルを追加して」「symlinkの状態を確認して」「Claude設定を変更して」のようにClaude Code設定の構成変更を行うときに使用する。

awesome-copilot-root-typespec-create-agent

16
from diegosouzapw/awesome-omni-skill

Generate a complete TypeSpec declarative agent with instructions, capabilities, and conversation starters for Microsoft 365 Copilot Use when: the task directly matches typespec create agent responsibilities within plugin awesome-copilot-root. Do not use when: a more specific framework or task-focused skill is clearly a better match.

awesome-copilot-root-mcp-m365-agent-expert

16
from diegosouzapw/awesome-omni-skill

Expert assistant for building MCP-based declarative agents for Microsoft 365 Copilot with Model Context Protocol integration Use when: the task directly matches mcp m365 agent expert responsibilities within plugin awesome-copilot-root. Do not use when: a more specific framework or task-focused skill is clearly a better match.

awesome-copilot-root-mcp-create-declarative-agent

16
from diegosouzapw/awesome-omni-skill

Skill converted from mcp-create-declarative-agent.prompt.md Use when: the task directly matches mcp create declarative agent responsibilities within plugin awesome-copilot-root. Do not use when: a more specific framework or task-focused skill is clearly a better match.

awesome-copilot-root-agent-governance

16
from diegosouzapw/awesome-omni-skill

Use when: the task directly matches agent governance responsibilities within plugin awesome-copilot-root. Do not use when: a more specific framework or task-focused skill is clearly a better match.

agent-tower-plugin

16
from diegosouzapw/awesome-omni-skill

Multi-agent deliberation for Claude Code - orchestrate AI coding assistants (Claude, Codex, Gemini) for council, debate, and consensus workflows