Skip to content

Claude Code plugin packaging: plugins/run-sleep.sh is not copied into the plugin cache, so sleep.sh cannot find its shared runner #52

@BoneLiu

Description

@BoneLiu

Summary

The Claude Code plugin (plugins/claude-code/) cannot run any of its commands (/sleep status, /sleep dry-run, etc.) when installed via Claude Code's /plugin marketplace add. The thin wrapper plugins/claude-code/scripts/sleep.sh looks for a sibling runner at plugins/run-sleep.sh that exists in the repo but is not copied into the Claude Code plugin cache.

Reproduction

Environment: Claude Code 2.1.165 on Linux, SkillOpt repo cloned to ~/Developer/SkillOpt.

# Add the plugin directory as a local marketplace
claude plugin marketplace add /home/<user>/Developer/SkillOpt/plugins/claude-code
# → "Successfully added marketplace: skillopt-sleep (declared in user settings)"

# Install the plugin
claude plugin install skillopt-sleep@skillopt-sleep
# → "Successfully installed plugin: skillopt-sleep@skillopt-sleep (scope: user)"

# Try the status command (or any other /sleep action)
bash /home/<user>/.claude/plugins/cache/skillopt-sleep/skillopt-sleep/0.1.0/scripts/sleep.sh status --project "$(pwd)" --scope invoked

Observed

bash: /home/<user>/.claude/plugins/cache/skillopt-sleep/skillopt-sleep/run-sleep.sh: No such file or directory

The cache only contains the plugins/claude-code/ subtree; the shared plugins/run-sleep.sh (which the thin wrapper calls via BASH_SOURCE[0]/../../run-sleep.sh) is missing.

Expected

Either:

  • /sleep commands work out-of-the-box after claude plugin install, or
  • The install fails fast with a clear error message ("missing shared runner; please also git clone siblings of the plugin folder") instead of silently producing a non-functional plugin.

Workaround (for anyone hitting this)

Set CLAUDE_PLUGIN_ROOT in your shell env to point at the plugin's directory inside the original clone (not the cache):

export CLAUDE_PLUGIN_ROOT=/home/<user>/Developer/SkillOpt/plugins/claude-code

This makes sleep.sh's fallback SHARED="$(cd "$CLAUDE_PLUGIN_ROOT/.." && pwd)/run-sleep.sh" resolve to /home/<user>/Developer/SkillOpt/plugins/run-sleep.sh.

Root cause

plugins/claude-code/scripts/sleep.sh is a thin wrapper that execs a shared runner at plugins/run-sleep.sh:

# plugins/claude-code/scripts/sleep.sh
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SHARED="$(cd "$HERE/../.." && pwd)/run-sleep.sh"        # expects <repo>/plugins/run-sleep.sh
if [ ! -f "$SHARED" ] && [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
  SHARED="$(cd "$CLAUDE_PLUGIN_ROOT/.." && pwd)/run-sleep.sh"
fi
exec bash "$SHARED" "$@"

The default path $HERE/../.. resolves to the cache root ~/.claude/plugins/cache/skillopt-sleep/skillopt-sleep/, which Claude Code does not populate with run-sleep.sh. The $SKILLOPT_SLEEP_REPO env var is read inside run-sleep.sh, not in sleep.sh, so it never gets a chance to help.

Suggested fixes (any of these would work)

  1. Ship the shared runner inside the plugin folder. Copy plugins/run-sleep.sh to plugins/claude-code/scripts/run-sleep.sh (and the engine package skillopt_sleep/ if not already reachable) and update the relative path in sleep.sh. Simplest fix; no upstream restructuring needed.

  2. Add a root-level .claude-plugin/marketplace.json to the SkillOpt repo so Claude Code's marketplace add <github-url> form (which only reads root-level manifests) also works. This would also enable the git-subdir install form documented in the plugin's own marketplace.json.

  3. Fail fast. If the shared runner cannot be found, sleep.sh should print a clear error explaining the packaging issue, rather than exec bash of a missing path.

Additional context

  • The plugins/claude-code/.claude-plugin/marketplace.json declares the plugin's source as git-subdir with path: "plugins/claude-code". This declaration would only work for a downstream marketplace consuming SkillOpt; it does not affect the local-path install path used here.
  • The README's recommended install command (/plugin marketplace add ./skillopt-sleep-plugin) points to a path that does not exist in the repo; the actual path is ./plugins/claude-code/. This is a separate, additional doc issue.
  • Verified the status action itself works correctly once the env-var workaround is in place — output:
    [sleep] nights so far: 0
    [sleep] project: /home/<user>
    [sleep] no staged proposals yet.
    
    So the issue is strictly in plugin packaging, not in the engine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions