-
-
Notifications
You must be signed in to change notification settings - Fork 38.4k
feat: slim, receipted profile plugin carriers for the plugin install path #2788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
af403cd
8bfd1f1
1c58cef
e0e02bb
61f3baa
483d3fe
abfd71e
1e1b03b
18919a4
9a6d3cd
add7b88
68b3a3c
0d0df38
a9aadcd
0a95c2e
cb71499
6c1e8a8
0ce5bec
2ff9a55
da95daf
9ef04d6
e59e802
769e17a
f2f673d
f1a6d0b
b7c8994
4eed1a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -220,6 +220,7 @@ commands: | |
| - plan | ||
| - plan-canvas | ||
| - plan-prd | ||
| - plugin-profiles | ||
| - pm2 | ||
| - projects | ||
| - promote | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| --- | ||
| description: Generate and manage slim ECC profile plugins - list profiles, plan token impact, generate a plugin, and activate it per project. | ||
| argument-hint: "[list | plan <profile> | generate <profile> | activate <plugin-name>]" | ||
| --- | ||
|
greptile-apps[bot] marked this conversation as resolved.
|
||
|
|
||
| # Plugin Profiles Command | ||
|
|
||
| Manage slim ECC profile plugins from inside Claude Code. Profile plugins cut | ||
| the per-session catalog cost (about 30k tokens for the full plugin, about 12k | ||
| for `minimal`) while keeping hook runtime parity and on-demand access to the | ||
| full skill catalog. See `docs/PLUGIN-PROFILES.md` for the underlying tool. | ||
|
|
||
| Run every command below from the ECC plugin root (`${CLAUDE_PLUGIN_ROOT}` when | ||
| set, otherwise the everything-claude-code checkout). | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Subcommands | ||
|
|
||
| ### `/plugin-profiles list` | ||
|
|
||
| Run `node scripts/plugin-profiles.js list` and show the available install | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Every generated profile copies this command, but no install-module path copies Useful? React with 👍 / 👎. |
||
| profiles with their module counts. | ||
|
|
||
| ### `/plugin-profiles plan <profile>` | ||
|
|
||
| Run `node scripts/plugin-profiles.js plan --profile <profile>` and report the | ||
| resolved surface (skills, agents, commands, runtime paths) and the estimated | ||
| catalog tokens per session, compared against the full plugin. | ||
|
|
||
| ### `/plugin-profiles generate <profile>` | ||
|
|
||
| 1. Run `node scripts/plugin-profiles.js generate --profile <profile>`. | ||
| 2. Show the generated plugin path and the printed next steps | ||
| (`claude plugin marketplace add ...`, `claude plugin install ...`). | ||
| 3. Offer the activation step below. | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| Pass through extra flags the user asks for (`--name`, `--out`, `--modules`, | ||
| `--with`, `--without`, `--no-catalog`, `--no-hooks`). | ||
|
|
||
| ### `/plugin-profiles activate <plugin-name>` | ||
|
|
||
| Offer to write the per-project opt-in to `.claude/settings.json` in the | ||
| current project: | ||
|
|
||
| ```json | ||
| { | ||
| "enabledPlugins": { | ||
| "ecc@ecc": false, | ||
| "<plugin-name>@ecc-profiles": true | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| } | ||
| ``` | ||
|
|
||
| Merge with any existing `enabledPlugins` block instead of overwriting the | ||
| file. ALWAYS show the resulting JSON and get user confirmation before | ||
| writing - never change plugin activation silently. Remind the user the | ||
| change takes effect on the next session. | ||
|
|
||
| ## Notes | ||
|
|
||
| - Custom selections work too: `generate --modules commands-core --with skill:react-patterns`. | ||
| - Regenerate after updating ECC: the plugin is a snapshot, not a live link. | ||
| - The generated `ecc-catalog` skill plus the skill-router hook keep the full | ||
| catalog reachable from any slim profile. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| # Plugin Profiles | ||
|
|
||
| Generate slim, per-project ECC plugins for Claude Code from the | ||
| selective-install manifests. | ||
|
|
||
| ## The Problem | ||
|
|
||
| Installing ECC as a Claude Code plugin loads the frontmatter of every skill, | ||
| agent, and command into session context — roughly 30k tokens for the full | ||
| catalog — in every session, in every project. The selective-install system | ||
| (`manifests/install-profiles.json`, `install-modules.json`, | ||
| `install-components.json`) already describes smaller surfaces, but it only | ||
| serves installer targets (`./install.sh`, `ecc install`). The marketplace | ||
| plugin path ignores it entirely, and Claude Code has no native way to enable a | ||
| subset of one plugin. | ||
|
|
||
| Plugin profiles close that gap: any install plan can be materialized as a | ||
| standalone slim plugin, published through a local marketplace, and chosen per | ||
| project via `enabledPlugins`. | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ```bash | ||
| # See available profiles | ||
| node scripts/plugin-profiles.js list | ||
|
|
||
| # Preview a profile's plugin surface and per-session context cost | ||
| node scripts/plugin-profiles.js plan --profile developer | ||
|
|
||
| # Generate the plugin + local marketplace (default: ~/.claude/ecc-profiles) | ||
| node scripts/plugin-profiles.js generate --profile developer | ||
|
|
||
| # Register and install it | ||
| claude plugin marketplace add ~/.claude/ecc-profiles | ||
| claude plugin install ecc-developer@ecc-profiles | ||
| ``` | ||
|
|
||
| Then choose the profile per project. In a project's `.claude/settings.json` | ||
| (or `settings.local.json`): | ||
|
|
||
| ```json | ||
| { | ||
| "enabledPlugins": { | ||
| "ecc@ecc": false, | ||
| "ecc-developer@ecc-profiles": true | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Settings resolution happens before session context assembly, so this is the | ||
| one lever that actually shrinks the injected catalog — a `SessionStart` hook | ||
| cannot remove catalog text that is already loaded. Subagents inherit the | ||
| session's plugin surface, so the slim profile applies to every spawned agent | ||
| automatically. | ||
|
|
||
| Note: `claude plugin install` enables the new plugin at user scope. If you | ||
| only want per-project use, set it back to `false` in `~/.claude/settings.json` | ||
| after installing, and enable it only inside the projects that want it. | ||
|
|
||
| ## What Gets Generated | ||
|
|
||
| For each selected module, paths are classified into the plugin surface: | ||
|
|
||
| | Module path | Plugin surface | Context cost | | ||
| |---|---|---| | ||
| | `skills/<id>` / `skills` | `skills/` (copied) | frontmatter per skill | | ||
| | `agents` / `agents/<f>.md` | `agents/` (copied) | frontmatter per agent | | ||
| | `commands` / `commands/<f>.md` | `commands/` (copied) | frontmatter per command | | ||
| | `hooks`, `scripts/**` | copied verbatim | zero (runtime only) | | ||
| | command runtime closure | copied verbatim | zero (runtime only) | | ||
| | `rules`, `.agents`, platform configs | skipped | installer-only surfaces | | ||
|
|
||
| Hook runtime is included by default (`--no-hooks` to opt out): hooks cost no | ||
| session context, so a slim profile keeps full GateGuard/session-hook parity | ||
| with the monolith. | ||
|
|
||
| Some commands ship as Markdown under `commands/` but are backed by code that | ||
| lives outside the modules a profile selects — `/plugin-profiles` is one, and | ||
| the `minimal` and `opencode` profiles omit `hooks-runtime` (and with it | ||
| `scripts/lib`) entirely. For those, the generator resolves the command's | ||
| transitive `require()` graph at generation time and copies it alongside the | ||
| command, so a generated profile never ships a slash command that fails on | ||
| first use. Runtime paths cost zero session context, so this is free in the | ||
| metric the profiles exist to optimize. The generated `.claude-plugin/plugin.json` follows the | ||
| Claude validator rules pinned in `tests/plugin-manifest.test.js` (no `agents` | ||
| or `hooks` keys, explicit empty `mcpServers`). | ||
|
|
||
| Approximate per-session catalog cost by profile (ecc@2.1.0): | ||
|
|
||
| | Profile | Skills | Catalog tokens | vs full | | ||
| |---|---|---|---| | ||
| | full | 280 | ~30k | — | | ||
| | developer | 121 | ~17k | −44% | | ||
| | minimal | 44 | ~12k | −60% | | ||
| | custom component selections | any | often 2–5k | −80% or more | | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## The ecc-catalog Escape Hatch | ||
|
|
||
| Every generated profile includes a synthesized `ecc-catalog` skill (disable | ||
| with `--no-catalog`): one cheap frontmatter entry whose body indexes the full | ||
| upstream skill catalog with descriptions, install status, and the source root | ||
| path. When a task needs a skill outside the profile, the agent reads that | ||
| skill's `SKILL.md` from the source tree on demand — a slim profile narrows the | ||
| default surface without ever losing capability. | ||
|
|
||
| ## Custom Selections | ||
|
|
||
| `plan` and `generate` accept the same selection vocabulary as the installer: | ||
|
|
||
| ```bash | ||
| # Profile plus extra components | ||
| node scripts/plugin-profiles.js generate --profile minimal \ | ||
| --with skill:react-patterns,agent:python-reviewer --name ecc-frontend | ||
|
|
||
| # Module-level, no profile | ||
| node scripts/plugin-profiles.js generate \ | ||
| --modules commands-core,workflow-quality --name ecc-lite | ||
|
|
||
| # Exclude components from a profile | ||
| node scripts/plugin-profiles.js generate --profile developer \ | ||
| --without capability:orchestration | ||
| ``` | ||
|
|
||
| Component IDs come from `manifests/install-components.json` plus synthetic | ||
| per-skill components (`skill:<dir>`), exactly as in `install-plan.js`. | ||
|
|
||
| ## In-Session Tooling | ||
|
|
||
| Two companions make profiles usable without leaving Claude Code: | ||
|
|
||
| - **`/plugin-profiles` command** (`commands/plugin-profiles.md`) wraps this | ||
| CLI: `list`, `plan <profile>`, `generate <profile>`, and `activate | ||
| <plugin-name>` (offers the per-project `enabledPlugins` edit with explicit | ||
| confirmation). | ||
| - **Skill-router hook** (`scripts/hooks/skill-router.js`, UserPromptSubmit, | ||
| id `user-prompt:skill-router`) scores each prompt against the skill catalog | ||
| with offline token matching and injects up to three matches as context — | ||
| installed skills directly, uninstalled ones with their on-demand SKILL.md | ||
| path. Generated profiles carry an `ecc-profile.json` pointing at the source | ||
| repository, so routing always covers the full catalog even under a minimal | ||
| profile. It emits nothing when no skill clearly matches, and is disabled | ||
| like any hook via `ECC_DISABLED_HOOKS=user-prompt:skill-router`. | ||
|
|
||
| ## Overwrite Safety | ||
|
|
||
| `generate` replaces an existing plugin directory of the same name, but only | ||
| after confirming it is one this tool produced — every generated plugin | ||
| carries an `ecc-profile.json` marker naming `everything-claude-code` as its | ||
| generator. A directory without that marker is refused: | ||
|
|
||
| ```text | ||
| Refusing to overwrite /path/to/ecc-minimal: it is not a generated profile | ||
| plugin (no ecc-profile.json marker). Choose another --name/--out, or pass | ||
| --force to replace it. | ||
| ``` | ||
|
|
||
| This matters because `--out` and `--name` together address an arbitrary | ||
| directory, and generation deletes the target tree before writing. Pass | ||
| `--force` only when you intend to replace unrelated contents. | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Refreshing After Updates | ||
|
|
||
| Generated plugins snapshot the repo at generation time. After updating ECC, | ||
| re-run the same `generate` command, then reinstall the plugin | ||
| (`claude plugin uninstall` + `install`) so the plugin cache picks up the new | ||
| content. The generated plugin version always tracks the source `package.json` | ||
| version. | ||
|
|
||
| ## Limitations | ||
|
|
||
| - Claude Code cannot partially enable a plugin, so a project uses either the | ||
| full `ecc` plugin or a generated profile — the profile replaces the | ||
| monolith in that project's `enabledPlugins`. | ||
| - Rules and platform configs are installer surfaces; Claude plugins do not | ||
| load them, so they are skipped (use `./install.sh` for those). | ||
| - The generated marketplace is local to the machine. Committing generated | ||
| profile plugins to a shared repo works, but treat them as build artifacts. | ||
| - `ecc-profile.json` embeds the machine-local absolute path of the source | ||
| checkout (and a catalog snapshot). Do not copy a generated plugin to | ||
| another machine — regenerate it there instead. On a machine where the | ||
| recorded path does not exist (or fails the ECC-checkout check), the | ||
| skill-router hook silently falls back to routing installed skills only. | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,6 +96,20 @@ | |
| "id": "pre:edit-write:gateguard-fact-force" | ||
| } | ||
| ], | ||
| "UserPromptSubmit": [ | ||
| { | ||
| "matcher": "*", | ||
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "node -e \"const p=require('path');const r=(function(){var p=require('path'),f=require('fs'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;if(e&&e.trim())return e.trim();var d=p.join(o.homedir(),'.claude');function L(x){try{return require(p.join(x,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(_){return null}}var r=L(d);if(r)return r;var s=['ecc','ecc@ecc','marketplaces/ecc','everything-claude-code','everything-claude-code@everything-claude-code','marketplaces/everything-claude-code'];for(var i=0;i<s.length;i++){r=L(p.join(d,'plugins',s[i]));if(r)return r}try{var g=['ecc','everything-claude-code'];for(var j=0;j<g.length;j++){var c=p.join(d,'plugins','cache',g[j]);var O=f.readdirSync(c);for(var k=0;k<O.length;k++){var q=p.join(c,O[k]);var V=f.readdirSync(q);for(var m=0;m<V.length;m++){r=L(p.join(q,V[m]));if(r)return r}}}}catch(_){}return d})();const s=p.join(r,'scripts/hooks/plugin-hook-bootstrap.js');process.env.CLAUDE_PLUGIN_ROOT=r;process.argv.splice(1,0,s);require(s)\" node scripts/hooks/run-with-flags.js user-prompt:skill-router scripts/hooks/skill-router.js standard,strict", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When this hook is excluded by the Useful? React with 👍 / 👎. |
||
| "timeout": 10 | ||
| } | ||
| ], | ||
| "description": "Suggest the best-matching skills for the submitted prompt (installed or on-demand from the full catalog); emits nothing when no skill clearly matches", | ||
| "id": "user-prompt:skill-router" | ||
| } | ||
| ], | ||
| "PreCompact": [ | ||
| { | ||
| "matcher": "*", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds the
plugin-profilescommand without updatingdocs/COMMAND-REGISTRY.json. The checked-in registry still contains 94 commands and noplugin-profilesentry, while the registry generator discovers this command;npm run command-registry:checkexits 1 and instructs contributors to regenerate the file. Runnpm run command-registry:writeand commit the resulting registry update.Artifacts
Evidence from the check
Command output from the check
Prompt To Fix With AI