fix(plugin): support BASE MCP in Codex - #4
Open
mh0pe wants to merge 16 commits into
Open
Conversation
Prefer CLAUDE_PROJECT_DIR when set and non-blank; fall back to the existing __dirname/HOOK_DIR relative resolution unchanged. Never throws on missing env var. Affected: src/packages/base-mcp/index.js and all six auto-register hooks (active, backlog, base-pulse-check, operator, satellite-detection, psmm-injector) plus apex-insights.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lugin
Adds --skills-dir [--dir <path>] flag to bin/install.js (purely additive,
no existing flags changed). Installs a Claude Code skills-directory plugin
at <cwd>/.claude/skills/base/ by default (or --dir <path>). Creates
.claude-plugin/plugin.json, commands/, skills/, base-framework/ (with
hooks/ and packages/), hooks/ + hooks.json (UserPromptSubmit + SessionStart
wiring), and mcp/ + .mcp.json. All @~/.claude/base-framework/ and
@./.claude/base-framework/ refs in copied .md files are rewritten to
${CLAUDE_PLUGIN_ROOT}/base-framework/. Hook and MCP command paths use
${CLAUDE_PLUGIN_ROOT}. Prints load instructions on completion.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…R resolution for hooks and MCP
- 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>
…E_PROJECT_DIR hooks, conflict guard)
- Finding 1 (HIGH): move .mcp.json write target from mcp/.mcp.json to
path.join(targetBase, '.mcp.json') so it lands at the plugin root.
The arg path \${CLAUDE_PLUGIN_ROOT}/mcp/base-mcp/index.js is unchanged.
- Finding 2 (HIGH): merge origin/feat/workspace-project-dir into this branch
so all 7 emitted hooks carry CLAUDE_PROJECT_DIR-first workspace resolution
(falls back to HOOK_DIR.parent.parent when env is unset). base-mcp/index.js
receives the same layered resolution. Plugin requires CLAUDE_PROJECT_DIR to
be set at runtime; Claude Code sets it automatically for plugin hooks.
This branch depends on feat/workspace-project-dir — note for PR description.
- Finding 3 (MEDIUM): add conflict guard — --skills-dir combined with --global
or --local prints an error and exits non-zero before installSkillsDir() runs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Emit hooks/install-mcp-deps.py (SessionStart hook) that installs @modelcontextprotocol/sdk deps into CLAUDE_PLUGIN_DATA and symlinks PLUGIN_ROOT/mcp/base-mcp/node_modules -> PLUGIN_DATA/node_modules so Node ESM can resolve bare specifiers from the importing file's directory. 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 all 5 UserPromptSubmit hooks + satellite-detection SessionStart hook - Adds install-mcp-deps.py as second SessionStart hook entry - Script emitted inline (not in src/hooks/) so it stays scoped to skills-dir 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>
…ve source tree, dual-mode npx, MCP-deps bootstrap, CI, .gitignore symlink fix Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…converged root tree Semantic conflict resolution: installSkillsDir() re-pointed from deleted src/ subtrees to the converged root layout introduced by feat/native-plugin: src/commands -> commands/ src/skill -> skills/base/ src/framework -> base-framework/ src/hooks -> hooks/ src/packages/base-mcp -> mcp/ (flat layout, index.js at mcp/index.js) MCP bootstrap deduped: replaced embedded heredoc install-mcp-deps.py with a file-copy from root hooks/install-mcp-deps.py (single source of truth). MCP .mcp.json args updated to mcp/index.js (not mcp/base-mcp/index.js). Symlink target corrected to mcp/node_modules (flat layout). Hook conflicts (active-hook.py, backlog-hook.py, base-pulse-check.py, operator.py, psmm-injector.py, satellite-detection.py, apex-insights.py) and mcp/index.js resolved to HEAD (native-plugin's more complete CLAUDE_PROJECT_DIR resolution with .base/ guard and home-dir protection). feat/workspace-project-dir is transitively subsumed via e780c10 in feat/install-skills-dir's history — all CLAUDE_PROJECT_DIR workspace resolution is present in mcp/index.js and hooks. Not re-merged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: adopt native-plugin + skills-dir + workspace/MCP capabilities
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mh0pe
marked this pull request as ready for review
July 23, 2026 16:17
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 changed
.codex-plugin/plugin.jsonfor BASEcwdRoot cause
The legacy
.mcp.jsonpasses${CLAUDE_PLUGIN_ROOT}/mcp/index.jsas an MCPargument. Claude Code expands that placeholder, but Codex treats MCP arguments
literally, so Node receives a nonexistent path.
The Codex-native manifest uses the supported relative-
cwdcontract and anexplicit launcher. Claude Code continues to use the existing
.mcp.json.Validation
node --test tests/codex-plugin.test.mjsactionlint .github/workflows/plugin-install.ymlCODEX_HOMEmarketplace add, plugin install, andcodex mcp listtools/listreturning 21 toolsReview note
This branch is based on the integrated
mh0pe/basefork main, so the upstreamcomparison also contains the native-plugin work already proposed in #1, #2,
and #3. The Codex-specific change is commit
84beab6.