git:20260801.af6b972 to git:20260905.0cab9b3

2 added, 0 removed. Audit A to A.

---
name: plugin-installer
plugin: plugin-manager
description: >-
Installs plugin components (skills, commands/workflows, rules, hooks, MCP)
into the .agents/ central store and symlinks them to agent environments that
require it (.claude/). DEFAULT method: run plugin_add.py for an interactive
require it (.claude/). DEFAULT method: run plugin_add.py (the primary TUI and headless orchestrator)
which internally delegates to plugin_installer.py (the execution engine) for the actual OS-level installation.
Trigger when a user says "install plugin", "deploy plugin", "add plugin",
"install from GitHub", or "sync plugin to agents".
allowed-tools: Bash, Write, Read
---
## Dependencies
This skill requires **Python 3.8+** and standard library only. No external packages needed.
**To install this skill's dependencies:**
```bash
pip-compile ./requirements.in
pip install -r ./requirements.txt
```
See `./requirements.txt` for the dependency lockfile (currently empty — standard library only).
---
# Agent Bridge
## Overview
This skill deploys plugin components to agent environments using the
`.agents/` central store + symlink pattern. It is the **full-stack** installer:
| Installer | Features | Requires Local Clone? | Recommended For |
|-----------|----------|-----------------------|-----------------|
| `uvx` ★ **default** | Runs `plugin_add.py` natively | No (runs from GitHub) | Everyone using `uv` |
| `bootstrap.py` | Downloads & runs `plugin_add.py` | No (downloads in-memory) | End users without `uv` |
| `plugin_add.py` | Full interactive TUI or Headless CI | Yes | Local developers debugging or updating locally |
| `plugin_installer.py` | Execution Engine (No UX) | Yes | Internal framework usage only |
> **Use `uvx`** (★ recommended default) for an interactive TUI that installs full plugins without requiring Node.js.
> **Use the `bootstrap.py` curl pipeline** if you do not have `uv` installed.
> **Use `plugin_add.py`** directly for scripted/CI installs from a local clone (e.g. `plugin_add.py --all -y`).
## Initial vs Subsequent Installs
There is no difference between an initial install and a subsequent install from the consumer's perspective. Because `uvx` and `bootstrap.py` execute ephemerally, they are inherently stateless tooling.
You do not need to "install the installer". Just run the `uvx` or `curl` command whenever you need to add or update a plugin. The state is strictly kept inside your `.agents/` repository.
---
## Attribution
The `plugin_add.py` interactive TUI (multiselect, arrow-key navigation, fuzzy search, `owner/repo` GitHub shorthand, temp-clone-then-install flow) is inspired by universal one-liner installation patterns:
- GitHub Repository Patterns: <https://github.com/vercel-labs/skills>
- Marketplace: <https://skills.sh>
This implementation re-creates those UX patterns in **pure Python stdlib** (no npm, no external packages) so they work on Windows without symlink issues and operate at the **plugin** level rather than individual SKILL.md files.
---
## Architecture: How Installation Works
```
plugins/<plugin>/
skills/ → .agents/skills/<skill>/ (canonical copy, all agents read from here)
.claude/skills/<skill> → symlink (Claude Code)
commands/ → .agents/workflows/<plugin>_<cmd>.md (canonical copy)
.claude/commands/<plugin>_<cmd>.md → symlink (Claude Code)
rules/ → .agents/rules/<plugin>_<rule>.md (canonical copy)
CLAUDE.md → appended (Claude Code)
hooks/hooks.json → .agents/hooks/<plugin>-hooks.json (canonical copy)
.claude/hooks/<plugin>-hooks.json → symlink (Claude only)
.mcp.json → ./.mcp.json (merged)
```
**Central store is always `.agents/` at project root.** Symlinks point from
each agent's own directory back into `.agents/`. This mirrors exactly how
`npx skills` manages its canonical store at `.agents/skills/`.
---
## Component Mapping Matrix
| Component | `.agents/` (canonical) | `.claude/` (Claude Code) |
|-----------|------------------------|--------------------------|
| `skills/` | `.agents/skills/<n>/` full copy (pruned & cleanly replaced on reinstall) | Reads directly from `.agents/skills/` |
| `commands/*.md` | `.agents/workflows/<plugin>_<cmd>.md` | `.claude/commands/<plugin>_<cmd>.md` → symlink |
| `rules/` | `.agents/rules/<plugin>_<rule>.md` | Appended → `CLAUDE.md` |
| `hooks/hooks.json` | `.agents/hooks/<plugin>-hooks.json` | `.claude/hooks/<plugin>-hooks.json` → symlink |
| `agents/*.md` | `.agents/agents/<plugin>-<agent>.md` | `.claude/agents/<plugin>-<agent>.md` → symlink |
| `.mcp.json` | Merged → `./.mcp.json` | Merged → `./.mcp.json` |
> **Clean Replacement Guarantee:** Re-installing any plugin completely wipes the destination target directory `.agents/skills/<skill-name>` prior to copying. This ensures non-additive changes (file deletions, script renames, removed symlinks) are cleanly pruned without leaving stale artifacts behind.
+ > **Rules Deploy Safely, Not Cleanly:** Unlike `skills/`, `.agent/rules/` is **never** blind-overwritten. `deploy_rules()` reads the existing `.agent/rules/<rule>.md` first and diff-merges the plugin's `rules/` source into it, preserving any downstream content the plugin source lacks (e.g. a same-day rule update not yet backported to `plugins/<plugin>/rules/`). On a genuine same-line conflict, the side with more content wins rather than the plugin source unconditionally winning. This closes a 2026-09-05 incident where a stale `plugins/dev-utils/rules/git-operations.md` silently clobbered a newer `.agent/rules/git-operations.md` during a routine sync. **Implication:** if a rule looks unchanged after a sync you expected to update it, check whether `.agent/rules/` already had newer content than the plugin source — update the plugin source (`plugins/<plugin>/rules/<rule>.md`) directly, don't fight the merge.
+
> **Antigravity, Gemini, and GitHub Copilot** all natively read from `.agents/`
> — no separate symlinks needed. The canonical `.agents/` copy is sufficient.
> **Commands naming:** Nested command folders are flattened to snake_case.
> `commands/ops/restart.md` → `<plugin>_ops_restart.md`
> **`commands/` vs `workflows/` naming:** The plugin source folder is always
> named `commands/`. The installer writes to `.agents/workflows/` (canonical)
> and `.claude/commands/` (symlink). Never rename the source folder.
> **`skills/` as slash commands (Claude Code):** In Claude Code, any `skills/<name>/SKILL.md`
> entry in a plugin is deployed to `.claude/skills/<name>/` and automatically functions as
> both a proactive skill AND a namespaced slash command (`/plugin-name:name`). This is the
> **preferred** pattern for new commands — use `commands/` as thin wrappers that delegate
> to skills. The installer handles both paths independently; no special flag needed.
---
## External Skills Compatibility
`plugin_installer.py` writes to the external skills lock file after installation
so that legacy tools remain aware of bridge-installed skills.
**Lock file locations:**
| File | Path | Purpose |
|------|------|---------|
| Global lock | `~/.agents/.skill-lock.json` | Tracks global installs; enables legacy check/update |
| Project lock | `<project>/skills-lock.json` | Tracks project installs; enables legacy experimental installs |
**Lock file schema (version 3):**
```json
{
"version": 3,
"skills": {
"my-skill": {
"source": "richfrem/agent-plugins-skills",
"sourceType": "github",
"sourceUrl": "https://github.com/richfrem/agent-plugins-skills.git",
"skillPath": "plugins/my-plugin/skills/my-skill",
"skillFolderHash": "<git-tree-sha>",
"installedAt": "<iso-timestamp>",
"updatedAt": "<iso-timestamp>"
}
},
"dismissed": {}
}
```
After running `plugin_installer.py`, each installed skill must be written to
the appropriate lock file. Skills only — commands, rules, hooks, and MCP are
not tracked by `npx skills`.
---
---
## Usage
### Bootstrapped (no clone required): `uvx` ★ Recommended
```bash
# Interactive TUI from any GitHub repo
uvx --from git+https://github.com/richfrem/agent-plugins-skills plugin-add richfrem/agent-plugins-skills
# Install from Anthropic official plugins (has plugins/ subdir)
uvx --from git+https://github.com/richfrem/agent-plugins-skills plugin-add anthropics/claude-plugins-official
# Install from flat-layout repo (no plugins/ dir — auto-discovered)
uvx --from git+https://github.com/richfrem/agent-plugins-skills plugin-add anthropics/knowledge-work-plugins
# Install all non-interactively
uvx --from git+https://github.com/richfrem/agent-plugins-skills plugin-add richfrem/agent-plugins-skills --all -y
# Local installation testing via uvx (uses remote script but local plugin files)
uvx --from git+https://github.com/richfrem/agent-plugins-skills plugin-add plugins/
uvx --from git+https://github.com/richfrem/agent-plugins-skills plugin-add plugins/agent-scaffolders
```
### Full Deployment: `plugin_add.py` (interactive TUI)
The recommended local invocation. Supports the same GitHub source syntax as `uvx`.
#### Source Format
`plugin_add.py` accepts a flexible `owner/repo[/subpath]` source:
| Source Format | What Happens |
|---|---|
| `richfrem/agent-plugins-skills` | Clone repo, auto-detect `plugins/` dir |
| `anthropics/claude-plugins-official/plugins` | Clone repo, use `plugins/` subpath directly |
| `anthropics/knowledge-work-plugins` | Clone repo, no `plugins/` dir — scans root for plugin-shaped dirs |
| `anthropics/knowledge-work-plugins/engineering` | Clone repo, drill into `engineering/` as a single plugin |
| `https://github.com/org/repo/tree/main/plugins` | Full GitHub URL — `tree/main/` is stripped automatically |
| `/local/path/to/repo` | Local clone — same discovery waterfall |
#### Plugin Discovery Waterfall
When a repo root is resolved, plugins are found via three-tier fallback:
1. **Classic monorepo** — `plugins/` subdir exists → scan its children
2. **Flat layout** — No `plugins/` dir → scan root-level dirs that have `.claude-plugin/plugin.json` or `skills/`
3. **Single plugin** — The pointed-to directory itself has `.claude-plugin/plugin.json` → treat as one plugin
```bash
# Interactive plugin picker — current repo
python ./scripts/plugin_add.py
# Install from remote GitHub repo (any layout)
python ./scripts/plugin_add.py richfrem/agent-plugins-skills
# Install specific subpath
python ./scripts/plugin_add.py anthropics/knowledge-work-plugins/engineering
# Full GitHub URL (tree/main/ stripped automatically)
python ./scripts/plugin_add.py https://github.com/anthropics/claude-plugins-official/tree/main/plugins
# Install all non-interactively
python ./scripts/plugin_add.py richfrem/agent-plugins-skills --all -y
# Dry-run preview
python ./scripts/plugin_add.py --dry-run
```
#### Fresh Project: `.claude/` Auto-Init
If no `.claude/` directory exists in the target project, `plugin_add.py` will prompt:
```
No .claude/ directory found in this project.
Initialize .claude/ for Claude Code integration? [Y/n]
```
Answering `Y` (default) creates `.claude/` so Claude Code symlinks are activated during installation. Answering `n` skips it — only `.agents/` (the canonical store) will be populated.
### The Execution Engine: `plugin_installer.py`
This script is the literal OS execution engine. You should generally run `plugin_add.py`, but you can explicitly trigger the bridge engine if you need to bypass plugin discovery entirely.
**Install a single plugin directly via bridge:**
```bash
python scripts/plugin_installer.py \
--plugin plugins/my-plugin
```
**Dry run (preview only, no writes):**
```bash
python scripts/plugin_installer.py \
--plugin plugins/my-plugin --dry-run
```
---
## Execution Protocol
> **CRITICAL**: Do not run the installer without a Recap-Before-Execute summary.
> Always propose `--dry-run` first for any destructive or first-time operation.
### Phase 1: Pre-flight Check
User-scope confirmation
- When the user requests a search or install, confirm the filesystem scope before running any discovery. Present the chosen path in plain text and ask for explicit approval (e.g. "Search only /Users/me/projects/agent-plugins-skills — proceed? (Y/n)").
- Prefer repository-scoped scans by default unless the user explicitly requests a wider scope (home dir or system-wide).
Before running the bridge, verify:
1. Plugin path exists and has `./plugin.json`
2. At least one of `.agents/`, `.claude/` exists
(do NOT create these automatically — if missing, print the exact `mkdir`
command and wait for user confirmation)
3. No `--target auto` is used anywhere in the call chain
### Phase 2: Recap-Before-Execute
State exactly what will happen:
```markdown
### Plugin Installation Plan
- **Plugin**: plugins/my-plugin (v1.2.0)
- **Components**:
- 2 skills → .agents/skills/ (canonical) + .claude/skills/ symlinks
- 3 commands → .agents/workflows/, .claude/commands/
- 1 rules file → .agents/rules/, appended to CLAUDE.md
- hooks.json → .agents/hooks/ + .claude/hooks/
- **Detected environments**: claude (.claude/)
- **Lock file**: will update skills-lock.json
> Proceed? (yes to run live, no to dry-run first)
```
### Phase 3: Execute
Wait for explicit confirmation before running live. Default to dry-run.
---
## Fallback Tree
### 1. Target directory not found
Do NOT create automatically. Print:
```bash
mkdir .claude # for Claude Code
```
Wait for user confirmation. A missing directory may mean an uninitialised project.
### 2. Plugin not found
Do NOT scan for similar names. Report the error and list `plugins/` contents.
Ask the user to confirm the correct plugin name.
### 3. Partial bridge (some components failed)
Report each failed component individually with its error. Do NOT claim success.
Offer to retry individual components once the user resolves the issue.
### 4. `--target auto` attempted
STOP immediately. Ask the user to specify the exact environment name
(`antigravity`, `claude`, `gemini`, `github`). Never run with `--target auto`.
### 5. Symlink failed (Windows)
Log `symlinkFailed: true`. Fallback chain:
1. `os.symlink()` — true symlink (needs Developer Mode)
2. `mklink /J` for dirs / `mklink /H` for files — no Developer Mode needed
3. `shutil.copy2()` — plain copy (last resort, no live sync)
Warn: "On Windows, enable Developer Mode for true symlink support."
### 6. Lock file write failed
Log the error but do not abort the install. Warn the user that
installed skills may not be tracked in `skills-lock.json`.
---
## DETECTABLE_AGENTS Reference
The installer auto-detects agent environments by checking for these directories
at project root. Antigravity, Gemini, and GitHub Copilot now natively read from
`.agents/` so no per-agent symlinks are needed for them. Only environments that
require their own directory layout (Claude Code, Azure) are listed here:
```python
DETECTABLE_AGENTS = {
".claude": {
"name": "claude",
"skills": ".claude/skills",
"agents": ".claude/agents",
"commands": ".claude/commands", # Markdown
"rules": None, # Append to CLAUDE.md instead
"rules_append_target": "CLAUDE.md",
"hooks": ".claude/hooks",
"rules_mode": "append",
},
".azure": {
"name": "azure",
"skills": ".azure/skills",
"commands": None,
"rules": None,
"hooks": None,
},
}
```
---
## When to Use This Skill
- **Deploying a new plugin locally** — full component installation including
commands, rules, and hooks that `npx skills` won't handle
- **After modifying a plugin** — re-run bridge to push changes to all envs
- **Adding a new target environment** — existing plugins need re-bridging
- **Reconciling with npx skills ecosystem** — write lock file entries so
`npx skills list/check/update` sees bridge-installed skills
- **Debugging missing commands or rules** — `npx skills` installs will be
missing these; bridge install completes the picture
## When NOT to Use This Skill
- **End-user consumption from remote repo** — use `plugin_add.py owner/repo` (auto-clones + interactive TUI) or the authoritative installation hub for individual skills.
- **Auditing plugin structure** — use `maintain-plugins`
## Related Skills
- `maintain-plugins` — structural audits, sync, orphan cleanup, README generation