This is the cross-surface contract for local MCP servers, skills, and hooks. It explains how GJC relates to Claude Code and OpenAI Codex layouts without making the per-surface references repeat one another.
GJC has two canonical persistence scopes:
- Project:
<project>/.gjc/(the repository's project root, or the opened project directory when there is no repository root). - User:
~/.gjc/agent/(the canonical user agent directory; the configured home-relative GJC config root and its legacy skill roots are described in Skills).
These .gjc scopes are the long-term GJC authority. A normal standalone session
loads native project/user configuration from them, applies the native
precedence rules below, and reports provenance from those files. A file under a
Claude Code or Codex convention directory is not silently copied, overlaid, or
used to invent another GJC configuration scope.
Claude Code and Codex are explicit import sources for the /extensions
transaction. Selecting a product and source scope reads only that bounded
source, normalizes the selected surfaces, and writes the accepted result into
the chosen .gjc destination. Import does not edit the source files. MCP and
skill files from those hosts are never implicit standalone-session authorities.
Hooks follow the same authority boundary: ordinary sessions adapt canonical
native .gjc/hooks/ modules to ExtensionRunner. Claude/Codex directory hook
providers remain available for explicit import and diagnostics, but their
foreign files are not imported or executed directly at startup. Codex still
owns managed hooks.json command scheduling. The accepted event and phase
rules are in Hooks; importing a hook creates the canonical
.gjc/hooks/ copy and its provenance boundary.
The table uses project and user to mean the selected source scope and selected destination scope. A user source is never scanned merely because the operator's home contains a foreign directory; choose it explicitly in the wizard (or use an explicit non-interactive command).
| Surface | Native GJC project | Native GJC user | Claude Code project source | Claude Code user source | Codex project source | Codex user source | GJC treatment |
|---|---|---|---|---|---|---|---|
| MCP | <project>/.gjc/mcp.json |
~/.gjc/agent/mcp.json |
<project>/.mcp.json for the #4492 import transaction; the doctor also reports .claude/mcp.json and .claude/.mcp.json convention candidates |
~/.claude.json for the import transaction |
<project>/.codex/config.toml, [mcp_servers.<name>] |
~/.codex/config.toml, [mcp_servers.<name>] |
Only native .gjc MCPs autoload in ordinary standalone sessions. Import adapters normalize bounded JSON/TOML entries, validate them, and write native mcp.json. |
| Skill | <project>/.gjc/skills/<name>/SKILL.md |
~/.gjc/agent/skills/<name>/SKILL.md |
<project>/.claude/skills/<name>/SKILL.md |
~/.claude/skills/<name>/SKILL.md |
<project>/.codex/skills/<name>/SKILL.md |
~/.codex/skills/<name>/SKILL.md |
Native .gjc skills are loaded by GJC. Claude/Codex skills are import candidates; they are not loaded directly into a GJC session. |
| Hook | `/.gjc/hooks/pre | post/` | `~/.gjc/agent/hooks/pre | post/` | `/.claude/hooks/pre | post/` | `~/.claude/hooks/pre |
The Claude MCP paths above are intentionally explicit: the /extensions
import implementation reads the project .mcp.json and user ~/.claude.json
forms. Host-specific files surfaced by gjc customize doctor remain
provenance diagnostics unless an import adapter accepts them. See
Standalone MCP configuration for the native startup
boundary and the host-specific compatibility notes.
/extensions keeps the destination scope separate from the source scope:
- Open the project or global
.gjcdashboard scope. - Choose Claude Code or Codex as the source product.
- Choose project-local or user-global source scope.
- Choose Skills, Hooks, MCPs, or all three.
- Choose a collision policy, review the normalized preview, and confirm.
A project import writes beneath <project>/.gjc/; a user import writes beneath
~/.gjc/agent/. A project source does not become user configuration, and a
user source does not write into the project unless the destination was selected
as project. The source is read only during preview/apply and is never mutated.
For non-interactive MCP/skill-only migration, gjc migrate --from claude-code|codex supports the user destination by default, --project for a
project destination, --dry-run for a plan, and --force for its explicit
update behavior. It is not a replacement for the all-surface /extensions
preview. Direct native MCP registration uses gjc mcp add; see the linked MCP
reference.
Import collision policy and runtime precedence are different decisions:
- Skills: project
.gjc/skillswins over user scope. Within project scope, ancestor directories are considered from the closest directory tocwdoutward. Within user scope, the canonical agent root precedes the configured legacy root and historical~/.gjc/skillsroot. Duplicate names are diagnosed. The four bundled workflow names (deep-interview,ralplan,team, andultragoal) are protected; a disk copy cannot replace the bundled definition. - Hooks: native project hooks win over the same native user hook. The
capability registry gives the native GJC provider precedence over the Claude
and Codex directory providers;
normalizeDirectoryHookpreserves the convention's phase and rejects unsupported convention/event combinations. Aprehook remains pre-tool authority and aposthook cannot acquire blocking authority. See Hooks for event, timeout, and runtime ownership details. - MCP: a native project server wins over a native user server with the same
name.
disabledServersfrom either native scope disables that name, and plugin-bundle MCPs have their separately documented collision authority; see GJC plugin bundles. Claude/Codex MCP files do not enter this precedence chain until an explicit import writes a native entry.
A shadowed entry remains useful evidence: it identifies the losing source
and the winner rather than silently deleting the loser. gjc customize doctor
and the /extensions inventory expose provenance, scope, effective status, and
shadowing separately.
Customization is executable or capability-bearing configuration. Treat every source as untrusted until the applicable policy is satisfied.
Filesystem skill discovery is enabled by default, but each scope has an explicit policy gate:
skills.enableddisables all filesystem skill discovery.skills.trustProjectSkillscontrols project.gjc/skills.skills.trustUserSkillscontrols user.gjcand legacy user roots.skills.ignoredSkills,skills.includeSkills, anddisabledExtensionsfilter individual names.skills.enablePiProjectandskills.enablePiUserare deprecated aliases retained for configured legacy settings.
The four bundled workflow skills are unaffected by these switches. A skill
must have a valid leading YAML frontmatter block with a non-empty
description; invalid or protected names are diagnosed rather than silently
replacing a bundled definition. See Skills for the complete
location and diagnostic contract.
Directory hooks are imported modules, not shell commands merely because a host
uses a hook directory. They execute as code in the GJC process and the normal
in-process hook API includes capabilities such as exec, message APIs,
renderers, and command registration. The current directory-hook loader does
not add a separate workspace-trust prompt, so not-enforced is the accurate
trust state: review and trust the source before loading it. Normalization
rejects unknown events, invalid phases, unsafe tool matchers, and semantic
mismatches. Distributable plugin hooks have a narrower API, but that API is not
an operating-system sandbox; see GJC plugin bundles.
Codex's managed hooks.json is a different authority. gjc setup hooks writes
managed UserPromptSubmit and Stop entries that invoke
gjc codex-native-hook; Codex owns their scheduling, timeout, cancellation,
environment, and command logging. GJC does not claim Claude named settings-hook
execution that its directory adapter does not support.
A native MCP server is startup-eligible only when it is not enabled: false,
not listed in either scope's disabledServers, and not autoload: false.
Project MCP loading is on by default and is disabled for an environment only by
an explicit mcp.enableProjectConfig: false. --no-mcp opts one standalone
session out of conventional native autoload; an exact-file --mcp-config is a
separate top-level opt-in that replaces conventional autoload.
Foreign MCP JSON/TOML is normalized through bounded compatibility adapters and
then validated against the native MCP contract before it can be written. A
nested auth/oauth shape that cannot be represented by the canonical import
contract is rejected instead of being silently dropped. Malformed definitions
are skipped with warnings; no partial definition is connected. Remote MCP
network and credential boundaries remain those in Standalone MCP
configuration.
The /extensions import wizard is deliberately a transaction:
- Preview is read-only. It scans only the selected product/scope and
surfaces, normalizes entries, computes destination names, and builds a
serialization-safe preview. Building a preview does not create
.gjcfiles. - Confirmation is explicit. Enter applies the currently reviewed plan; Escape cancels with no writes. The wizard pages the preview so every entry can be reviewed before confirmation.
- Collisions are explicit.
skipkeeps the existing destination and marks the source as a conflict;renamewrites a new<name>-imported(then-imported-2, and so on) destination;overwritereplaces an existing destination only because that policy was selected. Identical content is an idempotent no-op, not an overwrite. - Apply is fail-closed. Destination names are path-segment validated, containment-checked, and rechecked for symlinked ancestors and stale collisions. Skill and hook files use same-directory temporary files and atomic rename; MCP entries use the canonical atomic config writer.
- Verification and rollback are part of apply. Persisted files are read back before success is reported. A write, verification, or policy failure restores exactly what this transaction wrote; pre-existing symlinks and unrelated files are never treated as rollback targets.
No import policy edits the foreign source. Dashboard removal targets the exact native path and refuses symlinked files/directories. Hook enable/disable is not invented by the customization manager because it is not part of the canonical hook contract; unsupported mutation requests receive a diagnostic.
The preview and inspection surfaces are intentionally not raw configuration dumps:
- The preview DTO contains source/destination names, surface, status, and redacted reasons/descriptions. The opaque apply plan carries file contents and MCP values separately and is not rendered or serialized as the preview.
- MCP commands, arguments, endpoints, environment values, and header values are not printed as credentials. Previews identify environment/header keys without their values; command and endpoint descriptions are redacted.
- Unsupported nested auth/oauth is reported by reason and skipped rather than copied into a lossy destination.
gjc customize doctor --jsonis read-only and never emits credentials, endpoint tokens, auth headers, environment values, or unsafe raw config.gjc mcp listand the dashboard use the same redacted-display posture.
Do not paste secrets into shell history, prompts, screenshots, issue comments, or PR descriptions merely because a configuration is being migrated.
The inventory and doctor intentionally distinguish absence from policy and precedence outcomes:
| Status or diagnostic | Meaning | Typical next step |
|---|---|---|
enabled / loaded |
Native entry is accepted by policy and is the effective winner. | Start a new session or use the documented runtime reload boundary. |
imported |
A native skill retains an import provenance marker. | Treat .gjc as the authority; inspect the source only for comparison. |
disabled |
A trust switch, master switch, disabledExtensions, enabled: false, or disabledServers prevents use. |
Change the relevant policy intentionally, then reload. |
shadowed |
A higher-precedence project/ancestor/provider entry wins the same identity. | Inspect the winner and remove or rename the losing copy if it is no longer needed. |
invalid / rejected / unsupported |
Frontmatter, path, event, source format, or MCP semantics failed validation. | Fix the source or use the remediation reason; rejected content is not partially imported. |
conflict |
The selected destination exists under the chosen import policy. | Choose skip, rename, or explicit overwrite and review the new preview. |
quarantined |
A plugin surface failed its integrity or security policy. | Follow the plugin quarantine detail; do not bypass it by copying foreign files into .gjc. |
stored-only / restart-required |
The record is present but not active in this process, or a new session is required. | Restart/reload as the doctor or dashboard directs. |
Use the single read-only troubleshooting surface when behavior is unclear:
gjc customize doctor
gjc customize doctor --jsonIt reports convention, scope, precedence, shadowing, policy, bounded reason codes, remediation, and restart requirements without executing hooks or connecting MCP servers. An import candidate in the report is evidence that a foreign file exists; it is not evidence that GJC loaded it.
- Skills — native locations, trust settings, precedence, and discovery diagnostics.
- Hooks — canonical event normalization, directory layouts, Codex-managed hooks, plugin hook boundaries, and runtime contracts.
- Standalone MCP configuration — native autoload, disabled servers, exact-file mode, redaction, and network boundaries.
- GJC plugin bundles — loose
.gjccustomization versus versioned bundles, collision ownership, quarantine, and constrained hooks. - README
/extensionsoverview — the user-facing entry point and non-interactive command pointers.
The interactive manager and import transaction described here landed in #4492, resolving the umbrella customization behavior tracked by #4291.