feat(install): add --skills-dir mode (Claude Code skills-directory plugin) - #10
Open
mh0pe wants to merge 3 commits into
Open
feat(install): add --skills-dir mode (Claude Code skills-directory plugin)#10mh0pe wants to merge 3 commits into
mh0pe wants to merge 3 commits into
Conversation
…-directory plugin)
Adds --skills-dir [--dir <path>] flag that installs carl as a self-contained
Claude Code skills-directory plugin under .claude/skills/carl/ (or a custom path).
Writes .claude-plugin/plugin.json, copies hooks/ and mcp/, generates hooks.json
and .mcp.json with ${CLAUDE_PLUGIN_ROOT} references so the plugin is relocatable.
Existing --global / --local install paths are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, conflict guard)
- Move hooks.json from plugin root to hooks/hooks.json so auto-discovery finds it
- Wrap hooks.json content in {hooks:{...}} envelope to match framework spec
- Add conflict guard: --skills-dir with --global or --local now exits non-zero
- Fix rewriteFrameworkRefs: restore leading @ on @${CLAUDE_PLUGIN_ROOT} substitutions
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The plugin emitted by installSkillsDir() runs node mcp/index.js which
imports @modelcontextprotocol/sdk, but no node_modules are shipped.
Claude Code does not auto-install a plugin MCP's deps.
Fix: emit hooks/install-mcp-deps.py as a SessionStart hook that:
- Is idempotent (sentinel on node_modules/@modelcontextprotocol/sdk)
- Copies mcp/package.json into CLAUDE_PLUGIN_DATA and runs
`npm install --omit=dev --prefix CLAUDE_PLUGIN_DATA`
- Creates a symlink CLAUDE_PLUGIN_ROOT/mcp/node_modules ->
CLAUDE_PLUGIN_DATA/node_modules so Node's ESM walk-up resolver
finds the packages (NODE_PATH is inert for ESM; the symlink is
the operative mechanism)
- Is fail-open: any error prints a warning to stderr and exits 0
Also adds NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules to the emitted
.mcp.json env (belt-and-suspenders for any CJS callers) alongside the
existing CLAUDE_PROJECT_DIR.
Validated:
- node --check bin/install.js passes
- Negative control: mcp/index.js exits ERR_MODULE_NOT_FOUND without deps
- After install: server logs [CARL] init + "running on stdio" (boot proof)
- Idempotent: second run exits in <0.05s (sentinel short-circuits)
- Fail-open: npm stripped from PATH -> exits 0 with warning to stderr
- Conflict guard and --global/--local paths still work
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
|
First-session caveat for the MCP bootstrap: Claude Code may spawn the carl-mcp concurrently with the SessionStart deps-install hook, so on a brand-new install the MCP becomes available from the next session (or after a restart) once its deps are linked. Subsequent sessions short-circuit (deps already present). |
Author
|
Related PRs — part of a coordinated cross-repo offer (
Reviewing them together is recommended; the same change pattern is mirrored per repo. |
This was referenced Jun 14, 2026
feat(install): add --skills-dir mode (Claude Code skills-directory plugin)
ChristopherKahler/paul#19
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
--skills-dir [--dir <path>]to the installer. It writes a self-contained skills-directory plugin that Claude Code auto-loads in place ascarl@skills-dir:.claude-plugin/plugin.jsonhooks/carl-hook.py+hooks/hooks.json({hooks:{...}}envelope)mcp/+.mcp.json(carl-mcp)@-includes rewritten to${CLAUDE_PLUGIN_ROOT}Default target:
<cwd>/.claude/skills/carl/.Why
claude plugin install, no marketplace setup..claude/skills/but cannot see local marketplace installs; a committed skills-dir plugin is the supported way to ship a plugin to cloud.MCP bootstrapping
A skills-dir (or marketplace) plugin has no install step, so the carl-mcp npm deps aren't present at launch and
node mcp/index.jswould fail withERR_MODULE_NOT_FOUND. The emitted plugin therefore includes a fail-open SessionStart hook that idempotently installs the MCP deps into${CLAUDE_PLUGIN_DATA}and links them onto the MCP's module-resolution path (the MCP is ESM, so anode_modulessymlink is used;.mcp.jsonalso setsNODE_PATHas a fallback for any CJS consumer). The hook short-circuits when deps already exist and never blocks the session if npm is unavailable.Verified: after the SessionStart install, the MCP boots cleanly (
CARL MCP Server running on stdio, 30 tools) with noERR_MODULE_NOT_FOUND.Notes
--global/--local/--config-dirpaths are unchanged.--skills-dircannot be combined with--global/--local(errors out).@.carl/...) are left untouched; only framework@-includes are rewritten.🤖 Generated with Claude Code