---
name: lean-contribute
description: Walk a contributor through proposing a change to the LeanCode AI plugins marketplace — tweaking an existing plugin, adding a new one, and opening a PR against `leancodepl/ai-plugins`. Use when the user invokes `/lean-contribute`, asks how to contribute, asks how to add a plugin, or wants to propose an improvement. Manual-only entry point — does not auto-fire.
disable-model-invocation: true
---

# Contribute to LeanCode AI plugins

The canonical contributor reference for `leancodepl/ai-plugins`. The repo's `CONTRIBUTING.md` is a thin pointer at this file. Read top-to-bottom for the full picture, or jump to the relevant path.

## How to respond

1. Ask what kind of change the user wants:
   - **Tweak** an existing plugin (improve a skill or rule, fix a typo, sharpen wording).
   - **Add a new plugin** (a new vertical or a new focused capability).
   - **Add a skill or rule** to an existing plugin.
2. Walk through the matching path below. Print exact commands. Do not run `git`, `gh`, or any state-changing command on the contributor's behalf unless they explicitly ask.
3. After the PR is pushed, point the user at `gh pr checks <pr-number>` to follow CI verdicts. Do not run validators or linters locally — CI is the source of truth.

## Reading the current state

Before answering questions about what plugins or clients exist, read the source of truth:

- Plugins: `.claude-plugin/marketplace.json` at the repo root.
- Supported clients: the "Supported clients" section of the root `README.md`.

Do not enumerate plugins or clients from memory.

## Where to work

All contribution work happens **inside a local clone of `leancodepl/ai-plugins`**, not in a consumer project. If the contributor does not have the clone yet:

```
gh repo clone leancodepl/ai-plugins
cd ai-plugins
```

If `gh` is not installed, point them at https://cli.github.com/ or, for a fully GUI flow, https://desktop.github.com/.

## Plugin shape

Every plugin lives under `plugins/<plugin-name>/`:

```
plugins/<plugin-name>/
  skills/
    <plugin-name>-usage/
      SKILL.md              # routing entry point, always present
    <other-skill>/
      SKILL.md
  .cursor-plugin/plugin.json
  .claude-plugin/plugin.json
  README.md
  CHANGELOG.md
  .mcp.json                 # only if the plugin drives an MCP server
  rules/                    # optional
  cursor-rules/             # generated from rules/, never hand-edited
  claude-rules/             # generated from rules/, never hand-edited
```

Notes:

- **Both manifests must agree on `name`, `displayName`, `description`, `version`.** Cursor reads `.cursor-plugin/plugin.json` (`rules: "./cursor-rules/"`), Claude Code reads `.claude-plugin/plugin.json` (`rules: "./claude-rules/"`). If your plugin has no `rules/`, omit the `rules` key from both manifests.
- **Every plugin ships `skills/<plugin-name>-usage/SKILL.md`** as its routing entry point. The skill `name:` in frontmatter must exactly match the folder name.
- **Skill names are unique across the whole marketplace.** If your slug could collide, prefix it with the plugin name.
- **Skills and rules are both first-class.** Use the one that fits — skills for routing and workflows, rules for guidance the model should apply automatically (path-scoped via `globs`, or agent-requested via a precise `description`). Mark skills that should never auto-fire on inferred intent with `disable-model-invocation: true`.
- **Don't hand-edit `cursor-rules/` or `claude-rules/`.** They are generated by `python scripts/generate-rules.py` from `rules/*.md`. Commit source and generated files together.
- **Setup and configuration docs have one canonical home: the plugin `README.md`.** Anything a user has to do to make the plugin work (env vars, dependencies, post-install steps, sample directories, MCP servers, etc.) lives there in full. Other surfaces — `<plugin>-usage/SKILL.md`, other skills, top-level README bullets — reference the plugin README rather than restating the setup. The single exception is a skill that needs to print setup inline at runtime (because the user is not on GitHub at that moment); when you keep an inline copy for that reason, say so in the skill so the next contributor does not assume the duplication is accidental. This rule prevents the three-way drift between `README.md`, the usage SKILL, and any workhorse SKILL that all happen to mention the same env var.

## Skill conventions

- Each skill is a folder `skills/<slug>/` containing a single `SKILL.md`.
- Frontmatter `name:` must equal the folder name.
- `description` should include "Use when …" with concrete trigger terms.
- Add `argument-hint: "<args>"` when the skill takes arguments.
- Add `disable-model-invocation: true` when the skill should never auto-fire on inferred intent.
- Skill names must be unique across the whole marketplace.

Skills are also distributed through `vercel-labs/skills` as a cross-client install path (`npx skills add leancodepl/ai-plugins --skill <name> -a <agent>`, supports Cursor, Claude Code, and 50+ other agents). When authoring: keep `name:` globally unique (the CLI uses a flat namespace, no plugin scoping), make `description:` self-contained, and don't assume the plugin's `rules/` are present at runtime — a `<plugin>-usage` skill should be useful standalone.

## Rule conventions

- Author in `rules/`. A non-empty `description` is mandatory; `globs` is optional.
- Scope each rule with `globs` (path-scoped) or with a precise `description` alone (agent-requested); never `alwaysApply: true`. See AGENTS.md "Rule authoring" for which to pick.
- Run `python scripts/generate-rules.py` after any change. Commit source and generated files together.
- Do not hand-edit anything under `cursor-rules/` or `claude-rules/`.

## Path 1 — tweak an existing plugin

1. Open `plugins/<plugin-name>/` and find the file to change.
2. Edit it. Keep the change focused — one PR per concern.
3. If you edited any `rules/*.md`, regenerate locally with `python scripts/generate-rules.py`. (Python is the only tool a contributor should need locally; CI handles the rest.)
4. Bump the plugin's `version` in **both** `.cursor-plugin/plugin.json` and `.claude-plugin/plugin.json`. PATCH for wording / docs, MINOR for new behavior, MAJOR for renames or removals. See "Versioning" below for the full rules and what each client does with the bump.
5. Add a bullet to `plugins/<plugin-name>/CHANGELOG.md` under a new top entry matching the new version.
6. Update `plugins/<plugin-name>/README.md` if the change affects what the plugin lists.
7. Branch, commit, push, open a PR — see "Open the PR" below.

## Path 2 — add a skill or rule to an existing plugin

Same as Path 1, plus:

- New skill → `plugins/<plugin-name>/skills/<skill-slug>/SKILL.md` with frontmatter `name: <skill-slug>` and a `description` that includes "Use when …" with concrete trigger terms. Add `disable-model-invocation: true` if it should not auto-fire. Also append the skill's plugin-relative path (e.g. `"./skills/<skill-slug>"`) to the plugin's `skills` array in `.claude-plugin/marketplace.json` — that's the manifest `vercel-labs/skills` reads for cross-client distribution.
- New rule → `plugins/<plugin-name>/rules/<rule-slug>.md` with `description` and concrete `globs`. Never set `alwaysApply: true`. After saving, run `python scripts/generate-rules.py`.
- Mention the new asset in `plugins/<plugin-name>/README.md`.

## Path 3 — add a new plugin

1. Pick a kebab-case name with a prefix that reflects scope:
   - `<vertical>-<scope>` when the plugin is tied to a specific vertical: `backend-…`, `flutter-…`, `pmo-…`.
   - `lean-<scope>` when the plugin is cross-vertical or has no natural vertical (matches `lean-core`, `lean-anti-ai-slop`). Do not ship a bare slug. The prefix is what makes the marketplace listing predictable — users remember prefixes plus autocomplete, not full slugs.
2. Create `plugins/<plugin-name>/` with at minimum:
   - `skills/<plugin-name>-usage/SKILL.md`
   - `.cursor-plugin/plugin.json`
   - `.claude-plugin/plugin.json`
   - `README.md`
   - `CHANGELOG.md` with a `0.1.0` entry
3. Copy manifest shape from a sibling plugin. Set `version: "0.1.0"` and `stability: "experimental"`.
4. Register the plugin in **both** marketplace files:
   - `.cursor-plugin/marketplace.json` — append to `plugins[]`.
   - `.claude-plugin/marketplace.json` — append to `plugins[]` and include a `skills` array on the entry listing every skill's plugin-relative path (e.g. `"./skills/<plugin-name>-usage"`). `vercel-labs/skills` reads this for cross-client distribution; without it the CLI falls back to a recursive search.
   - Bumping `metadata.version` on either file is convention-only — see "Versioning" below.
5. Add the plugin to the human-readable overview in the root `README.md` under the matching vertical group.
6. Branch, commit, push, open a PR.

## Versioning

Per-plugin semver. Both `plugin.json` files must carry the same `version`.

- **PATCH** — wording, doc, regenerated output only.
- **MINOR** — new skill, new rule, new behavior; expanded scope; additive change.
- **MAJOR** — rename, removal, or reversal of existing guidance.

Optional `stability` field on each plugin: `experimental`, `beta`, or `stable`.

### What clients actually do with these versions

This trips contributors up — be explicit when walking the user through.

- **Claude Code**: per-plugin `plugin.json.version` is the cache key for `/plugin update`. **Bumping it is load-bearing.** If you ship changes without bumping, users running `/plugin update` see "already at the latest version" and never get your change. Bump it on every release. (See https://code.claude.com/docs/en/plugins-reference.md, "Version management".)
- **Cursor**: per-plugin `plugin.json.version` is **decorative**. Cursor pins installs to the git commit SHA internally, so any new commit on the tracked branch is a new version regardless of the string. (See https://cursor.com/docs/reference/plugins.)
- **`marketplace.json.metadata.version`** in either client: **decorative** — no documented refresh, cache, or update behavior. Bump by convention when plugins are added/removed/renamed if you want, but nothing depends on it.

### How users get updates (no reinstall needed)

- **Claude Code**: user runs `/plugin update` (or auto-update fires if they enabled it). Pulls the latest version using the cache key above.
- **Cursor**: a GitHub-push webhook re-indexes the marketplace on Cursor's side; clients pull on next refresh (window focus or periodic). If a specific user is stuck, the working fallback is Settings → Plugins → manual **Refresh**, and as a last resort clearing `~/.cursor/plugins/cache/<marketplace>/<plugin>/` and reinstalling. Forum reports show Cursor's auto-refresh can be flaky in practice.

No client requires a full reinstall on a normal version bump.

## Open the PR

```
git checkout -b <short-slug>
git add -A
git commit -m "<plugin-name>: <what changed>"
git push -u origin HEAD
gh pr create --repo leancodepl/ai-plugins \
  --title "<plugin-name>: <what changed>" \
  --body "<one paragraph: what and why; mention any breaking changes>"
```

If `gh` is not installed, push the branch and open the PR via the GitHub web UI. The PR description should answer: what changed, why, and which plugins it touches.

## CI is the safety net

Every PR runs three checks, all on GitHub Actions:

- **Structure** — manifest fields, marketplace registration, file shape.
- **Generated rules in sync** — fails if you changed a rule but forgot to regenerate.
- **Lint** — Go and Python formatters/linters for repo tooling.

Contributors do not need to install Go locally. Watch the verdicts with:

```
gh pr checks <pr-number>
```

A green PR means the shape is correct. Review focuses on guidance quality, not boilerplate.

## What NOT to do

- Do not hand-edit `cursor-rules/` or `claude-rules/`. They are generated.
- Do not bundle changes across multiple plugins in a single PR. One plugin per PR keeps review tight.
- Do not ship a Claude Code-visible change without bumping `plugin.json.version` — see "Versioning" above.
- Do not invent guidance that is not grounded in real usage. If you cannot point at a concrete reason for a rule or skill claim, leave it out.
- Do not enumerate plugins or clients from memory — read the source of truth.
