CLI for managing fine-grained Claude Code configuration per project.
Handles CLAUDE.md composition, plugin enablement, MCP servers, skill overrides, and rules β without manually juggling files.
Linux x86_64 β one line (no sudo required):
curl -fsSL https://raw.githubusercontent.com/aneveux/ccx/master/install.sh | bashThe installer drops the ccx binary into ~/.local/bin by default. Make sure that
directory is on your $PATH:
export PATH="$HOME/.local/bin:$PATH" # add to ~/.bashrc or ~/.zshrcOverride the install directory with CCX_INSTALL_DIR:
CCX_INSTALL_DIR=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/aneveux/ccx/master/install.sh | bashThe installer downloads the latest Linux x86_64 binary, verifies its SHA-256
checksum before installing (fail-closed β it aborts on a missing or mismatched
checksum), then moves the binary into the install directory. Set
CCX_SKIP_CHECKSUM=1 only if you understand the risk.
Or download a binary directly from Releases.
ccx ships no profiles in the binary β author your own with:
ccx profiles new my-profile
ccx profiles enable my-profilePrerequisites: Java 25, Maven 3.9+
JVM (development):
mvn verify
java -jar target/quarkus-app/quarkus-run.jar --helpNative binary (requires GraalVM 25 with native-image):
mvn package -Pnative
./target/ccx --help| Command | Description |
|---|---|
clean |
Remove ccx-managed config from project |
doctor |
Validate project configuration |
status |
Show current configuration summary |
plugins |
Enable/disable/list plugins |
profiles |
Manage profiles |
skills |
Manage skill overrides |
mcp |
Configure MCP servers |
claude-md |
Manage CLAUDE.md fragment references |
config |
Manage ccx configuration (global fragments) |
tui |
Interactive terminal dashboard |
Global options. Most commands accept --project-dir (the project to act on,
defaults to cwd). Read-only commands that resolve global config also accept
--global-dir (the Claude config dir, defaults to ~/.claude): plugins list,
plugins diff, plugins tags, plugins refresh, skills list, skills show.
doctor also accepts --global-dir, but defaults it to your home directory
(it resolves .claude/settings.json underneath).
doctor additionally probes for git, jq, and yq on your PATH β a missing
git is reported as [WARNING], missing jq/yq as [INFO].
| Subcommand | Description |
|---|---|
profiles list |
List all available profiles |
profiles show <name> |
Show profile details (plugins, rules, env, etc.) |
profiles enable <name> |
Apply a profile to this project |
profiles disable <name> |
Remove a profile from this project |
profiles reinstall <name> |
Re-apply a profile to its declared state |
profiles status |
Show drift state of all profiles |
profiles edit <name> |
Edit a profile's profile.yaml in $EDITOR |
profiles new <name> |
Scaffold a new profile directory |
| Subcommand | Description |
|---|---|
plugins list |
List all known plugins with status (--tag to filter) |
plugins enable <name> |
Enable a plugin in this project |
plugins disable <name> |
Disable a plugin |
plugins diff |
Show diff between project and global plugins |
plugins tags |
List available tag filters for plugins list |
plugins refresh |
Force re-fetch GitHub marketplace sources (--marketplace/-m to target one) |
| Subcommand | Description |
|---|---|
skills list |
List all discovered skills with their status |
skills enable <id> |
Enable a skill at auto (remove its override) |
skills disable <id> |
Disable a skill (off override) |
skills hide <id> |
Set a skill to user-invocable-only |
skills show <id> |
Show the current override status of a skill |
skills reset |
Remove all skill overrides from project settings |
| Subcommand | Description |
|---|---|
mcp list |
List configured MCP servers |
mcp enable <name> |
Add or update an MCP server in settings.json |
mcp disable <name> |
Remove an MCP server from settings.json |
mcp show <name> |
Show MCP server configuration details |
mcp enable adds a server or updates one that already exists. Describe the server
with flags:
# command server (--type defaults to "command")
ccx mcp enable filesystem --command mcp-server-fs --args --root --args .
# HTTP server
ccx mcp enable remote-api --type http --url https://mcp.example.com/sse--args is repeatable β each occurrence adds one argument. The --command,
--url, --args, and --type flags map to the command/url/args/type
fields of the server entry.
Power-user path: pass the full config as a JSON positional instead of flags.
ccx mcp enable filesystem '{"command": "mcp-server-fs", "args": ["--root", "."]}'
ccx mcp enable remote-api '{"type": "http", "url": "https://mcp.example.com/sse"}'Manage references to global CLAUDE.md fragments inside .claude/CLAUDE.md.
| Subcommand | Description |
|---|---|
claude-md list |
List configured fragments in the ccx marker block |
claude-md add <name> |
Add a global fragment reference to .claude/CLAUDE.md |
claude-md remove <name> |
Remove a global fragment reference |
claude-md init |
Initialize .claude/CLAUDE.md with an empty marker block |
A fragment named <name> must already exist at ~/.config/ccx/claudemd/<name>.md
(create one with ccx config claudemd new <name>). claude-md add writes an
absolute @<path> reference into the <!-- ccx:begin -->β¦<!-- ccx:end --> block.
| Subcommand | Description |
|---|---|
config claudemd new <name> |
Create a new global claudemd fragment |
| File | Purpose |
|---|---|
.claude/settings.json |
Plugins, permissions, MCP servers, skill overrides for this project |
.ccx/context.json |
Which profiles are enabled, their installation snapshots, and drift state |
.claude/rules/*.md |
Per-rule markdown files installed by profiles |
.claude/CLAUDE.md |
Project instructions; the <!-- ccx:begin -->β¦<!-- ccx:end --> block is managed by ccx |
~/.config/ccx/profiles/<name>/ |
User-authored profiles β one directory per profile |
~/.config/ccx/claudemd/<name>.md |
Global CLAUDE.md fragments referenced by claude-md add |
The project settings file ccx reads and writes. Only the keys ccx touches are shown; unknown keys are preserved.
{
"enabledPlugins": {
"java@my-marketplace": true
},
"permissions": {
"allow": ["Bash(mvn *)"],
"deny": ["Bash(rm -rf *)"]
},
"mcpServers": {
"filesystem": { "command": "mcp-server-fs", "args": ["--root", "."] }
},
"skillOverrides": {
"java:java": "off"
}
}A profile declares these blocks in its profile.yaml:
permissions:
deny: ["Bash(rm -rf *)"]
env:
JAVA_HOME: /usr/lib/jvm/java-21
ignore:
- "target/"
- "*.class"On ccx profiles enable, permissions and env merge into
.claude/settings.json, while ignore patterns append to a separate
.claudeignore file at the project root:
// .claude/settings.json
{
"permissions": { "deny": ["Bash(rm -rf *)"] },
"env": { "JAVA_HOME": "/usr/lib/jvm/java-21" }
}# .claudeignore
target/
*.classccx tracks which profiles are enabled and an installation snapshot per profile so it can detect drift. Written automatically β you do not edit this by hand.
{
"version": "1",
"enabledProfiles": {
"java": {
"enabledAt": "2026-06-24T10:15:30Z",
"snapshot": {
"plugins": ["java@my-marketplace"],
"rules": ["no-empty-catch"],
"permissionsAllow": ["Bash(mvn *)"],
"permissionsDeny": [],
"commands": [],
"fragments": [],
"env": {},
"ignorePatterns": ["target/"]
}
}
}
}ccx ships no profiles in the binary β author your own (see Creating your own profile below).
Once a profile exists under ~/.config/ccx/profiles/, apply it to a project:
# scaffold a profile, then edit ~/.config/ccx/profiles/java/profile.yaml
ccx profiles new java
# apply it to a project (auto-enables any profiles it extends)
ccx profiles enable java --project-dir ~/my-project
# check what was applied
ccx status --project-dir ~/my-project
# launch the interactive dashboard
ccx tui --project-dir ~/my-projectccx ships no profiles in the binary β author your own under
~/.config/ccx/profiles/ β one directory per profile. Scaffold one with:
ccx profiles new my-profile
# edit the generated ~/.config/ccx/profiles/my-profile/profile.yaml~/.config/ccx/
βββ profiles/
β βββ my-profile/
β βββ profile.yaml # the profile definition (see below)
β βββ rules/ # *.md rule files installed into .claude/rules/
β β βββ no-empty-catch.md
β βββ claudemd/ # *.md fragments injected into the CLAUDE.md block
β β βββ java.md
β βββ commands/ # *.md command files
β βββ ship.md
βββ claudemd/ # global fragments, shared across all profiles
βββ company-style.md
A profile is a named bundle of plugins, rules, permissions, CLAUDE.md fragments, environment variables, and ignore patterns. Every field is optional.
metadata:
name: my-profile
description: "Java + Quarkus working set"
# inherit from other profiles; merged depth-first
extends:
- base
plugins:
enable:
- java@my-marketplace
permissions:
allow:
- "Bash(mvn *)"
deny:
- "Bash(rm -rf *)"
claudeMd:
fragments:
- java # resolves to claudemd/java.md
env:
ENABLE_LSP_TOOL: "1"
ignore:
- "target/"Rule and claudemd fragments are plain markdown. Rule files are auto-discovered:
every *.md in a profile's rules/ directory is applied β there is no key for
them in profile.yaml. Example
~/.config/ccx/profiles/my-profile/claudemd/java.md:
# Java conventions
- Records over POJOs, sealed classes, pattern matching
- SLF4J for logging β never System.out
- Constructor injection, never field injectionFragments that aren't tied to a single profile live in
~/.config/ccx/claudemd/. Create one and reference it from a project:
# create a global fragment
ccx config claudemd new company-style
# reference it from the current project's .claude/CLAUDE.md
ccx claude-md add company-styleclaude-md add writes an absolute @<path> line into the ccx-managed block of
.claude/CLAUDE.md, so Claude Code picks up the fragment for that project.
Profiles are named bundles of plugins, fragments, rules, and permissions. When you enable a profile, ccx:
- Applies plugins to
.claude/settings.json - Writes rule files to
.claude/rules/ - Adds fragment references to the
<!-- ccx:begin -->β¦<!-- ccx:end -->block inCLAUDE.md - Records the installation snapshot in
.ccx/context.json
The snapshot enables drift detection: if you manually remove a plugin or rule file, profiles status will show ALTERED. Use profiles reinstall to bring the profile back to its declared state.
ccx tui [--project-dir .]Interactive 5-tab dashboard (Overview, Profiles, Plugins, MCPs, CLAUDE.md). Requires a real TTY.
| Key | Action |
|---|---|
| Tab / S-Tab | Next / previous tab |
| 1β5 | Jump to tab by number |
| β / β | Navigate list (scroll on Overview) |
/ |
Focus filter bar |
| Esc | Clear filter / blur filter bar |
| Enter | Context-dependent: toggle selected profile (Profiles), remove server (MCP), toggle fragment (CLAUDE.md) |
e / d / r |
Enable / disable / reinstall profile (Profiles tab) |
r |
Refresh marketplace (Plugins tab) |
q |
Quit |
? |
Toggle help overlay |
mvn verify# Install GraalVM 25 with native-image
mvn package -Pnative
./target/ccx --helpReleases are created by pushing a v* tag. The CI builds a Linux x86_64 native binary and publishes a GitHub Release via JReleaser.
git tag v0.1.0
git push origin v0.1.0How a build becomes an installed binary, end to end.
1. Build β mvn clean package -Pnative produces target/ccx, a
self-contained Linux x86_64 native executable (no JVM required at runtime).
2. Release β the release Maven profile runs JReleaser, which publishes the
native binary to GitHub Releases on the
aneveux/ccx repository with a conventional-commits changelog. The release is cut
by pushing a v* tag (see Release above); CI runs the native build and
the JReleaser publish step.
3. Install β install.sh (the curl β¦ | bash one-liner above) resolves the
latest release tag, downloads the ccx binary and its checksums_sha256.txt
sidecar, verifies the SHA-256 checksum (fail-closed: aborts on a missing or
mismatched checksum), and installs the binary to ~/.local/bin/ccx. Override the
target with CCX_INSTALL_DIR; bypass verification (not recommended) with
CCX_SKIP_CHECKSUM=1.
Documented limits (current):
- Linux x86_64 only. The release builds and publishes a single
linux-x86_64native binary;install.shfetches only that binary. - No macOS build yet. macOS arm64 native builds and the Homebrew tap are
stubbed but disabled in
pom.xmluntil a macOS native build job is added torelease.yml.