Sitelet https://github.com/stanlrt/cc-autoconfig
Skip to content

Repository files navigation

cc-autoconfig

Auto-detect a repository's stack and launch Claude Code with only the plugins that repo actually needs — so a Python service doesn't pay the context cost of your frontend plugins, and vice-versa.

You keep one login, one memory store, one set of installed plugins — cca runs on your regular ~/.claude config and just adds --plugin-dir flags for the stack it detects.

$ cd ~/code/my-api        # a Python project
$ cca
cc-autoconfig: stack=[python] profile=python
# ...Claude starts with the Python plugin dir appended via --plugin-dir

$ cd ~/code/my-site       # a SolidJS/TS project
$ cca
cc-autoconfig: stack=[node, typescript, frontend] profile=node+typescript+frontend
# ...Claude starts with the typescript-lsp and frontend-design plugin dirs appended

Why

Every enabled plugin loads its skill descriptions into the system prompt on every turn (often several thousand tokens each). Claude Code's enabledPlugins is global-only — per-project enabledPlugins is not supported — so there's no built-in way to say "load the Python tooling only in Python repos."

cca sidesteps that with claude --plugin-dir <path>: it detects the repo's stack, resolves each matched plugin's newest cached payload directory under ~/.claude/plugins/cache/, and execs claude with one --plugin-dir flag per resolved plugin, on top of whatever you already have enabled globally. It never touches ~/.claude itself — no separate config dir, no separate login, no writes at all. Login, memory, history, and installed plugins are exactly the shared, ambient ~/.claude you'd get from running plain claude.

Install

Works on Linux, macOS, and Windows. Needs Node.js (you already have it if you run Claude Code) and git. No runtime dependencies.

npm install -g cc-autoconfig          # or: npm i -g github:stanlrt/cc-autoconfig

cca is now on your PATH. The first run creates ~/.config/cc-autoconfig/config.json from a generic template — edit it to match the plugins you've installed (claude plugin list shows their name@marketplace ids), then use cca anywhere you'd type claude.

From source
git clone https://github.com/stanlrt/cc-autoconfig ~/code/cc-autoconfig
cd ~/code/cc-autoconfig && npm install -g .   # or ./install.sh on Unix

Nothing personal lives in this repo — your plugin choices, private plugins, and any tokens stay in ~/.config/cc-autoconfig/config.json, which is never committed here. See examples/ for starter configs.

Migration to 0.2.0

Versions before 0.2.0 worked by materialising a mirrored CLAUDE_CONFIG_DIR profile per stack under ~/.cache/cc-autoconfig/profiles/. That machinery is gone: cca now runs directly on your shared ~/.claude and injects --plugin-dir flags instead of switching config dirs.

  • Old profile directories under ~/.cache/cc-autoconfig/profiles/ are no longer read or written by cca — they're safe to delete (rm -rf ~/.cache/cc-autoconfig/profiles).
  • If a previous version of cca wrote plugin toggles into your base ~/.claude/settings.json's enabledPlugins, that global map is still respected by claude as-is — anything listed there loads in every session regardless of stack. Prune it down to only what you truly want everywhere; let cca's per-stack rules handle the rest.
  • disableMcp and per-profile CLAUDE_CONFIG_DIR are gone; there is nothing to migrate for MCP servers — they were already shared and still are.

Configure

~/.config/cc-autoconfig/config.json:

{
  "always": ["my-workflow-plugin@my-marketplace"],
  "rules": [
    {
      "name": "python",
      "match": { "files": ["pyproject.toml", "requirements.txt", "*.py"] },
      "plugins": ["pyright-lsp@claude-plugins-official"]
    },
    {
      "name": "frontend",
      "match": {
        "files": ["package.json"],
        "grep": { "file": "package.json", "pattern": "react|solid-js|vue|svelte" }
      },
      "plugins": ["frontend-design@claude-plugins-official"]
    }
  ]
}
  • always — plugin ids (name@marketplace) passed as --plugin-dir on every launch.
  • rules[] — each matched rule contributes its plugins (union). A rule matches when all the conditions it declares hold:
    • match.files — matches if any listed name exists at the repo root. Supports */? globs (e.g. tsconfig.*.json).
    • match.grep — { "file", "pattern" }; the file must exist and its contents match the regex (use it to tell a frontend app from a backend one).
    • match.path — regex tested against the repo's absolute path (e.g. "/work/acme/" to load a client's plugins only under that tree).
    • A rule with an empty/omitted match always matches — handy as a base rule.

Each plugin id must already be installed (claude plugin install …) so it has a cached payload under ~/.claude/plugins/cache/<marketplace>/<plugin>/; cca only resolves and points at that cache, it never installs anything. A plugin id that isn't installed is skipped with a warning, not a hard error.

The "profile" shown in cca's log line is just the sorted set of matched rule names joined with + (base if nothing matches) — it's informational only, not a directory or config dir.

Monorepos & multi-language repos

Detection is not limited to the repo root. cca walks the tree up to scanDepth levels (default 4), skipping heavy dirs (node_modules, .git, dist, .venv, target, …), and a rule matches if its markers appear anywhere in that subtree. Every matching rule contributes, so a repo with a TS frontend in apps/web/ and a Python service in services/api/ resolves to node+python+typescript and loads both toolchains' plugin dirs. match.grep likewise scans every file with the given name (e.g. every package.json in the tree).

Prefer strong project markers (package.json, pyproject.toml, go.mod) over loose file globs like *.py in rules — the latter would flip a whole repo's stack on a single stray script. Tune breadth with a top-level "scanDepth": N.

Dry run

CCA_DRY=1 cca      # prints the resolved plugin dirs (one per line), doesn't launch Claude

How it works (details)

On each launch cca:

  1. Resolves the repo root (git rev-parse --show-toplevel, else $PWD).
  2. Evaluates your rules against it, collecting the union of matched plugins (plus always).
  3. For each matched plugin@marketplace, finds its newest cached version dir under ~/.claude/plugins/cache/<marketplace>/<plugin>/ (semver-aware, with a mtime fallback for sha-versioned or unparsable dirs) and turns it into a --plugin-dir <path> flag. A plugin with nothing cached is skipped with a warning.
  4. execs claude with those flags appended to whatever arguments you passed cca, and sets the child's CLAUDE_CONFIG_DIR to the shared base (~/.claude, or CCA_BASE). This is set explicitly — not left to inherit — so a stale CLAUDE_CONFIG_DIR already in your environment (for example from an older cca that materialised per-profile dirs, or a nested launch) can't split your login back apart. Every cca session therefore lands on the same config dir and shares one login, exactly like plain claude.

cca is entirely read-only against ~/.claude: it reads the plugin cache and your config file, and never writes, links, or mutates anything under your base config. Login, .credentials.json, memory, history, and installed_plugins.json are exactly what plain claude would see — because it's the same directory.

Concurrency

Run cca in as many repos at once as you like. Because there's no per-stack config dir or materialisation step, two cca sessions in different repos are exactly as independent (or as shared) as two plain claude sessions in different terminals — same login, same memory, same installed-plugins state, no extra locking or contention introduced by cca.

Cross-platform

No filesystem tricks (no symlinks, no junctions) are needed anymore — cca only reads the plugin cache and spawns claude. CI runs the test suite on Linux, macOS, and Windows.

Environment overrides

  • CCA_CONFIG — path to the config file (default ~/.config/cc-autoconfig/config.json).
  • CCA_BASE — base config dir to resolve the plugin cache under, and the value forced into the child's CLAUDE_CONFIG_DIR (default ~/.claude). Set this if you intentionally run a non-default config dir.
  • CCA_DRY — when set, print the resolved --plugin-dir paths and exit instead of launching claude.

Caveats

  • A plugin must be installed (claude plugin install …) for a rule to resolve it to a --plugin-dir; cca only points at what's already cached, it doesn't install anything.
  • Detection walks up to scanDepth levels (default 4). Very deeply nested package roots may need a higher scanDepth; conversely, lower it to speed up huge trees.
  • Plugins enabled globally in ~/.claude/settings.json's enabledPlugins load in every cca session regardless of stack — cca only adds plugin dirs on top, it doesn't disable anything. Keep that map limited to plugins you genuinely want everywhere; let rules handle the rest.

Development

npm install      # dev deps (eslint, prettier) — the shipped tool has none
npm test         # node --test (detection, plugin version resolution)
npm run lint     # eslint
npm run format   # prettier --write

Tests are hermetic (temp dirs via CCA_BASE) and never touch your real ~/.claude. CI runs them on Linux, macOS, and Windows across Node 18/20/22.

License

MIT — see LICENSE.

About

Auto-detect a repo's stack and load the matching Claude Code plugins/MCPs via per-project CLAUDE_CONFIG_DIR profiles.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages