feat(plugin): native Claude Code plugin + dual-mode npx install#3
Open
mh0pe wants to merge 6 commits into
Open
feat(plugin): native Claude Code plugin + dual-mode npx install#3mh0pe wants to merge 6 commits into
mh0pe wants to merge 6 commits into
Conversation
- Add .claude-plugin/plugin.json (name=base, version=3.1.5 from package.json)
- Add .claude-plugin/marketplace.json for claude plugin marketplace add
- Copy src/commands -> commands/ with namespace-strip (base: prefix removed)
- Copy src/skill/base.md -> skills/base/base.md
- Copy src/framework -> base-framework/ (tasks, templates, context, etc.)
- Copy src/hooks -> hooks/ with CLAUDE_PROJECT_DIR-first WORKSPACE_ROOT fix
- Copy src/packages/base-mcp -> mcp/ with CLAUDE_PROJECT_DIR-first WORKSPACE_PATH fix
- Wire hooks/hooks.json: 5 UserPromptSubmit + 1 SessionStart (apex-insights excluded)
- Add .mcp.json registering base-mcp via node ${CLAUDE_PLUGIN_ROOT}/mcp/index.js
- Rewrite @~/.claude/base-framework/ -> @${CLAUDE_PLUGIN_ROOT}/base-framework/ in
commands/, skills/, base-framework/ plugin-native tree (66 refs)
- src/ tree untouched: npx install reads src/ which retains @~/.claude/ refs
- bin/install.js: add copyFileExpandingMacro() to expand ${CLAUDE_PLUGIN_ROOT}
during any copy, ensuring no literal placeholder in npx-installed output
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the duplicate src/ framework/command/hook/mcp trees that
shadowed the root plugin-native tree (commands/, base-framework/,
skills/, hooks/, mcp/). The root tree uses ${CLAUDE_PLUGIN_ROOT}
macros and is the single committed source of truth.
bin/install.js now reads from the root tree for all install modes
(--global, --local, --workspace). copyFileExpandingMacro is now
exercised for every copy path, substituting ${CLAUDE_PLUGIN_ROOT}
with the resolved install target so no literal placeholder survives
in npx-installed output.
Retained: src/templates/ (workspace.json + operator.json) — these
are the npx workspace-mode templates; they contain no macros and
have no equivalent in the root tree.
package.json files[] updated to ship the root tree instead of src/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add committed hooks/install-mcp-deps.py (SessionStart hook) that installs @modelcontextprotocol/sdk deps into CLAUDE_PLUGIN_DATA and symlinks PLUGIN_ROOT/mcp/node_modules -> PLUGIN_DATA/node_modules so Node ESM can resolve bare specifiers from the importing file's directory. The MCP server lives at mcp/index.js (not mcp/base-mcp/index.js as in the install-skills-dir branch). The symlink is placed at mcp/node_modules adjacent to mcp/index.js so ESM's directory-walk resolution succeeds. NODE_PATH added to .mcp.json env (CJS fallback; Node ESM requires the symlink bridge since ESM does not honour NODE_PATH at import time). - Idempotent: skips install if sdk marker already present; re-asserts symlink - Fail-open: warns to stderr and exits 0 on any error (npm missing, env unset) - Keeps 5 UserPromptSubmit hooks + satellite-detection SessionStart hook - Adds install-mcp-deps as second SessionStart hook entry - npx-mode safety: hook is harmless/fail-open when CLAUDE_PLUGIN_DATA unset Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… symlink - .gitignore: add slash-less `mcp/node_modules` entry so the SessionStart symlink (created by install-mcp-deps.py) is properly ignored. The existing `node_modules/` pattern matches directories only; a trailing slash does NOT match symlinks, causing the link to appear as untracked. - .github/workflows/plugin-install.yml: validate + auth-free install smoke on every push/PR (identical shape to paul/seed/carl native workflows). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
|
Related PRs — part of a coordinated cross-repo offer (native Claude Code plugin + dual-mode npx install) applied across the carl/base/paul/seed framework forks:
Reviewing them together is recommended; the same change pattern is mirrored per repo. |
This was referenced Jun 14, 2026
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
Makes the repo installable both as a native Claude Code marketplace plugin and via the existing npx commands, from one committed source. Adds root
.claude-plugin/plugin.json+.claude-plugin/marketplace.json(single-plugin,source: "."), so:works, while
npx @chrisai/base --global|--local|--workspacekeeps working unchanged.Single source of truth
The committed framework/command/skill/hook/mcp tree at the repo root uses
${CLAUDE_PLUGIN_ROOT}macros and is the sole source.bin/install.jsreads from that root tree for every install mode and substitutes${CLAUDE_PLUGIN_ROOT}with the real install target during copy (round-tripping to the old~/.claude/...semantics for--global). There is no parallelsrc/copy to drift out of sync (only the two pure-JSONsrc/templates/data files remain, used by--workspace).MCP bootstrapping
A marketplace/git plugin has no install step, so the base-mcp npm deps aren't present at launch and
node mcp/index.jswould fail withERR_MODULE_NOT_FOUND. A committed fail-open SessionStart hook (hooks/install-mcp-deps.py) idempotently installs the MCP deps into${CLAUDE_PLUGIN_DATA}and links them onto the MCP's module-resolution path (base-mcp is ESM, and Node's ESM loader ignoresNODE_PATH, so anode_modulessymlink is the operative mechanism;.mcp.jsonalso setsNODE_PATHas a CJS fallback). It short-circuits when deps exist and never blocks the session if npm /${CLAUDE_PLUGIN_DATA}is unavailable.Verified: after the bootstrap, the MCP boots cleanly (
BASE MCP Server running on stdio), noERR_MODULE_NOT_FOUND.First-session caveat: Claude Code may spawn the MCP concurrently with the SessionStart hook, so on a brand-new install base-mcp becomes available from the next session (or after a restart) once deps are linked.
CI
Adds
.github/workflows/plugin-install.yml:claude plugin validate . --strict(the CI-grade manifest gate) plus an install smoke (marketplace add+install base@base+list). Both verified to run auth-free.Notes
CLAUDE_PROJECT_DIRworkspace-resolution fix (the hooks need it to resolve the workspace correctly in a plugin layout); the no-env else-branch is byte-equivalent to the original default. Project-runtime refs (@.base/...) are untouched..mcp.jsonand runs its ownnpm install; the plugin-only${CLAUDE_PLUGIN_DATA}env and bootstrap hook don't leak into npx installs, and no literal${CLAUDE_PLUGIN_ROOT}appears in npx output.🤖 Generated with Claude Code