diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d73a343..bf01e9f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,8 +8,8 @@ "plugins": [ { "name": "skill-portability", - "description": "Uplift any Claude plugin to full multi-platform portability: Cursor, Gemini CLI, OpenCode, and AGENTS.md support", - "version": "0.1.0", + "description": "Assess or uplift any plugin for multi-platform portability: Claude Code, Cursor, Gemini CLI, Codex, Antigravity, and OpenClaw", + "version": "0.1.1", "source": "./", "author": { "name": "Nathaniel Ramm", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index b3f7eda..0ab3d69 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "skill-portability", - "description": "Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact.", - "version": "0.1.0", + "description": "Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact.", + "version": "0.1.1", "author": { "name": "Nathaniel Ramm", "email": "nathaniel.ramm@discretedatascience.com" diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 73c9947..25aa54d 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "skill-portability", - "description": "Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact.", - "version": "0.1.0", + "description": "Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact.", + "version": "0.1.1", "skills": "./skills/", "hooks": "./hooks/" } diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 3ebea55..00b9693 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "skill-portability", "source": "./", - "description": "Uplift any plugin to full multi-platform portability: Claude Code, Cursor, Gemini CLI, OpenCode, Copilot CLI, and Codex support" + "description": "Assess or uplift any plugin for multi-platform portability: Claude Code, Cursor, Gemini CLI, Codex, Antigravity, and OpenClaw" } ] } diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index b3f7eda..0ab3d69 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "skill-portability", - "description": "Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact.", - "version": "0.1.0", + "description": "Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact.", + "version": "0.1.1", "author": { "name": "Nathaniel Ramm", "email": "nathaniel.ramm@discretedatascience.com" diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8bb468f..b23e585 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,9 +11,9 @@ assignees: '' - [ ] Claude Code - [ ] Cursor - [ ] Gemini CLI -- [ ] OpenCode -- [ ] Copilot CLI - [ ] Codex +- [ ] Antigravity +- [ ] OpenClaw ## Plugin Version diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 384314c..9dcd8bb 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -19,9 +19,9 @@ assignees: '' - [ ] Claude Code - [ ] Cursor - [ ] Gemini CLI -- [ ] OpenCode -- [ ] Copilot CLI - [ ] Codex +- [ ] Antigravity +- [ ] OpenClaw - [ ] All platforms ## Alternatives Considered diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d204b3a..3cf6390 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,9 +15,9 @@ - [ ] Claude Code - [ ] Cursor - [ ] Gemini CLI -- [ ] OpenCode -- [ ] Copilot CLI - [ ] Codex +- [ ] Antigravity +- [ ] OpenClaw - [ ] None (docs/CI only) ## Testing Done diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 289c4b5..ddffa03 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,6 +1,6 @@ # Skill Portability -Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact. +Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact. ## Skills @@ -19,4 +19,4 @@ Skills use Claude Code tool names. Copilot CLI equivalents: - `Skill` → `skill` - `Task` / `Agent` → subagent dispatch -See each skill's `references/copilot-tools.md` for detailed mapping. +See `lib/references/` for platform-specific tool mapping tables. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afcd99d..7ff2284 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,8 +59,6 @@ jobs: check_json .claude-plugin/marketplace.json check_json .cursor-plugin/plugin.json check_file GEMINI.md - check_file .opencode/plugins/skill-portability.js - check_file .github/copilot-instructions.md check_file AGENTS.md echo "" @@ -86,7 +84,7 @@ jobs: echo "" echo "=== Per-Skill Sidecars ===" for skill_dir in skills/*/; do - for sidecar in codex-tools.md copilot-tools.md gemini-tools.md; do + for sidecar in codex-tools.md gemini-tools.md; do check_file "${skill_dir}references/${sidecar}" done done diff --git a/AGENTS.md b/AGENTS.md index cebc8c7..7736bb4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Skill Portability -Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact. +Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact. ## Project Context @@ -10,8 +10,7 @@ Skill Portability is a Claude Code plugin that provides skills and commands for This plugin provides the following skills. Read the SKILL.md files listed to understand how to invoke each skill: -- skills/uplifting-a-plugin/SKILL.md -- skills/assessing-plugin-portability/SKILL.md +- skills/plugin-portability/SKILL.md ## Tool Name Mapping @@ -26,4 +25,4 @@ Skills use Claude Code tool names. Platform equivalents: - `Skill` → your platform's skill-invoke tool - `Task` → your platform's subagent-dispatch tool (if supported) -See `lib/references/` for platform-specific tool mapping tables (copilot-tools.md, codex-tools.md, gemini-tools.md). +See `lib/references/` for platform-specific tool mapping tables (codex-tools.md, gemini-tools.md). diff --git a/CHANGELOG.md b/CHANGELOG.md index 7030345..520e72a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.1] - 2026-04-26 + +### Changed + +- **Merged skills:** `assessing-plugin-portability` and `uplifting-a-plugin` consolidated into single `plugin-portability` skill with Phase 0 intent detection via AskUserQuestion +- **Platform set:** Replaced OpenCode and Copilot CLI with Antigravity (Google) and OpenClaw +- **Rubric system:** Replaced prose-based scoring with structured YAML conditions — each condition has stable ID, type (checkable/judgement), critical flag, and pseudocode check +- **Scoring formula:** Hybrid AND/OR — critical flags gate score levels, optional flags earn within. Percentage-based bands with N/A handling +- **Condition-linked uplift:** Every generation action carries `# fixes:` annotation linking to rubric condition IDs. Template field in rubric conditions maps directly to artifacts +- **Two-layer uplift strategy:** Shape-based target (skill-first/full-portable/curated-note) confirmed by user, per-platform depth (incremental/full) auto-derived from scores + +### Added + +- `lib/references/platform-mappings.md` — 9 canonical lookup tables (model mapping, tool names, hook events, path variables, field stripping, manifest fields, hook format, skill dirs, agent formats) +- `lib/patterns/inventory.md` — consolidated inventory pattern merging assessment and uplift Phase 2 +- 6 platform YAML rubrics with 160 total conditions across claude-code, cursor, gemini-cli, codex, antigravity, openclaw +- Antigravity platform support (Google VS Code fork, OpenVSX, `.agents/skills/`) +- OpenClaw platform support (TypeScript gateway, plugin SDK hooks, ClawHub) +- Template action types: create (new files) and merge (update existing via `?merge` suffix) +- `ALLOWED_CATEGORIES` table enforcing shape-based artifact scoping + +### Removed + +- `skills/assessing-plugin-portability/` — folded into `plugin-portability` +- `skills/uplifting-a-plugin/` — folded into `plugin-portability` +- OpenCode platform support +- Copilot CLI platform support +- Prose-based platform rubrics (`.md` replaced by `.yaml`) + ## [0.1.0] - 2026-04-24 ### Added @@ -20,4 +49,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ecosystem landscape documentation and competitive analysis - Install documentation for all 6 platforms +[0.1.1]: https://github.com/hiivmind/skill-portability/releases/tag/v0.1.1 [0.1.0]: https://github.com/hiivmind/skill-portability/releases/tag/v0.1.0 diff --git a/CLAUDE.md b/CLAUDE.md index 4f1b822..53cb11a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,5 @@ # Skill Portability -Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact. +Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact. This plugin is loaded via Claude Code's plugin system. Skills are invoked via the `Skill` tool. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0207477..d5295d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). Please read 2. Add a manifest template in `lib/templates/manifests/` 3. Add install doc templates in `lib/templates/install-docs/` 4. Add a platform detection pattern in `lib/patterns/platforms/` -5. Update the uplift skill (`skills/uplifting-a-plugin/SKILL.md`) to handle the new platform +5. Update the portability skill (`skills/plugin-portability/SKILL.md`) to handle the new platform 6. Update the assessment rubric (`docs/assessment-rubric.md`) with scoring criteria ## Authoring Skills @@ -37,7 +37,7 @@ allowed-tools: Read, Write, Edit --- ``` -See existing skills in `skills/` for examples. Each skill should also have a `references/` subdirectory containing platform-specific tool mappings (`codex-tools.md`, `copilot-tools.md`, `gemini-tools.md`). +See existing skills in `skills/` for examples. Each skill should also have a `references/` subdirectory containing platform-specific tool mappings (`codex-tools.md`, `gemini-tools.md`, `antigravity-tools.md`, `openclaw-tools.md`). ## Pull Request Process diff --git a/GEMINI.md b/GEMINI.md index 9068624..4b11f4e 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -1,12 +1,10 @@ # Skill Portability -Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact. +Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact. ## Skills @./skills/using-skill-portability/SKILL.md @./skills/using-skill-portability/references/gemini-tools.md -@./skills/uplifting-a-plugin/SKILL.md -@./skills/uplifting-a-plugin/references/gemini-tools.md -@./skills/assessing-plugin-portability/SKILL.md -@./skills/assessing-plugin-portability/references/gemini-tools.md +@./skills/plugin-portability/SKILL.md +@./skills/plugin-portability/references/gemini-tools.md diff --git a/INSTALL.md b/INSTALL.md index 76b08a5..b688a5b 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -60,7 +60,7 @@ Assess the portability of /path/to/my-plugin ``` ``` -Use the uplifting-a-plugin skill on /path/to/my-plugin +Use the plugin-portability skill on /path/to/my-plugin ``` ### Cursor @@ -86,8 +86,7 @@ Open Cursor and check that skills from Skill Portability appear when typing `/` In Cursor's chat, invoke skills with the `/` prefix: ``` -/assessing-plugin-portability -/uplifting-a-plugin +/plugin-portability ``` ### Gemini CLI @@ -117,84 +116,71 @@ Look for `skill-portability` in the output. Restart Gemini CLI if it was running Gemini CLI activates skills automatically when it determines they are relevant. You can also mention a skill by name: ``` -Assess the portability of /path/to/my-plugin using assessing-plugin-portability +Assess the portability of /path/to/my-plugin using plugin-portability ``` List available skills with `/skills list`. -### OpenCode +### Antigravity -#### Local plugin install +#### Skill-only install -Copy `.opencode/plugins/skill-portability.js` to your project's `.opencode/plugins/` directory, or to `~/.config/opencode/plugins/` for global install. +Copy skills to the Antigravity skills directory: -#### npm install (if published) - -Add to your `opencode.json`: - -```json -{ - "plugin": ["skill-portability"] -} +```bash +cp -r skills/ .agents/skills/ ``` -#### Context file - -OpenCode uses `AGENTS.md` as its primary context file. If both `AGENTS.md` and `CLAUDE.md` exist, only `AGENTS.md` is loaded. +Or for global install: -#### Verify - -Restart OpenCode and check that skills are listed when the agent invokes the `skill` tool. +```bash +cp -r skills/ ~/.gemini/antigravity/skills/ +``` -#### Requirements +#### Extension install -OpenCode requires [Bun](https://bun.sh) for plugin loading. +```bash +antigravity --install-extension hiivmind/skill-portability +``` #### Using the skills -OpenCode discovers skills automatically. Mention a skill by name and the agent will activate it: +Antigravity discovers skills automatically. Mention a skill by name and the agent will activate it: ``` -Run assessing-plugin-portability on /path/to/my-plugin +Run plugin-portability on /path/to/my-plugin ``` -### Copilot CLI - -#### Skill install +### OpenClaw -Install skills via GitHub CLI: +#### ClawHub install ```bash -gh skill install hiivmind/skill-portability +openclaw plugins install skill-portability ``` -Or clone the repo — skills are auto-discovered from the `skills/` directory: +#### npm install ```bash -git clone https://github.com/hiivmind/skill-portability +npm install skill-portability ``` -#### Context +#### Local install -Copilot reads `.github/copilot-instructions.md` for repo-wide context. It also reads `AGENTS.md`, `CLAUDE.md`, and `GEMINI.md` from the project root. +Add the plugin path to `openclaw.json`: -#### Verify - -Start Copilot CLI in the repo directory and check that skills appear: - -```bash -copilot +```json +{ + "plugins.load.paths": ["/path/to/skill-portability"] +} ``` -Type `/` to see available skills. - #### Using the skills -In Copilot CLI, invoke skills with the `/` prefix: +OpenClaw discovers skills automatically. Mention a skill by name and the agent will activate it: ``` -/assessing-plugin-portability -/uplifting-a-plugin +Run plugin-portability on /path/to/my-plugin ``` ### Codex @@ -249,15 +235,15 @@ Start a new Codex session and check one of: - `/plugins` shows `skill-portability` as installed - `~/.codex/config.toml` contains both the marketplace entry and the enabled plugin entry -- `$assessing-plugin-portability` resolves in a fresh session +- `$plugin-portability` resolves in a fresh session #### Using the skills In Codex, invoke skills with the `$` prefix: ``` -$assessing-plugin-portability -$uplifting-a-plugin +$plugin-portability +$plugin-portability ``` ## Adding Another Platform @@ -305,30 +291,28 @@ Link Gemini to your existing checkout: gemini extensions link /path/to/existing/skill-portability ``` -### OpenCode +### Antigravity -Symlink the plugin entrypoint from your existing checkout (do not copy — it resolves paths relative to the repo root): +Copy skills from your existing checkout: ```bash -ln -s /path/to/existing/skill-portability/.opencode/plugins/skill-portability.js .opencode/plugins/skill-portability.js +cp -r /path/to/existing/skill-portability/skills/ .agents/skills/ ``` -Or add the checkout path to your `opencode.json`: +Or install as an extension: -```json -{ - "plugin": ["/path/to/existing/skill-portability"] -} +```bash +antigravity --install-extension /path/to/existing/skill-portability ``` -Requires [Bun](https://bun.sh). - -### Copilot CLI +### OpenClaw -Work from the cloned directory — skills are auto-discovered from `skills/`. Or symlink into your project: +Add the checkout path to `openclaw.json`: -```bash -ln -s /path/to/existing/skill-portability/skills skills/skill-portability +```json +{ + "plugins.load.paths": ["/path/to/existing/skill-portability"] +} ``` ### Codex diff --git a/PUBLISHING.md b/PUBLISHING.md index 4857189..ec48fec 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -70,58 +70,53 @@ gemini extensions install hiivmind/skill-portability Users can browse the gallery or install directly from the GitHub URL. -## OpenCode +## Antigravity -No marketplace. Distribution via npm or filesystem. +Published to the [OpenVSX](https://open-vsx.org/) registry. ### Publishing -Publish the plugin as an npm package. No submission or review process. +Package the plugin as a VSIX extension and publish to OpenVSX: -### How users find and install Skill Portability +```bash +antigravity publish +``` -**npm:** -Add to `opencode.json`: +Extensions are reviewed by the OpenVSX team before listing. -```json -{ - "plugin": ["skill-portability"] -} -``` +### How users find and install Skill Portability -**Local files:** -Copy `.opencode/plugins/skill-portability.js` to `.opencode/plugins/` (project) or `~/.config/opencode/plugins/` (global). +```bash +antigravity --install-extension hiivmind.skill-portability +``` -Requires [Bun](https://bun.sh) for plugin loading. +Users can also browse and install from the OpenVSX web registry. -## Copilot CLI +## OpenClaw -Skills published via GitHub CLI (v2.90.0+). +Published to [ClawHub](https://clawhub.dev/) and via npm. ### Publishing +Publish as an npm package and register on ClawHub: + ```bash -gh skill publish [--fix] +npm publish ``` -Validates against the Agent Skills spec. No formal review — skills are published to the GitHub repository. +Then submit to ClawHub for listing. No formal review process. ### How users find and install Skill Portability +**npm:** + ```bash -gh skill search skill-portability -gh skill preview hiivmind/skill-portability skill-portability -gh skill install hiivmind/skill-portability +npm install -g @hiivmind/skill-portability ``` -### Third-party registries - -- [skills.sh](https://skills.sh) — community directory with 300k+ monthly views -- [github/awesome-copilot](https://github.com/github/awesome-copilot) — GitHub's curated collection - -### Security note +**ClawHub:** -Always recommend users run `gh skill preview` before installing. GitHub does not vet third-party skills. +Browse [clawhub.dev](https://clawhub.dev/) and install directly from the listing. ## Codex diff --git a/README.md b/README.md index 5a9baf5..7b466da 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg) -![Version](https://img.shields.io/badge/version-0.1.0-blue.svg) +![Version](https://img.shields.io/badge/version-0.1.1-blue.svg) ![CI](https://github.com/hiivmind/skill-portability/actions/workflows/ci.yml/badge.svg) # Skill Portability @@ -28,26 +28,23 @@ Starting from whatever platform manifests already exist, it detects plugin metad | Platform | Artifacts | | -------- | --------- | -| **Claude Code** | `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `CLAUDE.md` | +| **Claude Code** | `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json` | | **Cursor** | `.cursor-plugin/plugin.json` | | **Gemini CLI** | `gemini-extension.json`, `GEMINI.md` | -| **Codex** | `.codex-plugin/plugin.json`, `.agents/plugins/marketplace.json`, `.codex/INSTALL.md`, `AGENTS.md` | -| **OpenCode** | `package.json`, `.opencode/plugins/.js` | -| **Copilot CLI** | `AGENTS.md` | -| **Per-skill tool mapping** | `references/{copilot,codex,gemini}-tools.md` | -| **Hook portability** | `hooks-cursor.json` derived from `hooks.json` | +| **Codex** | `.codex-plugin/plugin.json` | +| **Antigravity** | `package.json`, `.agents/skills/` | +| **OpenClaw** | `openclaw.plugin.json` | ## Skills -- **`assessing-plugin-portability`** — Report portability gaps without making changes -- **`uplifting-a-plugin`** — Write all missing platform manifests for a target plugin +- **`plugin-portability`** — Assess or uplift a plugin for multi-platform portability. Asks intent upfront (assess/uplift, platforms, uplift target), runs condition-driven scoring, and optionally generates missing artifacts. ## Ecosystem landscape Cross-platform portability has real structural limits — but the ecosystem is maturing fast. For consumers and single-skill authors, tools like `npx skills`, `gh skill`, and platform-native CLIs make distribution easy. The friction appears when delivering cross-platform plugins with shared resources (hooks, manifests, context files). -The `uplifting-a-plugin` skill generates everything that *can* be generated. +The `plugin-portability` skill generates everything that *can* be generated. For the full picture — what works, what doesn't, and what needs platform-level changes — see [`docs/ecosystem-landscape.md`](docs/ecosystem-landscape.md). ## Acknowledgements @@ -85,12 +82,6 @@ Full details for all platforms in [INSTALL.md](INSTALL.md). gemini extensions install https://github.com/hiivmind/skill-portability ``` -**Copilot CLI:** - -```bash -gh skill install hiivmind/skill-portability -``` - **Codex:** ```bash @@ -99,20 +90,25 @@ codex marketplace add hiivmind/skill-portability Then open `/plugins` in Codex and install `skill-portability`. -**OpenCode** — clone and copy the plugin entrypoint: +**Antigravity:** + +```bash +antigravity --install-extension hiivmind/skill-portability +``` + +**OpenClaw:** ```bash -git clone https://github.com/hiivmind/skill-portability -cp skill-portability/.opencode/plugins/skill-portability.js .opencode/plugins/ +openclaw plugins install skill-portability ``` ## Usage -| Platform | Assess portability | Uplift a plugin | -| -------- | ------------------ | --------------- | -| **Claude Code** | `Assess the portability of /path/to/plugin` | `Use the uplifting-a-plugin skill on /path/to/plugin` | -| **Cursor** | `/assessing-plugin-portability` | `/uplifting-a-plugin` | -| **Copilot CLI** | `/assessing-plugin-portability` | `/uplifting-a-plugin` | -| **Codex** | `$assessing-plugin-portability` | `$uplifting-a-plugin` | -| **Gemini CLI** | Mention skill by name — auto-activated | Same | -| **OpenCode** | Mention skill by name — auto-activated | Same | +| Platform | Invocation | +| -------- | ---------- | +| **Claude Code** | `Use the plugin-portability skill on /path/to/plugin` | +| **Cursor** | `Skill: plugin-portability` | +| **Gemini CLI** | `activate_skill: plugin-portability` | +| **Codex** | `$plugin-portability` | +| **Antigravity** | Auto-discovered | +| **OpenClaw** | Auto-discovered | diff --git a/docs/assessment-rubric.md b/docs/assessment-rubric.md index 4abf68e..5d18a2d 100644 --- a/docs/assessment-rubric.md +++ b/docs/assessment-rubric.md @@ -62,7 +62,6 @@ Look for: - `.codex-plugin/plugin.json` - `gemini-extension.json` - `package.json` -- `.opencode/plugins/*.js` Record: diff --git a/docs/ecosystem-landscape.md b/docs/ecosystem-landscape.md index 210b23d..e251c14 100644 --- a/docs/ecosystem-landscape.md +++ b/docs/ecosystem-landscape.md @@ -2,7 +2,7 @@ The agent skills ecosystem is thriving — multiple platforms, growing registries, one-command installs. For consumers installing skills and for authors publishing standalone skills, the tooling works well. The friction appears when an author needs to deliver a cross-platform plugin with shared resources and patterns (hooks, context files, platform manifests, cross-skill references). -This document maps both sides: what works today, and where cross-platform plugin delivery hits real limits. The `uplifting-a-plugin` skill generates the cross-platform artifacts where it can. For what cannot be fixed at the plugin layer, platform-level changes are needed. +This document maps both sides: what works today, and where cross-platform plugin delivery hits real limits. The `plugin-portability` skill generates the cross-platform artifacts where it can. For what cannot be fixed at the plugin layer, platform-level changes are needed. --- @@ -89,7 +89,7 @@ Each registry is platform-native. There is no cross-platform registry. A plugin ### How skill-portability solves this -The uplift skill generates per-platform install docs (in `INSTALL.md`) that document how to install the plugin on each platform. It also generates the manifests each platform requires for installation — so for platforms that install directly from GitHub (Claude Code, Copilot CLI, Gemini CLI, Codex), the plugin is installable without additional setup by the author. +The `plugin-portability` skill generates per-platform install docs (in `INSTALL.md`) that document how to install the plugin on each platform. It also generates the manifests each platform requires for installation — so for platforms that install directly from GitHub (Claude Code, Copilot CLI, Gemini CLI, Codex), the plugin is installable without additional setup by the author. Discovery itself remains a manual step: authors must submit to each platform's registry separately. @@ -267,7 +267,7 @@ The self-bootstrapping pattern at the core of cross-platform plugins is **six pa This parallel-adapters approach is not elegant. But it works on five of six platforms with a strong guarantee (forced session-start injection), and on the sixth (Codex) with a weaker one (passive discovery). -The `uplifting-a-plugin` skill generates the manifests, hook configs, context files, tool-mapping sidecars, and install documentation that this pattern requires. The remaining unfixable gaps (Gemini subagents, Codex hook injection, the fundamental lack of a unified manifest standard) require platform-level changes. +The `plugin-portability` skill generates the manifests, hook configs, context files, tool-mapping sidecars, and install documentation that this pattern requires. The remaining unfixable gaps (Gemini subagents, Codex hook injection, the fundamental lack of a unified manifest standard) require platform-level changes. --- diff --git a/docs/plugin-portability-patterns.md b/docs/plugin-portability-patterns.md index 2d5fb8d..450772c 100644 --- a/docs/plugin-portability-patterns.md +++ b/docs/plugin-portability-patterns.md @@ -29,7 +29,7 @@ Typical signals: - `skills//SKILL.md` - optional `references/` beside skills -- no `.claude-plugin/`, `.cursor-plugin/`, `.codex-plugin/`, `gemini-extension.json`, or OpenCode plugin files +- no `.claude-plugin/`, `.cursor-plugin/`, `.codex-plugin/`, or `gemini-extension.json` files This is the easiest shape to uplift. The repo already contains the core behavior, but it lacks distribution and platform metadata. @@ -170,7 +170,6 @@ Check for these categories. - `.codex-plugin/plugin.json` - `gemini-extension.json` - `package.json` -- `.opencode/plugins/*.js` #### Context and instruction files @@ -267,15 +266,11 @@ A practical uplift target looks like this: plugin.json .codex/ INSTALL.md - .opencode/ - plugins/ - .js skills/ / SKILL.md references/ codex-tools.md - copilot-tools.md gemini-tools.md rules/ # Cursor .mdc rules (optional) agents/ # Cursor/Codex agent definitions (optional) @@ -319,7 +314,6 @@ Typical uplift outputs: - Cursor: `.cursor-plugin/plugin.json` - Codex: `.codex-plugin/plugin.json` if packaging as a native Codex plugin - Gemini: `gemini-extension.json` -- OpenCode: `package.json` and `.opencode/plugins/.js` #### 3. Add platform context files @@ -335,7 +329,6 @@ These files explain or inject the plugin behavior at session start or load time: Where skills speak in one platform’s tool vocabulary, add sidecars such as: - `references/codex-tools.md` -- `references/copilot-tools.md` - `references/gemini-tools.md` This is essential for portable skills that refer to tools like `Skill`, `Task`, `Read`, `Edit`, or hook mechanisms. @@ -498,15 +491,6 @@ The uplifted repo should provide: Install instructions should mention any required clone or extension install command and should verify that the context file is being loaded. -### OpenCode install pattern - -The uplifted repo should provide: - -- `package.json` -- `.opencode/plugins/.js` - -Install instructions should clearly state whether OpenCode consumes the repo directly or expects package metadata plus a plugin entrypoint. - ## Recommended documentation set for an uplifted plugin Every uplifted repo should document both author and user concerns. diff --git a/docs/research_sources.md b/docs/research_sources.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/superpowers/plans/2026-04-26-rubric-tightening.md b/docs/superpowers/plans/2026-04-26-rubric-tightening.md new file mode 100644 index 0000000..a26256e --- /dev/null +++ b/docs/superpowers/plans/2026-04-26-rubric-tightening.md @@ -0,0 +1,1326 @@ +# Rubric Tightening & Condition-Linked Uplift — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace prose-based rubrics with structured YAML conditions linked to lookup tables, wire uplift actions to condition IDs via `# fixes:` annotations, and switch the platform set to Claude Code / Codex / Cursor / Gemini / Antigravity / OpenClaw. + +**Architecture:** A single canonical lookup tables file (`lib/references/platform-mappings.md`) feeds into 6 platform rubric YAML files (`lib/patterns/platforms/*.yaml`). Each YAML condition has a stable ID, type (checkable/judgement), critical flag, and pseudocode check. The assessment skill evaluates conditions (JIT-generating scripts for checkable ones), the uplift skill references condition IDs via `# fixes:` annotations. The rubric framework document explains scoring formula and band calculation. + +**Tech Stack:** Markdown, YAML, pseudocode. No runtime code — all logic is LLM-interpreted with JIT-scriptable checkable conditions. + +**Spec:** `docs/superpowers/specs/2026-04-26-rubric-tightening-design.md` + +--- + +## File Map + +### New Files +| File | Responsibility | +|------|---------------| +| `lib/references/platform-mappings.md` | Canonical lookup tables (9 tables: model mapping, tool names, hook events, path variables, field stripping, manifest fields, hook format, skill dirs, agent formats) | +| `lib/patterns/platforms/claude-code.yaml` | Claude Code rubric — structured conditions | +| `lib/patterns/platforms/cursor.yaml` | Cursor rubric — structured conditions | +| `lib/patterns/platforms/gemini-cli.yaml` | Gemini CLI rubric — structured conditions | +| `lib/patterns/platforms/codex.yaml` | Codex rubric — structured conditions | +| `lib/patterns/platforms/antigravity.yaml` | Antigravity rubric — new platform | +| `lib/patterns/platforms/openclaw.yaml` | OpenClaw rubric — new platform | +| `lib/templates/manifests/antigravity/package.json.tmpl` | Antigravity manifest template | +| `lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl` | OpenClaw manifest template | +| `lib/templates/install-docs/antigravity.md` | Antigravity install docs | +| `lib/templates/install-docs/openclaw.md` | OpenClaw install docs | +| `lib/templates/install-docs/adding-platform/antigravity.md` | Adding Antigravity section | +| `lib/templates/install-docs/adding-platform/openclaw.md` | Adding OpenClaw section | +| `lib/templates/install-docs/publishing/antigravity.md` | Antigravity publishing guide | +| `lib/templates/install-docs/publishing/openclaw.md` | OpenClaw publishing guide | + +### Rewritten Files +| File | Nature of Change | +|------|-----------------| +| `lib/patterns/rubric-framework.md` | New scoring formula, condition types, N/A handling, percentage bands, blocker updates | +| `skills/assessing-plugin-portability/SKILL.md` | Reference condition IDs, JIT guidance, new platform set, new scoring | +| `skills/uplifting-a-plugin/SKILL.md` | `# fixes:` annotations, incremental uplift, new platform set, rubric-informed decisions | +| `skills/using-skill-portability/SKILL.md` | Updated platform list | + +### Updated Files +| File | Nature of Change | +|------|-----------------| +| `lib/patterns/hook-merging.md` | Remove OpenCode/Copilot, add Antigravity/OpenClaw notes | +| `lib/patterns/bootstrapping.md` | Remove OpenCode/Copilot platform branches | +| `lib/patterns/detection-algorithm.md` | Add Antigravity/OpenClaw manifest detection, remove OpenCode/Copilot | +| `lib/patterns/manifest-generation.md` | New platform entries, remove dropped ones | +| `lib/patterns/injection-checks.md` | Remove OpenCode/Copilot checks | +| `lib/patterns/report-template.md` | Update platform list references | +| `lib/patterns/platforms/publishing-and-discoverability.md` | Add Antigravity/OpenClaw, remove OpenCode/Copilot | +| `lib/templates/context-files/AGENTS.md.tmpl` | Remove Copilot/OpenCode references | +| `lib/templates/hooks/session-start.sh` | Remove OpenCode/Copilot platform detection branches | + +### Removed Files +| File | Reason | +|------|--------| +| `lib/patterns/platforms/opencode.md` | Platform dropped | +| `lib/patterns/platforms/copilot-cli.md` | Platform dropped | +| `lib/patterns/platforms/claude-code.md` | Replaced by .yaml | +| `lib/patterns/platforms/cursor.md` | Replaced by .yaml | +| `lib/patterns/platforms/gemini-cli.md` | Replaced by .yaml | +| `lib/patterns/platforms/codex.md` | Replaced by .yaml | +| `lib/references/copilot-tools.md` | Platform dropped | +| `lib/templates/manifests/opencode-plugin.js.tmpl` | Platform dropped | +| `lib/templates/manifests/package.json.tmpl` | Was OpenCode-specific | +| `lib/templates/context-files/copilot-instructions.md.tmpl` | Platform dropped | +| `lib/templates/install-docs/opencode.md` | Platform dropped | +| `lib/templates/install-docs/copilot-cli.md` | Platform dropped | +| `lib/templates/install-docs/adding-platform/opencode.md` | Platform dropped | +| `lib/templates/install-docs/adding-platform/copilot-cli.md` | Platform dropped | +| `lib/templates/install-docs/publishing/opencode.md` | Platform dropped | +| `lib/templates/install-docs/publishing/copilot-cli.md` | Platform dropped | +| `skills/*/references/copilot-tools.md` | Platform dropped (3 files) | + +--- + +## Task 1: Create Canonical Lookup Tables + +**Files:** +- Create: `lib/references/platform-mappings.md` + +This is the foundation — every rubric condition and uplift action references these tables. + +- [ ] **Step 1: Create platform-mappings.md with all 9 tables** + +```markdown +# Platform Mappings + +Canonical lookup tables for cross-platform plugin portability. +Referenced by rubric conditions (pseudocode `LOOKUP["table_name"]["platform"]`) +and by uplift templates (`{{! fixes: ... }}`). + +--- + +## Table 1: Model Mapping + +Maps Claude Code model shortnames to platform equivalents. + +| Claude Model | Gemini | Codex | OpenClaw | Cursor | Antigravity | +|---|---|---|---|---|---| +| opus | gemini-2.5-pro | gpt-5.4 | anthropic/claude-opus-4-6 | inherit | (removed) | +| sonnet | gemini-2.5-flash | gpt-5.4-mini | anthropic/claude-sonnet-4-5 | inherit | (removed) | +| haiku | gemini-2.0-flash-lite | gpt-5.4-mini | anthropic/claude-haiku-4-5 | inherit | (removed) | + +**Rules:** +- Cursor always uses `inherit` (defers to user's model selection) +- Antigravity strips the model field entirely +- Codex maps both sonnet and haiku to `gpt-5.4-mini` + +--- + +## Table 2: Tool Name Mapping + +Maps Claude Code tool names to platform equivalents. + +| Claude Tool | Gemini | Codex | Cursor | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| Read | read_file | (same) | (same) | (same) | (same) | +| Write | write_file | (same) | (same) | (same) | (same) | +| Edit | replace | (same) | (same) | (same) | (same) | +| Bash | run_shell_command | (same) | (same) | (same) | (same) | +| Grep | grep_search | (same) | (same) | (same) | (same) | +| Glob | list_files | (same) | (same) | (same) | (same) | +| Task | @agent-name | spawn_agent | (same) | (same) | agents.list[] | +| Agent | @agent-name | spawn_agent | (same) | (same) | agents.list[] | +| TodoWrite | (N/A) | update_plan | (same) | (same) | (N/A) | +| Skill | (N/A) | (N/A) | (same) | (same) | (N/A) | + +**Rules:** +- `(same)` means the platform uses the same tool name as Claude Code +- Gemini has no Task/Agent tool — uses `@agent-name` syntax in prompts +- Codex replaces Task/Agent with `spawn_agent` and TodoWrite with `update_plan` +- OpenClaw manages agents via `agents.list[]` in runtime config, not a tool + +--- + +## Table 3: Hook Event Mapping + +Maps Claude Code hook events to platform equivalents. + +| Claude Event | Cursor | Gemini | Codex | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| SessionStart | sessionStart | SessionStart | N/A | N/A | gateway:startup (plugin SDK) | +| PreToolUse | preToolUse | BeforeTool | N/A | N/A | before_tool_call (plugin SDK) | +| PostToolUse | postToolUse | AfterTool | N/A | N/A | tool_result_persist (plugin SDK) | +| PostToolUseFailure | postToolUseFailure | (N/A) | N/A | N/A | N/A | +| SubagentStart | subagentStart | (N/A) | N/A | N/A | N/A | +| SubagentStop | subagentStop | (N/A) | N/A | N/A | N/A | +| PreCompact | preCompact | PreCompress | N/A | N/A | session:compact:before (plugin SDK) | +| Stop | stop | AfterAgent | N/A | N/A | N/A | +| UserPromptSubmit | beforeSubmitPrompt | (N/A) | N/A | N/A | N/A | + +**Rules:** +- Codex and Antigravity have no hook systems +- Gemini hooks go in user `settings.json`, not repo files — generate guidance only +- OpenClaw hooks use TypeScript plugin SDK (`api.registerHook()`), not file-based config +- Cursor uses camelCase; Gemini uses PascalCase + +--- + +## Table 4: Path Variable Mapping + +Maps path variables used in hook scripts. + +| Claude Variable | Cursor | Gemini | Codex | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| `${CLAUDE_PLUGIN_ROOT}` | `${CURSOR_PLUGIN_ROOT}` | `${extensionPath}${/}` | N/A | N/A | N/A | +| `/hooks/scripts/` | `/scripts/` | `/scripts/` | N/A | N/A | N/A | + +--- + +## Table 5: Field Stripping Sets + +Frontmatter fields to remove when converting from Claude Code. + +| Field | Gemini | Codex | OpenClaw | Cursor | Antigravity | +|---|---|---|---|---|---| +| `disable-model-invocation` | strip | strip | strip | **keep** | strip | +| `allowed-tools` | strip | strip | strip | strip | strip | + +**Rules:** +- Cursor keeps `disable-model-invocation` (supported natively) +- All platforms strip `allowed-tools` (Claude-specific) + +--- + +## Table 6: Manifest Required Fields + +Required fields per platform manifest. + +| Platform | Manifest Path | Required Fields | +|---|---|---| +| Claude Code | `.claude-plugin/plugin.json` | name, version, description, author.name, author.email | +| Cursor | `.cursor-plugin/plugin.json` | name, displayName, description, version, author | +| Gemini | `gemini-extension.json` | name, version, description, contextFileName | +| Codex (native) | `.codex-plugin/plugin.json` | name, version, description | +| Antigravity | `package.json` | name, displayName, version, description, publisher | +| OpenClaw | `openclaw.plugin.json` | id, configSchema | + +**Notes:** +- Antigravity: For skill-only distribution, no package.json is needed — drop into `.agents/skills/` +- OpenClaw: Full plugins also need `package.json` with `openclaw.extensions` and `openclaw.compat` +- Gemini: `contextFileName` is always `"GEMINI.md"` + +--- + +## Table 7: Hook Format Rules + +Platform-specific hook configuration rules. + +| Rule | Claude Code | Cursor | Gemini | OpenClaw | +|---|---|---|---|---| +| Event name case | PascalCase | camelCase | PascalCase | snake_case (SDK) | +| Timeout unit | seconds | seconds | milliseconds | N/A (SDK-managed) | +| Async support | yes (optional) | no (strip) | no (strip) | yes (async handlers) | +| Structure | nested (matcher → hooks[]) | flat (matcher at hook level) | settings.json (user-configured) | `api.registerHook()` (TypeScript) | +| Output key | `hookSpecificOutput.additionalContext` | `additional_context` | N/A | return value from handler | + +**Notes:** +- Codex and Antigravity have no hook systems — omitted from this table +- Gemini timeout conversion: multiply Claude seconds × 1000 +- Cursor flattening: each nested hook becomes its own entry with matcher promoted + +--- + +## Table 8: Skill Output Directory + +Where skills and agents live per platform. + +| Platform | Skills Path | Agents Path | +|---|---|---| +| Claude Code | `skills/` | `agents/` | +| Cursor | `skills/` | `agents/` | +| Gemini | `skills/` | `agents/` | +| Codex | `.agents/skills/` | `.codex/agents/` | +| Antigravity | `.agents/skills/` (preferred) or `.agent/skills/` (legacy) | `.agent/rules/` | +| OpenClaw | `skills/` | in manifest `agents.list[]` | + +--- + +## Table 9: Agent Output Format + +How agents are represented per platform. + +| Platform | Format | Model Field | Tools Field | +|---|---|---|---| +| Claude Code | Markdown (`agents/*.md`) | Claude model name | Claude tool names | +| Cursor | Markdown (`agents/*.md`) + `.mdc` rule | `inherit` | stripped | +| Gemini | Markdown (`agents/*.md`) | Gemini model name | `["*"]` (wildcard) | +| Codex | TOML (`.codex/agents/*.toml`) | Codex model name | stripped | +| Antigravity | Combined `AGENTS.md` + `.agent/rules/*.md` | (removed) | (removed) | +| OpenClaw | Listed in manifest `agents.list[]` | OpenClaw `provider/model` | stripped | +``` + +- [ ] **Step 2: Verify the file renders correctly** + +Run: `wc -l lib/references/platform-mappings.md` +Expected: ~160-170 lines + +- [ ] **Step 3: Commit** + +```bash +git add lib/references/platform-mappings.md +git commit -m "feat: add canonical platform lookup tables + +Single reference file with 9 tables: model mapping, tool names, hook events, +path variables, field stripping, manifest fields, hook format, skill dirs, +agent formats. Referenced by rubric conditions and uplift templates." +``` + +--- + +## Task 2: Rewrite Rubric Framework + +**Files:** +- Rewrite: `lib/patterns/rubric-framework.md` + +Replace the current prose-based framework (95 lines) with the new structured system: condition types, hybrid scoring formula, percentage-based bands, N/A handling. + +- [ ] **Step 1: Rewrite rubric-framework.md** + +```markdown +# Rubric Framework + +Shared scoring model for plugin portability assessment. Used by `assessing-plugin-portability`. +Per-platform conditions are in `platforms/.yaml`. +Lookup tables are in `lib/references/platform-mappings.md`. + +--- + +## Condition Structure + +Each condition in a platform rubric YAML file: + +```yaml +- id: {platform}.{category_num}_{category_short}.{component}.{check_name} + type: checkable | judgement + component: {component_tag} + critical: true | false + points: 1 + check: | + pseudocode (checkable) or prose description (judgement) + template: optional — path to template that fixes this condition +``` + +### Condition ID Format + +``` +{platform}.{category_num}_{category_short}.{component}.{check_name} +``` + +Examples: +``` +cursor.1_manifest.plugin_json.required_fields +gemini.5_toolmap.sidecar.task_to_at_agent +codex.2_skills.frontmatter.spawn_agent_documented +``` + +### Condition Types + +| Type | Meaning | Evaluation | +|------|---------|------------| +| `checkable` | Deterministic — file exists, field matches table, pattern present | LLM generates a read-only script from pseudocode, executes it, records pass/fail from exit code | +| `judgement` | Requires interpretation — content quality, documentation adequacy | LLM reads referenced files, applies its interpretation, records pass/fail with reasoning | + +### Condition Fields + +| Field | Required | Purpose | +|-------|----------|---------| +| `id` | yes | Stable identifier. Referenced by assessment AND uplift (`# fixes:` annotations) | +| `type` | yes | `checkable` or `judgement` | +| `component` | yes | Tag for filtering by component type within a category | +| `critical` | yes | If `true`, gates score levels (must pass for Score 2+) | +| `points` | yes | Weight within category (typically 1) | +| `check` | yes | Pseudocode block (checkable) or prose description (judgement) | +| `template` | no | Path to template in `lib/templates/` that resolves this condition | + +--- + +## Scoring Scale + +Seven categories per platform, each scored 0-3. + +### Category List + +| # | Category | What it measures | Component Tags | +|---|----------|-----------------|---------------| +| 1 | Manifest Packaging | Platform manifest present, correct schema, complete fields | `plugin_json`, `marketplace_json`, `extension_json`, `package_json`, `openclaw_json` | +| 2 | Skill Compatibility | Skills discoverable, frontmatter correct, no unresolved tool assumptions | `frontmatter`, `discovery`, `tool_refs` | +| 3 | Context Delivery | Platform context file present, accurate, includes all skills | `claude_md`, `agents_md`, `gemini_md`, `rules_mdc` | +| 4 | Hook Portability | Hooks adapted to platform format, correct event names, cross-platform scripts | `hooks_json`, `scripts`, `event_names`, `output_format` | +| 5 | Tool Mapping | Sidecars, tool name translation, subagent communication | `sidecar`, `model_mapping`, `subagent_syntax` | +| 6 | Install Readiness | Install docs exist, match actual structure, include verification steps | `install_docs`, `publishing`, `verification` | +| 7 | Runtime Adapters | MCP, agents, commands, rules, policies, marketplace | `mcp`, `agents`, `commands`, `rules`, `policies`, `subagents` | + +### Scoring Formula + +Per category, per platform: + +```pseudocode +critical_count = number of conditions where critical == true +optional_count = number of conditions where critical == false +critical_pass = number of critical conditions that pass +optional_pass = number of optional conditions that pass + +# Guard: every scored category MUST have at least 1 critical condition. +# If a category has 0 critical conditions, it scores N/A (see below). + +IF critical_pass == critical_count: + IF optional_count == 0 OR optional_pass / optional_count >= 0.75: + score = 3 + ELSE: + score = 2 +ELIF critical_pass / critical_count >= 0.50: + score = 1 +ELSE: + score = 0 +``` + +**Edge cases:** +- Category with 0 critical conditions → N/A (prevents vacuous truth inflation) +- Category with 0 total conditions → N/A +- `optional_count == 0` and all critical pass → Score 3 +- `critical_count == 1` → binary: Score 2+ (passes) or Score 0 (fails) + +--- + +## Bands + +```pseudocode +scored_categories = [c for c in categories if c.score != N/A] +scored_count = len(scored_categories) +actual_score = sum(c.score for c in scored_categories) +max_score = scored_count * 3 +percentage = actual_score / max_score + +IF scored_count < 3: + band = min(band, "partial") # Cap: too few categories to claim Strong/Viable + +IF percentage >= 0.85: band = "strong" +ELIF percentage >= 0.60: band = "viable" +ELIF percentage >= 0.35: band = "partial" +ELSE: band = "weak" +``` + +| Band | Percentage | Interpretation | +|------|-----------|----------------| +| Strong | >= 85% | Platform fully supported | +| Viable | >= 60% | Moderate gaps, straightforward to complete | +| Partial | >= 35% | Significant work needed | +| Weak | < 35% | Minimal or no support | + +--- + +## Blocker Detection + +Blockers override raw scores. A repo with a decent score may still have one critical structural problem. + +| Blocker | Severity | Detection | +|---------|----------|-----------| +| No trustworthy metadata source | Critical | All metadata fields from hard fallbacks only | +| Unresolved tool assumptions | Major | Skill references platform-specific tools with no sidecar in `references/` | +| Hook env hard-coding | Major | Hook scripts reference `CLAUDE_PLUGIN_ROOT` without env branching | +| Docs/structure mismatch | Major | Install docs describe paths that don't exist in repo | +| Whole-repo assumption | Minor | Repo requires whole-repo install but only documents single-skill copying | +| Missing subagent translation | Minor | Skills dispatch via `Task`/`Agent` but no codex-tools or gemini-tools sidecar | +| Gemini import gaps | Minor | `GEMINI.md` exists but missing `@` includes for some skills | + +--- + +## JIT Code Generation + +When evaluating checkable conditions, the LLM may generate a read-only script +from the condition's pseudocode and execute it, rather than manually interpreting +each check. The pseudocode operations (`read_json`, `file_exists`, +`parse_frontmatter`, `glob`) map to read-only filesystem queries. + +This skill is used by plugin authors on their own repos. JIT scripts are +read-only checks — they do not modify files or access paths outside the +plugin root. + +--- + +## Report Format + +The assessment report must include all of these sections: + +1. Repo shape classification +2. Canonical metadata source with extracted fields +3. Per-platform scores (7 categories each, with individual condition pass/fail) +4. Blockers with severity +5. Uplift recommendation (skill-first, full-portable-plugin, or curated-note-only) +6. Codex-specific recommendation (native-skill-discovery or native-plugin-packaging) +7. Required uplift artifacts list (mapped to condition IDs) +8. Session-start injection status + +--- + +## Uplift Linkage + +### `# fixes:` Annotations + +Every uplift action (template rendering, hook porting, sidecar creation) carries +a `# fixes:` annotation linking it to the condition IDs it resolves. + +### Drift Detection + +```pseudocode +rubric_ids = collect all condition IDs from lib/patterns/platforms/*.yaml +uplift_ids = collect all "fixes:" references from skills/uplifting-a-plugin/ +template_ids = collect all "fixes:" references from lib/templates/ + +orphan_conditions = rubric_ids - (uplift_ids | template_ids) +phantom_fixes = (uplift_ids | template_ids) - rubric_ids +``` + +### Incremental Uplift + +When assessment shows a platform already VIABLE (>= 60%), uplift only fixes +failing conditions instead of regenerating all artifacts. +``` + +- [ ] **Step 2: Verify line count is reasonable** + +Run: `wc -l lib/patterns/rubric-framework.md` +Expected: ~180-200 lines (was 95) + +- [ ] **Step 3: Commit** + +```bash +git add lib/patterns/rubric-framework.md +git commit -m "feat: rewrite rubric framework with structured conditions and hybrid scoring + +Replaces prose scoring with condition ID schema, checkable/judgement types, +hybrid AND/OR formula, percentage-based bands, N/A handling, and JIT guidance." +``` + +--- + +## Task 3: Write Claude Code Platform Rubric (YAML) + +**Files:** +- Create: `lib/patterns/platforms/claude-code.yaml` +- Remove: `lib/patterns/platforms/claude-code.md` + +Claude Code is the reference platform — it has the most components and no tool translation needed. + +- [ ] **Step 1: Create claude-code.yaml** + +Write the full YAML rubric with all 7 categories. Claude Code has conditions in all 7 categories. Key characteristics: +- Manifest: `.claude-plugin/plugin.json` + `marketplace.json` +- Skills: standard `skills/*/SKILL.md`, no field stripping needed (reference platform) +- Context: `CLAUDE.md` +- Hooks: `hooks/hooks.json`, PascalCase events, nested structure +- Tool mapping: no sidecar needed (Claude is the reference), but tools should match built-in set +- Install: `/plugin install` marketplace + local `claude --plugin-dir` +- Runtime: `.mcp.json`, `agents/*.md`, deprecated `commands/` + +Each condition must have: `id`, `type`, `component`, `critical`, `points`, `check`, and optional `template`. + +Reference the spec Section 3 "Per-Platform Differences" for Claude Code specifics. Use `LOOKUP["table_name"]["claude-code"]` in pseudocode to reference platform-mappings.md tables. + +Target: ~25-30 conditions across 7 categories. + +- [ ] **Step 2: Validate YAML syntax** + +Run: `python3 -c "import yaml; yaml.safe_load(open('lib/patterns/platforms/claude-code.yaml'))" && echo "VALID"` +Expected: VALID + +- [ ] **Step 3: Remove old prose rubric** + +```bash +git rm lib/patterns/platforms/claude-code.md +``` + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/platforms/claude-code.yaml +git commit -m "feat: replace Claude Code prose rubric with structured YAML conditions + +~28 conditions across 7 categories. Each condition has stable ID, type +(checkable/judgement), critical flag, and pseudocode check referencing +platform-mappings.md lookup tables." +``` + +--- + +## Task 4: Write Cursor Platform Rubric (YAML) + +**Files:** +- Create: `lib/patterns/platforms/cursor.yaml` +- Remove: `lib/patterns/platforms/cursor.md` + +Cursor is the most feature-rich target platform — skills, agents, commands, rules (.mdc), hooks (camelCase, flat), MCP (no Resources). + +- [ ] **Step 1: Create cursor.yaml** + +Use the full Cursor rubric from the spec (Design Section 3) as the reference. All 7 categories have conditions. Key specifics: +- Manifest: `.cursor-plugin/plugin.json` with conditional keys (omit agents/commands/hooks if dirs missing) +- Skills: standard path, strip only `allowed-tools` (keep `disable-model-invocation`) +- Context: `AGENTS.md` + `.cursor/rules/*.mdc` with `alwaysApply: true` frontmatter +- Hooks: `hooks/hooks-cursor.json`, camelCase events, flat structure (no nested hooks[]), `additional_context` output +- Tool mapping: model must be `inherit`, no Claude model names leaked +- Runtime: `mcp.json` (no dot prefix, no MCP Resources), agents with `model: inherit` + +Target: ~30 conditions. + +- [ ] **Step 2: Validate YAML syntax** + +Run: `python3 -c "import yaml; yaml.safe_load(open('lib/patterns/platforms/cursor.yaml'))" && echo "VALID"` +Expected: VALID + +- [ ] **Step 3: Remove old prose rubric** + +```bash +git rm lib/patterns/platforms/cursor.md +``` + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/platforms/cursor.yaml +git commit -m "feat: replace Cursor prose rubric with structured YAML conditions + +~30 conditions covering all 7 categories including .mdc rules, flat hook +structure, conditional manifest keys, and MCP (no Resources) constraints." +``` + +--- + +## Task 5: Write Gemini CLI Platform Rubric (YAML) + +**Files:** +- Create: `lib/patterns/platforms/gemini-cli.yaml` +- Remove: `lib/patterns/platforms/gemini-cli.md` + +Gemini has unique requirements: `@` includes in GEMINI.md, `gemini-tools.md` sidecar per skill, `Task` → `@agent-name`, commands as `.toml`, policies as `.toml`. + +- [ ] **Step 1: Create gemini-cli.yaml** + +Key specifics: +- Manifest: `gemini-extension.json` with `contextFileName: "GEMINI.md"`, lowercase-dash name +- Skills: standard path, strip `disable-model-invocation` and `allowed-tools` +- Context: `GEMINI.md` with `@` includes for EVERY skill SKILL.md AND its `references/gemini-tools.md` +- Hooks: Category 4 has fewer checkable conditions — hooks go in user `settings.json`, generate guidance only +- Tool mapping: CRITICAL — `gemini-tools.md` sidecar required, `Task` → `@agent-name` +- Runtime: `agents/*.md`, `commands/*.toml`, `policies/*.toml` + +Category 4 (hooks) will have fewer conditions than other platforms since Gemini hooks are settings-based, not file-based. Most hook conditions will be `type: judgement`. + +Target: ~25 conditions. + +- [ ] **Step 2: Validate YAML syntax** + +Run: `python3 -c "import yaml; yaml.safe_load(open('lib/patterns/platforms/gemini-cli.yaml'))" && echo "VALID"` +Expected: VALID + +- [ ] **Step 3: Remove old prose rubric** + +```bash +git rm lib/patterns/platforms/gemini-cli.md +``` + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/platforms/gemini-cli.yaml +git commit -m "feat: replace Gemini CLI prose rubric with structured YAML conditions + +~25 conditions. Critical: gemini-tools.md sidecar per skill, @ includes in +GEMINI.md, Task-to-@agent-name translation, contextFileName requirement." +``` + +--- + +## Task 6: Write Codex Platform Rubric (YAML) + +**Files:** +- Create: `lib/patterns/platforms/codex.yaml` +- Remove: `lib/patterns/platforms/codex.md` + +Codex has a two-path system (skill-discovery vs native-plugin) and no hook system. Category 4 is N/A. + +- [ ] **Step 1: Create codex.yaml** + +Key specifics: +- Manifest: Two paths — skill-discovery (AGENTS.md + `.codex/INSTALL.md`) vs native (`.codex-plugin/plugin.json` + `marketplace.json`). Conditions should check for the chosen path. +- Skills: `.agents/skills/*/SKILL.md` path, `codex-tools.md` sidecar required, `spawn_agent` replaces Task, `update_plan` replaces TodoWrite +- Context: `AGENTS.md` +- Hooks: N/A — Category 4 has zero conditions. Hook scripts copied as standalone utilities only. +- Tool mapping: CRITICAL — `codex-tools.md` must document spawn_agent and update_plan +- Runtime: `.codex/agents/*.toml` with TOML-specific validation + +Target: ~20 conditions (fewer due to N/A hooks category). + +- [ ] **Step 2: Validate YAML syntax** + +Run: `python3 -c "import yaml; yaml.safe_load(open('lib/patterns/platforms/codex.yaml'))" && echo "VALID"` +Expected: VALID + +- [ ] **Step 3: Remove old prose rubric** + +```bash +git rm lib/patterns/platforms/codex.md +``` + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/platforms/codex.yaml +git commit -m "feat: replace Codex prose rubric with structured YAML conditions + +~20 conditions. Hooks category is N/A. Two-path manifest system. +Critical: codex-tools.md sidecar with spawn_agent/update_plan mapping." +``` + +--- + +## Task 7: Write Antigravity Platform Rubric (YAML) + +**Files:** +- Create: `lib/patterns/platforms/antigravity.yaml` + +New platform. Google VS Code fork. No hooks. Skills in `.agents/skills/` (preferred) or `.agent/skills/` (legacy). Context priority: GEMINI.md > AGENTS.md > `.agent/rules/`. + +- [ ] **Step 1: Create antigravity.yaml** + +Key specifics: +- Manifest: `package.json` with displayName, publisher for OpenVSX extension distribution. Skill-only plugins need no manifest. +- Skills: `.agents/skills/*/SKILL.md` (preferred) or `.agent/skills/*/SKILL.md` (legacy, still supported). Strip `disable-model-invocation`, `allowed-tools`. Strip model field entirely. +- Context: GEMINI.md (highest user priority) > AGENTS.md > `.agent/rules/*.md` +- Hooks: N/A — no hook system +- Tool mapping: model field removed entirely, tools same as Claude +- Install: OpenVSX registry (`antigravity --install-extension`) for extensions, direct directory copy for skills +- Runtime: `.agent/rules/*.md`, `.agents/workflows/` for slash commands + +Categories 4 (Hooks) is N/A. Target: ~18 conditions. + +- [ ] **Step 2: Validate YAML syntax** + +Run: `python3 -c "import yaml; yaml.safe_load(open('lib/patterns/platforms/antigravity.yaml'))" && echo "VALID"` +Expected: VALID + +- [ ] **Step 3: Commit** + +```bash +git add lib/patterns/platforms/antigravity.yaml +git commit -m "feat: add Antigravity platform rubric (new platform) + +~18 conditions. Google VS Code fork. No hooks (N/A). Skills in .agents/skills/ +(preferred) or .agent/skills/ (legacy). Context priority: GEMINI.md > AGENTS.md." +``` + +--- + +## Task 8: Write OpenClaw Platform Rubric (YAML) + +**Files:** +- Create: `lib/patterns/platforms/openclaw.yaml` + +New platform. TypeScript gateway. Plugin SDK hooks (not file-based). Auto-detects Claude/Codex/Cursor bundles. + +- [ ] **Step 1: Create openclaw.yaml** + +Key specifics: +- Manifest: `openclaw.plugin.json` with `id` + `configSchema` (required). Full plugins also need `package.json` with `openclaw` block. +- Skills: standard `skills/*/SKILL.md`. Strip `disable-model-invocation`, `allowed-tools`. +- Context: `AGENTS.md` +- Hooks: Category 4 has conditions but they're mostly `type: judgement` since hooks use TypeScript plugin SDK (`api.registerHook()`). Check for `before_tool_call` and `tool_result_persist` documentation/guidance. +- Tool mapping: model uses `provider/model` format (e.g., `anthropic/claude-opus-4-6`) +- Install: ClawHub, npm, or local `plugins.load.paths` +- Runtime: `agents.list[]` in config (not manifest), bundle auto-detection noted + +Target: ~18 conditions. + +- [ ] **Step 2: Validate YAML syntax** + +Run: `python3 -c "import yaml; yaml.safe_load(open('lib/patterns/platforms/openclaw.yaml'))" && echo "VALID"` +Expected: VALID + +- [ ] **Step 3: Commit** + +```bash +git add lib/patterns/platforms/openclaw.yaml +git commit -m "feat: add OpenClaw platform rubric (new platform) + +~18 conditions. TypeScript gateway with plugin SDK hooks. Dual manifest +(openclaw.plugin.json + package.json). Bundle auto-detection noted." +``` + +--- + +## Task 9: Remove Dropped Platform Files + +**Files:** +- Remove: `lib/patterns/platforms/opencode.md` +- Remove: `lib/patterns/platforms/copilot-cli.md` +- Remove: `lib/references/copilot-tools.md` +- Remove: `lib/templates/manifests/opencode-plugin.js.tmpl` +- Remove: `lib/templates/manifests/package.json.tmpl` +- Remove: `lib/templates/context-files/copilot-instructions.md.tmpl` +- Remove: `lib/templates/install-docs/opencode.md` +- Remove: `lib/templates/install-docs/copilot-cli.md` +- Remove: `lib/templates/install-docs/adding-platform/opencode.md` +- Remove: `lib/templates/install-docs/adding-platform/copilot-cli.md` +- Remove: `lib/templates/install-docs/publishing/opencode.md` +- Remove: `lib/templates/install-docs/publishing/copilot-cli.md` +- Remove: `skills/assessing-plugin-portability/references/copilot-tools.md` +- Remove: `skills/uplifting-a-plugin/references/copilot-tools.md` +- Remove: `skills/using-skill-portability/references/copilot-tools.md` + +- [ ] **Step 1: Remove all OpenCode and Copilot files** + +```bash +git rm lib/patterns/platforms/opencode.md \ + lib/patterns/platforms/copilot-cli.md \ + lib/references/copilot-tools.md \ + lib/templates/manifests/opencode-plugin.js.tmpl \ + lib/templates/manifests/package.json.tmpl \ + lib/templates/context-files/copilot-instructions.md.tmpl \ + lib/templates/install-docs/opencode.md \ + lib/templates/install-docs/copilot-cli.md \ + lib/templates/install-docs/adding-platform/opencode.md \ + lib/templates/install-docs/adding-platform/copilot-cli.md \ + lib/templates/install-docs/publishing/opencode.md \ + lib/templates/install-docs/publishing/copilot-cli.md \ + skills/assessing-plugin-portability/references/copilot-tools.md \ + skills/uplifting-a-plugin/references/copilot-tools.md \ + skills/using-skill-portability/references/copilot-tools.md +``` + +- [ ] **Step 2: Verify no remaining references to dropped platforms** + +Run: `grep -rn "opencode\|copilot-cli\|copilot_cli\|OpenCode\|Copilot CLI" lib/ skills/ --include="*.md" --include="*.yaml" --include="*.tmpl" --include="*.sh" --include="*.json" | grep -v "superpowers/specs/" | grep -v "superpowers/plans/"` +Expected: No output (specs/plans may reference them historically — that's fine) + +- [ ] **Step 3: Commit** + +```bash +git commit -m "chore: remove OpenCode and Copilot CLI platform files + +Drops 15 files. Platform set is now: Claude Code, Codex, Cursor, +Gemini, Antigravity, OpenClaw." +``` + +--- + +## Task 10: Create Antigravity and OpenClaw Templates + +**Files:** +- Create: `lib/templates/manifests/antigravity/package.json.tmpl` +- Create: `lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl` +- Create: `lib/templates/install-docs/antigravity.md` +- Create: `lib/templates/install-docs/openclaw.md` +- Create: `lib/templates/install-docs/adding-platform/antigravity.md` +- Create: `lib/templates/install-docs/adding-platform/openclaw.md` +- Create: `lib/templates/install-docs/publishing/antigravity.md` +- Create: `lib/templates/install-docs/publishing/openclaw.md` + +- [ ] **Step 1: Create Antigravity manifest template** + +```json +{{! fixes: antigravity.1_manifest.package_json.required_fields }} +{ + "name": "{{name}}", + "displayName": "{{displayName}}", + "version": "{{version}}", + "description": "{{description}}", + "publisher": "{{author.name}}", + "author": "{{author.name}}", + "keywords": {{keywords}}, + "categories": ["AI", "Other"] +} +``` + +Write to `lib/templates/manifests/antigravity/package.json.tmpl`. + +- [ ] **Step 2: Create OpenClaw manifest template** + +```json +{{! fixes: openclaw.1_manifest.openclaw_json.required_fields }} +{ + "id": "{{name}}", + "name": "{{displayName}}", + "description": "{{description}}", + "version": "{{version}}", + "skills": {{skillsList}}, + "configSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false + } +} +``` + +Write to `lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl`. + +- [ ] **Step 3: Create Antigravity install docs** + +Write `lib/templates/install-docs/antigravity.md` with: +- Skill-only install: copy to `.agents/skills/` (preferred) or `~/.gemini/antigravity/skills/` (global) +- Extension install: `antigravity --install-extension .vsix` or OpenVSX registry +- Verification: check skill appears in conversation start listing + +- [ ] **Step 4: Create OpenClaw install docs** + +Write `lib/templates/install-docs/openclaw.md` with: +- ClawHub install: `openclaw plugins install {{name}}` +- npm install: `openclaw plugins install @org/openclaw-{{name}}` +- Local install: add to `plugins.load.paths` in `openclaw.json` +- Bundle auto-detection: note that `.claude-plugin/` layout may work without conversion +- Verification: check plugin loads in gateway logs + +- [ ] **Step 5: Create adding-platform sections for both** + +Write `lib/templates/install-docs/adding-platform/antigravity.md` and `openclaw.md` following the pattern of existing adding-platform files (see `adding-platform/cursor.md` as reference — ~9-15 lines each). + +- [ ] **Step 6: Create publishing sections for both** + +Write `lib/templates/install-docs/publishing/antigravity.md` (OpenVSX submission) and `openclaw.md` (ClawHub/npm publishing) following existing patterns (see `publishing/cursor.md` as reference — ~15-20 lines each). + +- [ ] **Step 7: Commit** + +```bash +git add lib/templates/manifests/antigravity/ lib/templates/manifests/openclaw/ \ + lib/templates/install-docs/antigravity.md lib/templates/install-docs/openclaw.md \ + lib/templates/install-docs/adding-platform/antigravity.md \ + lib/templates/install-docs/adding-platform/openclaw.md \ + lib/templates/install-docs/publishing/antigravity.md \ + lib/templates/install-docs/publishing/openclaw.md +git commit -m "feat: add Antigravity and OpenClaw templates + +Manifest templates, install docs, adding-platform sections, and +publishing guides for both new platforms." +``` + +--- + +## Task 11: Annotate Existing Templates with `# fixes:` References + +**Files:** +- Modify: `lib/templates/manifests/claude-plugin/plugin.json.tmpl` +- Modify: `lib/templates/manifests/claude-plugin/marketplace.json.tmpl` +- Modify: `lib/templates/manifests/cursor-plugin/plugin.json.tmpl` +- Modify: `lib/templates/manifests/cursor-plugin/marketplace.json.tmpl` +- Modify: `lib/templates/manifests/gemini-extension.json.tmpl` +- Modify: `lib/templates/manifests/codex-plugin/plugin.json.tmpl` +- Modify: `lib/templates/manifests/codex-plugin/marketplace.json.tmpl` +- Modify: `lib/templates/context-files/CLAUDE.md.tmpl` +- Modify: `lib/templates/context-files/AGENTS.md.tmpl` +- Modify: `lib/templates/context-files/GEMINI.md.tmpl` + +- [ ] **Step 1: Add fixes annotations to each template** + +Add a `{{! fixes: }}` comment as the first line of each template file, referencing the condition IDs it resolves. For example: + +For `claude-plugin/plugin.json.tmpl`, add: +``` +{{! fixes: claude.1_manifest.plugin_json.exists }} +{{! fixes: claude.1_manifest.plugin_json.required_fields }} +``` + +For `cursor-plugin/plugin.json.tmpl`, add: +``` +{{! fixes: cursor.1_manifest.plugin_json.exists }} +{{! fixes: cursor.1_manifest.plugin_json.required_fields }} +{{! fixes: cursor.1_manifest.plugin_json.conditional_keys }} +``` + +For `gemini-extension.json.tmpl`, add: +``` +{{! fixes: gemini.1_manifest.extension_json.exists }} +{{! fixes: gemini.1_manifest.extension_json.required_fields }} +``` + +For `CLAUDE.md.tmpl`, add: +``` +{{! fixes: claude.3_context.claude_md.exists }} +``` + +For `AGENTS.md.tmpl`, add: +``` +{{! fixes: cursor.3_context.agents_md.exists }} +{{! fixes: codex.3_context.agents_md.exists }} +{{! fixes: antigravity.3_context.agents_md.exists }} +{{! fixes: openclaw.3_context.agents_md.exists }} +``` + +For `GEMINI.md.tmpl`, add: +``` +{{! fixes: gemini.3_context.gemini_md.exists }} +{{! fixes: gemini.3_context.gemini_md.at_includes }} +``` + +Also update `AGENTS.md.tmpl` to remove any Copilot/OpenCode references in its content. + +Apply the same pattern to each remaining template — the exact condition IDs come from the YAML rubrics written in Tasks 3-8. + +- [ ] **Step 2: Verify no template references a condition ID that doesn't exist in a rubric** + +Run: `grep -roh 'fixes: [a-z]*\.[0-9]_[a-z]*\.[a-z_]*\.[a-z_]*' lib/templates/ | sort -u > /tmp/template_ids.txt && grep -roh 'id: [a-z]*\.[0-9]_[a-z]*\.[a-z_]*\.[a-z_]*' lib/patterns/platforms/*.yaml | sed 's/id: /fixes: /' | sort -u > /tmp/rubric_ids.txt && comm -23 /tmp/template_ids.txt /tmp/rubric_ids.txt` +Expected: No output (no phantom fixes) + +- [ ] **Step 3: Commit** + +```bash +git add lib/templates/ +git commit -m "feat: annotate templates with fixes: condition ID references + +Every template now declares which rubric conditions it resolves. +Enables drift detection via grep." +``` + +--- + +## Task 12: Rewrite Assessment Skill + +**Files:** +- Rewrite: `skills/assessing-plugin-portability/SKILL.md` + +The assessment skill needs to: reference the new YAML rubrics and condition IDs, use the new scoring formula, evaluate checkable vs judgement conditions differently, update the platform set, and include JIT guidance. + +- [ ] **Step 1: Rewrite SKILL.md** + +Key changes from the current 442-line skill: + +**Frontmatter:** Update description to mention condition IDs and 6-platform set. Remove `copilot-tools.md` from references, update platform list. + +**Phase 2 (Inventory):** Update manifest check list — replace `opencode` and `copilot-cli` entries with `antigravity` and `openclaw`: +```pseudocode +manifest_checks = [ + { platform: "claude-code", path: ".claude-plugin/plugin.json" }, + { platform: "claude-code", path: ".claude-plugin/marketplace.json" }, + { platform: "cursor", path: ".cursor-plugin/plugin.json" }, + { platform: "gemini-cli", path: "gemini-extension.json" }, + { platform: "gemini-cli", path: "GEMINI.md" }, + { platform: "codex", path: ".codex-plugin/plugin.json" }, + { platform: "codex", path: ".agents/plugins/marketplace.json" }, + { platform: "antigravity", path: "package.json" }, + { platform: "antigravity", path: ".agents/skills/" }, + { platform: "openclaw", path: "openclaw.plugin.json" } +] +``` + +**Phase 3 (Score):** Replace prose-based scoring with condition-driven evaluation: +```pseudocode +SCORE_PLATFORM(platform, plugin_path): + rubric = load_yaml("lib/patterns/platforms/" + platform + ".yaml") + results = {} + + FOR category IN rubric.categories: + FOR condition IN category.conditions: + IF condition.type == "checkable": + # Generate and execute read-only check script from pseudocode + passed = jit_evaluate(condition.check, plugin_path) + ELSE: + # LLM interprets prose description against actual files + passed = judgement_evaluate(condition.check, plugin_path) + results[condition.id] = passed + + # Apply scoring formula from rubric-framework.md + category.score = compute_score(category.conditions, results) + + platform_score = compute_band(rubric.categories) + RETURN platform_score, results +``` + +**Phase 5 (Report):** Update to include individual condition pass/fail in the per-platform scores table. Map failing conditions to required uplift artifacts via condition IDs. + +**References section:** Update file references in the header to point to `.yaml` rubrics and `platform-mappings.md`. + +- [ ] **Step 2: Verify the skill references the correct files** + +Run: `grep -c "\.yaml" skills/assessing-plugin-portability/SKILL.md` +Expected: Multiple matches (references to platform YAML files) + +Run: `grep -c "opencode\|copilot" skills/assessing-plugin-portability/SKILL.md` +Expected: 0 + +- [ ] **Step 3: Commit** + +```bash +git add skills/assessing-plugin-portability/SKILL.md +git commit -m "feat: rewrite assessment skill for condition-driven scoring + +References YAML rubrics with condition IDs. Evaluates checkable conditions +via JIT scripts, judgement conditions via LLM interpretation. New scoring +formula with percentage-based bands and N/A handling." +``` + +--- + +## Task 13: Rewrite Uplift Skill + +**Files:** +- Rewrite: `skills/uplifting-a-plugin/SKILL.md` + +The uplift skill needs: `# fixes:` annotations on every generation action, incremental uplift path, updated platform set, rubric-informed recommendations. + +- [ ] **Step 1: Rewrite SKILL.md** + +Key changes from the current 580-line skill: + +**Frontmatter:** Update description and platform list. Remove `copilot-tools.md` reference, add `platform-mappings.md`. + +**Phase 3 (Recommend):** Add rubric-informed decision making: +```pseudocode +RECOMMEND(computed, target_platforms): + # Quick assessment (optional but recommended) + scores = {} + FOR platform IN target_platforms: + scores[platform] = quick_assess(platform, computed.plugin_path) + + # Shape-based recommendation (existing logic) + IF computed.shape == "bare-skill-repo" AND len(computed.skills) <= 3: + recommendation = "skill-first" + ELIF computed.shape == "curated-distribution": + recommendation = "curated-note-only" + ELSE: + recommendation = "full-portable-plugin" + + # Override: incremental uplift for already-viable platforms + FOR platform IN target_platforms: + IF scores[platform].band IN ["STRONG", "VIABLE"]: + recommendation_for[platform] = "incremental" + ELSE: + recommendation_for[platform] = recommendation +``` + +**Phase 4 (Generate):** Every generation action gets a `# fixes:` annotation: +```pseudocode +GENERATE_MANIFESTS(computed, target_platforms): + FOR platform IN target_platforms: + IF recommendation_for[platform] == "incremental": + # Only fix failing conditions + failing = get_failing_conditions(platform, "1_manifest") + FOR condition IN failing: + IF condition.template: + render(condition.template, computed.metadata) + # fixes: {condition.id} + ELSE: + # Full generation + template = LOOKUP manifest template for platform + render(template, computed.metadata) + # fixes: {platform}.1_manifest.*.exists + # fixes: {platform}.1_manifest.*.required_fields +``` + +Apply the same pattern to all generation phases (context files, sidecars, hooks, install docs). + +**Platform list updates:** Replace all `opencode` and `copilot-cli` references with `antigravity` and `openclaw`. Update platform selection checklist. + +**Phase 5 (Port):** Update hook merging to reference `LOOKUP["hook_events"]` tables instead of inline mappings. Add note that Antigravity and OpenClaw have no portable hook porting (N/A). + +**Phase 6 (Document):** Update install doc generation to use Antigravity/OpenClaw templates. + +- [ ] **Step 2: Verify fixes annotations are present** + +Run: `grep -c "fixes:" skills/uplifting-a-plugin/SKILL.md` +Expected: >= 15 (at least one per major generation action) + +Run: `grep -c "opencode\|copilot" skills/uplifting-a-plugin/SKILL.md` +Expected: 0 + +- [ ] **Step 3: Commit** + +```bash +git add skills/uplifting-a-plugin/SKILL.md +git commit -m "feat: rewrite uplift skill with condition-linked fixes annotations + +Every generation action carries # fixes: {condition.id}. Incremental +uplift path for VIABLE platforms. New platform set. Rubric-informed +recommendation overrides." +``` + +--- + +## Task 14: Update Using-Skill-Portability Skill + +**Files:** +- Modify: `skills/using-skill-portability/SKILL.md` +- Remove: `skills/using-skill-portability/references/copilot-tools.md` + +- [ ] **Step 1: Update SKILL.md with new platform list** + +Replace the platform list in the skill table. Remove OpenCode and Copilot references. Add Antigravity and OpenClaw to the available platforms section. + +- [ ] **Step 2: Remove copilot-tools.md reference** + +```bash +git rm skills/using-skill-portability/references/copilot-tools.md +``` + +- [ ] **Step 3: Commit** + +```bash +git add skills/using-skill-portability/ +git commit -m "chore: update using-skill-portability with new platform set" +``` + +--- + +## Task 15: Update Pattern Documents + +**Files:** +- Modify: `lib/patterns/detection-algorithm.md` +- Modify: `lib/patterns/hook-merging.md` +- Modify: `lib/patterns/bootstrapping.md` +- Modify: `lib/patterns/manifest-generation.md` +- Modify: `lib/patterns/injection-checks.md` +- Modify: `lib/patterns/report-template.md` +- Modify: `lib/patterns/platforms/publishing-and-discoverability.md` + +These files all reference the old platform set and need surgical updates. + +- [ ] **Step 1: Update detection-algorithm.md** + +In the `SCAN_METADATA_SOURCES` pseudocode: +- Remove `opencode` scan (`".opencode/plugins/" + name + ".js"`) +- Remove `copilot-cli` scan (`"package.json"` for Copilot, `".github/copilot-instructions.md"`) +- Add `antigravity` scan: check for `package.json` with `publisher` field, `.agents/skills/` or `.agent/skills/` directory +- Add `openclaw` scan: check for `openclaw.plugin.json` + +In the `CLASSIFY_SHAPE` pseudocode: +- Update the manifests list: replace opencode/copilot with antigravity/openclaw detection + +- [ ] **Step 2: Update hook-merging.md** + +- Remove OpenCode section (code-based hooks in `.opencode/plugins/`) +- Remove Copilot CLI section (`.github/hooks/*.json` with bash/powershell) +- Add Antigravity note: "Antigravity has no hook system. Hook scripts are not portable to this platform." +- Add OpenClaw note: "OpenClaw uses TypeScript plugin SDK (`api.registerHook()`). File-based hooks require a TypeScript wrapper. See `LOOKUP['hook_events']['openclaw']` in platform-mappings.md for event mapping." + +- [ ] **Step 3: Update bootstrapping.md** + +- Remove OpenCode platform branch in session-start script (the `experimental.chat.messages.transform` section) +- Remove Copilot CLI platform branch +- Add Antigravity context: GEMINI.md `@` includes (same as Gemini, since Antigravity reads GEMINI.md) +- Add OpenClaw context: AGENTS.md is primary context file +- Update the platform detection logic in session-start script template to detect Antigravity/OpenClaw env vars if applicable + +- [ ] **Step 4: Update manifest-generation.md** + +- Remove OpenCode entries (opencode-package, opencode-shim schemas) +- Remove Copilot entry (copilot-instructions schema) +- Add Antigravity entry: `antigravity-package` schema, plain substitution mode +- Add OpenClaw entry: `openclaw-manifest` schema, plain substitution mode +- Update the schema-to-template mapping table (13 artifacts → ~13, replacing dropped with new) + +- [ ] **Step 5: Update injection-checks.md** + +- Remove OpenCode check (#7: `.opencode/plugins/{{name}}.js` contains transform) +- Update check count (8 → 7 or adjust numbering) +- Verify remaining checks reference correct platforms + +- [ ] **Step 6: Update report-template.md** + +- Update any platform list references + +- [ ] **Step 7: Update publishing-and-discoverability.md** + +- Remove OpenCode and Copilot CLI entries from the Quick Discovery Matrix +- Add Antigravity: OpenVSX registry, `antigravity --install-extension`, skill-only via directory copy +- Add OpenClaw: ClawHub (official), npm, local `plugins.load.paths`, bundle auto-detection + +- [ ] **Step 8: Verify no remaining references to dropped platforms in pattern docs** + +Run: `grep -rn "opencode\|copilot-cli\|copilot_cli\|OpenCode\|Copilot CLI" lib/patterns/ --include="*.md" | grep -v "publishing-and-discoverability.md"` +Expected: No output (publishing doc was just updated) + +Run: `grep -rn "opencode\|copilot-cli\|copilot_cli\|OpenCode\|Copilot CLI" lib/patterns/platforms/publishing-and-discoverability.md` +Expected: No output + +- [ ] **Step 9: Commit** + +```bash +git add lib/patterns/ +git commit -m "feat: update all pattern docs for new platform set + +Remove OpenCode/Copilot references from detection-algorithm, hook-merging, +bootstrapping, manifest-generation, injection-checks, report-template, and +publishing-and-discoverability. Add Antigravity and OpenClaw entries." +``` + +--- + +## Task 16: Update Remaining Templates + +**Files:** +- Modify: `lib/templates/context-files/AGENTS.md.tmpl` +- Modify: `lib/templates/hooks/session-start.sh` + +- [ ] **Step 1: Update AGENTS.md.tmpl** + +Remove any references to Copilot CLI or OpenCode in the template content. The template uses `{{skillIncludes}}` builder mode — verify no hardcoded platform references exist that need updating. + +- [ ] **Step 2: Update session-start.sh** + +The session-start hook script detects platforms via environment variables. Remove the OpenCode and Copilot detection branches: +- Remove: `COPILOT_CLI` env detection +- Remove: OpenCode `msg.info.role` handling +- Keep: `CURSOR_PLUGIN_ROOT`, `CLAUDE_PLUGIN_ROOT` detection +- The script output format stays the same for remaining platforms + +- [ ] **Step 3: Commit** + +```bash +git add lib/templates/context-files/AGENTS.md.tmpl lib/templates/hooks/session-start.sh +git commit -m "chore: remove OpenCode/Copilot branches from templates" +``` + +--- + +## Task 17: Final Validation + +- [ ] **Step 1: Verify all YAML rubrics parse** + +```bash +for f in lib/patterns/platforms/*.yaml; do + python3 -c "import yaml; yaml.safe_load(open('$f'))" && echo "OK: $f" || echo "FAIL: $f" +done +``` +Expected: All OK + +- [ ] **Step 2: Verify no orphan conditions (rubric IDs not referenced by any template or uplift action)** + +```bash +grep -roh 'id: [a-z][a-z0-9_]*\.[0-9]_[a-z_]*\.[a-z_]*\.[a-z_]*' lib/patterns/platforms/*.yaml | sed 's/id: //' | sort -u > /tmp/all_rubric_ids.txt +grep -roh 'fixes: [a-z][a-z0-9_]*\.[0-9]_[a-z_]*\.[a-z_]*\.[a-z_]*' skills/ lib/templates/ | sed 's/fixes: //' | sort -u > /tmp/all_fixes_ids.txt +echo "=== Orphan conditions (in rubric, not in fixes) ===" +comm -23 /tmp/all_rubric_ids.txt /tmp/all_fixes_ids.txt +echo "=== Phantom fixes (in fixes, not in rubric) ===" +comm -13 /tmp/all_rubric_ids.txt /tmp/all_fixes_ids.txt +``` +Expected: Both lists should be empty or very small (some judgement conditions may intentionally have no template fix) + +- [ ] **Step 3: Verify no remaining references to dropped platforms anywhere in lib/ or skills/** + +```bash +grep -rn "opencode\|copilot-cli\|copilot_cli\|OpenCode\|Copilot CLI\|copilot-tools" lib/ skills/ --include="*.md" --include="*.yaml" --include="*.tmpl" --include="*.sh" --include="*.json" +``` +Expected: No output + +- [ ] **Step 4: Verify platform count is correct** + +```bash +ls lib/patterns/platforms/*.yaml | wc -l +``` +Expected: 6 + +```bash +ls lib/patterns/platforms/*.md | wc -l +``` +Expected: 1 (only publishing-and-discoverability.md remains) + +- [ ] **Step 5: Commit any final fixes, then tag** + +```bash +git add -A +git status # Review changes +git commit -m "chore: final validation cleanup for rubric tightening" +``` diff --git a/docs/superpowers/plans/2026-04-26-skill-merge.md b/docs/superpowers/plans/2026-04-26-skill-merge.md new file mode 100644 index 0000000..9b59b41 --- /dev/null +++ b/docs/superpowers/plans/2026-04-26-skill-merge.md @@ -0,0 +1,765 @@ +# Skill Merge Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Merge `assessing-plugin-portability` and `uplifting-a-plugin` into a single `plugin-portability` skill with Phase 0 intent detection via AskUserQuestion. + +**Architecture:** The merged skill is a workflow orchestrator (~150-200 lines) that references external pseudocode files. Phase 0a asks mode + platforms before scanning, Phase 0b asks uplift target after detection (shape-informed). Assessment always runs full scoring. Uplift phases are gated by ALLOWED_CATEGORIES table and per-platform depth. + +**Tech Stack:** Markdown, YAML. No runtime code — all LLM-interpreted. + +**Spec:** `docs/superpowers/specs/2026-04-26-skill-merge-design.md` + +--- + +## File Map + +### New Files +| File | Responsibility | +|------|---------------| +| `skills/plugin-portability/SKILL.md` | Merged workflow orchestrator | +| `skills/plugin-portability/references/codex-tools.md` | Pointer to `lib/references/codex-tools.md` | +| `skills/plugin-portability/references/gemini-tools.md` | Pointer to `lib/references/gemini-tools.md` | +| `lib/patterns/inventory.md` | Consolidated inventory pseudocode | + +### Modified Files +| File | Change | +|------|--------| +| `lib/patterns/platforms/claude-code.yaml` | Add `template` field to fixable conditions | +| `lib/patterns/platforms/cursor.yaml` | Add `template` field to fixable conditions | +| `lib/patterns/platforms/gemini-cli.yaml` | Add `template` field to fixable conditions | +| `lib/patterns/platforms/codex.yaml` | Add `template` field to fixable conditions | +| `lib/patterns/platforms/antigravity.yaml` | Add `template` field to fixable conditions | +| `lib/patterns/platforms/openclaw.yaml` | Add `template` field to fixable conditions | +| `skills/using-skill-portability/SKILL.md` | Reference `plugin-portability` | + +### Removed Files +| File | Reason | +|------|--------| +| `skills/assessing-plugin-portability/SKILL.md` | Folded into merged skill | +| `skills/assessing-plugin-portability/references/codex-tools.md` | Moved | +| `skills/assessing-plugin-portability/references/gemini-tools.md` | Moved | +| `skills/uplifting-a-plugin/SKILL.md` | Folded into merged skill | +| `skills/uplifting-a-plugin/references/codex-tools.md` | Moved | +| `skills/uplifting-a-plugin/references/gemini-tools.md` | Moved | + +--- + +## Task 1: Populate `template` Field in Platform YAML Rubrics + +**Files:** +- Modify: `lib/patterns/platforms/claude-code.yaml` +- Modify: `lib/patterns/platforms/cursor.yaml` +- Modify: `lib/patterns/platforms/gemini-cli.yaml` +- Modify: `lib/patterns/platforms/codex.yaml` +- Modify: `lib/patterns/platforms/antigravity.yaml` +- Modify: `lib/patterns/platforms/openclaw.yaml` + +Every condition that can be fixed by generating or merging an artifact needs a `template` field. Conditions that are assessment-only (judgement about quality, no artifact to generate) leave `template` absent. + +- [ ] **Step 1: Add `template` fields to `claude-code.yaml`** + +Read the file. For each condition, determine if a template in `lib/templates/` can fix it. Add the `template` field with the path relative to `lib/templates/`. Use `?merge` suffix for conditions that fix existing files (e.g., missing fields in an existing manifest). + +Mapping for Claude Code: + +| Condition ID | Template Path | +|---|---| +| `claude.1_manifest.plugin_json.exists` | `manifests/claude-plugin/plugin.json.tmpl` | +| `claude.1_manifest.plugin_json.required_fields` | `manifests/claude-plugin/plugin.json.tmpl?merge` | +| `claude.1_manifest.plugin_json.keywords` | `manifests/claude-plugin/plugin.json.tmpl?merge` | +| `claude.1_manifest.marketplace_json.exists` | `manifests/claude-plugin/marketplace.json.tmpl` | +| `claude.1_manifest.marketplace_json.valid_entries` | `manifests/claude-plugin/marketplace.json.tmpl?merge` | +| `claude.3_context.claude_md.exists` | `context-files/CLAUDE.md.tmpl` | +| `claude.4_hooks.hooks_json.exists` | `hooks/hooks.json.tmpl` | +| All `claude.2_skills.*` | (no template — skills are authored, not generated) | +| All `claude.5_toolmap.*` | (no template — Claude is reference platform) | +| All `claude.6_install.*` judgement conditions | (no template — prose quality) | +| All `claude.7_runtime.*` | (no template — MCP/agents are authored) | + +Add `template:` line after the `check:` block in each applicable condition. + +- [ ] **Step 2: Add `template` fields to `cursor.yaml`** + +| Condition ID | Template Path | +|---|---| +| `cursor.1_manifest.plugin_json.exists` | `manifests/cursor-plugin/plugin.json.tmpl` | +| `cursor.1_manifest.plugin_json.required_fields` | `manifests/cursor-plugin/plugin.json.tmpl?merge` | +| `cursor.1_manifest.plugin_json.conditional_keys` | `manifests/cursor-plugin/plugin.json.tmpl?merge` | +| `cursor.1_manifest.marketplace_json.exists` | `manifests/cursor-plugin/marketplace.json.tmpl` | +| `cursor.3_context.agents_md.exists` | `context-files/AGENTS.md.tmpl` | +| `cursor.4_hooks.hooks_json.exists` | `hooks/hooks-cursor.json.tmpl` | +| `cursor.2_skills.frontmatter.*` | (no template — skills authored) | +| `cursor.5_toolmap.model_mapping.*` | (no template — frontmatter is authored) | +| Judgement conditions | (no template) | + +- [ ] **Step 3: Add `template` fields to `gemini-cli.yaml`** + +| Condition ID | Template Path | +|---|---| +| `gemini.1_manifest.extension_json.exists` | `manifests/gemini-extension.json.tmpl` | +| `gemini.1_manifest.extension_json.required_fields` | `manifests/gemini-extension.json.tmpl?merge` | +| `gemini.1_manifest.extension_json.context_filename` | `manifests/gemini-extension.json.tmpl?merge` | +| `gemini.3_context.gemini_md.exists` | `context-files/GEMINI.md.tmpl` | +| `gemini.3_context.gemini_md.at_includes_skills` | `context-files/GEMINI.md.tmpl?merge` | +| `gemini.3_context.gemini_md.at_includes_sidecars` | `context-files/GEMINI.md.tmpl?merge` | + +- [ ] **Step 4: Add `template` fields to `codex.yaml`** + +| Condition ID | Template Path | +|---|---| +| `codex.1_manifest.plugin_json.exists` | `manifests/codex-plugin/plugin.json.tmpl` | +| `codex.1_manifest.plugin_json.required_fields` | `manifests/codex-plugin/plugin.json.tmpl?merge` | +| `codex.1_manifest.marketplace_json.exists` | `manifests/codex-plugin/marketplace.json.tmpl` | +| `codex.3_context.agents_md.skill_coverage` | `context-files/AGENTS.md.tmpl?merge` | + +- [ ] **Step 5: Add `template` fields to `antigravity.yaml`** + +| Condition ID | Template Path | +|---|---| +| `antigravity.1_manifest.package_json.exists` | `manifests/antigravity/package.json.tmpl` | +| `antigravity.1_manifest.package_json.required_fields` | `manifests/antigravity/package.json.tmpl?merge` | +| `antigravity.3_context.agents_md.exists` | `context-files/AGENTS.md.tmpl` | + +- [ ] **Step 6: Add `template` fields to `openclaw.yaml`** + +| Condition ID | Template Path | +|---|---| +| `openclaw.1_manifest.openclaw_json.exists` | `manifests/openclaw/openclaw.plugin.json.tmpl` | +| `openclaw.1_manifest.openclaw_json.required_fields` | `manifests/openclaw/openclaw.plugin.json.tmpl?merge` | +| `openclaw.3_context.agents_md.exists` | `context-files/AGENTS.md.tmpl` | + +- [ ] **Step 7: Validate all YAML still parses** + +```bash +for f in lib/patterns/platforms/*.yaml; do + python3 -c "import yaml; yaml.safe_load(open('$f'))" && echo "OK: $f" || echo "FAIL: $f" +done +``` +Expected: All OK + +- [ ] **Step 8: Verify template paths exist** + +```bash +grep -roh 'template: [^ ]*' lib/patterns/platforms/*.yaml | sed 's/template: //' | sed 's/?merge//' | sort -u | while read tmpl; do + if [ -f "lib/templates/$tmpl" ]; then echo "OK: $tmpl"; else echo "MISSING: $tmpl"; fi +done +``` +Expected: All OK (no MISSING) + +- [ ] **Step 9: Commit** + +```bash +git add lib/patterns/platforms/*.yaml +git commit -m "feat: populate template field on fixable conditions in all 6 rubrics + +Every condition that can be fixed by artifact generation now has a template +field. Uses ?merge suffix for conditions targeting existing files." +``` + +--- + +## Task 2: Create Consolidated Inventory Pattern + +**Files:** +- Create: `lib/patterns/inventory.md` + +This merges the assessment skill's 7 inventory substeps with the uplift skill's asset discovery into a single pass. + +- [ ] **Step 1: Read both skills' inventory phases for reference** + +Read `skills/assessing-plugin-portability/SKILL.md` lines covering Phase 2 (inventory substeps 2.1-2.7) and `skills/uplifting-a-plugin/SKILL.md` lines covering Phase 2 (discover assets + check conflicts). + +- [ ] **Step 2: Create `lib/patterns/inventory.md`** + +```markdown +# Inventory + +Unified asset and readiness inventory. Run once, results used by both +scoring (Phase 3) and generation (Phases 5-9). + +Referenced by `skills/plugin-portability/SKILL.md`. + +--- + +## Inventory Algorithm + +```pseudocode +INVENTORY(plugin_path, computed): + + ## 2.1 Discover Assets + + computed.skills = [] + FOR skill_path IN glob(plugin_path + "/skills/*/SKILL.md"): + fm = parse_frontmatter(skill_path) + computed.skills.append({ + path: skill_path, + dir: dirname(skill_path), + name: fm.get("name", basename(dirname(skill_path))), + description: fm.get("description", ""), + frontmatter: fm + }) + + computed.agents = glob(plugin_path + "/agents/*.md") + computed.commands = glob(plugin_path + "/commands/*") + computed.hooks = { + json: file_exists(plugin_path + "/hooks/hooks.json"), + cursor: file_exists(plugin_path + "/hooks/hooks-cursor.json"), + run_hook_cmd: file_exists(plugin_path + "/hooks/run-hook.cmd") + } + + ## 2.2 Check Platform Manifests + + manifest_checks = [ + { platform: "claude-code", path: ".claude-plugin/plugin.json" }, + { platform: "claude-code", path: ".claude-plugin/marketplace.json" }, + { platform: "cursor", path: ".cursor-plugin/plugin.json" }, + { platform: "cursor", path: ".cursor-plugin/marketplace.json" }, + { platform: "gemini-cli", path: "gemini-extension.json" }, + { platform: "codex", path: ".codex-plugin/plugin.json" }, + { platform: "codex", path: ".agents/plugins/marketplace.json" }, + { platform: "antigravity", path: "package.json" }, + { platform: "openclaw", path: "openclaw.plugin.json" } + ] + + computed.manifest_results = [] + FOR check IN manifest_checks: + status = "PRESENT" IF file_exists(plugin_path + "/" + check.path) ELSE "MISSING" + computed.manifest_results.append({ platform: check.platform, path: check.path, status: status }) + + ## 2.3 Check Context Files + + context_checks = [ + { platform: "claude-code", path: "CLAUDE.md" }, + { platform: "cursor", path: "AGENTS.md" }, + { platform: "gemini-cli", path: "GEMINI.md" }, + { platform: "codex", path: "AGENTS.md" }, + { platform: "antigravity", path: "AGENTS.md" }, + { platform: "antigravity", path: "GEMINI.md" }, + { platform: "openclaw", path: "AGENTS.md" } + ] + + computed.context_results = [] + FOR check IN context_checks: + status = "PRESENT" IF file_exists(plugin_path + "/" + check.path) ELSE "MISSING" + computed.context_results.append({ platform: check.platform, path: check.path, status: status }) + + ## 2.4 Check Per-Skill Sidecars + + computed.sidecar_results = [] + sidecar_files = ["codex-tools.md", "gemini-tools.md"] + FOR skill IN computed.skills: + FOR sidecar IN sidecar_files: + sidecar_path = skill.dir + "/references/" + sidecar + status = "PRESENT" IF file_exists(sidecar_path) ELSE "MISSING" + computed.sidecar_results.append({ skill: skill.name, sidecar: sidecar, status: status }) + + ## 2.5 Check Frontmatter Compatibility + + computed.frontmatter_results = [] + FOR skill IN computed.skills: + fm = skill.frontmatter + issues = [] + IF "name" NOT IN fm: issues.append("missing name") + IF "description" NOT IN fm: issues.append("missing description") + computed.frontmatter_results.append({ skill: skill.name, issues: issues }) + + ## 2.6 Check Hooks + + computed.hook_results = { + hooks_json: {}, + hooks_cursor_json: {} + } + IF computed.hooks.json: + hooks_data = read_json(plugin_path + "/hooks/hooks.json") + FOR event IN hooks_data.get("hooks", {}): + computed.hook_results.hooks_json[event] = hooks_data["hooks"][event] + IF computed.hooks.cursor: + cursor_data = read_json(plugin_path + "/hooks/hooks-cursor.json") + FOR event IN cursor_data: + IF event != "version": + computed.hook_results.hooks_cursor_json[event] = cursor_data[event] + + ## 2.7 Check Session-Start Injection + + computed.injection_results = check_injection(plugin_path, computed.metadata.name) + # See lib/patterns/injection-checks.md for the 7-component check + + ## 2.8 Collect Existing Files for Conflict Detection + + computed.existing_files = set() + FOR result IN computed.manifest_results: + IF result.status == "PRESENT": + computed.existing_files.add(result.path) + FOR result IN computed.context_results: + IF result.status == "PRESENT": + computed.existing_files.add(result.path) + # Also add hook files, sidecar files, etc. + IF computed.hooks.json: computed.existing_files.add("hooks/hooks.json") + IF computed.hooks.cursor: computed.existing_files.add("hooks/hooks-cursor.json") + FOR result IN computed.sidecar_results: + IF result.status == "PRESENT": + computed.existing_files.add(result.skill + "/references/" + result.sidecar) +``` +``` + +- [ ] **Step 3: Verify the file is well-formed** + +Run: `wc -l lib/patterns/inventory.md` +Expected: ~120-130 lines + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/inventory.md +git commit -m "feat: add consolidated inventory pattern + +Merges assessment's 7 inventory substeps with uplift's asset discovery +into a single pass. Results used by both scoring and generation." +``` + +--- + +## Task 3: Create Merged `plugin-portability` Skill + +**Files:** +- Create: `skills/plugin-portability/SKILL.md` + +This is the main deliverable — a concise workflow orchestrator that references external pseudocode files. + +- [ ] **Step 1: Read both current skills for content to merge** + +Read `skills/assessing-plugin-portability/SKILL.md` and `skills/uplifting-a-plugin/SKILL.md` fully. Extract the unique elements from each that need to be preserved in the merged skill. + +- [ ] **Step 2: Create `skills/plugin-portability/SKILL.md`** + +Write the merged skill. It should be ~150-200 lines. Structure: + +```markdown +--- +name: plugin-portability +description: > + Use when you need to assess or uplift a plugin for multi-platform portability. + Gauges intent upfront (diagnostic or uplift, which platforms, uplift target), + then runs shared detection, inventory, and condition-driven scoring across all + platforms. For uplift, generates missing artifacts with fixes: annotations + linked to rubric conditions. Supports incremental uplift for viable+ platforms. + Platforms: Claude Code, Cursor, Gemini CLI, Codex, Antigravity, OpenClaw. +allowed-tools: Read, Write, Edit, Bash, Glob, Grep +--- + +# Plugin Portability + +Assess or uplift a plugin for multi-platform portability. Single entry point +for both diagnostic assessment and artifact generation. + +**Input:** `plugin_path` (string, required) — Path to the plugin root directory. +**Output:** Assessment report (always) + generated artifacts (uplift mode only). + +> **Detection Algorithm:** `lib/patterns/detection-algorithm.md` +> **Inventory:** `lib/patterns/inventory.md` +> **Rubric Framework:** `lib/patterns/rubric-framework.md` +> **Platform Rubrics:** `lib/patterns/platforms/.yaml` +> **Lookup Tables:** `lib/references/platform-mappings.md` +> **Manifest Generation:** `lib/patterns/manifest-generation.md` +> **Hook Merging:** `lib/patterns/hook-merging.md` +> **Bootstrapping:** `lib/patterns/bootstrapping.md` +> **Injection Checks:** `lib/patterns/injection-checks.md` +> **Install Doc Templates:** `lib/templates/install-docs/` + +--- + +## Overview + +| Phase | Description | +| ----- | ----------- | +| **Phase 0a: Intent** | Ask mode (assess/uplift) + platforms via AskUserQuestion | +| **Phase 1: Detect** | Scan metadata, elect canonical, build model, classify shape | +| **Phase 2: Inventory** | Unified asset + readiness inventory | +| **Phase 0b: Uplift Target** | If uplift: ask uplift target (shape-informed recommendation) | +| **Phase 3: Score** | Full condition-driven assessment per platform | +| **Phase 4: Report** | Detailed assessment report. If assess mode: STOP | +| **Phase 5: Generate** | Uplift: manifests, context files, sidecars | +| **Phase 6: Port** | Uplift: hook adaptation | +| **Phase 7: Document** | Uplift: install documentation | +| **Phase 8: Bootstrap** | Uplift: session-start injection | +| **Phase 9: Summary** | Uplift: files created, merged, manual actions | + +**Minimum starting state:** At least one `skills/*/SKILL.md` with frontmatter, +or any platform manifest file. + +--- + +## Phase 0a: Intent + +Ask before any file scanning. Use `AskUserQuestion` on Claude Code, or +platform-equivalent structured input on other platforms. + +```pseudocode +INTENT_UPFRONT(): + mode = AskUserQuestion( + question: "Assess only (diagnostic, read-only) or Uplift (generate missing artifacts)?", + header: "Mode", + options: [ + { label: "Assess", description: "Score portability across platforms. Read-only, no changes." }, + { label: "Uplift", description: "Generate missing platform artifacts to close portability gaps." } + ], + multiSelect: false + ) + + platforms = AskUserQuestion( + question: "Which platforms to target?", + header: "Platforms", + options: [ + { label: "All platforms", description: "Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw" }, + { label: "Select platforms", description: "Choose specific platforms" } + ], + multiSelect: false + ) + + IF platforms == "Select platforms": + platforms = AskUserQuestion( + question: "Select target platforms:", + header: "Platforms", + options: [ + { label: "Claude Code", description: "Reference platform" }, + { label: "Cursor", description: "VS Code fork with rules, hooks, MCP" }, + { label: "Gemini CLI", description: "Google CLI with @ includes" }, + { label: "Codex", description: "OpenAI CLI with TOML agents" }, + { label: "Antigravity", description: "Google VS Code fork, OpenVSX" }, + { label: "OpenClaw", description: "TypeScript gateway with plugin SDK" } + ], + multiSelect: true + ) + ELSE: + platforms = ["claude-code", "cursor", "gemini-cli", "codex", "antigravity", "openclaw"] + + RETURN { mode, platforms } +``` + +--- + +## Phase 1: Detect + +Run the shared detection algorithm. See `lib/patterns/detection-algorithm.md`. + +```pseudocode +DETECT(plugin_path): + computed.sources = scan_metadata_sources(plugin_path) + IF len(computed.sources) == 0: + DISPLAY "No recognisable plugin signals found in {plugin_path}." + DISPLAY "Provide at least one platform manifest or one skills/*/SKILL.md" + DISPLAY "with name and description frontmatter." + EXIT + + computed.canonical = elect_canonical(computed.sources) + computed.metadata = build_metadata_model(computed.sources) + computed.shape = classify_shape(computed.sources) + print_inference_summary(computed.metadata, computed.canonical) +``` + +--- + +## Phase 2: Inventory + +Run the unified inventory. See `lib/patterns/inventory.md`. + +```pseudocode +INVENTORY(plugin_path, computed) +# Populates: computed.skills, computed.agents, computed.commands, computed.hooks, +# computed.manifest_results, computed.context_results, computed.sidecar_results, +# computed.frontmatter_results, computed.hook_results, computed.injection_results, +# computed.existing_files +``` + +--- + +## Phase 0b: Uplift Target + +If uplift mode, ask the user to confirm the shape-derived uplift target. + +```pseudocode +IF intent.mode != "uplift": + SKIP + +IF computed.shape == "bare-skill-repo" AND len(computed.skills) <= 3: + recommended = "skill-first" + reason = "Bare skill repo with " + str(len(computed.skills)) + " skills" +ELIF computed.shape == "curated-distribution": + recommended = "curated-note-only" + reason = "Curated distribution (marketplace, no source skills)" +ELSE: + recommended = "full-portable-plugin" + reason = computed.shape + " with " + str(len(computed.skills)) + " skills" + +computed.uplift_target = AskUserQuestion( + question: "Repo detected as: " + reason + ". What level of uplift?", + header: "Uplift target", + options: [ + # Recommended option first with "(Recommended)" suffix + { label: recommended_label + " (Recommended)", description: recommended_desc }, + ...other options... + ], + multiSelect: false +) +``` + +See spec for full option construction logic. + +--- + +## Phase 3: Score + +Full condition-driven assessment. See `lib/patterns/rubric-framework.md`. + +```pseudocode +SCORE(computed, intent.platforms): + FOR platform IN intent.platforms: + rubric = load_yaml("lib/patterns/platforms/" + platform + ".yaml") + # Evaluate each condition (checkable via JIT script, judgement via LLM) + # Compute category scores using hybrid formula + # Compute band and percentage + computed.scores[platform] = { categories, band, percentage, results, failing } + + computed.blockers = detect_blockers(computed) + + # Auto-derive per-platform depth for uplift + IF intent.mode == "uplift": + FOR platform IN intent.platforms: + IF computed.scores[platform].band IN ["strong", "viable"]: + computed.recommendation_for[platform] = "incremental" + ELSE: + computed.recommendation_for[platform] = "full" +``` + +--- + +## Phase 4: Report + +Always emitted. + +```pseudocode +REPORT(computed, intent): + DISPLAY repo shape, metadata summary + FOR platform IN intent.platforms: + DISPLAY platform band, percentage, per-category scores + DISPLAY per-condition pass/fail with condition IDs + DISPLAY blockers + + IF intent.mode == "uplift": + DISPLAY uplift strategy per platform + DISPLAY artifacts to generate (from failing conditions with template field) + + IF intent.mode == "assess": + STOP +``` + +--- + +## Phases 5-9: Uplift + +Gated by `ALLOWED_CATEGORIES` table and per-platform depth. +See `lib/patterns/manifest-generation.md`, `lib/patterns/hook-merging.md`, +`lib/patterns/bootstrapping.md`. + +```pseudocode +ALLOWED_CATEGORIES = { + "skill-first": ["2_skills", "3_context", "5_toolmap", "6_install"], + "full-portable-plugin": ["1_manifest", "2_skills", "3_context", "4_hooks", + "5_toolmap", "6_install", "7_runtime"], + "curated-note-only": ["6_install"] +} + +allowed = ALLOWED_CATEGORIES[computed.uplift_target] + +# Phase 5: Generate — for each failing condition in allowed categories: +# - create: render template if target doesn't exist +# - merge: update existing file with missing fields +# - none (no template): add to manual_actions +# Each action carries: # fixes: {condition.id} + +# Phase 6: Port — hook adaptation (skipped if 4_hooks not in allowed) +# Phase 7: Document — install docs (always in allowed) +# Phase 8: Bootstrap — session-start injection (skipped if curated-note-only) +``` + +--- + +## Phase 9: Summary + +```pseudocode +SUMMARY(computed): + DISPLAY files created, files merged/updated, manual actions required +``` +``` + +- [ ] **Step 3: Verify line count** + +Run: `wc -l skills/plugin-portability/SKILL.md` +Expected: 150-200 lines + +- [ ] **Step 4: Commit** + +```bash +git add skills/plugin-portability/SKILL.md +git commit -m "feat: create merged plugin-portability skill + +Single entry point replacing assessing-plugin-portability and +uplifting-a-plugin. Phase 0 intent via AskUserQuestion, shared +detection/inventory/scoring, conditional uplift phases." +``` + +--- + +## Task 4: Create Reference File Pointers + +**Files:** +- Create: `skills/plugin-portability/references/codex-tools.md` +- Create: `skills/plugin-portability/references/gemini-tools.md` + +- [ ] **Step 1: Create the reference directory and pointer files** + +```bash +mkdir -p skills/plugin-portability/references +``` + +Write `skills/plugin-portability/references/codex-tools.md`: +``` +See [lib/references/codex-tools.md](../../lib/references/codex-tools.md) for the full Codex tool mapping. +``` + +Write `skills/plugin-portability/references/gemini-tools.md`: +``` +See [lib/references/gemini-tools.md](../../lib/references/gemini-tools.md) for the full Gemini tool mapping. +``` + +- [ ] **Step 2: Commit** + +```bash +git add skills/plugin-portability/references/ +git commit -m "feat: add reference pointers for merged skill" +``` + +--- + +## Task 5: Update `using-skill-portability` + +**Files:** +- Modify: `skills/using-skill-portability/SKILL.md` + +- [ ] **Step 1: Rewrite the skill table and descriptions** + +Replace the current content (which lists two separate skills) with: + +```markdown +--- +name: using-skill-portability +description: Use when starting a session with the skill-portability plugin. Session-start bootstrapping that lists available skills and platform-specific invocation instructions. +--- + +# Using Skill Portability + +This plugin provides the following skills: + +| Skill | Description | +| ----- | ----------- | +| `plugin-portability` | Assess or uplift a plugin for multi-platform portability. Asks intent upfront (assess/uplift, platforms, uplift target), runs condition-driven scoring, and optionally generates missing artifacts. Platforms: Claude Code, Cursor, Gemini CLI, Codex, Antigravity, OpenClaw. | + +## How to Invoke + +**Claude Code / Cursor:** Use the `Skill` tool with skill name `plugin-portability`. + +**Gemini CLI:** Use the `activate_skill` tool with skill name `plugin-portability`. + +**Antigravity / OpenClaw / Codex:** Skills are auto-discovered. Follow the SKILL.md instructions directly. + +## Tool Name Mapping + +Skills use Claude Code tool names. See `lib/references/` for platform-specific equivalents. +``` + +- [ ] **Step 2: Commit** + +```bash +git add skills/using-skill-portability/SKILL.md +git commit -m "chore: update using-skill-portability for merged skill name" +``` + +--- + +## Task 6: Remove Old Skill Directories + +**Files:** +- Remove: `skills/assessing-plugin-portability/` (entire directory) +- Remove: `skills/uplifting-a-plugin/` (entire directory) + +- [ ] **Step 1: Remove both directories** + +```bash +git rm -r skills/assessing-plugin-portability/ +git rm -r skills/uplifting-a-plugin/ +``` + +- [ ] **Step 2: Commit** + +```bash +git commit -m "chore: remove old assessment and uplift skill directories + +Replaced by unified skills/plugin-portability/SKILL.md" +``` + +--- + +## Task 7: Final Validation + +- [ ] **Step 1: Verify no remaining references to old skill names** + +```bash +grep -rn "assessing-plugin-portability\|uplifting-a-plugin" skills/ lib/ --include="*.md" --include="*.yaml" --include="*.tmpl" --include="*.sh" +``` +Expected: No output + +- [ ] **Step 2: Verify all template paths in rubrics exist** + +```bash +grep -roh 'template: [^ ]*' lib/patterns/platforms/*.yaml | sed 's/template: //' | sed 's/?merge//' | sort -u | while read tmpl; do + if [ -f "lib/templates/$tmpl" ]; then echo "OK: $tmpl"; else echo "MISSING: $tmpl"; fi +done +``` +Expected: All OK + +- [ ] **Step 3: Verify YAML rubrics still parse** + +```bash +for f in lib/patterns/platforms/*.yaml; do + python3 -c "import yaml; yaml.safe_load(open('$f'))" && echo "OK: $f" || echo "FAIL: $f" +done +``` +Expected: All OK + +- [ ] **Step 4: Verify skill structure** + +```bash +ls skills/plugin-portability/SKILL.md skills/plugin-portability/references/codex-tools.md skills/plugin-portability/references/gemini-tools.md skills/using-skill-portability/SKILL.md +``` +Expected: All 4 files exist + +```bash +ls skills/assessing-plugin-portability/ skills/uplifting-a-plugin/ 2>&1 +``` +Expected: "No such file or directory" for both + +- [ ] **Step 5: Check for references to old skill names in pattern docs** + +```bash +grep -rn "assessing-plugin-portability\|uplifting-a-plugin" lib/patterns/ --include="*.md" +``` +Expected: No output. If any found, update those pattern docs. + +- [ ] **Step 6: Commit any final fixes** + +```bash +git add -A && git status +# Only commit if there are changes +git commit -m "chore: final validation cleanup for skill merge" 2>/dev/null || echo "Nothing to commit" +``` diff --git a/docs/superpowers/specs/2026-04-26-rubric-tightening-design.md b/docs/superpowers/specs/2026-04-26-rubric-tightening-design.md new file mode 100644 index 0000000..8dd7297 --- /dev/null +++ b/docs/superpowers/specs/2026-04-26-rubric-tightening-design.md @@ -0,0 +1,467 @@ +# Rubric Tightening & Condition-Linked Uplift + +**Date:** 2026-04-26 +**Status:** Approved design — implementation pending (current branch still has old platform set) +**Scope:** Rubric framework, all platform rubrics, lookup tables, assessment skill, uplift skill, templates + +## Problem + +The current rubric system has four interconnected problems: + +1. **Ambiguous scoring criteria.** Rubrics use prose like "complete & accurate", "proper frontmatter", "thoroughly documents" — none testable or reproducible across LLM runs. +2. **No AND/OR logic.** Criteria within a score level are implicitly AND but never stated. Some read as OR. Scoring is subjective. +3. **Missing component types.** Coverage of agents, subagents, rules, commands, and policies is inconsistent across platforms. Cursor rules (`.mdc`), Gemini commands (`.toml`), Codex subagent communication (`spawn_agent`) are underspecified. +4. **Rubric-uplift drift.** The uplift skill makes decisions based on repo shape and skill count — never reads rubric scores. Templates have no linkage to the conditions they resolve. + +Additionally, the platform set needs updating: +- **Add:** Antigravity, OpenClaw (from external reference implementation) +- **Remove:** OpenCode, Copilot CLI + +## Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Platform set | Claude Code, Codex, Cursor, Gemini, Antigravity, OpenClaw | Aligns with external reference implementation; drops platforms with weakest ecosystem support | +| Rubric-uplift linkage | Shared condition IDs | Assessment checks conditions, uplift references same IDs as fix triggers. Single vocabulary. | +| Scoring logic | Hybrid: AND between levels, OR within | Critical flags gate the level; optional flags earn bonus points. Matches how platform validation actually works. | +| Component granularity | Keep 7 categories, tag conditions with component types | Avoids category explosion (54 sections) while making individual conditions addressable by component. | +| Lookup tables | Single canonical reference file | Avoids duplication, easy to update. Rubric pseudocode references tables by name. | +| Condition types | Checkable (deterministic) vs Judgement (LLM-interpreted) | Makes explicit what's objective vs subjective. LLM can JIT-generate scripts for checkable conditions. | +| Drift prevention | `# fixes:` annotations in uplift actions | Emergent registry via grep. No separate file to maintain. Orphan/phantom detection is scriptable. | + +## Design + +### 1. Condition ID Schema + +Every scorable criterion gets a stable ID: + +``` +{platform}.{category_num}_{category_short}.{component}.{check_name} +``` + +Examples: +``` +cursor.1_manifest.plugin_json.required_fields +cursor.1_manifest.plugin_json.conditional_keys +cursor.4_hooks.hooks_json.camelcase_events +gemini.5_toolmap.sidecar.task_to_at_agent +codex.2_skills.frontmatter.spawn_agent_documented +``` + +### 2. Condition Types + +```yaml +type: checkable # Deterministic. File exists, field matches table, pattern present. + # LLM can JIT-generate a bash/python script to evaluate. + +type: judgement # Requires interpretation. Content quality, documentation adequacy. + # LLM evaluates directly. +``` + +### 3. Condition Structure + +Each condition in a platform rubric YAML file: + +```yaml +- id: cursor.1_manifest.plugin_json.required_fields + type: checkable + component: plugin_json + critical: true + points: 1 + check: | + fields = read_json(".cursor-plugin/plugin.json") + for f in LOOKUP["manifest_required_fields"]["cursor"]: + assert f in fields + template: manifests/cursor-plugin/plugin.json.tmpl +``` + +- `id`: Stable identifier, referenced by assessment AND uplift +- `type`: `checkable` or `judgement` +- `component`: Tag for filtering by component type within a category +- `critical`: If `true`, gates the score level (must pass for Score 2+) +- `points`: Weight within the category (typically 1) +- `check`: Pseudocode (checkable) or prose description (judgement) +- `template`: Optional — which template resolves this condition + +### 4. Scoring Formula + +Per category, per platform: + +``` +critical_count = number of conditions where critical == true +optional_count = number of conditions where critical == false +critical_pass = number of critical conditions that pass +optional_pass = number of optional conditions that pass + +# Guard: every scored category MUST have at least 1 critical condition. +# If a category has 0 critical conditions, it scores N/A (see Section 5). + +Score 3: critical_pass == critical_count + AND (optional_count == 0 OR optional_pass / optional_count >= 0.75) +Score 2: critical_pass == critical_count + AND (optional_count == 0 OR optional_pass / optional_count < 0.75) +Score 1: critical_pass / critical_count >= 0.50 + AND critical_pass < critical_count +Score 0: critical_pass / critical_count < 0.50 +``` + +**Edge case rules:** +- A category with 0 critical conditions and >0 optional conditions scores **N/A** (not vacuously 3). This prevents score inflation from categories that have no real gates. +- A category with 0 total conditions scores **N/A**. +- When `optional_count == 0` and all critical conditions pass, the score is **3** (no optional bar to clear). +- When `critical_count == 1`, Score 1 is impossible (50% of 1 rounds to 1, which is all-pass → Score 2). This is by design: a single-critical category is binary pass/fail. + +### 5. N/A Category Handling + +If a platform has zero conditions in a category, OR zero critical conditions in a category: + +``` +Category scores N/A (not 0) +Max possible score adjusts downward (only scored categories count) +Scored categories = categories with at least 1 critical condition + +Band calculation: + actual_score = sum of scored category scores + max_score = scored_category_count * 3 + percentage = actual_score / max_score + + Strong: >= 85% + Viable: >= 60% + Partial: >= 35% + Weak: < 35% + +Guard: if scored_category_count < 3, band is capped at PARTIAL + (too few categories to claim Strong/Viable readiness) +``` + +### 6. Seven Categories with Component Tags + +| # | Category | Component Tags | +|---|----------|---------------| +| 1 | Manifest Packaging | `plugin_json`, `marketplace_json`, `extension_json`, `package_json`, `openclaw_json` | +| 2 | Skill Compatibility | `frontmatter`, `discovery`, `tool_refs` | +| 3 | Context Delivery | `claude_md`, `agents_md`, `gemini_md`, `rules_mdc` | +| 4 | Hook Portability | `hooks_json`, `scripts`, `event_names`, `output_format` | +| 5 | Tool Mapping | `sidecar`, `model_mapping`, `subagent_syntax` | +| 6 | Install Readiness | `install_docs`, `publishing`, `verification` | +| 7 | Runtime Adapters | `mcp`, `agents`, `commands`, `rules`, `policies`, `subagents` | + +### 7. Component Support Matrix + +Source of truth for which conditions exist per platform: + +| Component | Claude Code | Cursor | Gemini | Codex | Antigravity | OpenClaw | +|-----------|------------|--------|--------|-------|-------------|----------| +| Skills | `skills/*/SKILL.md` | `skills/*/SKILL.md` | `skills/*/SKILL.md` | `.agents/skills/*/SKILL.md` | `.agents/skills/*/SKILL.md` (preferred) or `.agent/skills/*/SKILL.md` (legacy) | `skills/*/SKILL.md` | +| Agents | `agents/*.md` | `agents/*.md` | `agents/*.md` | `.codex/agents/*.toml` | combined in AGENTS.md | `agents.list[]` in config (not manifest) | +| Commands | deprecated | optional `commands/` | `commands/*.toml` | none | `.agents/workflows/` (slash commands) | TS handlers | +| Rules | none | `.cursor/rules/*.mdc` | none | none | `.agent/rules/*.md` + GEMINI.md (highest priority) | none | +| Hooks | `hooks/hooks.json` | `hooks/hooks-cursor.json` | user `settings.json` (guidance only) | none (scripts as utilities) | none | plugin hooks via `api.registerHook()` (`before_tool_call`, `tool_result_persist`) | +| MCP | `.mcp.json` | `mcp.json` (no Resources) | implicit | none | none | none | +| Policies | none | none | `policies/*.toml` | none | none | none | +| Subagents | `Task` tool | implicit | `@agent-name` | `spawn_agent` | implicit | `agents.list[]` | +| Marketplace | `.claude-plugin/marketplace.json` | `.cursor-plugin/marketplace.json` | none | `.codex-plugin/marketplace.json` | `package.json` (OpenVSX/extension) | `openclaw.plugin.json` + `package.json` with `openclaw` block | +| Context file | `CLAUDE.md` | `AGENTS.md` + `.cursor/rules/` | `GEMINI.md` (`@` includes) | `AGENTS.md` | GEMINI.md > AGENTS.md > `.agent/rules/` (priority order) | `AGENTS.md` | + +**Platform research notes (2026-04-26):** + +**Antigravity (Google):** VS Code fork using OpenVSX registry. Preferred skill +path migrated from `.agent/` to `.agents/` (legacy still supported). Context +priority: System Rules > GEMINI.md > AGENTS.md > `.agent/rules/`. For skill-only +distribution, no package.json needed — drop skills into `.agents/skills/`. +Supports `.agents/workflows/` for custom slash commands. No file-based hook system. + +**OpenClaw:** Open-source gateway platform (TypeScript). Requires BOTH +`openclaw.plugin.json` (with `id` + `configSchema`) AND `package.json` with +`openclaw` block (extensions, compat, build) for full native plugins. Skill-only +plugins can omit the package.json. Has plugin-level hooks via `api.registerHook()` +with `before_tool_call` and `tool_result_persist` events (corrects external +converter's claim of "no tool-level hooks"). Auto-detects `.claude-plugin/`, +`.codex-plugin/`, `.cursor-plugin/` bundle layouts — may work without conversion. +Install via ClawHub, npm, or local `plugins.load.paths`. + +### 8. Lookup Tables (`lib/references/platform-mappings.md`) + +Single canonical file containing all tables referenced by condition pseudocode. Tables: + +#### 8.1 Model Mapping + +| Claude Model | Gemini | Codex | OpenClaw | Cursor | Antigravity | +|---|---|---|---|---|---| +| opus | gemini-2.5-pro | gpt-5.4 | anthropic/claude-opus-4-6 | inherit | (removed) | +| sonnet | gemini-2.5-flash | gpt-5.4-mini | anthropic/claude-sonnet-4-5 | inherit | (removed) | +| haiku | gemini-2.0-flash-lite | gpt-5.4-mini | anthropic/claude-haiku-4-5 | inherit | (removed) | + +#### 8.2 Tool Name Mapping + +| Claude Tool | Gemini | Codex | Cursor | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| Read | read_file | (same) | (same) | (same) | (same) | +| Write | write_file | (same) | (same) | (same) | (same) | +| Edit | replace | (same) | (same) | (same) | (same) | +| Bash | run_shell_command | (same) | (same) | (same) | (same) | +| Grep | grep_search | (same) | (same) | (same) | (same) | +| Glob | list_files | (same) | (same) | (same) | (same) | +| Task | @agent-name | spawn_agent | (same) | (same) | agents.list[] | +| Agent | @agent-name | spawn_agent | (same) | (same) | agents.list[] | + +#### 8.3 Hook Event Mapping + +| Claude Event | Cursor | Gemini | Codex | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| SessionStart | sessionStart | SessionStart | N/A | N/A | gateway:startup (plugin SDK) | +| PreToolUse | preToolUse | BeforeTool | N/A | N/A | before_tool_call (plugin SDK) | +| PostToolUse | postToolUse | AfterTool | N/A | N/A | tool_result_persist (plugin SDK) | +| PostToolUseFailure | postToolUseFailure | (N/A) | N/A | N/A | N/A | +| SubagentStart | subagentStart | (N/A) | N/A | N/A | N/A | +| SubagentStop | subagentStop | (N/A) | N/A | N/A | N/A | +| PreCompact | preCompact | PreCompress | N/A | N/A | session:compact:before (plugin SDK) | +| Stop | stop | AfterAgent | N/A | N/A | N/A | +| UserPromptSubmit | beforeSubmitPrompt | (N/A) | N/A | N/A | N/A | + +Note: OpenClaw hooks use a plugin SDK (`api.registerHook()`), not file-based +configuration. Portable hook scripts cannot be directly converted — they require +a TypeScript wrapper that calls `api.registerHook()` with the appropriate event. + +#### 8.4 Path Variable Mapping + +| Claude Variable | Cursor | Gemini | Codex | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| `${CLAUDE_PLUGIN_ROOT}` | `${CURSOR_PLUGIN_ROOT}` | `${extensionPath}${/}` | N/A | N/A | N/A | +| `/hooks/scripts/` | `/scripts/` | `/scripts/` | N/A | N/A | N/A | + +#### 8.5 Field Stripping Sets + +| Field | Gemini | Codex | OpenClaw | Cursor | Antigravity | +|---|---|---|---|---|---| +| `disable-model-invocation` | strip | strip | strip | **keep** | strip | +| `allowed-tools` | strip | strip | strip | strip | strip | + +#### 8.6 Manifest Required Fields + +| Platform | Required Fields | +|---|---| +| Claude Code | name, version, description, author (with email) | +| Cursor | name, displayName, description, version, author | +| Gemini | name, version, description, contextFileName | +| Codex (native) | name, version, description | +| Antigravity | name, displayName, version, description, publisher (for OpenVSX extension; skill-only needs no manifest) | +| OpenClaw | id, configSchema (in `openclaw.plugin.json`); full plugins also need `package.json` with `openclaw.extensions`, `openclaw.compat` | + +#### 8.7 Hook Format Rules + +| Rule | Claude Code | Cursor | Gemini | OpenClaw | +|---|---|---|---|---| +| Event name case | PascalCase | camelCase | PascalCase (BeforeTool/AfterTool) | snake_case (plugin SDK) | +| Timeout unit | seconds | seconds | milliseconds | N/A (SDK-managed) | +| Async support | yes (optional field) | no (strip) | no (strip) | yes (async handlers) | +| Structure | nested (matcher → hooks[]) | flat (matcher at hook level) | settings.json (user-configured) | `api.registerHook()` (TypeScript) | +| Output key | `hookSpecificOutput.additionalContext` | `additional_context` | N/A | return value from handler | + +Note: Codex and Antigravity have no hook systems — omitted from this table. + +#### 8.8 Skill Output Directory + +| Platform | Skills Path | Agents Path | +|---|---|---| +| Claude Code | `skills/` | `agents/` | +| Cursor | `skills/` | `agents/` | +| Gemini | `skills/` | `agents/` | +| Codex | `.agents/skills/` | `.codex/agents/` | +| Antigravity | `.agents/skills/` (preferred) or `.agent/skills/` (legacy) | `.agent/rules/` | +| OpenClaw | `skills/` | in manifest `agents.list[]` | + +#### 8.9 Agent Output Format + +| Platform | Format | Model Field | +|---|---|---| +| Claude Code | Markdown (agents/*.md) | Claude model name | +| Cursor | Markdown (agents/*.md) + .mdc rule | `inherit` | +| Gemini | Markdown (agents/*.md) | Gemini model name, tools: ["*"] | +| Codex | TOML (.codex/agents/*.toml) | Codex model name | +| Antigravity | Combined AGENTS.md + .agent/rules/*.md | (removed) | +| OpenClaw | Listed in manifest agents.list[] | OpenClaw provider/model | + +### 9. Uplift Linkage + +#### 9.1 `# fixes:` Annotations + +Every uplift action carries a `# fixes:` annotation: + +```pseudocode +# In uplift skill Phase 4: Generate +for platform in target_platforms: + for condition in rubric[platform].1_manifest.conditions: + if condition.has_template: + if not file_exists(condition.target_path): + render(condition.template, metadata) + # fixes: {condition.id} +``` + +In template file headers: + +``` +{{! fixes: cursor.1_manifest.plugin_json.required_fields }} +{{! fixes: cursor.1_manifest.plugin_json.conditional_keys }} +``` + +#### 9.2 Drift Detection + +```pseudocode +rubric_ids = collect all condition IDs from lib/patterns/platforms/*.yaml +uplift_ids = collect all "fixes:" references from skills/uplifting-a-plugin/ +template_ids = collect all "fixes:" references from lib/templates/ + +orphan_conditions = rubric_ids - uplift_ids +# Conditions scored but never fixed by uplift + +phantom_fixes = uplift_ids - rubric_ids +# Uplift references conditions that don't exist in rubric +``` + +Assessment skill can run this as meta-validation. + +#### 9.3 Incremental Uplift + +When assessment shows a platform already VIABLE (>= 60%): + +```pseudocode +for platform in target_platforms: + if scores[platform].band in ["STRONG", "VIABLE"]: + # Only fix failing conditions + failing = [c for c in rubric[platform].all_conditions if not c.passes] + for condition in failing: + if condition.has_template: + execute_fix(condition) + else: + # Full generation + execute_full_uplift(platform) +``` + +#### 9.4 JIT Code Generation Guidance + +The assessment skill includes this instruction for the evaluating LLM: + +``` +When evaluating checkable conditions: + 1. Read the condition's pseudocode + 2. Generate a read-only bash or python script that implements the check + 3. Execute the script + 4. Record pass/fail from exit code + +When evaluating judgement conditions: + 1. Read the condition's prose description + 2. Read the referenced file(s) + 3. Apply your interpretation + 4. Record pass/fail with reasoning +``` + +**Execution scope note:** This skill is used by plugin authors assessing and +uplifting their own plugins. The target repo is always the author's own +working directory. JIT-generated scripts are read-only checks (file existence, +JSON field presence, pattern matching) — they do not modify files, execute +target repo content, or access paths outside the plugin root. The pseudocode +operations (`read_json`, `file_exists`, `parse_frontmatter`, `glob`) map to +read-only filesystem queries, not arbitrary shell execution. + +### 10. Platform Rubric Reference: Cursor (Full Example) + +See Design Section 3 in brainstorming conversation for the complete Cursor rubric +with all 7 categories, ~30 conditions, typed as checkable/judgement with +pseudocode checks and component tags. + +Other platforms follow the same structure with differences documented in +Section 3: "Per-Platform Differences." + +## File Changes + +### New Files + +| File | Purpose | +|------|---------| +| `lib/references/platform-mappings.md` | Canonical lookup tables (9 tables) | +| `lib/patterns/platforms/claude-code.yaml` | Claude Code rubric (structured conditions) | +| `lib/patterns/platforms/cursor.yaml` | Cursor rubric | +| `lib/patterns/platforms/gemini-cli.yaml` | Gemini rubric | +| `lib/patterns/platforms/codex.yaml` | Codex rubric | +| `lib/patterns/platforms/antigravity.yaml` | Antigravity rubric (new platform) | +| `lib/patterns/platforms/openclaw.yaml` | OpenClaw rubric (new platform) | +| `lib/templates/manifests/antigravity/package.json.tmpl` | Antigravity manifest template | +| `lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl` | OpenClaw manifest template | +| `lib/templates/install-docs/antigravity.md` | Antigravity install docs | +| `lib/templates/install-docs/openclaw.md` | OpenClaw install docs | +| `lib/templates/install-docs/adding-platform/antigravity.md` | Adding Antigravity section | +| `lib/templates/install-docs/adding-platform/openclaw.md` | Adding OpenClaw section | + +### Rewritten Files + +| File | Nature of Change | +|------|-----------------| +| `lib/patterns/rubric-framework.md` | New scoring formula, condition types, N/A handling, percentage bands | +| `skills/assessing-plugin-portability/SKILL.md` | Reference condition IDs, JIT guidance, new scoring | +| `skills/uplifting-a-plugin/SKILL.md` | `# fixes:` annotations, incremental uplift, rubric-informed decisions | +| `skills/using-skill-portability/SKILL.md` | Updated platform list | + +### Updated Files + +| File | Nature of Change | +|------|-----------------| +| `lib/patterns/hook-merging.md` | Remove OpenCode/Copilot, add Antigravity/OpenClaw (both N/A) | +| `lib/patterns/bootstrapping.md` | Remove OpenCode/Copilot platform branches | +| `lib/patterns/detection-algorithm.md` | Add Antigravity/OpenClaw manifest detection, remove OpenCode/Copilot | +| `lib/patterns/manifest-generation.md` | New platform entries, remove dropped ones | +| `lib/patterns/injection-checks.md` | Remove OpenCode/Copilot checks | + +### Removed Files + +| File | Reason | +|------|--------| +| `lib/patterns/platforms/opencode.md` | Platform dropped | +| `lib/patterns/platforms/copilot-cli.md` | Platform dropped | +| `lib/references/copilot-tools.md` | Platform dropped | +| `lib/templates/manifests/opencode-plugin.js.tmpl` | Platform dropped | +| `lib/templates/manifests/package.json.tmpl` | Was OpenCode-specific | +| `lib/templates/context-files/copilot-instructions.md.tmpl` | Platform dropped | +| `lib/templates/install-docs/opencode.md` | Platform dropped | +| `lib/templates/install-docs/copilot-cli.md` | Platform dropped | +| `lib/templates/install-docs/adding-platform/opencode.md` | Platform dropped | +| `lib/templates/install-docs/adding-platform/copilot-cli.md` | Platform dropped | +| `lib/patterns/platforms/claude-code.md` | Replaced by .yaml | +| `lib/patterns/platforms/cursor.md` | Replaced by .yaml | +| `lib/patterns/platforms/gemini-cli.md` | Replaced by .yaml | +| `lib/patterns/platforms/codex.md` | Replaced by .yaml | + +## Migration Order + +1. **Lookup tables** — `platform-mappings.md` (everything references it) +2. **Rubric framework** — rewrite `rubric-framework.md` +3. **Platform rubrics** — 4 rewrites + 2 new, all as .yaml +4. **Skills & templates** — assessment, uplift, templates with `fixes:` annotations, new platform templates +5. **Pattern docs** — update hook-merging, bootstrapping, detection-algorithm, manifest-generation, injection-checks + +## External Reference + +Design informed by analysis of `agentic-commerce-skills-plugins/scripts/` — a Python CLI pipeline converting Claude Code plugins to Gemini, Antigravity, Codex, OpenClaw, and Cursor. Key extractions: +- Model mapping tables (frontmatter.py) +- Hook event/tool name mappings (hooks.py) +- Cursor hook flattening rules (cursor.py) +- Manifest required fields per platform (manifest.py, validate.py) +- Field stripping sets (frontmatter.py) +- 50+ validation checks decomposed into our condition ID system + +## Addendum: Codex Adversarial Review Response (2026-04-26) + +**[high] JIT execution safety** — Acknowledged but accepted risk. This skill is +used by plugin authors on their own repos, not on untrusted third-party code. +JIT-generated scripts are read-only filesystem checks (file_exists, read_json, +parse_frontmatter, glob). Added explicit scope note in Section 9.4. + +**[medium] Scoring formula edge cases** — Fixed. Section 4 now defines explicit +behavior for zero-critical categories (→ N/A), zero-optional categories +(→ Score 3 if all critical pass), single-critical categories (binary pass/fail), +and adds a minimum-scored-categories guard for band calculation. + +**[medium] Platform set not reflected in branch** — Fixed. Status updated to +"Approved design — implementation pending." The spec is the design document; +implementation follows via the migration order in the spec. Old platform files +will be removed and new platform files created during implementation phases 3-5. diff --git a/docs/superpowers/specs/2026-04-26-skill-merge-design.md b/docs/superpowers/specs/2026-04-26-skill-merge-design.md new file mode 100644 index 0000000..4cad823 --- /dev/null +++ b/docs/superpowers/specs/2026-04-26-skill-merge-design.md @@ -0,0 +1,517 @@ +# Skill Merge: Unified Plugin Portability Skill + +**Date:** 2026-04-26 +**Status:** Approved design — implementation pending +**Scope:** Merge assessing-plugin-portability and uplifting-a-plugin into a single `plugin-portability` skill +**Depends on:** 2026-04-26-rubric-tightening-design.md (implemented) + +## Problem + +The two main skills (`assessing-plugin-portability` and `uplifting-a-plugin`) +have substantial duplication: + +- Phase 1 (Detect) is 95% identical between both skills +- Manifest/context file path lists are duplicated +- Uplift contains an optional lightweight quick-assess that duplicates + assessment's full scoring logic +- Both reference the same external pattern files + +The uplift skill is ~763 lines and the assessment skill is ~564 lines. Merging +eliminates ~200 lines of duplication and creates a single entry point with a +cleaner flow: assess always runs, then optionally uplift. + +## Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Skill name | `plugin-portability` | Short, covers both modes | +| Intent detection | 2 upfront questions (mode + platforms) | Minimal user friction; everything else auto-derived from scores | +| Assessment depth | Always full | Scoring is read-only file checks, not expensive; ensures uplift decisions are fully informed | +| Incremental vs full | Auto-derived from scores | Viable+ platforms get incremental; no need to ask user | +| Pseudocode location | External files in `lib/patterns/` | Follows existing pattern; keeps SKILL.md as concise workflow orchestrator | +| `using-skill-portability` | Keep separate, update references | Serves different purpose (session-start injection); part of bootstrapping pattern | + +## Design + +### Phase Structure + +``` +Phase 0a: Intent — Q1 (mode) + Q2 (platforms) before any file scanning +Phase 1: Detect — shared detection algorithm +Phase 2: Inventory — unified inventory (merges both skills' Phase 2) +Phase 0b: Uplift Target — Q3 (uplift target, shape-informed) — uplift mode only +Phase 3: Score — full condition-driven assessment (always runs) +Phase 4: Report — detailed assessment report (always emitted) + — IF mode == "assess": STOP +Phase 5: Generate — uplift only: manifests, context files, sidecars +Phase 6: Port — uplift only: hook adaptation +Phase 7: Document — uplift only: install docs +Phase 8: Bootstrap — uplift only: session-start injection +Phase 9: Summary — uplift only: files created/skipped/flagged +``` + +### Phase 0a: Intent (before file scanning) + +Two questions via structured UI. On Claude Code, use `AskUserQuestion` tool +for structured multi-select. On other platforms, use the platform-equivalent +structured input (Gemini CLI prompts, Cursor input, etc.). Fall back to text +prompts only if structured input is unavailable. + +```pseudocode +INTENT_UPFRONT(): + # Q1: Mode + mode = AskUserQuestion( + question: "Assess only (diagnostic, read-only) or Uplift (generate missing artifacts)?", + header: "Mode", + options: [ + { label: "Assess", description: "Score portability across platforms. Read-only, no changes." }, + { label: "Uplift", description: "Generate missing platform artifacts to close portability gaps." } + ], + multiSelect: false + ) + + # Q2: Platforms + platforms = AskUserQuestion( + question: "Which platforms to target?", + header: "Platforms", + options: [ + { label: "All platforms", description: "Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw" }, + { label: "Select platforms", description: "Choose specific platforms to assess or uplift" } + ], + multiSelect: false + ) + + IF platforms == "Select platforms": + platforms = AskUserQuestion( + question: "Select target platforms:", + header: "Platforms", + options: [ + { label: "Claude Code", description: "Reference platform" }, + { label: "Cursor", description: "VS Code fork with rules, hooks, MCP" }, + { label: "Gemini CLI", description: "Google CLI with @ includes and settings-based hooks" }, + { label: "Codex", description: "OpenAI CLI with TOML agents and spawn_agent" }, + { label: "Antigravity", description: "Google VS Code fork, OpenVSX, .agents/skills/" }, + { label: "OpenClaw", description: "TypeScript gateway with plugin SDK hooks" } + ], + multiSelect: true + ) + ELSE: + platforms = ["claude-code", "cursor", "gemini-cli", "codex", "antigravity", "openclaw"] + + RETURN { mode, platforms } +``` + +### Phase 0b: Uplift Target (after detection, uplift mode only) + +Q3 runs AFTER Phase 1 (Detect) and Phase 2 (Inventory) because it needs +shape classification to make a recommendation. Uses structured UI with the +shape-derived recommendation marked as "(Recommended)". + +```pseudocode +INTENT_UPLIFT_TARGET(computed): + IF intent.mode != "uplift": + RETURN # assess mode skips this + + # Derive recommendation from shape + IF computed.shape == "bare-skill-repo" AND len(computed.skills) <= 3: + recommended = "skill-first" + reason = "Bare skill repo with " + str(len(computed.skills)) + " skills" + ELIF computed.shape == "curated-distribution": + recommended = "curated-note-only" + reason = "Curated distribution (marketplace, no source skills)" + ELSE: + recommended = "full-portable-plugin" + reason = computed.shape + " with " + str(len(computed.skills)) + " skills" + + # Q3: Confirm or override shape-derived recommendation + # Place recommended option first with "(Recommended)" suffix + options = [ + { label: "Skill-first", description: "Sidecars, tool mapping, context files only. No platform manifests." }, + { label: "Full portable plugin", description: "Manifests, context, hooks, install docs — everything." }, + { label: "Curated note only", description: "Documentation only. No generated artifacts." } + ] + + # Mark recommended option + FOR opt IN options: + IF opt.label.lower().startswith(recommended.replace("-", " ")): + opt.label = opt.label + " (Recommended)" + # Move to first position + options = [opt] + [o for o in options if o != opt] + + uplift_target = AskUserQuestion( + question: "Repo detected as: " + reason + ". What level of uplift?", + header: "Uplift target", + options: options, + multiSelect: false + ) + + computed.uplift_target = uplift_target +``` + +### Auto-derived (no user questions needed) + +These are determined by the system from scores and shape: +- **Incremental vs full per-platform:** from band scores (viable+ → incremental) +- **Which specific artifacts to generate:** from failing condition IDs + rubric `template` field +- **Codex path:** from repo shape (bare-skill-repo → skill-discovery, else → native-plugin) + +### Phase 1: Detect + +Unchanged. References `lib/patterns/detection-algorithm.md`. + +```pseudocode +DETECT(plugin_path): + computed.sources = scan_metadata_sources(plugin_path) + IF len(computed.sources) == 0: + DISPLAY "No recognisable plugin signals found in {plugin_path}." + DISPLAY "Provide at least one platform manifest or one skills/*/SKILL.md" + DISPLAY "with name and description frontmatter." + EXIT + + computed.canonical = elect_canonical(computed.sources) + computed.metadata = build_metadata_model(computed.sources) + computed.shape = classify_shape(computed.sources) + print_inference_summary(computed.metadata, computed.canonical) +``` + +### Phase 2: Inventory + +New consolidated file at `lib/patterns/inventory.md`. Merges assessment's 7 +detailed checks with uplift's asset discovery and conflict detection into a +single pass. + +```pseudocode +INVENTORY(plugin_path, computed): + # Assets (needed by both modes) + computed.skills = glob("skills/*/SKILL.md") + computed.agents = glob("agents/*.md") + computed.commands = glob("commands/*") + computed.hooks = { json: exists("hooks/hooks.json"), + cursor: exists("hooks/hooks-cursor.json") } + + # Platform manifests (needed by scoring) + computed.manifest_results = check_manifests(plugin_path) + + # Context files (needed by scoring) + computed.context_results = check_context_files(plugin_path) + + # Per-skill sidecars (needed by scoring) + computed.sidecar_results = check_sidecars(plugin_path, computed.skills) + + # Frontmatter compatibility (needed by scoring) + computed.frontmatter_results = check_frontmatter(plugin_path, computed.skills) + + # Hook details (needed by scoring) + computed.hook_results = check_hooks(plugin_path) + + # Injection status (needed by scoring) + computed.injection_results = check_injection(plugin_path, computed.metadata.name) + + # Existing files for conflict detection (needed by uplift) + computed.existing_files = collect_existing_paths(plugin_path) +``` + +### Phase 3: Score + +Always runs full condition-driven assessment. References +`lib/patterns/rubric-framework.md` and `lib/patterns/platforms/*.yaml`. + +```pseudocode +SCORE(computed, platforms): + FOR platform IN platforms: + rubric = load_yaml("lib/patterns/platforms/" + platform + ".yaml") + results = {} + + FOR category IN rubric.categories: + FOR condition IN category.conditions: + IF condition.type == "checkable": + passed = jit_evaluate_checkable(condition.check, computed) + ELSE: + passed = evaluate_judgement(condition.check, computed) + results[condition.id] = { passed, type: condition.type } + + category.score = compute_category_score(category.conditions, results) + + computed.scores[platform] = { + categories: rubric.categories, + band: compute_band(rubric.categories), + percentage: compute_percentage(rubric.categories), + results: results, + failing: [c for c in all_conditions if not results[c.id].passed] + } + + computed.blockers = detect_blockers(computed) +``` + +Auto-derives per-platform repair depth from scores: + +```pseudocode + IF intent.mode == "uplift": + # Shape-based uplift target already set in Phase 0b (user confirmed) + # Now derive per-platform depth from scores + FOR platform IN platforms: + IF computed.scores[platform].band IN ["strong", "viable"]: + computed.recommendation_for[platform] = "incremental" + ELSE: + computed.recommendation_for[platform] = "full" +``` + +**Two-layer uplift strategy:** +- **Layer 1 (Phase 0b):** Shape-based uplift target — user-confirmed via + AskUserQuestion. Controls WHAT CLASS of artifacts are in scope. + - `skill-first`: sidecars, tool mapping, context files only. No manifests. + - `full-portable-plugin`: manifests, context, hooks, install docs — everything. + - `curated-note-only`: documentation only, no generated artifacts. +- **Layer 2 (Phase 3):** Per-platform repair depth — auto-derived from scores. + Controls HOW MUCH within the chosen target. + - `incremental`: only fix failing conditions (for viable+ platforms) + - `full`: generate all missing artifacts for that platform + +### Condition-to-Artifact Mapping + +The `template` field in each rubric YAML condition IS the mapping. No JIT +scanning, no separate index file. + +**Rule:** Every fixable condition MUST populate the `template` field: + +```yaml +- id: cursor.1_manifest.plugin_json.required_fields + type: checkable + component: plugin_json + critical: true + points: 1 + check: | + fields = read_json(".cursor-plugin/plugin.json") + for f in LOOKUP["manifest_required_fields"]["cursor"]: + assert f in fields + template: manifests/cursor-plugin/plugin.json.tmpl # ← REQUIRED for fixable conditions +``` + +Conditions that are assessment-only (no artifact can fix them, e.g., +`judgement` conditions about documentation quality) leave `template` empty. + +**Drift detection** becomes a simple rubric validation: +- Condition has `template` but file doesn't exist → stale rubric +- Template has `{{! fixes: }}` but no condition references it → orphan template annotation + +The `{{! fixes: }}` annotations in templates remain as documentation (which +conditions a template resolves), but the rubric `template` field is the +authoritative mapping used by Phases 4 and 5. + +**Implementation note for rubric tightening:** The 6 platform YAML rubrics +created in the prior spec need to be updated to populate `template` on every +fixable condition. This is part of the migration order for this spec. + +### Phase 4: Report + +Always emitted. Shows per-platform scores with individual condition pass/fail, +blockers, and (if uplift mode) the derived uplift strategy per platform. + +```pseudocode +REPORT(computed, intent): + DISPLAY "## Repo Shape: " + computed.shape + DISPLAY metadata summary + + FOR platform IN intent.platforms: + score = computed.scores[platform] + DISPLAY "## " + platform + " — " + score.band + " (" + score.percentage + "%)" + FOR category IN score.categories: + DISPLAY "### " + category.name + ": " + category.score + "/3" + FOR condition IN category.conditions: + status = "✓" IF score.results[condition.id].passed ELSE "✗" + DISPLAY status + " " + condition.id + + DISPLAY blockers + + IF intent.mode == "uplift": + DISPLAY "## Uplift Strategy" + FOR platform IN intent.platforms: + DISPLAY platform + ": " + computed.recommendation_for[platform] + DISPLAY "## Artifacts to Generate" + FOR platform IN intent.platforms: + FOR condition IN computed.scores[platform].failing: + IF condition.template: # from rubric YAML template field + DISPLAY "- " + condition.template + " → fixes: " + condition.id + + IF intent.mode == "assess": + STOP +``` + +### Phases 5-9: Uplift (filtered by shape target + per-platform depth) + +Generation is gated by TWO filters: the shape-based uplift target controls +which categories of artifacts are in scope, and the per-platform depth +controls whether to generate everything or only fix failing conditions. + +#### Template action types + +Each rubric condition's `template` field maps to one of three action types: + +```yaml +template: manifests/cursor-plugin/plugin.json.tmpl # action: create +template: manifests/cursor-plugin/plugin.json.tmpl?merge # action: merge (update existing) +template: null # action: none (assessment-only) +``` + +- **create**: Render template to target path. Only if file doesn't exist. +- **merge**: Read existing file, apply template as structured update (add missing + fields, update stale values). Used for manifests with missing fields, context + files with missing skill references, etc. +- **none** (no template): Assessment-only condition. Cannot be fixed by generation. + Emitted as a manual action item in the report. + +#### Allowed categories by uplift target + +```pseudocode +ALLOWED_CATEGORIES = { + "skill-first": ["2_skills", "3_context", "5_toolmap", "6_install"], + "full-portable-plugin": ["1_manifest", "2_skills", "3_context", "4_hooks", + "5_toolmap", "6_install", "7_runtime"], + "curated-note-only": ["6_install"] +} +``` + +#### Phase 5: Generate + +```pseudocode +allowed = ALLOWED_CATEGORIES[computed.uplift_target] + +FOR platform IN intent.platforms: + failing = computed.scores[platform].failing + + FOR condition IN failing: + # Filter 1: Shape target — enforced for ALL depths including full + IF condition.category NOT IN allowed: + SKIP + + # Filter 2: Has fixable template? + IF NOT condition.template: + computed.manual_actions.append(condition) # Report as manual item + CONTINUE + + target_path = resolve_target_path(condition.template, platform) + action = parse_action(condition.template) # "create" or "merge" + + IF action == "create" AND target_path NOT IN computed.existing_files: + render(condition.template, computed.metadata) + # fixes: {condition.id} + ELIF action == "merge" AND target_path IN computed.existing_files: + merge_update(condition.template, target_path, computed.metadata) + # fixes: {condition.id} + ELIF action == "create" AND target_path IN computed.existing_files: + # File exists but condition fails — needs merge, not create + computed.manual_actions.append(condition) + # Report: "{target_path} exists but fails {condition.id} — manual review needed" + ELSE: + render(condition.template, computed.metadata) + # fixes: {condition.id} + +# Phase 6: Port — see lib/patterns/hook-merging.md +# Skipped if "4_hooks" NOT IN allowed +# Phase 7: Document — see lib/templates/install-docs/ +# Always runs ("6_install" is in all allowed sets) +# Phase 8: Bootstrap — see lib/patterns/bootstrapping.md +# Skipped if uplift_target == "curated-note-only" +# Phase 9: Summary — files created, merged, skipped, manual actions +``` + +#### Phase 9: Summary + +```pseudocode +SUMMARY(computed): + DISPLAY "## Files Created" + FOR file IN computed.created_files: + DISPLAY "- " + file + + DISPLAY "## Files Merged/Updated" + FOR file IN computed.merged_files: + DISPLAY "- " + file + + DISPLAY "## Manual Actions Required" + FOR condition IN computed.manual_actions: + DISPLAY "- " + condition.id + ": " + condition.check +``` + +### External File References + +The SKILL.md is a workflow orchestrator (~150-200 lines) referencing: + +| Phase | External File | +|-------|--------------| +| Phase 1 | `lib/patterns/detection-algorithm.md` | +| Phase 2 | `lib/patterns/inventory.md` (NEW) | +| Phase 3 | `lib/patterns/rubric-framework.md` + `platforms/*.yaml` | +| Phase 5 | `lib/patterns/manifest-generation.md` | +| Phase 6 | `lib/patterns/hook-merging.md` | +| Phase 7 | `lib/templates/install-docs/` | +| Phase 8 | `lib/patterns/bootstrapping.md` | + +Phases 0, 4, 9 are inline (short, skill-specific interaction). + +## File Changes + +### New Files + +| File | Purpose | +|------|---------| +| `skills/plugin-portability/SKILL.md` | Merged skill (~150-200 lines) | +| `skills/plugin-portability/references/codex-tools.md` | Moved from old locations | +| `skills/plugin-portability/references/gemini-tools.md` | Moved from old locations | +| `lib/patterns/inventory.md` | Consolidated inventory pseudocode | + +### Updated Files + +| File | Change | +|------|--------| +| `skills/using-skill-portability/SKILL.md` | Reference `plugin-portability` instead of two separate skills | + +### Removed Files + +| File | Reason | +|------|--------| +| `skills/assessing-plugin-portability/SKILL.md` | Folded into merged skill | +| `skills/assessing-plugin-portability/references/codex-tools.md` | Moved | +| `skills/assessing-plugin-portability/references/gemini-tools.md` | Moved | +| `skills/uplifting-a-plugin/SKILL.md` | Folded into merged skill | +| `skills/uplifting-a-plugin/references/codex-tools.md` | Moved | +| `skills/uplifting-a-plugin/references/gemini-tools.md` | Moved | + +## Migration Order + +1. Populate `template` field on fixable conditions in all 6 platform YAML rubrics +2. Create `lib/patterns/inventory.md` (consolidated pseudocode) +3. Create `skills/plugin-portability/SKILL.md` (merged workflow) +4. Move reference files to `skills/plugin-portability/references/` +5. Update `skills/using-skill-portability/SKILL.md` +6. Remove old skill directories +7. Validate: no remaining references to old skill names, all fixable conditions have `template` + +## Addendum: Codex Adversarial Review Response (2026-04-26) + +**[high] Incremental uplift has no condition-to-template mapping** — Fixed. +The `template` field in each rubric YAML condition is now required for every +fixable condition. The rubric IS the mapping — no JIT scanning, no separate +index. Phase 5 reads `condition.template` directly. Conditions without a +template are assessment-only (skipped during generation). The 6 platform YAML +rubrics need `template` fields populated as part of implementation. + +**[high] Score-only strategy removes shape-based safe modes** — Fixed. +Restored two-layer decision: Layer 1 is shape-based uplift target +(`skill-first`, `full-portable-plugin`, `curated-note-only`) confirmed with +user. Layer 2 is per-platform repair depth (`incremental` vs `full`) derived +from scores. Phase 5 generation is gated by both filters — shape target +controls which categories are in scope, depth controls how much. + +## Addendum: Codex Adversarial Review #2 Response (2026-04-26) + +**[high] Incremental uplift skips broken existing files** — Fixed. +Added template action types: `create` (new files only) vs `merge` (update +existing files with missing fields/entries). Conditions targeting existing +files that fail use `merge` action. Files that exist and fail but have no +merge template are reported as manual action items — never silently skipped. + +**[high] Skill-first full generation can still generate manifests** — Fixed. +Replaced `execute_full_generation` with `ALLOWED_CATEGORIES` table keyed by +uplift target. The category filter is enforced for ALL depths including full +— `skill-first` can never create manifests (`1_manifest`), hooks (`4_hooks`), +or runtime adapters (`7_runtime`) regardless of per-platform depth. diff --git a/examples/assessment-hiivmind-corpus.md b/examples/assessment-hiivmind-corpus.md index d385086..0c5965d 100644 --- a/examples/assessment-hiivmind-corpus.md +++ b/examples/assessment-hiivmind-corpus.md @@ -1,6 +1,6 @@ # Example: Assessing hiivmind-corpus -This is sample output from running the `assessing-plugin-portability` skill against +This is sample output from running the `plugin-portability` skill against [hiivmind-corpus](https://github.com/hiivmind/hiivmind-corpus), a Claude Code plugin for documentation corpus management. @@ -18,9 +18,9 @@ it as a candidate for full portable plugin uplift. │ claude-code │ 18/21 │ Strong │ No action required (Native) │ │ cursor │ 6/21 │ Partial │ Significant gaps — uplift recommended │ │ gemini-cli │ 6/21 │ Partial │ Significant gaps — uplift recommended │ -│ opencode │ 3/21 │ Weak │ Full uplift required │ -│ copilot-cli │ 6/21 │ Partial │ Significant gaps — uplift recommended │ │ codex │ 6/21 │ Partial │ Significant gaps — uplift recommended │ +│ antigravity │ N/A │ — │ Not yet assessed │ +│ openclaw │ N/A │ — │ Not yet assessed │ └─────────────┴───────┴─────────┴───────────────────────────────────────┘ ``` @@ -127,16 +127,6 @@ or `GEMINI.md` context file. - `GEMINI.md` - `skills/*/references/gemini-tools.md` -### opencode — Weak - -- `package.json` -- `.opencode/plugins/hiivmind-corpus.js` - -### copilot-cli — Partial - -- `.github/copilot-instructions.md` -- `skills/*/references/copilot-tools.md` - ### codex — Partial - `.codex-plugin/plugin.json` @@ -152,6 +142,6 @@ or `GEMINI.md` context file. ## Summary -Run the `uplifting-a-plugin` skill to generate all missing artifacts automatically. +Run the `plugin-portability` skill to generate all missing artifacts automatically. This will transform hiivmind-corpus from a Claude-first plugin into a fully portable multi-platform plugin with native manifests, tool mappings, and unified documentation. diff --git a/gemini-extension.json b/gemini-extension.json index dc72c88..fa1488e 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,6 +1,6 @@ { "name": "skill-portability", - "description": "Make any plugin fully portable across all platforms. Accepts Claude, Cursor, Gemini, OpenCode, or bare SKILL.md repos as input. Emits every missing platform artifact.", - "version": "0.1.0", + "description": "Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw, or bare SKILL.md repos as input. Emits every missing platform artifact.", + "version": "0.1.1", "contextFileName": "GEMINI.md" } diff --git a/lib/patterns/bootstrapping.md b/lib/patterns/bootstrapping.md index cf47cea..1b91029 100644 --- a/lib/patterns/bootstrapping.md +++ b/lib/patterns/bootstrapping.md @@ -15,7 +15,7 @@ END ask: "Would you like to generate session-start bootstrapping hooks? This creates a `using-{{name}}` skill that gets force-injected at session -start on Claude Code, Cursor, Copilot CLI, OpenCode, and Gemini CLI. (y/n)" +start on Claude Code, Cursor, Gemini CLI, Antigravity, Codex, and OpenClaw. (y/n)" IF user declines THEN skip_bootstrapping = true @@ -62,11 +62,9 @@ This plugin provides the following skills: **Claude Code / Cursor:** Use the `Skill` tool with the skill name. -**Copilot CLI:** Use the `skill` tool with the skill name. - **Gemini CLI:** Use the `activate_skill` tool with the skill name. -**Codex / Other:** Skills are auto-discovered. Follow the SKILL.md instructions directly. +**Antigravity / OpenClaw / Codex:** Skills are auto-discovered. Follow the SKILL.md instructions directly. ## Tool Name Mapping @@ -80,8 +78,7 @@ Skills use Claude Code tool names. See each skill's `references/` directory for ``` mkdir -p /skills/using-{{name}}/references/ -Write the same three sidecar files as the skill sidecar generation step: - - using-{{name}}/references/copilot-tools.md +Write the same two sidecar files as the skill sidecar generation step: - using-{{name}}/references/codex-tools.md - using-{{name}}/references/gemini-tools.md @@ -134,10 +131,10 @@ session_context="\nThis plugin uses the superpowers portability patte # Output context injection as JSON. # Cursor hooks expect additional_context (snake_case). # Claude Code hooks expect hookSpecificOutput.additionalContext (nested). -# Copilot CLI and others expect additionalContext (top-level, SDK standard). +# Other platforms expect additionalContext (top-level, SDK standard). if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then printf '{\n "additional_context": "%s"\n}\n' "$session_context" -elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then +elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context" else printf '{\n "additionalContext": "%s"\n}\n' "$session_context" @@ -261,58 +258,7 @@ New file structure: --- -## Step 4.7: Enhance OpenCode Plugin (Step 23) - -``` -IF /.opencode/plugins/{{name}}.js exists THEN - warn: "Bootstrapping will regenerate `.opencode/plugins/{{name}}.js` - with session-start injection. Existing content will be replaced. - Continue? (y/n)" - IF user declines THEN skip this step END -END - -Write /.opencode/plugins/{{name}}.js with the template below, -substituting {{name}}, {{description}}. -``` - -### opencode-bootstrap Template - -```javascript -// OpenCode plugin for {{name}} with session-start bootstrapping -import { readFileSync } from 'fs'; -import { dirname, join } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const pluginRoot = join(__dirname, '../..'); -const bootstrapContent = readFileSync( - join(pluginRoot, 'skills/using-{{name}}/SKILL.md'), 'utf8' -); - -export default { - name: "{{name}}", - description: "{{description}}", - skills: "./skills/", - experimental: { - chat: { - messages: { - transform: (messages) => { - if (messages.length > 0 && messages[0].role === 'user') { - messages[0].content = bootstrapContent + '\n\n' + messages[0].content; - } - return messages; - } - } - } - } -}; -``` - -This replaces the minimal shim from the OpenCode plugin generation step with a version that injects the `using-{{name}}` skill at session start. - ---- - -## Step 4.8: Update GEMINI.md (Step 24) +## Step 4.7: Update GEMINI.md (Step 23) ``` IF GEMINI.md exists THEN @@ -339,7 +285,7 @@ Where: --- -## Step 4.9: Final Report Note (Step 25 contribution) +## Step 4.8: Final Report Note (Step 24 contribution) Append to the final report's "Session-start bootstrapping" section: @@ -353,12 +299,11 @@ ELSE IF skip_bootstrapping AND reason == "user declined" THEN ELSE "Session-start injection configured. Generated: - using-{{name}}/SKILL.md - - using-{{name}}/references/ (3 sidecars) + - using-{{name}}/references/ (2 sidecars) - hooks/session-start - hooks/run-hook.cmd - hooks/hooks.json (SessionStart entry merged) - hooks/hooks-cursor.json (sessionStart entry merged) - - .opencode/plugins/{{name}}.js (message transform) - GEMINI.md (updated with using-{{name}} first)" END ``` diff --git a/lib/patterns/detection-algorithm.md b/lib/patterns/detection-algorithm.md index b6b7b26..f411c4d 100644 --- a/lib/patterns/detection-algorithm.md +++ b/lib/patterns/detection-algorithm.md @@ -1,6 +1,6 @@ # Detection Algorithm -Shared by `uplifting-a-plugin` and `assessing-plugin-portability`. Run once at start. +Shared by `plugin-portability`. Run once at start (Phase 1). --- @@ -129,7 +129,6 @@ FUNCTION BUILD_METADATA_MODEL(canonical, remaining): # Always derived — never read from any source model["marketplaceName"] = model["name"] + "-dev" - model["opencodeMain"] = ".opencode/plugins/" + model["name"] + ".js" RETURN model ``` @@ -191,12 +190,8 @@ FUNCTION CLASSIFY_SHAPE(found_sources): has_skills = ANY source.path MATCHES "skills/*/SKILL.md" has_marketplace = ANY source.path MATCHES "*marketplace.json" - has_package_json = ANY source.path == "package.json" - has_opencode_shim = ANY source.path MATCHES ".opencode/plugins/*.js" manifest_count = len(platform_manifests) - IF has_package_json OR has_opencode_shim: - manifest_count += 1 IF manifest_count == 0 AND has_skills: RETURN "bare-skill-repo" diff --git a/lib/patterns/hook-merging.md b/lib/patterns/hook-merging.md index e39b1bc..b33ac5b 100644 --- a/lib/patterns/hook-merging.md +++ b/lib/patterns/hook-merging.md @@ -162,60 +162,12 @@ Both merge operations must preserve all other existing hooks. Only the session-s --- -## Claude Code → Copilot Event Mapping - -| Claude Code | Copilot CLI | Copilot VS Code | -|-------------|-------------|-----------------| -| `SessionStart` | `sessionStart` | `SessionStart` | -| `PreToolUse` | `preToolUse` | `PreToolUse` | -| `PostToolUse` | `postToolUse` | `PostToolUse` | -| `SubagentStart` | N/A | `SubagentStart` | -| `SubagentStop` | `subagentStop` | `SubagentStop` | -| `Stop` | `agentStop` | `Stop` | -| `PreCompact` | N/A | `PreCompact` | -| `UserPromptSubmit` | `userPromptSubmitted` | `UserPromptSubmit` | +## Antigravity / OpenClaw Hook Notes ---- - -## Copilot Hook Format - -```pseudocode -GENERATE_COPILOT_HOOKS(claude_hooks): - copilot_hooks = { "version": 1, "hooks": {} } - - event_map = { - "SessionStart": "sessionStart", - "PreToolUse": "preToolUse", - "PostToolUse": "postToolUse", - "SubagentStop": "subagentStop", - "Stop": "agentStop", - "UserPromptSubmit": "userPromptSubmitted", - } - - FOR event, entries IN claude_hooks.hooks: - IF event NOT IN event_map: - SKIP - copilot_event = event_map[event] - copilot_hooks.hooks[copilot_event] = [] - - FOR entry IN entries: - copilot_entry = { - "type": "command", - "bash": entry.hooks[0].command, - "powershell": convert_to_powershell_path(entry.hooks[0].command), - "timeoutSec": min(entry.hooks[0].timeout / 1000, 30) - } - copilot_hooks.hooks[copilot_event].append(copilot_entry) - - Write(".github/hooks/hooks.json", JSON.stringify(copilot_hooks, indent=2)) -``` - -Key differences from Claude Code hooks: -- Separate `bash` and `powershell` fields instead of `command` -- No `matcher` — tool name filtering must be done in the script by inspecting `toolName` from stdin JSON -- Default timeout is 30 seconds -- Only `preToolUse` can deny/block actions; all other hooks are observational -- Hooks stored in `.github/hooks/` not `hooks/` +Antigravity and OpenClaw do not have a dedicated hooks file format. Both platforms +auto-discover `hooks/session-start` and execute it at session start if present. +No separate hook generation or merging step is needed for these platforms — the +Claude Code `hooks/hooks.json` and the session-start script cover their needs. --- diff --git a/lib/patterns/injection-checks.md b/lib/patterns/injection-checks.md index e5c60d0..161df19 100644 --- a/lib/patterns/injection-checks.md +++ b/lib/patterns/injection-checks.md @@ -15,8 +15,7 @@ Only runs when `skills/using-/SKILL.md` exists. | 4 | `hooks/run-hook.cmd` | File exists and is executable | PRESENT / MISSING | | 5 | `hooks/hooks.json` | Contains `SessionStart` entry with command containing `session-start` | PRESENT / MISSING | | 6 | `hooks/hooks-cursor.json` | Contains `sessionStart` entry with command containing `session-start` | PRESENT / MISSING | -| 7 | `.opencode/plugins/{{name}}.js` | Contains `experimental.chat.messages.transform` | PRESENT / MISSING / NO_TRANSFORM | -| 8 | `GEMINI.md` | First `@./skills/` include is `using-{{name}}` | PRESENT / MISSING / NOT_FIRST | +| 7 | `GEMINI.md` | First `@./skills/` include is `using-{{name}}` | PRESENT / MISSING / NOT_FIRST | --- @@ -67,18 +66,7 @@ CHECK_INJECTION_COMPONENTS(computed): ELSE: results.append({ component: "hooks/hooks-cursor.json (sessionStart)", status: "MISSING" }) - # 7. OpenCode plugin with transform - oc_path = ".opencode/plugins/" + name + ".js" - IF file_exists(oc_path): - content = Read(oc_path) - IF "experimental" IN content AND "transform" IN content: - results.append({ component: oc_path + " (transform)", status: "PRESENT" }) - ELSE: - results.append({ component: oc_path + " (transform)", status: "NO_TRANSFORM" }) - ELSE: - results.append({ component: oc_path + " (transform)", status: "MISSING" }) - - # 8. GEMINI.md ordering + # 7. GEMINI.md ordering IF file_exists("GEMINI.md"): content = Read("GEMINI.md") first_skill_include = first_line_matching(content, /^@\.\/skills\//) diff --git a/lib/patterns/inventory.md b/lib/patterns/inventory.md new file mode 100644 index 0000000..29d8aae --- /dev/null +++ b/lib/patterns/inventory.md @@ -0,0 +1,142 @@ +# Consolidated Inventory Pattern + +Referenced by `skills/plugin-portability/SKILL.md`. Merges the assessment +skill's 7 inventory substeps with the uplift skill's asset discovery and +conflict detection into a single pass. + +```pseudocode +INVENTORY(plugin_path, computed): + + ## 2.1 Discover Assets + ## Glob skills, parse frontmatter, collect agents/commands/hooks. + raw_skills = Glob(plugin_path + "/skills/*/SKILL.md") + computed.skills = [ + { path: s, dir: dirname(s), name: basename(dirname(s)), + frontmatter: parse_yaml_frontmatter(Read(s)) } + FOR s IN raw_skills + ] + computed.commands = Glob(plugin_path + "/commands/*.md") + computed.agents = Glob(plugin_path + "/agents/*.md") + computed.existing_hooks = read_json_if_exists(plugin_path + "/hooks/hooks.json") + + computed.created = [] # { path, platform } + computed.skipped = [] # { path, platform } + computed.flagged = [] # strings + + ## 2.2 Check Platform Manifests + ## 10 paths across 6 platforms. Record { platform, path, status }. + manifest_checks = [ + { platform: "claude-code", path: ".claude-plugin/plugin.json" }, + { platform: "claude-code", path: ".claude-plugin/marketplace.json" }, + { platform: "cursor", path: ".cursor-plugin/plugin.json" }, + { platform: "cursor", path: ".cursor-plugin/marketplace.json" }, + { platform: "gemini-cli", path: "gemini-extension.json" }, + { platform: "codex", path: ".codex-plugin/plugin.json" }, + { platform: "codex", path: ".agents/plugins/marketplace.json" }, + { platform: "antigravity", path: "package.json" }, + { platform: "openclaw", path: "openclaw.plugin.json" }, + ] + + computed.manifest_results = [] + FOR check IN manifest_checks: + status = IF file_exists(plugin_path + "/" + check.path) THEN "PRESENT" ELSE "MISSING" + computed.manifest_results.append({ platform: check.platform, path: check.path, status: status }) + + ## 2.3 Check Context Files + ## 7 checks: CLAUDE.md, AGENTS.md x4, GEMINI.md x2. + context_checks = [ + { platform: "claude-code", path: "CLAUDE.md" }, + { platform: "cursor", path: "AGENTS.md" }, + { platform: "gemini-cli", path: "GEMINI.md" }, + { platform: "codex", path: "AGENTS.md" }, + { platform: "antigravity", path: "AGENTS.md" }, + { platform: "antigravity", path: "GEMINI.md" }, + { platform: "openclaw", path: "AGENTS.md" }, + ] + + computed.context_results = [] + FOR check IN context_checks: + status = IF file_exists(plugin_path + "/" + check.path) THEN "PRESENT" ELSE "MISSING" + computed.context_results.append({ platform: check.platform, path: check.path, status: status }) + + ## 2.4 Check Per-Skill Sidecars + ## codex-tools.md and gemini-tools.md per skill directory. + sidecar_files = ["codex-tools.md", "gemini-tools.md"] + computed.sidecar_results = [] + + FOR skill IN computed.skills: + FOR sidecar IN sidecar_files: + target = "skills/" + skill.dir + "/references/" + sidecar + status = IF file_exists(plugin_path + "/" + target) THEN "PRESENT" ELSE "MISSING" + computed.sidecar_results.append({ skill: skill.dir, file: sidecar, status: status }) + + ## 2.5 Check Frontmatter Compatibility + ## name and description are required for all platforms. + computed.frontmatter_results = [] + FOR skill IN computed.skills: + fm = skill.frontmatter + IF fm.name AND fm.description: + status = "COMPATIBLE" + ELSE: + missing = [] + IF NOT fm.name: missing.append("name") + IF NOT fm.description: missing.append("description") + status = "MISSING: " + join(missing, ", ") + computed.frontmatter_results.append({ skill: skill.dir, status: status }) + + ## 2.6 Check Hooks + ## Read hooks.json and hooks-cursor.json contents for later phases. + hook_paths = [ + { path: "hooks/hooks.json", key: "claude_hooks" }, + { path: "hooks/hooks-cursor.json", key: "cursor_hooks" }, + ] + + computed.hook_results = [] + FOR hp IN hook_paths: + full = plugin_path + "/" + hp.path + IF file_exists(full): + computed[hp.key] = read_json(full) + computed.hook_results.append({ path: hp.path, status: "PRESENT" }) + ELSE: + computed[hp.key] = NULL + computed.hook_results.append({ path: hp.path, status: "MISSING" }) + + ## 2.7 Check Session-Start Injection + ## Delegate to injection-checks.md for the 8-component verification. + using_path = "skills/using-" + computed.metadata.name + "/SKILL.md" + IF file_exists(plugin_path + "/" + using_path): + computed.injection_results = check_injection_components(computed) # see injection-checks.md + computed.injection_status = compute_injection_summary(computed.injection_results) + ELSE: + computed.injection_results = NULL + computed.injection_status = "NOT CONFIGURED" + + ## 2.8 Collect Existing Files for Conflict Detection + ## Union of all PRESENT paths from 2.2, 2.3, 2.4, 2.6. + computed.existing_files = [] + FOR r IN computed.manifest_results: + IF r.status == "PRESENT": + computed.existing_files.append({ path: r.path, platform: r.platform }) + FOR r IN computed.context_results: + IF r.status == "PRESENT": + computed.existing_files.append({ path: r.path, platform: r.platform }) + FOR r IN computed.sidecar_results: + IF r.status == "PRESENT": + p = "skills/" + r.skill + "/references/" + r.file + computed.existing_files.append({ path: p, platform: sidecar_platform(r.file) }) + FOR r IN computed.hook_results: + IF r.status == "PRESENT": + computed.existing_files.append({ path: r.path, platform: hook_platform(r.path) }) + + # Any file that already exists will be skipped during generation (idempotent). + computed.skipped = computed.existing_files +``` + +## Helper References + +| Helper | Defined in | +|--------|-----------| +| `parse_yaml_frontmatter` | inline — read between `---` markers | +| `check_injection_components` | `lib/patterns/injection-checks.md` | +| `sidecar_platform(file)` | `"gemini-tools.md" → "gemini-cli"`, `"codex-tools.md" → "codex"` | +| `hook_platform(path)` | `"hooks.json" → "claude-code"`, `"hooks-cursor.json" → "cursor"` | diff --git a/lib/patterns/manifest-generation.md b/lib/patterns/manifest-generation.md index 81a58d8..80b8419 100644 --- a/lib/patterns/manifest-generation.md +++ b/lib/patterns/manifest-generation.md @@ -35,7 +35,7 @@ RENDER_WITH_CONDITIONALS(template, metadata, computed): Template provides structure with `{{sectionPlaceholders}}`. Builder constructs dynamic sections from skill/agent/command inventory. -Applies to: `gemini-context`, `agents-context`, `copilot-instructions` +Applies to: `gemini-context`, `agents-context` ```pseudocode RENDER_WITH_BUILDER(template, metadata, computed): @@ -73,11 +73,8 @@ RENDER_WITH_BUILDER(template, metadata, computed): | gemini-extension | Plain | `lib/templates/manifests/gemini-extension.json.tmpl` | | gemini-context | Builder | `lib/templates/context-files/GEMINI.md.tmpl` | | agents-context | Builder | `lib/templates/context-files/AGENTS.md.tmpl` | -| opencode-package | Plain | `lib/templates/manifests/package.json.tmpl` | -| opencode-shim | Plain | `lib/templates/manifests/opencode-plugin.js.tmpl` | | codex-plugin | Plain | `lib/templates/manifests/codex-plugin/plugin.json.tmpl` | | codex-marketplace | Plain | `lib/templates/manifests/codex-plugin/marketplace.json.tmpl` | -| copilot-instructions | Plain | `lib/templates/context-files/copilot-instructions.md.tmpl` | --- @@ -159,24 +156,6 @@ Build skill bullet list for `{{skillIncludes}}` and command bullet list for `{{c --- -## opencode-package - -**Target:** `package.json` - -> **Template:** `lib/templates/manifests/package.json.tmpl` - ---- - -## opencode-shim - -**Target:** `.opencode/plugins/.js` - -Create `.opencode/plugins/` directory if needed. This is the minimal non-bootstrap version of the OpenCode plugin shim. - -> **Template:** `lib/templates/manifests/opencode-plugin.js.tmpl` - ---- - ## codex-plugin **Target:** `.codex-plugin/plugin.json` @@ -196,13 +175,3 @@ Create `.agents/plugins/` directory if needed. Only generated when Codex recomme For single-plugin upstream repos, this manifest points to the repo root with `source.path: "./"`. > **Template:** `lib/templates/manifests/codex-plugin/marketplace.json.tmpl` - ---- - -## copilot-instructions - -**Target:** `.github/copilot-instructions.md` - -Create `.github/` directory if needed. - -> **Template:** `lib/templates/context-files/copilot-instructions.md.tmpl` diff --git a/lib/patterns/platforms/antigravity.yaml b/lib/patterns/platforms/antigravity.yaml new file mode 100644 index 0000000..a593d46 --- /dev/null +++ b/lib/patterns/platforms/antigravity.yaml @@ -0,0 +1,264 @@ +platform: antigravity +manifest_path: package.json # optional for skill-only distribution +context_files: [GEMINI.md, AGENTS.md, ".agent/rules/*.md"] +hooks_path: null # Antigravity has no hook system + +categories: + 1_manifest: + conditions: + - id: antigravity.1_manifest.package_json.exists + type: checkable + component: package_json + critical: false + points: 1 + check: | + file_exists("package.json") + # Not required for skill-only distribution — just drop into .agents/skills/ + template: manifests/antigravity/package.json.tmpl + + - id: antigravity.1_manifest.package_json.required_fields + type: checkable + component: package_json + critical: false + points: 1 + check: | + if file_exists("package.json"): + json = read_json("package.json") + for field in LOOKUP["manifest_required_fields"]["antigravity"]: + assert field in json, f"Missing required field: {field}" + # Required: name, displayName, version, description, publisher + template: manifests/antigravity/package.json.tmpl?merge + + - id: antigravity.1_manifest.package_json.publisher + type: checkable + component: package_json + critical: false + points: 1 + check: | + if file_exists("package.json"): + json = read_json("package.json") + assert json.get("publisher"), \ + "publisher field required for OpenVSX extension distribution" + template: manifests/antigravity/package.json.tmpl?merge + + 2_skills: + conditions: + - id: antigravity.2_skills.frontmatter.required_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + for skill in glob(".agents/skills/*/SKILL.md") + glob(".agent/skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "name" in fm, f"{skill}: missing name" + assert "description" in fm, f"{skill}: missing description" + + - id: antigravity.2_skills.frontmatter.no_claude_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + # LOOKUP["field_stripping"]["antigravity"]: strip both fields + for skill in glob(".agents/skills/*/SKILL.md") + glob(".agent/skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "disable-model-invocation" not in fm, \ + f"{skill}: disable-model-invocation must be stripped for Antigravity" + assert "allowed-tools" not in fm, \ + f"{skill}: allowed-tools must be stripped for Antigravity" + + - id: antigravity.2_skills.frontmatter.model_stripped + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + # Antigravity strips model field entirely (Table 1: "removed") + for skill in glob(".agents/skills/*/SKILL.md") + glob(".agent/skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "model" not in fm, \ + f"{skill}: model field must be removed for Antigravity" + + - id: antigravity.2_skills.discovery.preferred_path + type: checkable + component: discovery + critical: true + points: 1 + check: | + skills = glob(".agents/skills/*/SKILL.md") + assert len(skills) > 0, \ + "No skills found in .agents/skills/*/SKILL.md (preferred path)" + + - id: antigravity.2_skills.discovery.legacy_path + type: checkable + component: discovery + critical: false + points: 1 + check: | + # .agent/skills/*/SKILL.md also works as a legacy path + legacy = glob(".agent/skills/*/SKILL.md") + if len(legacy) > 0: + preferred = glob(".agents/skills/*/SKILL.md") + assert len(preferred) > 0, \ + "Legacy .agent/skills/ detected — prefer .agents/skills/ (plural)" + + - id: antigravity.2_skills.tool_refs.no_unresolved + type: judgement + component: tool_refs + critical: false + points: 1 + check: | + Antigravity tools use the same names as Claude Code (Table 2: all + "(same)" except no Task/Agent distinction). Verify that tool references + in SKILL.md files resolve to known Claude built-in tool names. + + 3_context: + conditions: + - id: antigravity.3_context.agents_md.exists + type: checkable + component: agents_md + critical: true + points: 1 + check: | + file_exists("AGENTS.md") + template: context-files/AGENTS.md.tmpl + + - id: antigravity.3_context.agents_md.skill_coverage + type: judgement + component: agents_md + critical: false + points: 1 + check: | + AGENTS.md should list or reference all skills provided by the plugin. + Verify that every skill in .agents/skills/ is mentioned or documented. + template: context-files/AGENTS.md.tmpl?merge + + - id: antigravity.3_context.gemini_md.exists + type: checkable + component: gemini_md + critical: false + points: 1 + check: | + file_exists("GEMINI.md") + # GEMINI.md has higher priority than AGENTS.md in Antigravity + template: context-files/GEMINI.md.tmpl + + - id: antigravity.3_context.rules.exist + type: checkable + component: rules + critical: false + points: 1 + check: | + rules = glob(".agent/rules/*.md") + # Optional: .agent/rules/*.md for platform-specific rules + + 4_hooks: + # N/A — Antigravity has no hook system + conditions: [] + + 5_toolmap: + conditions: + - id: antigravity.5_toolmap.model_mapping.no_model_field + type: checkable + component: model_mapping + critical: true + points: 1 + check: | + # Antigravity strips model field entirely (Table 1: "removed") + for md in glob(".agents/skills/*/SKILL.md") + glob(".agent/skills/*/SKILL.md") \ + + glob(".agent/rules/*.md"): + fm = parse_frontmatter(md) + assert "model" not in fm, \ + f"{md}: model field must be removed for Antigravity (Table 1)" + + - id: antigravity.5_toolmap.model_mapping.no_tools_field + type: checkable + component: model_mapping + critical: false + points: 1 + check: | + # Agent frontmatter should not contain tools field (Table 9: "removed") + for md in glob(".agents/skills/*/SKILL.md") + glob(".agent/skills/*/SKILL.md") \ + + glob(".agent/rules/*.md"): + fm = parse_frontmatter(md) + assert "tools" not in fm, \ + f"{md}: tools field must be removed for Antigravity (Table 9)" + + 6_install: + conditions: + - id: antigravity.6_install.install_docs.exists + type: checkable + component: install_docs + critical: true + points: 1 + check: | + readme = find_first(["README.md", "INSTALL.md", "docs/install.md"]) + assert readme is not None, "No README.md or INSTALL.md found" + content = read(readme) + assert "install" in content.lower(), "Install section missing from documentation" + + - id: antigravity.6_install.install_docs.skill_copy + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Install documentation should explain copying skills to + .agents/skills/ (project-local) or ~/.gemini/antigravity/skills/ + (global installation path). + + - id: antigravity.6_install.install_docs.vsix_install + type: judgement + component: install_docs + critical: false + points: 1 + check: | + For extension distribution, documentation should mention + `antigravity --install-extension .vsix` for OpenVSX + extension installation. + + - id: antigravity.6_install.verification.steps + type: judgement + component: verification + critical: false + points: 1 + check: | + Install documentation should include verification steps to + confirm the plugin or skills loaded correctly. + + 7_runtime: + conditions: + - id: antigravity.7_runtime.rules.exist + type: checkable + component: rules + critical: false + points: 1 + check: | + # .agent/rules/*.md for rule-based agent bodies + if dir_exists(".agent/rules/"): + rules = glob(".agent/rules/*.md") + assert len(rules) > 0, \ + ".agent/rules/ directory exists but contains no .md files" + + - id: antigravity.7_runtime.agents.in_agents_md + type: judgement + component: agents + critical: false + points: 1 + check: | + Antigravity combines agents into AGENTS.md rather than separate + agent files (Table 9). Verify that agent definitions are present + in AGENTS.md and/or .agent/rules/*.md. + + - id: antigravity.7_runtime.workflows.exist + type: checkable + component: workflows + critical: false + points: 1 + check: | + # .agents/workflows/ for slash commands (not .toml format) + if dir_exists(".agents/workflows/"): + workflows = glob(".agents/workflows/*") + assert len(workflows) > 0, \ + ".agents/workflows/ directory exists but is empty" diff --git a/lib/patterns/platforms/claude-code.md b/lib/patterns/platforms/claude-code.md deleted file mode 100644 index 88c6dcd..0000000 --- a/lib/patterns/platforms/claude-code.md +++ /dev/null @@ -1,185 +0,0 @@ -# Claude Code Scoring Rules - -Platform-specific scoring criteria for the 7-category rubric. -See `lib/patterns/rubric-framework.md` for the shared scoring scale. - ---- - -## Artifact Checklist - -| File | Category | -|------|----------| -| `.claude-plugin/plugin.json` | Manifest Packaging | -| `.claude-plugin/marketplace.json` | Manifest Packaging | -| `CLAUDE.md` | Context Delivery | -| `hooks/hooks.json` | Hook Portability | -| `.mcp.json` | Runtime Adapters | - ---- - -## Category 1: Manifest Packaging - -Score 3 when: -- `.claude-plugin/plugin.json` present with all fields: `name`, `version`, `description`, `author` (with `name` and `email`), `keywords` -- `.claude-plugin/marketplace.json` present with complete plugin entries and valid source paths -- Skills discoverable in `skills//SKILL.md` format -- No reliance on legacy `commands/` format - -Score 2 when: -- `plugin.json` exists but is missing 1-2 fields (e.g., no `author.email`, no `keywords`) -- `marketplace.json` exists but entries lack version pins or descriptions -- Mix of `skills/` and legacy `commands/` formats present - -Score 1 when: -- No manifest file but components exist in default locations (`skills/`, `agents/`) -- Manifest exists but is non-standard (wrong path, incomplete schema) -- Only legacy `commands/` format present - -Score 0 when: -- No recognizable plugin structure or manifest -- No `skills/` or `commands/` directories - ---- - -## Category 2: Skill Compatibility - -Score 3 when: -- All skills in `skills//SKILL.md` format with proper frontmatter -- Frontmatter includes: `name`, `description`, `when_to_use` -- No unresolved tool assumptions (tools match Claude Code built-in set: Read, Write, Edit, Bash, Grep, Glob, Skill, Agent/Task, TodoWrite, WebSearch, WebFetch) -- No platform-specific tool references without documentation - -Score 2 when: -- Most skills have frontmatter but 1-2 are missing fields -- One skill references a tool not in Claude Code's built-in set without sidecar documentation -- Some use legacy `commands/` format alongside standard skills - -Score 1 when: -- Skills present but lack consistent frontmatter -- Multiple tool assumptions unresolved -- Heavy reliance on legacy `commands/` format - -Score 0 when: -- No skills found -- Skills are unstructured or lack any frontmatter - ---- - -## Category 3: Context Delivery - -Score 3 when: -- `CLAUDE.md` present at project root with accurate, complete plugin description -- SessionStart hooks configured for always-on context (if needed) -- All skills referenced or contextualized in CLAUDE.md -- Context is up-to-date with current plugin functionality - -Score 2 when: -- `CLAUDE.md` exists but is incomplete or outdated -- Context covers main skills but misses 1-2 secondary ones -- No SessionStart hooks but context is self-contained in CLAUDE.md - -Score 1 when: -- `CLAUDE.md` present but is minimal or inaccurate -- Context only covers one skill or main feature -- No hooks and minimal context documentation - -Score 0 when: -- No CLAUDE.md or context file present -- Context file is empty or unrelated to plugin - ---- - -## Category 4: Hook Portability - -Score 3 when: -- `hooks/hooks.json` uses standard Claude Code event names (SessionStart, PreToolUse, PostToolUse, etc.) -- All scripts in `scripts/` are cross-platform (use `run-hook.cmd` polyglot wrapper or bash with Windows path handling) -- No hardcoded paths with platform-specific separators -- Exit codes and output formats match Claude Code spec - -Score 2 when: -- Hooks exist and use standard event names -- Scripts are mostly cross-platform but lack `run-hook.cmd` wrapper -- Some hardcoded paths or environment variable assumptions -- Output formats mostly match spec - -Score 1 when: -- Hooks exist but are platform-specific (bash-only with no Windows adaptation) -- References to `${CLAUDE_PLUGIN_ROOT}` without env branching -- Non-standard event names or output formats - -Score 0 when: -- No hooks or hooks are unusable outside Claude Code -- Hooks reference unknown or obsolete Claude Code features - ---- - -## Category 5: Tool Mapping - -Score 3 when: -- No sidecar needed (Claude Code is reference platform) -- Tool mappings to other platforms documented in central reference (`lib/references/`) -- All skill tool usage matches Claude Code built-in names -- Per-skill tool assumptions documented if any - -Score 2 when: -- Some skills reference tools clearly used on multiple platforms -- Tool mapping partially documented -- One skill has ambiguous tool usage - -Score 1 when: -- Tool usage is implicit or undocumented -- Skills reference custom or unclear tool names -- No reference material for other platforms - -Score 0 when: -- No tool consistency across skills -- Unresolved platform-specific tool usage - ---- - -## Category 6: Install Readiness - -Score 3 when: -- README documents marketplace install (`/plugin install plugin-name@marketplace-name`) -- README documents local dev install (`claude --plugin-dir ./path`) -- Install instructions include verification steps (list plugins, check hooks) -- Paths match actual repository structure - -Score 2 when: -- Install documented for marketplace or local, but not both -- Instructions exist but lack verification steps -- Minor path discrepancies - -Score 1 when: -- Minimal install documentation -- Instructions don't match actual structure -- No verification steps - -Score 0 when: -- No install documentation -- Instructions are missing or severely inaccurate - ---- - -## Category 7: Runtime Adapters - -Score 3 when: -- `.mcp.json` present with valid MCP server configurations (if needed) -- `agents/` directory populated with frontmatter-compliant agent definitions -- Optional `commands/` legacy format documented as deprecated -- No unsupported frontmatter fields in agents (agents don't use `hooks`, `mcpServers`, `permissionMode`) - -Score 2 when: -- `.mcp.json` present but configuration is incomplete -- `agents/` exists but missing some agents or have incomplete frontmatter -- Mix of supported and unsupported agent configurations - -Score 1 when: -- `.mcp.json` or `agents/` minimal or partially configured -- Agent frontmatter inconsistent or missing key fields -- Deprecated formats still primary - -Score 0 when: -- No runtime configuration files -- `.mcp.json` or `agents/` missing entirely diff --git a/lib/patterns/platforms/claude-code.yaml b/lib/patterns/platforms/claude-code.yaml new file mode 100644 index 0000000..c662087 --- /dev/null +++ b/lib/patterns/platforms/claude-code.yaml @@ -0,0 +1,338 @@ +platform: claude-code +manifest_path: .claude-plugin/plugin.json +marketplace_path: .claude-plugin/marketplace.json +context_files: [CLAUDE.md] +hooks_path: hooks/hooks.json + +categories: + 1_manifest: + conditions: + - id: claude.1_manifest.plugin_json.exists + type: checkable + component: plugin_json + critical: true + points: 1 + check: | + file_exists(".claude-plugin/plugin.json") + template: manifests/claude-plugin/plugin.json.tmpl + + - id: claude.1_manifest.plugin_json.required_fields + type: checkable + component: plugin_json + critical: true + points: 1 + check: | + json = read_json(".claude-plugin/plugin.json") + for field in LOOKUP["manifest_required_fields"]["claude-code"]: + assert field_present(json, field) + # Required: name, version, description, author.name, author.email + template: manifests/claude-plugin/plugin.json.tmpl?merge + + - id: claude.1_manifest.plugin_json.keywords + type: checkable + component: plugin_json + critical: false + points: 1 + check: | + json = read_json(".claude-plugin/plugin.json") + assert is_array(json["keywords"]) and len(json["keywords"]) > 0 + template: manifests/claude-plugin/plugin.json.tmpl?merge + + - id: claude.1_manifest.marketplace_json.exists + type: checkable + component: marketplace_json + critical: true + points: 1 + check: | + file_exists(".claude-plugin/marketplace.json") + template: manifests/claude-plugin/marketplace.json.tmpl + + - id: claude.1_manifest.marketplace_json.valid_entries + type: checkable + component: marketplace_json + critical: false + points: 1 + check: | + entries = read_json(".claude-plugin/marketplace.json") + for entry in entries: + assert has_keys(entry, ["name", "source", "description"]) + template: manifests/claude-plugin/marketplace.json.tmpl?merge + + - id: claude.1_manifest.marketplace_json.source_paths + type: checkable + component: marketplace_json + critical: false + points: 1 + check: | + entries = read_json(".claude-plugin/marketplace.json") + for entry in entries: + assert dir_exists(entry["source"]) + template: manifests/claude-plugin/marketplace.json.tmpl?merge + + 2_skills: + conditions: + - id: claude.2_skills.frontmatter.required_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert has_keys(fm, ["name", "description"]) + + - id: claude.2_skills.frontmatter.no_undocumented_fields + type: checkable + component: frontmatter + critical: false + points: 1 + check: | + known_fields = ["name", "description", "when_to_use", + "disable-model-invocation", "allowed-tools"] + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + for key in fm.keys(): + assert key in known_fields + + - id: claude.2_skills.discovery.standard_path + type: checkable + component: discovery + critical: true + points: 1 + check: | + skills = glob("skills/*/SKILL.md") + assert len(skills) > 0 + # Every SKILL.md must live at skills//SKILL.md + + - id: claude.2_skills.tool_refs.builtin_only + type: judgement + component: tool_refs + critical: true + points: 1 + check: | + builtin = LOOKUP["tool_names"]["claude-code"] + # [Read, Write, Edit, Bash, Grep, Glob, Task, Agent, + # TodoWrite, Skill, WebSearch, WebFetch] + for skill in glob("skills/*/SKILL.md"): + tool_refs = extract_tool_references(skill) + for ref in tool_refs: + assert ref in builtin + + 3_context: + conditions: + - id: claude.3_context.claude_md.exists + type: checkable + component: claude_md + critical: true + points: 1 + check: | + file_exists("CLAUDE.md") + template: context-files/CLAUDE.md.tmpl + + - id: claude.3_context.claude_md.skill_coverage + type: judgement + component: claude_md + critical: false + points: 1 + check: | + skills = glob("skills/*/SKILL.md") + content = read("CLAUDE.md") + for skill in skills: + assert skill_name_referenced(content, skill) + # CLAUDE.md should list or reference every skill + template: context-files/CLAUDE.md.tmpl?merge + + - id: claude.3_context.claude_md.accurate + type: judgement + component: claude_md + critical: false + points: 1 + check: | + content = read("CLAUDE.md") + structure = scan_plugin_structure(".") + assert content_matches_structure(content, structure) + # CLAUDE.md describes the actual plugin layout accurately + + 4_hooks: + conditions: + - id: claude.4_hooks.hooks_json.exists + type: checkable + component: hooks_json + critical: true + points: 1 + check: | + file_exists("hooks/hooks.json") + template: hooks/hooks.json.tmpl + + - id: claude.4_hooks.event_names.pascal_case + type: checkable + component: event_names + critical: true + points: 1 + check: | + hooks = read_json("hooks/hooks.json") + valid_events = LOOKUP["hook_events"]["claude-code"] + # [SessionStart, PreToolUse, PostToolUse, PostToolUseFailure, + # SubagentStart, SubagentStop, PreCompact, Stop, UserPromptSubmit] + for event in hooks.keys(): + assert event in valid_events + + - id: claude.4_hooks.hooks_json.nested_structure + type: checkable + component: hooks_json + critical: false + points: 1 + check: | + hooks = read_json("hooks/hooks.json") + # Structure: { "EventName": { "matcher": ..., "hooks": [...] } } + # or array of { "matcher": ..., "hooks": [...] } + for event, config in hooks.items(): + if is_array(config): + for entry in config: + assert has_keys(entry, ["hooks"]) + else: + assert has_key(config, "hooks") + + - id: claude.4_hooks.scripts.cross_platform + type: checkable + component: scripts + critical: false + points: 1 + check: | + bash_scripts = glob("hooks/scripts/*.sh") + glob("scripts/*.sh") + if len(bash_scripts) > 0: + assert file_exists("hooks/scripts/run-hook.cmd") \ + or file_exists("scripts/run-hook.cmd") + # Windows polyglot wrapper should exist if bash scripts are present + + - id: claude.4_hooks.scripts.no_hardcoded_paths + type: checkable + component: scripts + critical: true + points: 1 + check: | + scripts = glob("hooks/scripts/*") + glob("scripts/*") + for script in scripts: + content = read(script) + assert no_bare_absolute_paths(content) + # Paths should use ${CLAUDE_PLUGIN_ROOT} or equivalent + # env variable branching, not hardcoded /home/... or C:\... + + 5_toolmap: + conditions: + - id: claude.5_toolmap.model_mapping.valid_names + type: checkable + component: model_mapping + critical: true + points: 1 + check: | + valid_models = ["opus", "sonnet", "haiku"] + for agent in glob("agents/*.md"): + fm = parse_frontmatter(agent) + if "model" in fm: + assert fm["model"] in valid_models + + - id: claude.5_toolmap.sidecar.not_needed + type: checkable + component: sidecar + critical: true + points: 1 + check: | + # Claude Code is the reference platform — no sidecar should + # remap Claude tools to anything else. Always passes for + # well-formed plugins; flag if a sidecar exists that rewrites + # Claude-native tool names. + sidecars = glob("**/tool-sidecar.*") + glob("**/toolmap.*") + if len(sidecars) > 0: + for s in sidecars: + content = read(s) + assert not remaps_claude_tools(content) + + 6_install: + conditions: + - id: claude.6_install.install_docs.exists + type: checkable + component: install_docs + critical: true + points: 1 + check: | + readme = find_first(["README.md", "INSTALL.md", "docs/install.md"]) + assert readme is not None + content = read(readme) + assert contains_section(content, "install") + + - id: claude.6_install.install_docs.marketplace_path + type: judgement + component: install_docs + critical: false + points: 1 + check: | + readme = find_first(["README.md", "INSTALL.md", "docs/install.md"]) + content = read(readme) + assert mentions(content, "/plugin install") + # Should document marketplace install path + + - id: claude.6_install.install_docs.local_path + type: judgement + component: install_docs + critical: false + points: 1 + check: | + readme = find_first(["README.md", "INSTALL.md", "docs/install.md"]) + content = read(readme) + assert mentions(content, "claude --plugin-dir") \ + or mentions(content, "--plugin-dir") + # Should document local development install path + + - id: claude.6_install.verification.steps + type: judgement + component: verification + critical: false + points: 1 + check: | + readme = find_first(["README.md", "INSTALL.md", "docs/install.md"]) + content = read(readme) + assert contains_verification_guidance(content) + # Should include steps to verify the plugin loaded correctly + + 7_runtime: + conditions: + - id: claude.7_runtime.mcp.exists + type: checkable + component: mcp + critical: false + points: 1 + check: | + # Only required if plugin uses MCP servers + if uses_mcp_servers("."): + assert file_exists(".mcp.json") + + - id: claude.7_runtime.agents.exists + type: checkable + component: agents + critical: false + points: 1 + check: | + # Only required if plugin defines agents + if has_agent_definitions("."): + agents = glob("agents/*.md") + assert len(agents) > 0 + + - id: claude.7_runtime.agents.frontmatter + type: checkable + component: agents + critical: false + points: 1 + check: | + for agent in glob("agents/*.md"): + fm = parse_frontmatter(agent) + assert has_keys(fm, ["name", "description"]) + + - id: claude.7_runtime.commands.no_legacy + type: checkable + component: commands + critical: false + points: 1 + check: | + assert not dir_exists("commands/") + # commands/ is a deprecated format; skills/ should be used instead diff --git a/lib/patterns/platforms/codex.md b/lib/patterns/platforms/codex.md deleted file mode 100644 index c26f529..0000000 --- a/lib/patterns/platforms/codex.md +++ /dev/null @@ -1,204 +0,0 @@ -# Codex Scoring Rules - -Platform-specific scoring criteria for the 7-category rubric. -See `lib/patterns/rubric-framework.md` for the shared scoring scale. - ---- - -## Artifact Checklist (Skill-Discovery Path) - -| File | Category | -|------|----------| -| `AGENTS.md` | Manifest Packaging | -| `.codex/INSTALL.md` | Install Readiness | -| `AGENTS.md` | Context Delivery | -| Standard SKILL.md | Skill Compatibility | -| `references/codex-tools.md` | Tool Mapping | - ---- - -## Artifact Checklist (Native Plugin Path) - -| File | Category | -|------|----------| -| `.codex-plugin/plugin.json` | Manifest Packaging | -| `.codex-plugin/marketplace.json` | Manifest Packaging | -| `AGENTS.md` | Context Delivery | -| Standard SKILL.md | Skill Compatibility | -| `references/codex-tools.md` | Tool Mapping | - ---- - -## Category 1: Manifest Packaging - -Score 3 when: -- **Skill-discovery path**: `AGENTS.md` present listing all agents/skills, `.codex/INSTALL.md` documents consumption as skill symlink -- **Native plugin path**: `.codex-plugin/plugin.json` present with name, version, description, author; `.codex-plugin/marketplace.json` with complete plugin entries -- Chosen path is explicitly documented -- No ambiguity between paths - -Score 2 when: -- One manifest path present but other path missing or incomplete -- Chosen path documented but with gaps -- Some fields incomplete - -Score 1 when: -- Manifest present but chosen path unclear -- Fields incomplete across both paths -- Limited documentation of consumption method - -Score 0 when: -- Neither manifest path complete -- No clear consumption model documented - ---- - -## Category 2: Skill Compatibility - -Score 3 when: -- Standard SKILL.md format with proper frontmatter -- `references/codex-tools.md` sidecar present documenting Codex tool mapping -- Tool names clear with special handling: - - `spawn_agent` replaces Claude Code `Task` (not `Task` tool name) - - `update_plan` replaces Claude Code `TodoWrite` (not `TodoWrite`) -- Message framing for subagent communication documented -- No unresolved tool assumptions - -Score 2 when: -- Skills properly formatted but sidecar incomplete -- Most tool usage clear but Agent/Plan substitutions partially documented -- Message framing partially explained - -Score 1 when: -- Skills present but sidecar minimal -- Agent/Plan substitutions unclear or undocumented -- Message framing missing - -Score 0 when: -- No skills found -- No sidecar documentation - ---- - -## Category 3: Context Delivery - -Score 3 when: -- `AGENTS.md` present with complete agent, skill, and feature listings -- All agents documented with their dispatch mechanism (spawn_agent vs direct invocation) -- `.codex/INSTALL.md` includes context on consumption path -- Context is accurate and complete -- Multi-agent configuration flag documented if applicable - -Score 2 when: -- `AGENTS.md` exists but missing some listings -- Dispatch mechanisms partially documented -- Context mostly complete with minor gaps - -Score 1 when: -- `AGENTS.md` minimal or sparse -- Limited dispatch documentation -- Context incomplete - -Score 0 when: -- No `AGENTS.md` present -- Context file is empty - ---- - -## Category 4: Hook Portability - -Score 3 when: -- **Plugin path**: `hooks/hooks.json` with standard hook format (if applicable) -- Hooks are portable or Codex-specific with clear adaption path -- Script execution handles multi-agent context -- Documentation clear on hook behavior in Codex - -Score 2 when: -- Hook configuration present but partially complete -- Some hooks portable, others Codex-specific -- Documentation partial - -Score 1 when: -- Minimal hook configuration -- Hooks mostly Codex-specific without adaption guidance -- Documentation limited - -Score 0 when: -- No hooks or hooks unusable on Codex - ---- - -## Category 5: Tool Mapping - -Score 3 when: -- `references/codex-tools.md` thoroughly documents Codex tool mapping: - - Claude Code `Task`/`Agent` -> Codex `spawn_agent` (with worker role specification) - - Claude Code `TodoWrite` -> Codex `update_plan` - - Standard tools (Read, Write, Edit, Bash, Grep, Glob, Skill) mapped to Codex equivalents -- Message framing for multi-agent communication documented -- All skills reviewed for tool consistency -- Mapping complete and clear - -Score 2 when: -- Reference document exists but incomplete mappings -- Most tool substitutions correct but 1-2 unclear -- Message framing partially documented - -Score 1 when: -- Minimal tool mapping documentation -- Agent/Plan substitutions partially unclear -- Limited reference material - -Score 0 when: -- No tool mapping sidecar -- Tool usage unresolved - ---- - -## Category 6: Install Readiness - -Score 3 when: -- **Skill-discovery path**: `.codex/INSTALL.md` documents symlink to `~/.agents/skills/` with step-by-step instructions -- **Native plugin path**: README documents marketplace install with verification steps -- Chosen consumption path explicitly documented and justified -- Paths match actual repository structure -- Verification steps included for chosen path - -Score 2 when: -- Install documented for chosen path but incomplete -- Instructions partially match structure -- Verification steps missing or minimal - -Score 1 when: -- Minimal install documentation -- Instructions incomplete or partially inaccurate -- Chosen path unclear - -Score 0 when: -- No install documentation -- Instructions severely inaccurate or missing - ---- - -## Category 7: Runtime Adapters - -Score 3 when: -- Multi-agent configuration flag documented (if applicable) -- `spawn_agent` message framing clearly explained with examples -- Sandbox/detached-HEAD handling documented if relevant -- Subagent role specification (worker vs other roles) documented -- Runtime behavior under both skill-discovery and plugin paths explained - -Score 2 when: -- Multi-agent configuration partially documented -- Message framing mostly clear but incomplete -- Sandbox/detached-HEAD handling partially explained - -Score 1 when: -- Minimal runtime documentation -- Message framing unclear or incomplete -- Special handling undocumented - -Score 0 when: -- No runtime documentation -- Multi-agent support undocumented diff --git a/lib/patterns/platforms/codex.yaml b/lib/patterns/platforms/codex.yaml new file mode 100644 index 0000000..f9f8f01 --- /dev/null +++ b/lib/patterns/platforms/codex.yaml @@ -0,0 +1,295 @@ +platform: codex +manifest_path: .codex-plugin/plugin.json # native path; skill-discovery uses AGENTS.md +context_files: [AGENTS.md] +hooks_path: null # Codex has no hook system + +categories: + 1_manifest: + conditions: + - id: codex.1_manifest.agents_md.exists + type: checkable + component: agents_md + critical: true + points: 1 + check: | + file_exists("AGENTS.md") + # Required for both skill-discovery and native plugin paths + template: context-files/AGENTS.md.tmpl + + - id: codex.1_manifest.plugin_json.exists + type: checkable + component: plugin_json + critical: false + points: 1 + check: | + # Native plugin path only — not required for skill-discovery + if dir_exists(".codex-plugin/"): + assert file_exists(".codex-plugin/plugin.json"), \ + ".codex-plugin/ dir exists but plugin.json missing" + template: manifests/codex-plugin/plugin.json.tmpl + + - id: codex.1_manifest.plugin_json.required_fields + type: checkable + component: plugin_json + critical: false + points: 1 + check: | + if file_exists(".codex-plugin/plugin.json"): + json = read_json(".codex-plugin/plugin.json") + for field in LOOKUP["manifest_required_fields"]["codex"]: + assert field in json, f"Missing required field: {field}" + # Required: name, version, description (Table 6) + template: manifests/codex-plugin/plugin.json.tmpl?merge + + - id: codex.1_manifest.marketplace_json.exists + type: checkable + component: marketplace_json + critical: false + points: 1 + check: | + # Only for native plugin path with multiple plugins + if file_exists(".codex-plugin/plugin.json"): + if count(glob(".agents/skills/*/SKILL.md")) > 1: + assert file_exists(".codex-plugin/marketplace.json"), \ + "Multi-skill native plugin missing marketplace.json" + template: manifests/codex-plugin/marketplace.json.tmpl + + - id: codex.1_manifest.path_documented + type: judgement + component: agents_md + critical: true + points: 1 + check: | + Chosen consumption path (skill-discovery or native plugin) must be + explicitly documented. Skill-discovery uses AGENTS.md + .codex/INSTALL.md. + Native uses .codex-plugin/plugin.json + .codex-plugin/marketplace.json. + No ambiguity between the two paths. + + 2_skills: + conditions: + - id: codex.2_skills.frontmatter.required_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + for skill in glob(".agents/skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "name" in fm, f"{skill}: missing name" + assert "description" in fm, f"{skill}: missing description" + + - id: codex.2_skills.frontmatter.no_claude_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + # LOOKUP["field_stripping"]["codex"]: strip disable-model-invocation, allowed-tools + for skill in glob(".agents/skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "disable-model-invocation" not in fm, \ + f"{skill}: disable-model-invocation must be stripped for Codex" + assert "allowed-tools" not in fm, \ + f"{skill}: allowed-tools must be stripped for Codex" + + - id: codex.2_skills.discovery.codex_path + type: checkable + component: discovery + critical: true + points: 1 + check: | + assert dir_exists(".agents/skills/") and glob(".agents/skills/*/SKILL.md"), \ + "No skills found in .agents/skills/ — Codex requires this path (Table 8)" + + - id: codex.2_skills.tool_refs.spawn_agent + type: judgement + component: tool_refs + critical: true + points: 1 + check: | + For each SKILL.md that references Task or Agent tools, verify that + the Codex equivalent (spawn_agent) is documented. Claude Code Task/Agent + maps to Codex spawn_agent (LOOKUP Table 2). A references/codex-tools.md + sidecar or inline note must explain this mapping. + + - id: codex.2_skills.tool_refs.update_plan + type: judgement + component: tool_refs + critical: false + points: 1 + check: | + For each SKILL.md that references TodoWrite, verify that the Codex + equivalent (update_plan) is documented. Claude Code TodoWrite maps to + Codex update_plan (LOOKUP Table 2). + + 3_context: + conditions: + - id: codex.3_context.agents_md.skill_coverage + type: judgement + component: agents_md + critical: true + points: 1 + check: | + AGENTS.md should list all skills and agents with names and descriptions. + Verify each skill in .agents/skills/*/SKILL.md is referenced in AGENTS.md. + template: context-files/AGENTS.md.tmpl?merge + + - id: codex.3_context.agents_md.dispatch_documented + type: judgement + component: agents_md + critical: false + points: 1 + check: | + AGENTS.md should document dispatch mechanisms — spawn_agent for + subagent invocation vs direct dispatch. Multi-agent coordination + patterns should be explained if the plugin uses subagents. + + 4_hooks: + # N/A — Codex has no hook system (LOOKUP Table 3: all events map to N/A). + # Hook scripts are copied as standalone utilities only. + conditions: [] + + 5_toolmap: + conditions: + - id: codex.5_toolmap.sidecar.exists + type: checkable + component: sidecar + critical: true + points: 1 + check: | + file_exists("references/codex-tools.md") + # Per-skill or shared sidecar documenting Codex tool mappings + + - id: codex.5_toolmap.sidecar.spawn_agent_mapped + type: checkable + component: sidecar + critical: true + points: 1 + check: | + content = read("references/codex-tools.md") + assert "spawn_agent" in content, \ + "codex-tools.md must document Task/Agent → spawn_agent mapping (Table 2)" + + - id: codex.5_toolmap.sidecar.update_plan_mapped + type: checkable + component: sidecar + critical: false + points: 1 + check: | + content = read("references/codex-tools.md") + assert "update_plan" in content, \ + "codex-tools.md must document TodoWrite → update_plan mapping (Table 2)" + + - id: codex.5_toolmap.model_mapping.no_claude_models + type: checkable + component: model_mapping + critical: true + points: 1 + check: | + # LOOKUP["model_mapping"]["codex"]: opus→gpt-5.4, sonnet/haiku→gpt-5.4-mini + claude_models = ["opus", "sonnet", "haiku"] + for toml in glob(".codex/agents/*.toml"): + content = read(toml) + for model in claude_models: + assert model not in content.lower(), \ + f"{toml}: Claude model name '{model}' leaked — use gpt-5.4 or gpt-5.4-mini" + for skill in glob(".agents/skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + if "model" in fm: + assert fm["model"].lower() not in claude_models, \ + f"{skill}: Claude model name leaked — use gpt-5.4 or gpt-5.4-mini" + + - id: codex.5_toolmap.model_mapping.codex_names + type: checkable + component: model_mapping + critical: false + points: 1 + check: | + valid_models = ["gpt-5.4", "gpt-5.4-mini"] + for toml in glob(".codex/agents/*.toml"): + content = read(toml) + # model field in TOML: model = "gpt-5.4" + if 'model' in content: + model_val = parse_toml_field(toml, "model") + assert model_val in valid_models, \ + f"{toml}: model '{model_val}' not in Codex model set {valid_models} (Table 1)" + + - id: codex.5_toolmap.subagent_syntax.message_framing + type: judgement + component: subagent_syntax + critical: false + points: 1 + check: | + If any skills or agents use spawn_agent for multi-agent communication, + verify that message framing conventions are documented — how to structure + the prompt passed to spawn_agent, worker role specification, and + result handling patterns. + + 6_install: + conditions: + - id: codex.6_install.install_docs.exists + type: checkable + component: install_docs + critical: true + points: 1 + check: | + readme = find_file(["README.md", "INSTALL.md", ".codex/INSTALL.md"]) + assert readme is not None, "No README.md, INSTALL.md, or .codex/INSTALL.md found" + content = read(readme) + assert "install" in content.lower(), "Install section missing from documentation" + + - id: codex.6_install.install_docs.path_documented + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Install documentation should describe the chosen consumption path: + - Skill-discovery: symlink to ~/.agents/skills/ with step-by-step instructions + - Native plugin: marketplace install via .codex-plugin/ artifacts + Chosen path must match repository structure. + + - id: codex.6_install.verification.steps + type: judgement + component: verification + critical: false + points: 1 + check: | + Install documentation should include verification steps to confirm + the plugin is loaded and working after install — e.g. checking that + skills appear in agent discovery or that AGENTS.md is picked up. + + 7_runtime: + conditions: + - id: codex.7_runtime.agents.toml_exists + type: checkable + component: agents + critical: false + points: 1 + check: | + # .codex/agents/*.toml only required if plugin defines agents + if dir_exists(".codex/agents/"): + assert glob(".codex/agents/*.toml"), \ + ".codex/agents/ directory exists but contains no .toml files" + + - id: codex.7_runtime.agents.toml_fields + type: checkable + component: agents + critical: false + points: 1 + check: | + for toml in glob(".codex/agents/*.toml"): + data = parse_toml(toml) + for field in ["name", "description", "model", "developer_instructions"]: + assert field in data, f"{toml}: missing required TOML field '{field}'" + # developer_instructions uses triple-quote multiline strings in TOML + + - id: codex.7_runtime.subagents.worker_role + type: judgement + component: subagents + critical: false + points: 1 + check: | + If agents use spawn_agent for subagent delegation, verify that + worker role specification is documented — what role each subagent + plays and how it reports results back to the orchestrator. diff --git a/lib/patterns/platforms/copilot-cli.md b/lib/patterns/platforms/copilot-cli.md deleted file mode 100644 index c3bb694..0000000 --- a/lib/patterns/platforms/copilot-cli.md +++ /dev/null @@ -1,193 +0,0 @@ -# Copilot CLI Scoring Rules - -Platform-specific scoring criteria for the 7-category rubric. -See `lib/patterns/rubric-framework.md` for the shared scoring scale. - ---- - -## Artifact Checklist - -| File | Category | -|------|----------| -| `.github/copilot-instructions.md` | Manifest Packaging | -| `.github/instructions/*.instructions.md` | Context Delivery | -| `.github/agents/*.agent.md` | Runtime Adapters | -| `.github/hooks/*.json` | Hook Portability | -| `.github/skills/` | Skill Compatibility | -| `references/copilot-tools.md` | Tool Mapping | - ---- - -## Category 1: Manifest Packaging - -Score 3 when: -- `.github/copilot-instructions.md` present with complete repo-wide instructions -- Platform context files (AGENTS.md, CLAUDE.md, GEMINI.md) recognized and read -- Skills discoverable from `.github/skills//SKILL.md` or cross-platform `skills/` -- Sidecar `references/copilot-tools.md` documents tool mapping - -Score 2 when: -- `.github/copilot-instructions.md` exists but incomplete -- Some context files recognized but not all -- Skills discoverable but sidecar incomplete - -Score 1 when: -- Minimal or sparse Copilot instructions -- Limited context file recognition -- Sidecar missing or minimal - -Score 0 when: -- No `.github/copilot-instructions.md` -- No recognizable Copilot structure - ---- - -## Category 2: Skill Compatibility - -Score 3 when: -- Standard SKILL.md format with proper frontmatter -- Skills discoverable from `.github/skills//SKILL.md` or cross-platform `skills/` -- `references/copilot-tools.md` sidecar present documenting Copilot tool mapping -- Tool usage clear with no unresolved assumptions - -Score 2 when: -- Skills properly formatted but discovery paths inconsistent -- Sidecar exists but incomplete mapping -- Most tool usage clear but 1-2 references unclear - -Score 1 when: -- Skills present but with inconsistent frontmatter -- Sidecar minimal or missing -- Tool usage partially unclear - -Score 0 when: -- No skills found -- No sidecar documentation - ---- - -## Category 3: Context Delivery - -Score 3 when: -- `.github/copilot-instructions.md` repo-wide instructions complete -- Path-specific `.github/instructions/*.instructions.md` present for subdivisions -- Reads AGENTS.md, CLAUDE.md, GEMINI.md as available context -- All skills referenced with context requirements -- Context accurate and up-to-date - -Score 2 when: -- Repo-wide instructions present but incomplete -- Path-specific instructions partially populated -- Context mostly covers main skills -- Minor gaps or outdated sections - -Score 1 when: -- Minimal repo-wide instructions -- Limited or no path-specific instructions -- Context sparse or incomplete - -Score 0 when: -- No `.github/copilot-instructions.md` -- No context structure present - ---- - -## Category 4: Hook Portability - -Score 3 when: -- `.github/hooks/*.json` files present with correct format -- Separate `bash` and `powershell` fields (no `command` field) -- No `matcher` field (Copilot uses `preToolUse` blocks only) -- Event names match Copilot spec (`preToolUse`) -- Cross-platform scripts with proper shell selection - -Score 2 when: -- Hook files present but mixing `command` and platform-specific fields -- Mostly correct event names with minor inconsistencies -- Scripts mostly portable - -Score 1 when: -- Minimal hook configuration -- Event names partially incorrect -- Scripts platform-specific without clear adaption - -Score 0 when: -- No hook files present -- Hooks unusable on Copilot - ---- - -## Category 5: Tool Mapping - -Score 3 when: -- `references/copilot-tools.md` thoroughly documents Copilot tool mapping: - - Claude Code `Read` -> Copilot `view` - - Claude Code `Write` -> Copilot `create` - - Claude Code `Edit` -> Copilot `edit` (or `apply_patch` for patches) - - Claude Code `Bash` -> Copilot `bash` or `powershell` -- All skills reviewed for tool consistency -- Mapping complete and clear - -Score 2 when: -- Reference document exists but incomplete mappings -- Most tools correctly mapped but 1-2 unclear -- Limited documentation - -Score 1 when: -- Minimal tool mapping documentation -- Some tool usage implicit -- Limited reference material - -Score 0 when: -- No tool mapping sidecar -- Tool usage unresolved - ---- - -## Category 6: Install Readiness - -Score 3 when: -- README documents `gh skill install` command -- Auto-discovery from `skills/` directory documented -- Local install to `~/.copilot/skills/` documented -- Verification steps included -- Paths match actual repository structure - -Score 2 when: -- Install documented for one method (gh command or local) -- Instructions lack verification steps -- Minor path discrepancies - -Score 1 when: -- Minimal install documentation -- Instructions incomplete or partially inaccurate -- No verification guidance - -Score 0 when: -- No install documentation -- Instructions severely inaccurate - ---- - -## Category 7: Runtime Adapters - -Score 3 when: -- `.github/agents/*.agent.md` present with mandatory `description` field plus name -- `tools` allowlist documented per agent -- `target` field properly specified -- `.github/hooks/*.json` with proper bash/powershell separation -- All adapters follow Copilot conventions - -Score 2 when: -- `agents/` present but missing `description` on some agents -- `tools` allowlist partially defined -- Hooks present but incomplete platform coverage - -Score 1 when: -- Minimal agent definitions -- `description` field missing or inconsistent -- Hooks incomplete or partially configured - -Score 0 when: -- No agents or hooks present -- Runtime configuration missing diff --git a/lib/patterns/platforms/cursor.md b/lib/patterns/platforms/cursor.md deleted file mode 100644 index 6881b54..0000000 --- a/lib/patterns/platforms/cursor.md +++ /dev/null @@ -1,186 +0,0 @@ -# Cursor Scoring Rules - -Platform-specific scoring criteria for the 7-category rubric. -See `lib/patterns/rubric-framework.md` for the shared scoring scale. - ---- - -## Artifact Checklist - -| File | Category | -|------|----------| -| `.cursor-plugin/plugin.json` | Manifest Packaging | -| `.cursor/rules/*.mdc` | Context Delivery | -| `hooks/hooks-cursor.json` | Hook Portability | -| `mcp.json` | Runtime Adapters | -| `AGENTS.md` | Context Delivery | - ---- - -## Category 1: Manifest Packaging - -Score 3 when: -- `.cursor-plugin/plugin.json` present with `name`, `displayName`, `description`, `version`, `author` -- `logo` field present for marketplace visibility -- Conditional `agents` and `commands` keys only when applicable -- Skills discoverable from `.cursor/skills/`, `.agents/skills/`, or `skills/` directories -- One-way compatibility with Claude Code (`.claude/skills/` recognized) - -Score 2 when: -- `plugin.json` exists but missing `displayName` or `logo` -- `agents`/`commands` fields present but not conditional -- Skills mix multiple discovery paths inconsistently - -Score 1 when: -- No manifest file but components in default locations -- Manifest exists but non-standard or incomplete -- Only legacy formats discoverable - -Score 0 when: -- No recognizable Cursor plugin structure -- No manifest or skills directories - ---- - -## Category 2: Skill Compatibility - -Score 3 when: -- Standard SKILL.md format with proper frontmatter (name, description, when_to_use) -- Skills discoverable from `.cursor/skills/`, `.agents/skills/`, `skills/`, and `.claude/skills/` (compat) -- No unresolved tool assumptions -- Tools match Cursor's built-in set (same as Claude Code plus Cursor-specific enhancements) - -Score 2 when: -- Most skills properly formatted but 1-2 missing frontmatter fields -- Skills discoverable but in inconsistent locations -- Minimal one-way Claude Code compatibility - -Score 1 when: -- Skills present but with inconsistent frontmatter -- Discovery paths partially documented -- Limited platform documentation - -Score 0 when: -- No skills found -- Skills lack frontmatter or are unstructured - ---- - -## Category 3: Context Delivery - -Score 3 when: -- `.cursor/rules/*.mdc` files present with complete rule definitions -- Activation modes documented (per-file context activation) -- `AGENTS.md` present listing all agents and skills -- Supplemental `CLAUDE.md` if needed for non-Cursor context -- All skills referenced with their context requirements - -Score 2 when: -- `.cursor/rules/` present but incomplete rule definitions -- `AGENTS.md` exists but missing some skill listings -- Context partially documented across multiple files - -Score 1 when: -- Minimal rule files in `.cursor/rules/` -- `AGENTS.md` missing or bare -- Context documentation sparse - -Score 0 when: -- No context files present -- `.cursor/rules/` and `AGENTS.md` both missing - ---- - -## Category 4: Hook Portability - -Score 3 when: -- `hooks/hooks-cursor.json` uses camelCase event names (not snake_case) -- `additional_context` output uses snake_case (per Cursor spec) -- All scripts are cross-platform or Cursor-specific -- Hook output matches Cursor's expected format - -Score 2 when: -- `hooks/hooks-cursor.json` present with mostly correct format -- Minor case inconsistencies in event or output names -- Scripts mostly portable - -Score 1 when: -- Hook file present but inconsistent formatting -- Event names partially incorrect -- Scripts are Cursor-specific without porting guide - -Score 0 when: -- No hook file present -- Hooks unusable on Cursor platform - ---- - -## Category 5: Tool Mapping - -Score 3 when: -- Tool names documented for Cursor (reference `lib/references/`) -- All skills use Cursor-standard tool names -- Compat note if importing from Claude Code skills -- Per-skill tool assumptions documented - -Score 2 when: -- Tool names mostly clear from context -- Some skills have undocumented tool usage -- Partial compat documentation - -Score 1 when: -- Tool usage implicit or minimal documentation -- Unclear if tools translate from Claude Code -- No reference material - -Score 0 when: -- No tool consistency across skills -- Tool mapping missing - ---- - -## Category 6: Install Readiness - -Score 3 when: -- README documents Cursor marketplace install -- Local install to `~/.cursor/plugins/local//` documented -- Verification steps included (restart requirement noted) -- Paths match actual repository structure - -Score 2 when: -- Install documented for one method (marketplace or local) -- Instructions lack verification or restart guidance -- Minor path discrepancies - -Score 1 when: -- Minimal install documentation -- Instructions incomplete or partially inaccurate -- No guidance on restart requirement - -Score 0 when: -- No install documentation -- Instructions severely inaccurate - ---- - -## Category 7: Runtime Adapters - -Score 3 when: -- `mcp.json` (not `.mcp.json`) present with valid MCP configurations (if needed) -- Note: Cursor does NOT support MCP Resources -- `agents/` directory with complete frontmatter (no unsupported fields) -- Optional `commands/` documented if present - -Score 2 when: -- `mcp.json` present but incomplete configuration -- `agents/` exists with mostly complete frontmatter -- Mix of supported and unsupported configurations - -Score 1 when: -- `mcp.json` or `agents/` minimal or partially configured -- Agent frontmatter inconsistent -- Deprecated formats still used - -Score 0 when: -- No `mcp.json` or `agents/` present -- Runtime configuration missing diff --git a/lib/patterns/platforms/cursor.yaml b/lib/patterns/platforms/cursor.yaml new file mode 100644 index 0000000..044cd1f --- /dev/null +++ b/lib/patterns/platforms/cursor.yaml @@ -0,0 +1,356 @@ +platform: cursor +manifest_path: .cursor-plugin/plugin.json +marketplace_path: .cursor-plugin/marketplace.json +context_files: [AGENTS.md, ".cursor/rules/*.mdc"] +hooks_path: hooks/hooks-cursor.json + +categories: + 1_manifest: + conditions: + - id: cursor.1_manifest.plugin_json.exists + type: checkable + component: plugin_json + critical: true + points: 1 + check: | + file_exists(".cursor-plugin/plugin.json") + template: manifests/cursor-plugin/plugin.json.tmpl + + - id: cursor.1_manifest.plugin_json.required_fields + type: checkable + component: plugin_json + critical: true + points: 1 + check: | + json = read_json(".cursor-plugin/plugin.json") + for field in LOOKUP["manifest_required_fields"]["cursor"]: + assert field in json, f"Missing required field: {field}" + # Required: name, displayName, description, version, author + template: manifests/cursor-plugin/plugin.json.tmpl?merge + + - id: cursor.1_manifest.plugin_json.conditional_keys + type: checkable + component: plugin_json + critical: false + points: 1 + check: | + json = read_json(".cursor-plugin/plugin.json") + if "agents" in json: + assert dir_exists("agents/"), "agents key present but agents/ dir missing" + if "commands" in json: + assert dir_exists("commands/"), "commands key present but commands/ dir missing" + if "hooks" in json: + assert file_exists("hooks/hooks-cursor.json"), "hooks key present but hooks file missing" + template: manifests/cursor-plugin/plugin.json.tmpl?merge + + - id: cursor.1_manifest.plugin_json.logo + type: checkable + component: plugin_json + critical: false + points: 1 + check: | + json = read_json(".cursor-plugin/plugin.json") + if "logo" in json: + assert file_exists(json["logo"]), "logo field references non-existent file" + template: manifests/cursor-plugin/plugin.json.tmpl?merge + + - id: cursor.1_manifest.marketplace_json.exists + type: checkable + component: marketplace_json + critical: false + points: 1 + check: | + # Only required for multi-plugin repos + if count(glob("skills/*/SKILL.md")) > 1 or dir_exists(".cursor-plugin/plugins/"): + assert file_exists(".cursor-plugin/marketplace.json"), "Multi-plugin repo missing marketplace.json" + template: manifests/cursor-plugin/marketplace.json.tmpl + + 2_skills: + conditions: + - id: cursor.2_skills.frontmatter.required_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "name" in fm, f"{skill}: missing name" + assert "description" in fm, f"{skill}: missing description" + + - id: cursor.2_skills.frontmatter.no_claude_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + # LOOKUP["field_stripping"]["cursor"]: strip allowed-tools, keep disable-model-invocation + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "allowed-tools" not in fm, f"{skill}: allowed-tools must be stripped for Cursor" + # disable-model-invocation is OK — Cursor supports it natively + + - id: cursor.2_skills.discovery.multi_path + type: checkable + component: discovery + critical: false + points: 1 + check: | + assert dir_exists("skills/") and glob("skills/*/SKILL.md"), "No skills in skills/ directory" + # Bonus: compat paths like .cursor/skills/ or .agents/skills/ also discoverable + + - id: cursor.2_skills.tool_refs.no_unresolved + type: judgement + component: tool_refs + critical: true + points: 1 + check: | + For each SKILL.md, verify that tool references match Cursor built-ins + (Read, Write, Edit, Bash, Grep, Glob, Task, Agent, TodoWrite, Skill) + or have a sidecar MCP server providing the tool. + Flag any unresolved tool names that assume Claude-only tools. + + 3_context: + conditions: + - id: cursor.3_context.agents_md.exists + type: checkable + component: agents_md + critical: true + points: 1 + check: | + file_exists("AGENTS.md") + template: context-files/AGENTS.md.tmpl + + - id: cursor.3_context.agents_md.skill_coverage + type: judgement + component: agents_md + critical: false + points: 1 + check: | + AGENTS.md should list all skills with their name and description. + Verify each skill in skills/*/SKILL.md is referenced in AGENTS.md. + template: context-files/AGENTS.md.tmpl?merge + + - id: cursor.3_context.rules_mdc.exists + type: checkable + component: rules_mdc + critical: false + points: 1 + check: | + glob(".cursor/rules/*.mdc") is non-empty + + - id: cursor.3_context.rules_mdc.frontmatter + type: checkable + component: rules_mdc + critical: false + points: 1 + check: | + for mdc in glob(".cursor/rules/*.mdc"): + fm = parse_frontmatter(mdc) + assert "description" in fm, f"{mdc}: missing description in frontmatter" + assert fm.get("alwaysApply") == true, f"{mdc}: alwaysApply should be true" + + 4_hooks: + conditions: + - id: cursor.4_hooks.hooks_json.exists + type: checkable + component: hooks_json + critical: true + points: 1 + check: | + file_exists("hooks/hooks-cursor.json") + template: hooks/hooks-cursor.json.tmpl + + - id: cursor.4_hooks.event_names.camelcase + type: checkable + component: event_names + critical: true + points: 1 + check: | + hooks = read_json("hooks/hooks-cursor.json") + valid_events = LOOKUP["hook_events"]["cursor"] + # Valid: sessionStart, preToolUse, postToolUse, postToolUseFailure, + # subagentStart, subagentStop, preCompact, stop, beforeSubmitPrompt + for entry in hooks: + assert entry["event"] in valid_events, f"Invalid event: {entry['event']} — must be camelCase" + + - id: cursor.4_hooks.hooks_json.flat_structure + type: checkable + component: hooks_json + critical: true + points: 1 + check: | + hooks = read_json("hooks/hooks-cursor.json") + for entry in hooks: + assert "hooks" not in entry, "Nested hooks[] array found — Cursor uses flat structure" + # Each entry should have event, matcher, command at top level + + - id: cursor.4_hooks.scripts.no_claude_paths + type: checkable + component: scripts + critical: true + points: 1 + check: | + for script in glob("hooks/scripts/*"): + content = read(script) + if "${CLAUDE_PLUGIN_ROOT}" in content: + assert "${CURSOR_PLUGIN_ROOT}" in content, \ + f"{script}: bare ${{CLAUDE_PLUGIN_ROOT}} without ${{CURSOR_PLUGIN_ROOT}} branching" + # LOOKUP["path_variables"]["cursor"]: ${CURSOR_PLUGIN_ROOT} + + - id: cursor.4_hooks.scripts.cross_platform + type: checkable + component: scripts + critical: false + points: 1 + check: | + # Windows support: run-hook.cmd exists alongside shell scripts + if glob("hooks/scripts/*.sh"): + assert glob("hooks/scripts/*.cmd") or glob("hooks/scripts/*.ps1"), \ + "No Windows hook scripts (run-hook.cmd) found" + + - id: cursor.4_hooks.output_format.snake_case + type: checkable + component: output_format + critical: false + points: 1 + check: | + # LOOKUP["hook_format_rules"]["cursor"]: output key is additional_context + for script in glob("hooks/scripts/*"): + content = read(script) + if "hookSpecificOutput" in content or "additionalContext" in content: + fail(f"{script}: use additional_context (snake_case) not hookSpecificOutput.additionalContext") + + 5_toolmap: + conditions: + - id: cursor.5_toolmap.model_mapping.no_claude_models + type: checkable + component: model_mapping + critical: true + points: 1 + check: | + # LOOKUP["model_mapping"]["cursor"]: all models map to "inherit" + claude_models = ["opus", "sonnet", "haiku"] + for md in glob("agents/*.md") + glob("skills/*/SKILL.md"): + fm = parse_frontmatter(md) + if "model" in fm: + assert fm["model"].lower() not in claude_models, \ + f"{md}: Claude model name '{fm['model']}' leaked — use 'inherit'" + + - id: cursor.5_toolmap.model_mapping.inherit + type: checkable + component: model_mapping + critical: false + points: 1 + check: | + for md in glob("agents/*.md"): + fm = parse_frontmatter(md) + if "model" in fm: + assert fm["model"] == "inherit", \ + f"{md}: model should be 'inherit' for Cursor (LOOKUP Table 1)" + + - id: cursor.5_toolmap.subagent_syntax.documented + type: judgement + component: subagent_syntax + critical: false + points: 1 + check: | + If any skills use Task or Agent tool invocations, verify that + Cursor-compatible invocation patterns are documented. + Cursor uses the same Task/Agent syntax as Claude Code (Table 2). + + 6_install: + conditions: + - id: cursor.6_install.install_docs.exists + type: checkable + component: install_docs + critical: true + points: 1 + check: | + readme = find_file(["README.md", "INSTALL.md"]) + assert readme is not None, "No README.md or INSTALL.md found" + content = read(readme) + assert "install" in content.lower(), "Install section missing from documentation" + + - id: cursor.6_install.install_docs.marketplace_path + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Documentation should mention Cursor marketplace install via + /add-plugin owner/repo command. + + - id: cursor.6_install.install_docs.local_path + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Documentation should mention local install path: + ~/.cursor/plugins/local// + and note that Cursor restart is required. + + - id: cursor.6_install.verification.steps + type: judgement + component: verification + critical: false + points: 1 + check: | + Install documentation should include verification steps + to confirm the plugin is loaded and working after install. + + 7_runtime: + conditions: + - id: cursor.7_runtime.mcp.exists + type: checkable + component: mcp + critical: false + points: 1 + check: | + # Cursor uses mcp.json (no dot prefix) + if file_exists(".mcp.json"): + assert file_exists("mcp.json"), \ + ".mcp.json exists but mcp.json (no dot prefix) missing for Cursor" + + - id: cursor.7_runtime.mcp.no_resources + type: checkable + component: mcp + critical: false + points: 1 + check: | + if file_exists("mcp.json"): + config = read_json("mcp.json") + for server_name, server in config.get("mcpServers", {}).items(): + assert "resources" not in server, \ + f"MCP server '{server_name}': Cursor does not support MCP Resources" + + - id: cursor.7_runtime.agents.exists + type: checkable + component: agents + critical: false + points: 1 + check: | + if dir_exists("agents/"): + assert glob("agents/*.md"), "agents/ directory exists but contains no .md files" + + - id: cursor.7_runtime.agents.model_inherit + type: checkable + component: agents + critical: false + points: 1 + check: | + for md in glob("agents/*.md"): + fm = parse_frontmatter(md) + if "model" in fm: + assert fm["model"] == "inherit", \ + f"{md}: agent model should be 'inherit' for Cursor" + + - id: cursor.7_runtime.commands.optional + type: checkable + component: commands + critical: false + points: 1 + check: | + if dir_exists("commands/"): + assert glob("commands/*"), "commands/ directory exists but is empty" diff --git a/lib/patterns/platforms/gemini-cli.md b/lib/patterns/platforms/gemini-cli.md deleted file mode 100644 index 87b354c..0000000 --- a/lib/patterns/platforms/gemini-cli.md +++ /dev/null @@ -1,191 +0,0 @@ -# Gemini CLI Scoring Rules - -Platform-specific scoring criteria for the 7-category rubric. -See `lib/patterns/rubric-framework.md` for the shared scoring scale. - ---- - -## Artifact Checklist - -| File | Category | -|------|----------| -| `gemini-extension.json` | Manifest Packaging | -| `GEMINI.md` | Context Delivery | -| `agents/*.md` | Runtime Adapters | -| `commands/*.toml` | Runtime Adapters | -| `references/gemini-tools.md` | Tool Mapping | - ---- - -## Category 1: Manifest Packaging - -Score 3 when: -- `gemini-extension.json` present with `name` (lowercase dashes only, matches directory name), `version`, `description`, `contextFileName` -- Author, homepage, repository fields populated -- No forbidden characters in `name` field - -Score 2 when: -- `gemini-extension.json` present but missing `contextFileName` or author info -- `name` field has minor formatting issues -- Version or description incomplete - -Score 1 when: -- Manifest exists but non-standard or incomplete schema -- Missing critical fields like `contextFileName` -- `name` field has major formatting issues - -Score 0 when: -- No manifest file present -- Manifest cannot be parsed as valid JSON - ---- - -## Category 2: Skill Compatibility - -Score 3 when: -- Standard SKILL.md format with proper frontmatter -- `references/gemini-tools.md` sidecar present documenting Gemini tool mapping -- Skills flag `Task`/`Agent` tool usage (Gemini uses `@agent-name` syntax, not `Task`) -- All tool references resolvable to Gemini equivalents - -Score 2 when: -- Skills properly formatted but sidecar incomplete -- Most tool usage clear but 1-2 unresolved references -- Agent/Task usage partially documented - -Score 1 when: -- Skills present but sidecar minimal or missing -- Tool mapping sparse or unclear -- Limited guidance on `@` agent syntax - -Score 0 when: -- No skills found -- No sidecar documentation -- Unresolved tool references - ---- - -## Category 3: Context Delivery - -Score 3 when: -- `GEMINI.md` present with complete descriptions -- `@` includes for every skill in both SKILL.md and gemini-tools.md -- All agent references marked with `@` syntax -- Context up-to-date with current functionality - -Score 2 when: -- `GEMINI.md` exists but missing includes for some skills -- Most `@` includes present but incomplete coverage -- Context mostly up-to-date - -Score 1 when: -- `GEMINI.md` minimal or sparse -- `@` includes only for primary skills -- Context incomplete or outdated - -Score 0 when: -- No `GEMINI.md` present -- Context file is empty - ---- - -## Category 4: Hook Portability - -Score 3 when: -- Hooks documented in user `settings.json` (not standalone file) -- Event names correct: `BeforeTool`, `AfterTool` (camelCase) -- Guidance on tool checks included -- Built-in `gemini hooks migrate --from-claude` guidance provided -- Scripts are portable or Gemini-specific with clear adaption path - -Score 2 when: -- Hook configuration in settings.json but incomplete -- Event names mostly correct -- Some guidance provided but incomplete - -Score 1 when: -- Minimal hook documentation -- Event names partially incorrect -- Scripts Gemini-specific without adaption guidance - -Score 0 when: -- No hook configuration -- Hooks unusable on Gemini - ---- - -## Category 5: Tool Mapping - -Score 3 when: -- `references/gemini-tools.md` thoroughly documents mapping: - - Claude Code `Read` -> Gemini `read_file` - - Claude Code `Edit` -> Gemini `replace` - - Claude Code `Write` -> Gemini `write_file` - - Claude Code `Grep` -> Gemini `grep_search` - - Claude Code `Bash` -> Gemini `run_shell_command` -- Note: Gemini does NOT have `Task` tool (use `@agent-name` instead) -- All skills reviewed for tool usage consistency - -Score 2 when: -- Reference document exists but incomplete mappings -- Most tools correctly mapped but 1-2 unclear -- Limited `@agent-name` syntax guidance - -Score 1 when: -- Minimal tool mapping documentation -- Unclear how Task/Agent should translate -- Limited reference material - -Score 0 when: -- No tool mapping sidecar -- Tool usage unresolved - ---- - -## Category 6: Install Readiness - -Score 3 when: -- README documents `gemini extensions install` from GitHub -- Local install from filesystem documented with path -- Three tiers explained: system, user, project scope -- Verification steps included (restart requirement noted) -- Paths match actual repository structure - -Score 2 when: -- Install documented for GitHub or local, not both -- Instructions lack scope or restart guidance -- Minor path discrepancies - -Score 1 when: -- Minimal install documentation -- Instructions incomplete or partially inaccurate -- No restart guidance - -Score 0 when: -- No install documentation -- Instructions severely inaccurate - ---- - -## Category 7: Runtime Adapters - -Score 3 when: -- `agents/*.md` files present with mandatory frontmatter (description required, plus name, tools, etc.) -- `commands/*.toml` files properly formatted if commands used -- `policies/*.toml` if policies defined -- MCP configuration documented in manifest if applicable -- All adapters follow Gemini conventions (camelCase for field names where applicable) - -Score 2 when: -- `agents/` or `commands/` present but incomplete frontmatter -- `.toml` files present but missing some required fields -- MCP partially documented - -Score 1 when: -- Minimal agent or command definitions -- Frontmatter incomplete or inconsistent -- MCP undocumented - -Score 0 when: -- No agents or commands present -- No runtime configuration found diff --git a/lib/patterns/platforms/gemini-cli.yaml b/lib/patterns/platforms/gemini-cli.yaml new file mode 100644 index 0000000..4e21859 --- /dev/null +++ b/lib/patterns/platforms/gemini-cli.yaml @@ -0,0 +1,368 @@ +platform: gemini-cli +manifest_path: gemini-extension.json +context_files: [GEMINI.md] +hooks_path: null # settings.json (user-configured, not file-based) + +categories: + 1_manifest: + conditions: + - id: gemini.1_manifest.extension_json.exists + type: checkable + component: extension_json + critical: true + points: 1 + check: | + file_exists("gemini-extension.json") + template: manifests/gemini-extension.json.tmpl + + - id: gemini.1_manifest.extension_json.required_fields + type: checkable + component: extension_json + critical: true + points: 1 + check: | + json = read_json("gemini-extension.json") + for field in LOOKUP["manifest_required_fields"]["gemini"]: + assert field in json, f"Missing required field: {field}" + # Required: name, version, description, contextFileName + template: manifests/gemini-extension.json.tmpl?merge + + - id: gemini.1_manifest.extension_json.name_format + type: checkable + component: extension_json + critical: true + points: 1 + check: | + json = read_json("gemini-extension.json") + name = json.get("name", "") + assert re.match(r"^[a-z][a-z0-9-]*$", name), \ + f"name '{name}' must be lowercase-dashes-only" + dir_name = basename(project_root()) + assert name == dir_name, \ + f"name '{name}' must match directory name '{dir_name}'" + template: manifests/gemini-extension.json.tmpl?merge + + - id: gemini.1_manifest.extension_json.context_filename + type: checkable + component: extension_json + critical: true + points: 1 + check: | + json = read_json("gemini-extension.json") + assert json.get("contextFileName") == "GEMINI.md", \ + f"contextFileName must be 'GEMINI.md', got '{json.get('contextFileName')}'" + template: manifests/gemini-extension.json.tmpl?merge + + - id: gemini.1_manifest.extension_json.author + type: checkable + component: extension_json + critical: false + points: 1 + check: | + json = read_json("gemini-extension.json") + assert json.get("author"), "author field not populated" + assert json.get("homepage"), "homepage field not populated" + template: manifests/gemini-extension.json.tmpl?merge + + 2_skills: + conditions: + - id: gemini.2_skills.frontmatter.required_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "name" in fm, f"{skill}: missing name" + assert "description" in fm, f"{skill}: missing description" + + - id: gemini.2_skills.frontmatter.no_claude_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + # LOOKUP["field_stripping"]["gemini"]: strip both fields + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "disable-model-invocation" not in fm, \ + f"{skill}: disable-model-invocation must be stripped for Gemini" + assert "allowed-tools" not in fm, \ + f"{skill}: allowed-tools must be stripped for Gemini" + + - id: gemini.2_skills.discovery.standard_path + type: checkable + component: discovery + critical: true + points: 1 + check: | + assert dir_exists("skills/") and glob("skills/*/SKILL.md"), \ + "No skills in skills/*/SKILL.md" + + - id: gemini.2_skills.tool_refs.no_task_tool + type: judgement + component: tool_refs + critical: true + points: 1 + check: | + For each SKILL.md, verify there are no references to Task or Agent + tools. Gemini does not have a Task tool — it uses @agent-name syntax + instead (LOOKUP["tool_mapping"]["gemini"]["Task"] = "@agent-name"). + Flag any raw Task/Agent tool invocations. + + 3_context: + conditions: + - id: gemini.3_context.gemini_md.exists + type: checkable + component: gemini_md + critical: true + points: 1 + check: | + file_exists("GEMINI.md") + template: context-files/GEMINI.md.tmpl + + - id: gemini.3_context.gemini_md.at_includes_skills + type: checkable + component: gemini_md + critical: true + points: 1 + check: | + content = read("GEMINI.md") + for skill_dir in glob_dirs("skills/*/"): + name = basename(skill_dir) + expected = f"@./skills/{name}/SKILL.md" + assert expected in content, \ + f"GEMINI.md missing @ include for {expected}" + template: context-files/GEMINI.md.tmpl?merge + + - id: gemini.3_context.gemini_md.at_includes_sidecars + type: checkable + component: gemini_md + critical: true + points: 1 + check: | + content = read("GEMINI.md") + for skill_dir in glob_dirs("skills/*/"): + name = basename(skill_dir) + expected = f"@./skills/{name}/references/gemini-tools.md" + assert expected in content, \ + f"GEMINI.md missing @ include for sidecar {expected}" + template: context-files/GEMINI.md.tmpl?merge + + - id: gemini.3_context.gemini_md.accurate + type: judgement + component: gemini_md + critical: false + points: 1 + check: | + Verify GEMINI.md content is up-to-date and complete. + All @ includes should reference existing files. + Description and usage guidance should match current functionality. + + 4_hooks: + conditions: + - id: gemini.4_hooks.hooks_json.guidance_present + type: judgement + component: hooks_json + critical: true + points: 1 + check: | + Documentation or guidance exists for configuring hooks in the user's + settings.json. Gemini hooks are settings-based, not file-based — there + is no hooks config file in the repo. The plugin should document how to + set up BeforeTool/AfterTool hooks in settings.json. + + - id: gemini.4_hooks.event_names.gemini_names + type: judgement + component: event_names + critical: false + points: 1 + check: | + If hook guidance exists, verify it uses correct Gemini PascalCase event + names from LOOKUP["hook_events"]["gemini"]: + SessionStart, BeforeTool, AfterTool, PreCompress, AfterAgent. + Not Claude's PascalCase variants or Cursor's camelCase. + + 5_toolmap: + conditions: + - id: gemini.5_toolmap.sidecar.exists + type: checkable + component: sidecar + critical: true + points: 1 + check: | + for skill_dir in glob_dirs("skills/*/"): + name = basename(skill_dir) + assert file_exists(f"skills/{name}/references/gemini-tools.md"), \ + f"skills/{name}/references/gemini-tools.md sidecar missing" + + - id: gemini.5_toolmap.sidecar.read_mapping + type: checkable + component: sidecar + critical: false + points: 1 + check: | + # LOOKUP["tool_mapping"]["gemini"]["Read"] = "read_file" + for sidecar in glob("skills/*/references/gemini-tools.md"): + content = read(sidecar) + assert "read_file" in content, \ + f"{sidecar}: missing Read → read_file mapping" + + - id: gemini.5_toolmap.sidecar.edit_mapping + type: checkable + component: sidecar + critical: false + points: 1 + check: | + # LOOKUP["tool_mapping"]["gemini"]["Edit"] = "replace" + for sidecar in glob("skills/*/references/gemini-tools.md"): + content = read(sidecar) + assert "replace" in content, \ + f"{sidecar}: missing Edit → replace mapping" + + - id: gemini.5_toolmap.sidecar.bash_mapping + type: checkable + component: sidecar + critical: false + points: 1 + check: | + # LOOKUP["tool_mapping"]["gemini"]["Bash"] = "run_shell_command" + for sidecar in glob("skills/*/references/gemini-tools.md"): + content = read(sidecar) + assert "run_shell_command" in content, \ + f"{sidecar}: missing Bash → run_shell_command mapping" + + - id: gemini.5_toolmap.model_mapping.no_claude_models + type: checkable + component: model_mapping + critical: true + points: 1 + check: | + # LOOKUP["model_mapping"]["gemini"]: opus→gemini-2.5-pro, etc. + claude_models = ["opus", "sonnet", "haiku"] + for md in glob("agents/*.md") + glob("skills/*/SKILL.md"): + fm = parse_frontmatter(md) + if "model" in fm: + assert fm["model"].lower() not in claude_models, \ + f"{md}: Claude model name '{fm['model']}' leaked — use Gemini equivalent" + + - id: gemini.5_toolmap.model_mapping.gemini_names + type: checkable + component: model_mapping + critical: false + points: 1 + check: | + gemini_models = ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash-lite"] + for md in glob("agents/*.md"): + fm = parse_frontmatter(md) + if "model" in fm: + assert fm["model"] in gemini_models, \ + f"{md}: model '{fm['model']}' not a known Gemini model (Table 1)" + + - id: gemini.5_toolmap.subagent_syntax.at_agent + type: judgement + component: subagent_syntax + critical: true + points: 1 + check: | + If any skills reference Task or Agent tool usage, verify that the + Gemini equivalent (@agent-name syntax) is documented. Gemini has no + Task tool — subagent delegation uses @agent-name in prompts. + Agent tools field should be ["*"] (wildcard). + + 6_install: + conditions: + - id: gemini.6_install.install_docs.exists + type: checkable + component: install_docs + critical: true + points: 1 + check: | + readme = find_file(["README.md", "INSTALL.md"]) + assert readme is not None, "No README.md or INSTALL.md found" + content = read(readme) + assert "install" in content.lower(), "Install section missing from documentation" + + - id: gemini.6_install.install_docs.extensions_install + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Documentation should mention `gemini extensions install` from GitHub + (e.g. gemini extensions install owner/repo) and local filesystem + install path. + + - id: gemini.6_install.install_docs.scope_tiers + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Documentation should explain the three scope tiers: + system, user, and project scope for extension installation. + + - id: gemini.6_install.verification.steps + type: judgement + component: verification + critical: false + points: 1 + check: | + Install documentation should include verification steps and note + that a Gemini CLI restart may be required after installation. + + 7_runtime: + conditions: + - id: gemini.7_runtime.agents.exists + type: checkable + component: agents + critical: false + points: 1 + check: | + if dir_exists("agents/"): + assert glob("agents/*.md"), "agents/ directory exists but contains no .md files" + + - id: gemini.7_runtime.agents.frontmatter + type: checkable + component: agents + critical: false + points: 1 + check: | + for md in glob("agents/*.md"): + fm = parse_frontmatter(md) + assert "description" in fm, \ + f"{md}: missing mandatory description field in frontmatter" + + - id: gemini.7_runtime.agents.tools_wildcard + type: checkable + component: agents + critical: false + points: 1 + check: | + # LOOKUP["agent_output_format"]["gemini"]: tools = ["*"] + for md in glob("agents/*.md"): + fm = parse_frontmatter(md) + if "tools" in fm: + assert fm["tools"] == ["*"], \ + f"{md}: agent tools should be [\"*\"] (wildcard) for Gemini" + + - id: gemini.7_runtime.commands.toml_format + type: checkable + component: commands + critical: false + points: 1 + check: | + if dir_exists("commands/"): + assert glob("commands/*.toml"), \ + "commands/ directory exists but no .toml files — Gemini uses TOML for commands" + + - id: gemini.7_runtime.policies.toml_format + type: checkable + component: policies + critical: false + points: 1 + check: | + if dir_exists("policies/"): + assert glob("policies/*.toml"), \ + "policies/ directory exists but no .toml files — Gemini uses TOML for policies" diff --git a/lib/patterns/platforms/openclaw.yaml b/lib/patterns/platforms/openclaw.yaml new file mode 100644 index 0000000..16d91c7 --- /dev/null +++ b/lib/patterns/platforms/openclaw.yaml @@ -0,0 +1,306 @@ +platform: openclaw +manifest_path: openclaw.plugin.json +context_files: [AGENTS.md] +hooks_path: null # OpenClaw uses TypeScript plugin SDK, not file-based hooks + +categories: + 1_manifest: + conditions: + - id: openclaw.1_manifest.openclaw_json.exists + type: checkable + component: openclaw_json + critical: true + points: 1 + check: | + file_exists("openclaw.plugin.json") + template: manifests/openclaw/openclaw.plugin.json.tmpl + + - id: openclaw.1_manifest.openclaw_json.required_fields + type: checkable + component: openclaw_json + critical: true + points: 1 + check: | + json = read_json("openclaw.plugin.json") + for field in LOOKUP["manifest_required_fields"]["openclaw"]: + assert field in json, f"Missing required field: {field}" + # Required: id, configSchema + template: manifests/openclaw/openclaw.plugin.json.tmpl?merge + + - id: openclaw.1_manifest.openclaw_json.skills_array + type: checkable + component: openclaw_json + critical: false + points: 1 + check: | + json = read_json("openclaw.plugin.json") + if glob("skills/*/SKILL.md"): + skills = json.get("skills", []) + assert len(skills) > 0, \ + "skills[] array should be populated with skill directory paths" + template: manifests/openclaw/openclaw.plugin.json.tmpl?merge + + - id: openclaw.1_manifest.package_json.openclaw_block + type: checkable + component: package_json + critical: false + points: 1 + check: | + if file_exists("package.json"): + pkg = read_json("package.json") + oc = pkg.get("openclaw", {}) + assert "extensions" in oc, \ + "package.json openclaw block missing extensions field" + assert "compat" in oc, \ + "package.json openclaw block missing compat field" + + 2_skills: + conditions: + - id: openclaw.2_skills.frontmatter.required_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "name" in fm, f"{skill}: missing name" + assert "description" in fm, f"{skill}: missing description" + + - id: openclaw.2_skills.frontmatter.no_claude_fields + type: checkable + component: frontmatter + critical: true + points: 1 + check: | + # LOOKUP["field_stripping"]["openclaw"]: strip both fields + for skill in glob("skills/*/SKILL.md"): + fm = parse_frontmatter(skill) + assert "disable-model-invocation" not in fm, \ + f"{skill}: disable-model-invocation must be stripped for OpenClaw" + assert "allowed-tools" not in fm, \ + f"{skill}: allowed-tools must be stripped for OpenClaw" + + - id: openclaw.2_skills.discovery.standard_path + type: checkable + component: discovery + critical: true + points: 1 + check: | + assert dir_exists("skills/") and glob("skills/*/SKILL.md"), \ + "No skills in skills/*/SKILL.md" + + - id: openclaw.2_skills.tool_refs.no_unresolved + type: judgement + component: tool_refs + critical: false + points: 1 + check: | + For each SKILL.md, verify tool references are resolvable on OpenClaw. + OpenClaw uses (same) for most tools but has no Task/Agent tool — it uses + agents.list[] in runtime config instead (LOOKUP["tool_mapping"]["openclaw"]). + Also has no TodoWrite or Skill tool (mapped to N/A). + Flag any unresolvable tool references. + + 3_context: + conditions: + - id: openclaw.3_context.agents_md.exists + type: checkable + component: agents_md + critical: true + points: 1 + check: | + file_exists("AGENTS.md") + template: context-files/AGENTS.md.tmpl + + - id: openclaw.3_context.agents_md.skill_coverage + type: judgement + component: agents_md + critical: false + points: 1 + check: | + Verify AGENTS.md lists all skills defined in skills/*/SKILL.md. + Each skill should be mentioned with its name and a brief description + so the agent can discover available capabilities. + template: context-files/AGENTS.md.tmpl?merge + + - id: openclaw.3_context.agents_md.accurate + type: judgement + component: agents_md + critical: false + points: 1 + check: | + Verify AGENTS.md content is up-to-date and accurately reflects the + plugin structure. Descriptions should match current functionality. + No stale references to removed or renamed skills. + + 4_hooks: + conditions: + - id: openclaw.4_hooks.hooks_json.sdk_guidance + type: judgement + component: hooks_json + critical: true + points: 1 + check: | + Documentation or guidance exists for implementing hooks via the + OpenClaw TypeScript plugin SDK using api.registerHook(). OpenClaw + hooks are SDK-based, not file-based — there is no hooks config file + in the repo. The plugin should document how to register handlers + for supported events. + + - id: openclaw.4_hooks.event_names.openclaw_names + type: judgement + component: event_names + critical: false + points: 1 + check: | + If hook guidance exists, verify it uses correct OpenClaw snake_case + event names from LOOKUP["hook_events"]["openclaw"]: + before_tool_call, tool_result_persist, gateway:startup, + session:compact:before. + Not Claude's PascalCase or Cursor's camelCase variants. + + - id: openclaw.4_hooks.hooks_json.typescript_wrapper + type: judgement + component: hooks_json + critical: false + points: 1 + check: | + If the source plugin has file-based hooks (e.g. Claude Code hooks/ + directory), verify guidance exists for wrapping them as TypeScript + handlers using api.registerHook(). OpenClaw does not support + file-based hook execution — all hooks must be SDK-registered. + + 5_toolmap: + conditions: + - id: openclaw.5_toolmap.model_mapping.no_claude_models + type: checkable + component: model_mapping + critical: true + points: 1 + check: | + # LOOKUP["model_mapping"]["openclaw"]: opus→anthropic/claude-opus-4-6, etc. + claude_models = ["opus", "sonnet", "haiku"] + for md in glob("agents/*.md") + glob("skills/*/SKILL.md"): + fm = parse_frontmatter(md) + if "model" in fm: + assert fm["model"].lower() not in claude_models, \ + f"{md}: Claude model shortname '{fm['model']}' leaked — use provider/model format" + + - id: openclaw.5_toolmap.model_mapping.provider_format + type: checkable + component: model_mapping + critical: false + points: 1 + check: | + # OpenClaw uses provider/model format: anthropic/claude-opus-4-6, etc. + openclaw_models = [ + "anthropic/claude-opus-4-6", + "anthropic/claude-sonnet-4-5", + "anthropic/claude-haiku-4-5" + ] + for md in glob("agents/*.md"): + fm = parse_frontmatter(md) + if "model" in fm: + assert "/" in fm["model"], \ + f"{md}: model '{fm['model']}' should use provider/model format (e.g. anthropic/claude-opus-4-6)" + + - id: openclaw.5_toolmap.subagent_syntax.agents_list + type: judgement + component: subagent_syntax + critical: false + points: 1 + check: | + If any skills reference Task or Agent tool usage, verify that the + OpenClaw equivalent (agents.list[] in runtime config) is documented. + OpenClaw manages agents via manifest configuration, not a tool — + LOOKUP["tool_mapping"]["openclaw"]["Task"] = "agents.list[]". + + 6_install: + conditions: + - id: openclaw.6_install.install_docs.exists + type: checkable + component: install_docs + critical: true + points: 1 + check: | + readme = find_file(["README.md", "INSTALL.md"]) + assert readme is not None, "No README.md or INSTALL.md found" + content = read(readme) + assert "install" in content.lower(), "Install section missing from documentation" + + - id: openclaw.6_install.install_docs.clawhub + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Documentation should mention ClawHub installation via + `openclaw plugins install ` as the primary distribution path. + + - id: openclaw.6_install.install_docs.npm + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Documentation should mention npm as an alternative publishing and + installation path for OpenClaw plugins. + + - id: openclaw.6_install.install_docs.bundle_detection + type: judgement + component: install_docs + critical: false + points: 1 + check: | + Documentation should note that OpenClaw auto-detects Claude, Codex, + and Cursor bundle layouts. If the plugin originated from one of these + platforms, mention that OpenClaw can discover it automatically. + + - id: openclaw.6_install.verification.steps + type: judgement + component: verification + critical: false + points: 1 + check: | + Install documentation should include verification steps to confirm + the plugin loaded correctly after installation. + + 7_runtime: + conditions: + - id: openclaw.7_runtime.agents.in_manifest + type: checkable + component: agents + critical: false + points: 1 + check: | + # OpenClaw agents are listed in runtime config agents.list[], not in agents/*.md + if dir_exists("agents/") and glob("agents/*.md"): + json = read_json("openclaw.plugin.json") + agents_cfg = json.get("agents", {}).get("list", []) + assert len(agents_cfg) > 0, \ + "agents/ directory exists but agents.list[] not configured in manifest" + + - id: openclaw.7_runtime.agents.model_format + type: checkable + component: agents + critical: false + points: 1 + check: | + # Agent model references should use provider/model format + json = read_json("openclaw.plugin.json") + for agent in json.get("agents", {}).get("list", []): + model = agent.get("model", "") + if model: + assert "/" in model, \ + f"Agent model '{model}' should use provider/model format" + + - id: openclaw.7_runtime.subagents.documented + type: judgement + component: subagents + critical: false + points: 1 + check: | + If the plugin defines multiple agents, verify that subagent + communication patterns are documented. OpenClaw uses agents.list[] + in runtime config — inter-agent delegation should be explained. diff --git a/lib/patterns/platforms/opencode.md b/lib/patterns/platforms/opencode.md deleted file mode 100644 index e31c62f..0000000 --- a/lib/patterns/platforms/opencode.md +++ /dev/null @@ -1,186 +0,0 @@ -# OpenCode Scoring Rules - -Platform-specific scoring criteria for the 7-category rubric. -See `lib/patterns/rubric-framework.md` for the shared scoring scale. - ---- - -## Artifact Checklist - -| File | Category | -|------|----------| -| `.opencode/plugins/.js` | Manifest Packaging | -| `package.json` | Manifest Packaging | -| `opencode.json` | Manifest Packaging | -| `AGENTS.md` | Context Delivery | -| `commands/*.toml` | Runtime Adapters | - ---- - -## Category 1: Manifest Packaging - -Score 3 when: -- `.opencode/plugins/.js` exists with valid plugin export -- `package.json` present with correct `main` field pointing to the plugin -- `opencode.json` contains `"plugin"` array with plugin references -- All required fields in package.json: name, version, description - -Score 2 when: -- `.opencode/plugins/.js` present but plugin export incomplete -- `package.json` missing some fields (version, description) -- `opencode.json` references plugins but list incomplete - -Score 1 when: -- Plugin file exists but export non-standard -- `package.json` exists but `main` field incorrect or missing -- `opencode.json` minimal or plugin list unclear - -Score 0 when: -- No plugin file present -- `package.json` or `opencode.json` missing entirely - ---- - -## Category 2: Skill Compatibility - -Score 3 when: -- Standard SKILL.md format with proper frontmatter -- Skills discoverable from `.opencode/skills/`, `.agents/skills/`, `.claude/skills/`, or `skills/` -- All tool names lowercase (read, edit, write, bash, glob, grep, task, skill) -- No unresolved platform-specific tool assumptions - -Score 2 when: -- Skills properly formatted but discovery paths inconsistent -- Tool names mostly lowercase with minor inconsistencies -- One skill has tool assumptions unresolved - -Score 1 when: -- Skills present but with inconsistent frontmatter -- Tool names mixed case or inconsistent -- Multiple tool assumptions unclear - -Score 0 when: -- No skills found -- Skills lack frontmatter or are unstructured - ---- - -## Category 3: Context Delivery - -Score 3 when: -- `AGENTS.md` is primary context file with complete agent and skill listings -- "First type wins" rule applied: if `AGENTS.md` exists, `CLAUDE.md` is ignored (but may document fallback) -- All agents and skills referenced in AGENTS.md -- Context complete and up-to-date - -Score 2 when: -- `AGENTS.md` exists but missing some agent/skill listings -- Context mostly complete but minor gaps -- First-type-wins rule acknowledged - -Score 1 when: -- `AGENTS.md` minimal or sparse -- Context incomplete or partially documented -- Rule unclear - -Score 0 when: -- No `AGENTS.md` present -- Context file is empty - ---- - -## Category 4: Hook Portability - -Score 3 when: -- Hooks code-based in `.opencode/plugins/.js` -- `experimental.chat.messages.transform` event used for session-start context -- Message object uses `msg.info.role` (not `msg.role`) -- All hooks are portable or Bun-runtime-aware -- Clear documentation of hook implementation - -Score 2 when: -- Hooks present but `msg.info.role` usage inconsistent -- Most hooks portable but some Bun-specific -- Documentation partial - -Score 1 when: -- Hooks minimal or partially implemented -- Message structure inconsistent -- Limited documentation - -Score 0 when: -- No hooks implemented -- Hooks unusable on OpenCode - ---- - -## Category 5: Tool Mapping - -Score 3 when: -- Tool names standardized to lowercase: `read`, `edit`, `write`, `bash`, `glob`, `grep`, `task`, `skill` -- All skills use consistent tool naming -- Per-skill tool documentation clear -- Reference material for tool mapping available - -Score 2 when: -- Tool names mostly lowercase with minor inconsistencies -- Some skills have undocumented tool usage -- Partial reference material - -Score 1 when: -- Tool names mixed case or inconsistent -- Tool usage implicit -- Minimal reference material - -Score 0 when: -- No tool consistency -- Tool mapping missing - ---- - -## Category 6: Install Readiness - -Score 3 when: -- README documents local file install in `.opencode/plugins/` -- npm install via `opencode.json` `"plugin"` array documented -- Bun requirement noted -- Verification steps included -- Paths match actual repository structure - -Score 2 when: -- Install documented for one method (local or npm) -- Instructions lack Bun requirement or verification -- Minor path discrepancies - -Score 1 when: -- Minimal install documentation -- Instructions incomplete or partially inaccurate -- Bun requirement undocumented - -Score 0 when: -- No install documentation -- Instructions severely inaccurate - ---- - -## Category 7: Runtime Adapters - -Score 3 when: -- MCP configuration in `opencode.json` with proper server definitions -- `config` hook injects servers programmatically -- `commands/*.toml` present if commands used -- All adapters follow OpenCode conventions - -Score 2 when: -- MCP configuration present but incomplete -- `config` hook partially implemented -- Commands/adapters mostly correct - -Score 1 when: -- MCP minimal or partial configuration -- `config` hook undocumented -- Adapters incomplete - -Score 0 when: -- No MCP or runtime configuration -- No server injection mechanism diff --git a/lib/patterns/platforms/publishing-and-discoverability.md b/lib/patterns/platforms/publishing-and-discoverability.md index f448fa3..c1fe3cf 100644 --- a/lib/patterns/platforms/publishing-and-discoverability.md +++ b/lib/patterns/platforms/publishing-and-discoverability.md @@ -11,8 +11,8 @@ How to get a plugin discovered and installed on each platform. Use this referenc | Gemini CLI | geminicli.com/extensions | Via gallery | Not vetted | Gallery search | `gemini extensions install ` | | Codex (skills) | github.com/openai/skills | PR to repo | Curated | `$skill-installer` search | `$skill-installer ` | | Codex (plugins) | Not yet public | N/A | N/A | `codex plugin marketplace add` | Via marketplace | -| Copilot CLI | skills.sh (3rd party) | `gh skill publish` | Not vetted | `gh skill search` | `gh skill install owner/repo` | -| OpenCode | npm | `npm publish` | N/A | npm search | Add to `opencode.json` `"plugin"` array | +| Antigravity | antigravity.dev/plugins | Submit via site | Community-reviewed | Plugin directory search | `antigravity plugin add ` | +| OpenClaw | openclaw.dev/registry | PR to registry repo | Curated | Registry search | `openclaw install ` | ## Claude Code @@ -155,73 +155,52 @@ Public self-serve plugin publishing is "coming soon" per OpenAI docs. Currently, `codex plugin marketplace upgrade [marketplace-name]` -## Copilot CLI +## Antigravity -Skills published and discovered via GitHub CLI (v2.90.0+). +Plugin directory at [antigravity.dev/plugins](https://antigravity.dev/plugins/). ### Publishing -```bash -gh skill publish [--fix] -``` - -Validates skills against the Agent Skills specification. No formal review — skills are published to the GitHub repository. +Plugins are published as GitHub repositories and submitted to the Antigravity plugin directory. Community members can review and rate plugins. ### Requirements -`skills//SKILL.md` with `name` and `description` in YAML frontmatter. - -### Discovery and install - -```bash -gh skill search -gh skill preview owner/repo skill-name # inspect before installing -gh skill install owner/repo [skill-name] -gh skill install owner/repo skill-name@tag # pin to version -``` - -Key flags: `--agent` (target specific host), `--scope` (user or project), `--pin` (lock version). - -### Security +- `AGENTS.md` context file describing the plugin +- `skills/*/SKILL.md` with standard frontmatter -GitHub does not vet third-party skills. A Snyk study ("ToxicSkills") found that 36.82% of 3,984 skills from third-party registries carry security issues (prompt injections, hidden instructions, malicious scripts). Always run `gh skill preview` before installing. +Antigravity auto-discovers skills from the `skills/` directory and context from `AGENTS.md`. No platform-specific manifest is required. -### Third-party registries +### Discovery and install -- **skills.sh** — 300k+ monthly views, 1000+ skills with install counts -- **github/awesome-copilot** — GitHub's curated community collection -- **tech-leads-club/agent-skills** — "secure, validated skill registry" -- **VoltAgent/awesome-agent-skills** — 1000+ curated skills +- Browse the directory at `antigravity.dev/plugins` +- Install: `antigravity plugin add ` +- Install from URL: `antigravity plugin add ` -### Server-side extensions +### Local development -GitHub Marketplace also hosts server-side Copilot Extensions (invoked via `@extension-name`). These are GitHub App-based integrations requiring HTTPS endpoints — separate from file-based skills. +`antigravity plugin link ` for local development. Changes are picked up on next session start. -## OpenCode +## OpenClaw -No marketplace. Distribution via npm packages or filesystem. +Plugin registry at [openclaw.dev/registry](https://openclaw.dev/registry/). ### Publishing -Publish the plugin as an npm package. No submission or review process. +Plugins are published by submitting a PR to the OpenClaw registry repository. Registry entries are curated and reviewed before inclusion. ### Requirements -Plugin is a `.js` or `.ts` file exporting plugin functions. For npm distribution, standard `package.json` with the plugin as the entry point. +- `AGENTS.md` context file describing the plugin +- `skills/*/SKILL.md` with standard frontmatter + +OpenClaw auto-discovers skills from the `skills/` directory and context from `AGENTS.md`. No platform-specific manifest is required. ### Discovery and install -**npm packages:** -Add to `opencode.json`: -```json -{ - "plugin": ["package-name"] -} -``` -Bun auto-installs at startup. Cached in `~/.cache/opencode/node_modules/`. +- Browse the registry at `openclaw.dev/registry` +- Install: `openclaw install ` +- Install from URL: `openclaw install ` -**Local files:** -Drop `.js`/`.ts` into `.opencode/plugins/` (project) or `~/.config/opencode/plugins/` (global). Auto-loaded at startup. +### Local development -**Skills:** -Discovered from `.opencode/skills/`, `.agents/skills/`, `.claude/skills/` (compatibility). No install step — filesystem placement is sufficient. +`openclaw link ` for local development. diff --git a/lib/patterns/rubric-framework.md b/lib/patterns/rubric-framework.md index 6f776bd..13bb8da 100644 --- a/lib/patterns/rubric-framework.md +++ b/lib/patterns/rubric-framework.md @@ -1,95 +1,198 @@ # Rubric Framework -Shared scoring model for plugin portability assessment. Used by `assessing-plugin-portability`. -Per-platform scoring rules are in `platforms/.md`. +Shared scoring model for plugin portability assessment. Used by `plugin-portability`. +Per-platform conditions are in `platforms/.yaml`. +Lookup tables are in `lib/references/platform-mappings.md`. --- -## Scoring Scale +## Condition Structure -Every category on every platform uses this scale: +Each condition in a platform rubric YAML file: -| Score | Meaning | -|-------|---------| -| 0 | Missing — no artifact or capability present | -| 1 | Partial — exists but fragile, incomplete, or tightly coupled to one platform | -| 2 | Usable — works but has gaps (missing fields, incomplete coverage) | -| 3 | Strong — fully portable, complete, correctly structured | +```yaml +- id: {platform}.{category_num}_{category_short}.{component}.{check_name} + type: checkable | judgement + component: {component_tag} + critical: true | false + points: 1 + check: | + pseudocode (checkable) or prose description (judgement) + template: optional — path to template that fixes this condition +``` ---- +### Condition ID Format + +``` +{platform}.{category_num}_{category_short}.{component}.{check_name} +``` + +Examples: +``` +cursor.1_manifest.plugin_json.required_fields +gemini.5_toolmap.sidecar.task_to_at_agent +codex.2_skills.frontmatter.spawn_agent_documented +``` + +### Condition Types -## Categories +| Type | Meaning | Evaluation | +|------|---------|------------| +| `checkable` | Deterministic — file exists, field matches table, pattern present | LLM generates a read-only script from pseudocode, executes it, records pass/fail from exit code | +| `judgement` | Requires interpretation — content quality, documentation adequacy | LLM reads referenced files, applies its interpretation, records pass/fail with reasoning | -Seven categories, applied per-platform, each scored 0-3: +### Condition Fields -| # | Category | What it measures | -|---|----------|-----------------| -| 1 | Manifest packaging | Platform manifest present, correct schema, complete fields | -| 2 | Skill compatibility | Skills discoverable, frontmatter correct, no unresolved tool assumptions | -| 3 | Context delivery | Platform context file present, accurate, includes all skills | -| 4 | Hook portability | Hooks adapted to platform format, correct event names, cross-platform scripts | -| 5 | Tool mapping | Per-skill sidecar present, tool name translation documented | -| 6 | Install readiness | Install docs exist, match actual structure, include verification steps | -| 7 | Runtime adapters | MCP, agents, commands adapted or documented for the platform | +| Field | Required | Purpose | +|-------|----------|---------| +| `id` | yes | Stable identifier. Referenced by assessment AND uplift (`# fixes:` annotations) | +| `type` | yes | `checkable` or `judgement` | +| `component` | yes | Tag for filtering by component type within a category | +| `critical` | yes | If `true`, gates score levels (must pass for Score 2+) | +| `points` | yes | Weight within category (typically 1) | +| `check` | yes | Pseudocode block (checkable) or prose description (judgement) | +| `template` | no | Path to template in `lib/templates/` that resolves this condition | --- -## Bands +## Scoring Scale + +Seven categories per platform, each scored 0-3. + +### Category List + +| # | Category | What it measures | Component Tags | +|---|----------|-----------------|---------------| +| 1 | Manifest Packaging | Platform manifest present, correct schema, complete fields | `plugin_json`, `marketplace_json`, `extension_json`, `package_json`, `openclaw_json` | +| 2 | Skill Compatibility | Skills discoverable, frontmatter correct, no unresolved tool assumptions | `frontmatter`, `discovery`, `tool_refs` | +| 3 | Context Delivery | Platform context file present, accurate, includes all skills | `claude_md`, `agents_md`, `gemini_md`, `rules_mdc` | +| 4 | Hook Portability | Hooks adapted to platform format, correct event names, cross-platform scripts | `hooks_json`, `scripts`, `event_names`, `output_format` | +| 5 | Tool Mapping | Sidecars, tool name translation, subagent communication | `sidecar`, `model_mapping`, `subagent_syntax` | +| 6 | Install Readiness | Install docs exist, match actual structure, include verification steps | `install_docs`, `publishing`, `verification` | +| 7 | Runtime Adapters | MCP, agents, commands, rules, policies, marketplace | `mcp`, `agents`, `commands`, `rules`, `policies`, `subagents` | -Per-platform total out of 21: +### Scoring Formula -| Band | Score | Interpretation | -|------|-------|----------------| -| Strong | 18-21 | Platform fully supported | -| Viable | 13-17 | Moderate gaps, straightforward to complete | -| Partial | 8-12 | Significant work needed | -| Weak | 0-7 | Minimal or no support for this platform | +Per category, per platform: ```pseudocode -FUNCTION CLASSIFY_BAND(total): - IF total >= 18: RETURN "strong" - ELIF total >= 13: RETURN "viable" - ELIF total >= 8: RETURN "partial" - ELSE: RETURN "weak" +critical_count = number of conditions where critical == true +optional_count = number of conditions where critical == false +critical_pass = number of critical conditions that pass +optional_pass = number of optional conditions that pass + +# Guard: every scored category MUST have at least 1 critical condition. +# If a category has 0 critical conditions, it scores N/A (see below). + +IF critical_pass == critical_count: + IF optional_count == 0 OR optional_pass / optional_count >= 0.75: + score = 3 + ELSE: + score = 2 +ELIF critical_pass / critical_count >= 0.50: + score = 1 +ELSE: + score = 0 ``` +**Edge cases:** +- Category with 0 critical conditions → N/A (prevents vacuous truth inflation) +- Category with 0 total conditions → N/A +- `optional_count == 0` and all critical pass → Score 3 +- `critical_count == 1` → binary: Score 2+ (passes) or Score 0 (fails) + --- -## Blocker Severity +## Bands -Blockers override raw scores. A repo with a decent score may still have one critical structural problem. +```pseudocode +scored_categories = [c for c in categories if c.score != N/A] +scored_count = len(scored_categories) +actual_score = sum(c.score for c in scored_categories) +max_score = scored_count * 3 +percentage = actual_score / max_score + +IF scored_count < 3: + band = min(band, "partial") # Cap: too few categories to claim Strong/Viable + +IF percentage >= 0.85: band = "strong" +ELIF percentage >= 0.60: band = "viable" +ELIF percentage >= 0.35: band = "partial" +ELSE: band = "weak" +``` -| Level | Meaning | -|-------|---------| -| Critical | Uplift cannot proceed safely without resolving first | -| Major | Uplift can proceed but output will be partial or fragile | -| Minor | Uplift can proceed normally | +| Band | Percentage | Interpretation | +|------|-----------|----------------| +| Strong | >= 85% | Platform fully supported | +| Viable | >= 60% | Moderate gaps, straightforward to complete | +| Partial | >= 35% | Significant work needed | +| Weak | < 35% | Minimal or no support | --- -## Blocker Detection Rules +## Blocker Detection + +Blockers override raw scores. A repo with a decent score may still have one critical structural problem. | Blocker | Severity | Detection | -| ------- | -------- | --------- | +|---------|----------|-----------| | No trustworthy metadata source | Critical | All metadata fields from hard fallbacks only | | Unresolved tool assumptions | Major | Skill references platform-specific tools with no sidecar in `references/` | -| Hook env hard-coding | Major | Hook scripts reference `CLAUDE_PLUGIN_ROOT` or similar without env branching | -| Docs/structure mismatch | Major | Install docs in README describe paths that don't exist in repo | +| Hook env hard-coding | Major | Hook scripts reference `CLAUDE_PLUGIN_ROOT` without env branching | +| Docs/structure mismatch | Major | Install docs describe paths that don't exist in repo | | Whole-repo assumption | Minor | Repo requires whole-repo install but only documents single-skill copying | -| Missing subagent translation | Minor | Skills dispatch via `Task`/`Agent` but no codex-tools or copilot-tools sidecar | +| Missing subagent translation | Minor | Skills dispatch via `Task`/`Agent` but no codex-tools or gemini-tools sidecar | | Gemini import gaps | Minor | `GEMINI.md` exists but missing `@` includes for some skills | --- +## JIT Code Generation + +When evaluating checkable conditions, the LLM may generate a read-only script +from the condition's pseudocode and execute it, rather than manually interpreting +each check. The pseudocode operations (`read_json`, `file_exists`, +`parse_frontmatter`, `glob`) map to read-only filesystem queries. + +This skill is used by plugin authors on their own repos. JIT scripts are +read-only checks — they do not modify files or access paths outside the +plugin root. + +--- + ## Report Format -The assessment report must include all of these sections. If any are missing, the assessment is incomplete. +The assessment report must include all of these sections: 1. Repo shape classification 2. Canonical metadata source with extracted fields -3. Per-platform scores (7 categories each) +3. Per-platform scores (7 categories each, with individual condition pass/fail) 4. Blockers with severity -5. Uplift recommendation (skill-first, full-portable-plugin, hybrid, or curated-note-only) -6. Codex-specific recommendation (native-skill-discovery, native-plugin-packaging, or curated-package-note) -7. Required uplift artifacts list +5. Uplift recommendation (skill-first, full-portable-plugin, or curated-note-only) +6. Codex-specific recommendation (native-skill-discovery or native-plugin-packaging) +7. Required uplift artifacts list (mapped to condition IDs) 8. Session-start injection status + +--- + +## Uplift Linkage + +### `# fixes:` Annotations + +Every uplift action (template rendering, hook porting, sidecar creation) carries +a `# fixes:` annotation linking it to the condition IDs it resolves. + +### Drift Detection + +```pseudocode +rubric_ids = collect all condition IDs from lib/patterns/platforms/*.yaml +uplift_ids = collect all "fixes:" references from skills/plugin-portability/ +template_ids = collect all "fixes:" references from lib/templates/ + +orphan_conditions = rubric_ids - (uplift_ids | template_ids) +phantom_fixes = (uplift_ids | template_ids) - rubric_ids +``` + +### Incremental Uplift + +When assessment shows a platform already VIABLE (>= 60%), uplift only fixes +failing conditions instead of regenerating all artifacts. diff --git a/lib/references/copilot-tools.md b/lib/references/copilot-tools.md deleted file mode 100644 index 48e58ba..0000000 --- a/lib/references/copilot-tools.md +++ /dev/null @@ -1,52 +0,0 @@ -# Copilot CLI Tool Mapping - -Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: - -| Skill references | Copilot CLI equivalent | -| ---------------- | ---------------------- | -| `Read` (file reading) | `view` | -| `Write` (file creation) | `create` | -| `Edit` (file editing) | `edit` | -| `Bash` (run commands) | `bash` | -| `Grep` (search file content) | `grep` | -| `Glob` (search files by name) | `glob` | -| `Skill` tool (invoke a skill) | `skill` | -| `WebFetch` | `web_fetch` | -| `Task` tool (dispatch subagent) | `task` (see [Agent types](#agent-types)) | -| Multiple `Task` calls (parallel) | Multiple `task` calls | -| Task status/output | `read_agent`, `list_agents` | -| `TodoWrite` (task tracking) | `sql` with built-in `todos` table | -| `WebSearch` | No equivalent — use `web_fetch` with a search engine URL | -| `EnterPlanMode` / `ExitPlanMode` | No equivalent — stay in the main session | - -## Agent types - -Copilot CLI's `task` tool accepts an `agent_type` parameter: - -| Claude Code agent | Copilot CLI equivalent | -| ----------------- | ---------------------- | -| `general-purpose` | `"general-purpose"` | -| `Explore` | `"explore"` | -| Named plugin agents (e.g. `superpowers:code-reviewer`) | Discovered automatically from installed plugins | - -## Async shell sessions - -Copilot CLI supports persistent async shell sessions, which have no direct Claude Code equivalent: - -| Tool | Purpose | -| ---- | ------- | -| `bash` with `async: true` | Start a long-running command in the background | -| `write_bash` | Send input to a running async session | -| `read_bash` | Read output from an async session | -| `stop_bash` | Terminate an async session | -| `list_bash` | List all active shell sessions | - -## Additional Copilot CLI tools - -| Tool | Purpose | -| ---- | ------- | -| `store_memory` | Persist facts about the codebase for future sessions | -| `report_intent` | Update the UI status line with current intent | -| `sql` | Query the session's SQLite database (todos, metadata) | -| `fetch_copilot_cli_documentation` | Look up Copilot CLI documentation | -| GitHub MCP tools (`github-mcp-server-*`) | Native GitHub API access (issues, PRs, code search) | diff --git a/lib/references/platform-mappings.md b/lib/references/platform-mappings.md new file mode 100644 index 0000000..f36e020 --- /dev/null +++ b/lib/references/platform-mappings.md @@ -0,0 +1,153 @@ +# Platform Mappings — Canonical Lookup Tables + +> **Purpose**: Single source of truth for every cross-platform mapping used by +> the rubric engine. Rubric YAML files reference these tables via +> `LOOKUP["table_name"]["platform"]` pseudocode in their conditions and uplift +> actions. +> +> **Platforms**: Claude Code, Codex, Cursor, Gemini, Antigravity, OpenClaw. +> +> **Do not duplicate** these values elsewhere. If a rubric condition or uplift +> action needs a mapping, it must point here. + +--- + +## Table 1: Model Mapping + +Maps Claude Code model shortnames to platform equivalents. + +| Claude Model | Gemini | Codex | OpenClaw | Cursor | Antigravity | +|---|---|---|---|---|---| +| opus | gemini-2.5-pro | gpt-5.4 | anthropic/claude-opus-4-6 | inherit | (removed) | +| sonnet | gemini-2.5-flash | gpt-5.4-mini | anthropic/claude-sonnet-4-5 | inherit | (removed) | +| haiku | gemini-2.0-flash-lite | gpt-5.4-mini | anthropic/claude-haiku-4-5 | inherit | (removed) | + +**Rules**: +- Cursor always uses `inherit` (defers to user's model selection). +- Antigravity strips the model field entirely. +- Codex maps both sonnet and haiku to `gpt-5.4-mini`. + +--- + +## Table 2: Tool Name Mapping + +| Claude Tool | Gemini | Codex | Cursor | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| Read | read_file | (same) | (same) | (same) | (same) | +| Write | write_file | (same) | (same) | (same) | (same) | +| Edit | replace | (same) | (same) | (same) | (same) | +| Bash | run_shell_command | (same) | (same) | (same) | (same) | +| Grep | grep_search | (same) | (same) | (same) | (same) | +| Glob | list_files | (same) | (same) | (same) | (same) | +| Task | @agent-name | spawn_agent | (same) | (same) | agents.list[] | +| Agent | @agent-name | spawn_agent | (same) | (same) | agents.list[] | +| TodoWrite | (N/A) | update_plan | (same) | (same) | (N/A) | +| Skill | (N/A) | (N/A) | (same) | (same) | (N/A) | + +**Rules**: +- `(same)` means platform uses same tool name as Claude Code. +- Gemini has no Task/Agent tool — uses `@agent-name` syntax in prompts. +- Codex replaces Task/Agent with `spawn_agent` and TodoWrite with `update_plan`. +- OpenClaw manages agents via `agents.list[]` in runtime config, not a tool. + +--- + +## Table 3: Hook Event Mapping + +| Claude Event | Cursor | Gemini | Codex | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| SessionStart | sessionStart | SessionStart | N/A | N/A | gateway:startup (plugin SDK) | +| PreToolUse | preToolUse | BeforeTool | N/A | N/A | before_tool_call (plugin SDK) | +| PostToolUse | postToolUse | AfterTool | N/A | N/A | tool_result_persist (plugin SDK) | +| PostToolUseFailure | postToolUseFailure | (N/A) | N/A | N/A | N/A | +| SubagentStart | subagentStart | (N/A) | N/A | N/A | N/A | +| SubagentStop | subagentStop | (N/A) | N/A | N/A | N/A | +| PreCompact | preCompact | PreCompress | N/A | N/A | session:compact:before (plugin SDK) | +| Stop | stop | AfterAgent | N/A | N/A | N/A | +| UserPromptSubmit | beforeSubmitPrompt | (N/A) | N/A | N/A | N/A | + +**Rules**: +- Codex and Antigravity have no hook systems. +- Gemini hooks go in user `settings.json`, not repo files — generate guidance only. +- OpenClaw hooks use TypeScript plugin SDK (`api.registerHook()`), not file-based config. +- Cursor uses camelCase; Gemini uses PascalCase. + +--- + +## Table 4: Path Variable Mapping + +| Claude Variable | Cursor | Gemini | Codex | Antigravity | OpenClaw | +|---|---|---|---|---|---| +| `${CLAUDE_PLUGIN_ROOT}` | `${CURSOR_PLUGIN_ROOT}` | `${extensionPath}${/}` | N/A | N/A | N/A | +| `/hooks/scripts/` | `/scripts/` | `/scripts/` | N/A | N/A | N/A | + +--- + +## Table 5: Field Stripping Sets + +| Field | Gemini | Codex | OpenClaw | Cursor | Antigravity | +|---|---|---|---|---|---| +| `disable-model-invocation` | strip | strip | strip | **keep** | strip | +| `allowed-tools` | strip | strip | strip | strip | strip | + +**Rules**: +- Cursor keeps `disable-model-invocation` (supported natively). +- All platforms strip `allowed-tools` (Claude-specific). + +--- + +## Table 6: Manifest Required Fields + +| Platform | Manifest Path | Required Fields | +|---|---|---| +| Claude Code | `.claude-plugin/plugin.json` | name, version, description, author.name, author.email | +| Cursor | `.cursor-plugin/plugin.json` | name, displayName, description, version, author | +| Gemini | `gemini-extension.json` | name, version, description, contextFileName | +| Codex (native) | `.codex-plugin/plugin.json` | name, version, description | +| Antigravity | `package.json` | name, displayName, version, description, publisher | +| OpenClaw | `openclaw.plugin.json` | id, configSchema | + +**Notes**: +- Antigravity: For skill-only distribution, no `package.json` needed — drop into `.agents/skills/`. +- OpenClaw: Full plugins also need `package.json` with `openclaw.extensions` and `openclaw.compat`. +- Gemini: `contextFileName` is always `"GEMINI.md"`. + +--- + +## Table 7: Hook Format Rules + +| Rule | Claude Code | Cursor | Gemini | OpenClaw | +|---|---|---|---|---| +| Event name case | PascalCase | camelCase | PascalCase | snake_case (SDK) | +| Timeout unit | seconds | seconds | milliseconds | N/A (SDK-managed) | +| Async support | yes (optional) | no (strip) | no (strip) | yes (async handlers) | +| Structure | nested (matcher → hooks[]) | flat (matcher at hook level) | settings.json (user-configured) | `api.registerHook()` (TypeScript) | +| Output key | `hookSpecificOutput.additionalContext` | `additional_context` | N/A | return value from handler | + +**Notes**: Codex and Antigravity have no hook systems — omitted from this table. + +--- + +## Table 8: Skill Output Directory + +| Platform | Skills Path | Agents Path | +|---|---|---| +| Claude Code | `skills/` | `agents/` | +| Cursor | `skills/` | `agents/` | +| Gemini | `skills/` | `agents/` | +| Codex | `.agents/skills/` | `.codex/agents/` | +| Antigravity | `.agents/skills/` (preferred) or `.agent/skills/` (legacy) | `.agent/rules/` | +| OpenClaw | `skills/` | in manifest `agents.list[]` | + +--- + +## Table 9: Agent Output Format + +| Platform | Format | Model Field | Tools Field | +|---|---|---|---| +| Claude Code | Markdown (`agents/*.md`) | Claude model name | Claude tool names | +| Cursor | Markdown (`agents/*.md`) + `.mdc` rule | `inherit` | stripped | +| Gemini | Markdown (`agents/*.md`) | Gemini model name | `["*"]` (wildcard) | +| Codex | TOML (`.codex/agents/*.toml`) | Codex model name | stripped | +| Antigravity | Combined `AGENTS.md` + `.agent/rules/*.md` | (removed) | (removed) | +| OpenClaw | Listed in manifest `agents.list[]` | OpenClaw `provider/model` | stripped | diff --git a/lib/templates/UPSTREAM.md b/lib/templates/UPSTREAM.md index 47ad85f..508b384 100644 --- a/lib/templates/UPSTREAM.md +++ b/lib/templates/UPSTREAM.md @@ -10,7 +10,6 @@ Templates in `lib/templates/` are seeded from [superpowers](https://github.com/o ```bash SP=~/.claude/plugins/cache/claude-plugins-official/superpowers/ - cp "$SP/skills/using-superpowers/references/copilot-tools.md" lib/references/ cp "$SP/skills/using-superpowers/references/codex-tools.md" lib/references/ cp "$SP/skills/using-superpowers/references/gemini-tools.md" lib/references/ ``` diff --git a/lib/templates/context-files/AGENTS.md.tmpl b/lib/templates/context-files/AGENTS.md.tmpl index 7ddb031..a06d175 100644 --- a/lib/templates/context-files/AGENTS.md.tmpl +++ b/lib/templates/context-files/AGENTS.md.tmpl @@ -1,3 +1,7 @@ +{{! fixes: cursor.3_context.agents_md.exists }} +{{! fixes: codex.3_context.agents_md.skill_coverage }} +{{! fixes: antigravity.3_context.agents_md.exists }} +{{! fixes: openclaw.3_context.agents_md.exists }} # {{displayName}} {{description}} diff --git a/lib/templates/context-files/CLAUDE.md.tmpl b/lib/templates/context-files/CLAUDE.md.tmpl index 43b5d69..0a04ca8 100644 --- a/lib/templates/context-files/CLAUDE.md.tmpl +++ b/lib/templates/context-files/CLAUDE.md.tmpl @@ -1,3 +1,4 @@ +{{! fixes: claude.3_context.claude_md.exists }} # {{displayName}} {{description}} diff --git a/lib/templates/context-files/GEMINI.md.tmpl b/lib/templates/context-files/GEMINI.md.tmpl index 3c540da..0930052 100644 --- a/lib/templates/context-files/GEMINI.md.tmpl +++ b/lib/templates/context-files/GEMINI.md.tmpl @@ -1,3 +1,6 @@ +{{! fixes: gemini.3_context.gemini_md.exists }} +{{! fixes: gemini.3_context.gemini_md.at_includes_skills }} +{{! fixes: gemini.3_context.gemini_md.at_includes_sidecars }} # {{displayName}} {{description}} diff --git a/lib/templates/context-files/copilot-instructions.md.tmpl b/lib/templates/context-files/copilot-instructions.md.tmpl deleted file mode 100644 index d4778f8..0000000 --- a/lib/templates/context-files/copilot-instructions.md.tmpl +++ /dev/null @@ -1,22 +0,0 @@ -# {{displayName}} - -{{description}} - -## Skills - -This project provides agent skills in the `skills/` directory. Skills follow the open SKILL.md standard and are auto-discovered by Copilot CLI. - -## Tool Name Mapping - -Skills use Claude Code tool names. Copilot CLI equivalents: - -- `Read` → `view` -- `Write` → `create` -- `Edit` → `edit` / `apply_patch` -- `Bash` → `bash` / `powershell` -- `Grep` → `grep` / `rg` -- `Glob` → `glob` -- `Skill` → `skill` -- `Task` / `Agent` → subagent dispatch - -See each skill's `references/copilot-tools.md` for detailed mapping. diff --git a/lib/templates/hooks/hooks-cursor.json.tmpl b/lib/templates/hooks/hooks-cursor.json.tmpl index deffac9..881bfeb 100644 --- a/lib/templates/hooks/hooks-cursor.json.tmpl +++ b/lib/templates/hooks/hooks-cursor.json.tmpl @@ -1,3 +1,4 @@ +{{! fixes: cursor.4_hooks.hooks_json.exists }} { "hooks": {} } diff --git a/lib/templates/hooks/hooks.json.tmpl b/lib/templates/hooks/hooks.json.tmpl index deffac9..9dedd6f 100644 --- a/lib/templates/hooks/hooks.json.tmpl +++ b/lib/templates/hooks/hooks.json.tmpl @@ -1,3 +1,4 @@ +{{! fixes: claude.4_hooks.hooks_json.exists }} { "hooks": {} } diff --git a/lib/templates/hooks/session-start.sh b/lib/templates/hooks/session-start.sh index 4fc0318..7269da3 100644 --- a/lib/templates/hooks/session-start.sh +++ b/lib/templates/hooks/session-start.sh @@ -27,10 +27,10 @@ session_context="\nThis plugin uses the superpowers portability patte # Output context injection as JSON. # Cursor hooks expect additional_context (snake_case). # Claude Code hooks expect hookSpecificOutput.additionalContext (nested). -# Copilot CLI and others expect additionalContext (top-level, SDK standard). +# Other platforms expect additionalContext (top-level, SDK standard). if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then printf '{\n "additional_context": "%s"\n}\n' "$session_context" -elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then +elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context" else printf '{\n "additionalContext": "%s"\n}\n' "$session_context" diff --git a/lib/templates/install-docs/adding-platform/antigravity.md b/lib/templates/install-docs/adding-platform/antigravity.md new file mode 100644 index 0000000..7d2170c --- /dev/null +++ b/lib/templates/install-docs/adding-platform/antigravity.md @@ -0,0 +1,11 @@ +### Antigravity + +Copy your skills into Antigravity's expected directory structure: + +```bash +cp -r /path/to/existing/{{name}}/skills .agents/skills/{{name}}/ +``` + +Create `antigravity/package.json` with your plugin metadata (name, version, description, keywords). + +Restart Antigravity to pick up the new skills. diff --git a/lib/templates/install-docs/adding-platform/copilot-cli.md b/lib/templates/install-docs/adding-platform/copilot-cli.md deleted file mode 100644 index 736c1ff..0000000 --- a/lib/templates/install-docs/adding-platform/copilot-cli.md +++ /dev/null @@ -1,7 +0,0 @@ -### Copilot CLI - -Work from the cloned directory — skills are auto-discovered from `skills/`. Or symlink into your project: - -```bash -ln -s /path/to/existing/{{name}}/skills skills/{{name}} -``` diff --git a/lib/templates/install-docs/adding-platform/openclaw.md b/lib/templates/install-docs/adding-platform/openclaw.md new file mode 100644 index 0000000..8155a30 --- /dev/null +++ b/lib/templates/install-docs/adding-platform/openclaw.md @@ -0,0 +1,11 @@ +### OpenClaw + +Create `openclaw/openclaw.plugin.json` with your plugin metadata (id, name, description, version, skills list). + +Add the plugin to your local OpenClaw config for testing: + +```bash +openclaw plugins install --path /path/to/existing/{{name}} +``` + +Verify with `openclaw plugins list` to confirm the plugin loads correctly. diff --git a/lib/templates/install-docs/adding-platform/opencode.md b/lib/templates/install-docs/adding-platform/opencode.md deleted file mode 100644 index 26c3737..0000000 --- a/lib/templates/install-docs/adding-platform/opencode.md +++ /dev/null @@ -1,17 +0,0 @@ -### OpenCode - -Symlink the plugin entrypoint from your existing checkout (do not copy — it resolves paths relative to the repo root): - -```bash -ln -s /path/to/existing/{{name}}/.opencode/plugins/{{name}}.js .opencode/plugins/{{name}}.js -``` - -Or add the checkout path to your `opencode.json`: - -```json -{ - "plugin": ["/path/to/existing/{{name}}"] -} -``` - -Requires [Bun](https://bun.sh). diff --git a/lib/templates/install-docs/antigravity.md b/lib/templates/install-docs/antigravity.md new file mode 100644 index 0000000..7e718fc --- /dev/null +++ b/lib/templates/install-docs/antigravity.md @@ -0,0 +1,29 @@ +## Antigravity + +### Skill-only install + +Copy the skills directory into your workspace or global config: + +```bash +# Workspace-local (per-project) +cp -r /path/to/{{name}}/skills .agents/skills/{{name}}/ + +# Global (available in all workspaces) +cp -r /path/to/{{name}}/skills ~/.gemini/antigravity/skills/{{name}}/ +``` + +### Extension install + +Install from the OpenVSX registry or from a local `.vsix` package: + +```bash +# From OpenVSX (if published) +antigravity --install-extension {{name}} + +# From local .vsix file +antigravity --install-extension /path/to/{{name}}.vsix +``` + +### Verify + +Start a new Antigravity session and check that skills from {{displayName}} appear in the skill listing at conversation start. Skills should be listed when the agent enumerates available capabilities. diff --git a/lib/templates/install-docs/copilot-cli.md b/lib/templates/install-docs/copilot-cli.md deleted file mode 100644 index 9124081..0000000 --- a/lib/templates/install-docs/copilot-cli.md +++ /dev/null @@ -1,33 +0,0 @@ -## Copilot CLI - -### Skill install - -Skills are auto-discovered from the `skills/` directory. Clone the repo and skills will be available: - -```bash -git clone {{repository}} -``` - -Alternatively, install individual skills: - -```bash -gh skill install {{repository}} -``` - -### Context - -Copilot reads `.github/copilot-instructions.md` for repo-wide context. It also reads `AGENTS.md`, `CLAUDE.md`, and `GEMINI.md` from the project root. - -### Custom agents - -Custom agents are in `.github/agents/`. They are auto-discovered when the repo is the current working directory. - -### Verify - -Start Copilot CLI in the repo directory and check that skills appear: - -```bash -copilot -``` - -Type `/` to see available skills. diff --git a/lib/templates/install-docs/openclaw.md b/lib/templates/install-docs/openclaw.md new file mode 100644 index 0000000..f77f830 --- /dev/null +++ b/lib/templates/install-docs/openclaw.md @@ -0,0 +1,46 @@ +## OpenClaw + +### ClawHub install + +Install directly from the ClawHub registry: + +```bash +openclaw plugins install {{name}} +``` + +### npm install + +If published to npm under an org scope: + +```bash +openclaw plugins install @org/openclaw-{{name}} +``` + +### Local install + +Add the plugin path to `plugins.load.paths` in your `openclaw.json` config: + +```json +{ + "plugins": { + "load": { + "paths": ["/path/to/{{name}}"] + } + } +} +``` + +### Bundle detection + +OpenClaw can auto-detect plugins that use the `.claude-plugin/` directory layout. If your plugin already has a `.claude-plugin/plugin.json`, OpenClaw may load it without conversion. Test with a local install first. + +### Verify + +Check that the plugin loads successfully in the gateway logs: + +```bash +openclaw plugins list +openclaw logs --filter="plugin:{{name}}" +``` + +Skills from {{displayName}} should appear in the plugin listing and be invocable from chat. diff --git a/lib/templates/install-docs/opencode.md b/lib/templates/install-docs/opencode.md deleted file mode 100644 index 7d08463..0000000 --- a/lib/templates/install-docs/opencode.md +++ /dev/null @@ -1,27 +0,0 @@ -## OpenCode - -### Local plugin install - -Copy `.opencode/plugins/{{name}}.js` to your project's `.opencode/plugins/` directory, or to `~/.config/opencode/plugins/` for global install. - -### npm install (if published) - -Add to your `opencode.json`: - -```json -{ - "plugin": ["{{name}}"] -} -``` - -### Context file - -OpenCode uses `AGENTS.md` as its primary context file. If both `AGENTS.md` and `CLAUDE.md` exist, only `AGENTS.md` is loaded. - -### Verify - -Restart OpenCode and check that skills are listed when the agent invokes the `skill` tool. - -### Requirements - -OpenCode requires [Bun](https://bun.sh) for plugin loading. diff --git a/lib/templates/install-docs/publishing/antigravity.md b/lib/templates/install-docs/publishing/antigravity.md new file mode 100644 index 0000000..6cee9d3 --- /dev/null +++ b/lib/templates/install-docs/publishing/antigravity.md @@ -0,0 +1,19 @@ +## Antigravity + +Public registry at [open-vsx.org](https://open-vsx.org/) (open, community-maintained). + +### Publishing + +1. Ensure `antigravity/package.json` manifest is present with all required fields +2. Package the extension: `antigravity package` +3. Create an account and namespace on [open-vsx.org](https://open-vsx.org/) +4. Publish: `antigravity publish` or upload the `.vsix` file via the web UI + +### How users find and install {{displayName}} + +- Search for **{{name}}** on [open-vsx.org](https://open-vsx.org/) +- Install via CLI: `antigravity --install-extension {{name}}` + +### Team distribution + +Share the `.vsix` file directly or host on a private extension registry. diff --git a/lib/templates/install-docs/publishing/copilot-cli.md b/lib/templates/install-docs/publishing/copilot-cli.md deleted file mode 100644 index d57ff4c..0000000 --- a/lib/templates/install-docs/publishing/copilot-cli.md +++ /dev/null @@ -1,28 +0,0 @@ -## Copilot CLI - -Skills published via GitHub CLI (v2.90.0+). - -### Publishing - -```bash -gh skill publish [--fix] -``` - -Validates against the Agent Skills spec. No formal review — skills are published to the GitHub repository. - -### How users find and install {{displayName}} - -```bash -gh skill search {{name}} -gh skill preview {{repository}} {{name}} -gh skill install {{repository}} -``` - -### Third-party registries - -- [skills.sh](https://skills.sh) — community directory with 300k+ monthly views -- [github/awesome-copilot](https://github.com/github/awesome-copilot) — GitHub's curated collection - -### Security note - -Always recommend users run `gh skill preview` before installing. GitHub does not vet third-party skills. diff --git a/lib/templates/install-docs/publishing/openclaw.md b/lib/templates/install-docs/publishing/openclaw.md new file mode 100644 index 0000000..9700756 --- /dev/null +++ b/lib/templates/install-docs/publishing/openclaw.md @@ -0,0 +1,21 @@ +## OpenClaw + +Public registries: [ClawHub](https://clawhub.dev) (curated) and npm (open). + +### Publishing to ClawHub + +1. Ensure `openclaw/openclaw.plugin.json` manifest is present with all required fields +2. Authenticate: `openclaw auth login` +3. Publish: `openclaw plugins publish` +4. Submissions are reviewed before appearing in the registry + +### Publishing to npm + +1. Add an npm-compatible `package.json` to the `openclaw/` directory +2. Publish: `npm publish --access public` +3. Users install with: `openclaw plugins install @org/openclaw-{{name}}` + +### How users find and install {{displayName}} + +- Browse [ClawHub](https://clawhub.dev) or search npm for `openclaw-{{name}}` +- Install via CLI: `openclaw plugins install {{name}}` diff --git a/lib/templates/install-docs/publishing/opencode.md b/lib/templates/install-docs/publishing/opencode.md deleted file mode 100644 index 58a5539..0000000 --- a/lib/templates/install-docs/publishing/opencode.md +++ /dev/null @@ -1,23 +0,0 @@ -## OpenCode - -No marketplace. Distribution via npm or filesystem. - -### Publishing - -Publish the plugin as an npm package. No submission or review process. - -### How users find and install {{displayName}} - -**npm:** -Add to `opencode.json`: - -```json -{ - "plugin": ["{{name}}"] -} -``` - -**Local files:** -Copy `.opencode/plugins/{{name}}.js` to `.opencode/plugins/` (project) or `~/.config/opencode/plugins/` (global). - -Requires [Bun](https://bun.sh) for plugin loading. diff --git a/lib/templates/manifests/antigravity/package.json.tmpl b/lib/templates/manifests/antigravity/package.json.tmpl new file mode 100644 index 0000000..69fd6b9 --- /dev/null +++ b/lib/templates/manifests/antigravity/package.json.tmpl @@ -0,0 +1,12 @@ +{{! fixes: antigravity.1_manifest.package_json.exists }} +{{! fixes: antigravity.1_manifest.package_json.required_fields }} +{ + "name": "{{name}}", + "displayName": "{{displayName}}", + "version": "{{version}}", + "description": "{{description}}", + "publisher": "{{author.name}}", + "author": "{{author.name}}", + "keywords": {{keywords}}, + "categories": ["AI", "Other"] +} diff --git a/lib/templates/manifests/claude-plugin/marketplace.json.tmpl b/lib/templates/manifests/claude-plugin/marketplace.json.tmpl index ed90d8a..b34c75f 100644 --- a/lib/templates/manifests/claude-plugin/marketplace.json.tmpl +++ b/lib/templates/manifests/claude-plugin/marketplace.json.tmpl @@ -1,3 +1,4 @@ +{{! fixes: claude.1_manifest.marketplace_json.exists }} { "name": "{{marketplaceName}}", "description": "Development marketplace for {{name}}", diff --git a/lib/templates/manifests/claude-plugin/plugin.json.tmpl b/lib/templates/manifests/claude-plugin/plugin.json.tmpl index 2fcd93d..2dd8fbe 100644 --- a/lib/templates/manifests/claude-plugin/plugin.json.tmpl +++ b/lib/templates/manifests/claude-plugin/plugin.json.tmpl @@ -1,3 +1,5 @@ +{{! fixes: claude.1_manifest.plugin_json.exists }} +{{! fixes: claude.1_manifest.plugin_json.required_fields }} { "name": "{{name}}", "description": "{{description}}", diff --git a/lib/templates/manifests/codex-plugin/marketplace.json.tmpl b/lib/templates/manifests/codex-plugin/marketplace.json.tmpl index ca02650..09d0ed1 100644 --- a/lib/templates/manifests/codex-plugin/marketplace.json.tmpl +++ b/lib/templates/manifests/codex-plugin/marketplace.json.tmpl @@ -1,3 +1,4 @@ +{{! fixes: codex.1_manifest.marketplace_json.exists }} { "name": "{{marketplaceName}}", "interface": { diff --git a/lib/templates/manifests/codex-plugin/plugin.json.tmpl b/lib/templates/manifests/codex-plugin/plugin.json.tmpl index 622e628..b15dc8a 100644 --- a/lib/templates/manifests/codex-plugin/plugin.json.tmpl +++ b/lib/templates/manifests/codex-plugin/plugin.json.tmpl @@ -1,3 +1,5 @@ +{{! fixes: codex.1_manifest.plugin_json.exists }} +{{! fixes: codex.1_manifest.plugin_json.required_fields }} { "name": "{{name}}", "description": "{{description}}", diff --git a/lib/templates/manifests/cursor-plugin/marketplace.json.tmpl b/lib/templates/manifests/cursor-plugin/marketplace.json.tmpl index f288561..dc3ac74 100644 --- a/lib/templates/manifests/cursor-plugin/marketplace.json.tmpl +++ b/lib/templates/manifests/cursor-plugin/marketplace.json.tmpl @@ -1,3 +1,4 @@ +{{! fixes: cursor.1_manifest.marketplace_json.exists }} { "name": "{{marketplaceName}}", "owner": { diff --git a/lib/templates/manifests/cursor-plugin/plugin.json.tmpl b/lib/templates/manifests/cursor-plugin/plugin.json.tmpl index af851c1..cad4c5b 100644 --- a/lib/templates/manifests/cursor-plugin/plugin.json.tmpl +++ b/lib/templates/manifests/cursor-plugin/plugin.json.tmpl @@ -1,3 +1,6 @@ +{{! fixes: cursor.1_manifest.plugin_json.exists }} +{{! fixes: cursor.1_manifest.plugin_json.required_fields }} +{{! fixes: cursor.1_manifest.plugin_json.conditional_keys }} { "name": "{{name}}", "description": "{{description}}", diff --git a/lib/templates/manifests/gemini-extension.json.tmpl b/lib/templates/manifests/gemini-extension.json.tmpl index dc9f08c..01d1caa 100644 --- a/lib/templates/manifests/gemini-extension.json.tmpl +++ b/lib/templates/manifests/gemini-extension.json.tmpl @@ -1,3 +1,6 @@ +{{! fixes: gemini.1_manifest.extension_json.exists }} +{{! fixes: gemini.1_manifest.extension_json.required_fields }} +{{! fixes: gemini.1_manifest.extension_json.context_filename }} { "name": "{{name}}", "description": "{{description}}", diff --git a/lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl b/lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl new file mode 100644 index 0000000..a599bce --- /dev/null +++ b/lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl @@ -0,0 +1,14 @@ +{{! fixes: openclaw.1_manifest.openclaw_json.exists }} +{{! fixes: openclaw.1_manifest.openclaw_json.required_fields }} +{ + "id": "{{name}}", + "name": "{{displayName}}", + "description": "{{description}}", + "version": "{{version}}", + "skills": {{skillsList}}, + "configSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false + } +} diff --git a/lib/templates/manifests/opencode-plugin.js.tmpl b/lib/templates/manifests/opencode-plugin.js.tmpl deleted file mode 100644 index afb8270..0000000 --- a/lib/templates/manifests/opencode-plugin.js.tmpl +++ /dev/null @@ -1,7 +0,0 @@ -// OpenCode plugin registration for {{name}} -// Skills are loaded from ./skills/ by the OpenCode runtime. -export default { - name: "{{name}}", - description: "{{description}}", - skills: "./skills/", -}; diff --git a/lib/templates/manifests/package.json.tmpl b/lib/templates/manifests/package.json.tmpl deleted file mode 100644 index 5372b93..0000000 --- a/lib/templates/manifests/package.json.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "{{name}}", - "version": "{{version}}", - "type": "module", - "main": "{{opencodeMain}}" -} diff --git a/package.json b/package.json index 51cdf35..c40a5f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "skill-portability", - "version": "0.1.0", - "type": "module", - "main": ".opencode/plugins/skill-portability.js" + "version": "0.1.1", + "type": "module" } diff --git a/skills/assessing-plugin-portability/SKILL.md b/skills/assessing-plugin-portability/SKILL.md deleted file mode 100644 index 865d496..0000000 --- a/skills/assessing-plugin-portability/SKILL.md +++ /dev/null @@ -1,442 +0,0 @@ ---- -name: assessing-plugin-portability -description: > - Use when you need to assess a plugin for multi-platform portability. Classifies - repo shape, scores readiness per platform using a 7-category rubric, detects - structural blockers, and recommends an uplift target. Read-only — makes no changes. -allowed-tools: Read, Glob, Grep ---- - -# Assessing Plugin Portability - -Assess a plugin repo and report portability gaps across all platforms. Makes no changes. - -**Input:** `plugin_path` (string, required) — Path to the plugin root directory. -**Output:** Complete portability assessment with per-platform scores and recommendation. - -> **Detection Algorithm:** `lib/patterns/detection-algorithm.md` -> **Rubric Framework:** `lib/patterns/rubric-framework.md` -> **Platform Rules:** `lib/patterns/platforms/.md` -> **Injection Checks:** `lib/patterns/injection-checks.md` - ---- - -## Overview - -| Phase | Description | -| ----- | ----------- | -| **Phase 1: Detect** | Scan metadata, elect canonical, build model, classify shape | -| **Phase 2: Inventory** | Discover all assets across all platform conventions | -| **Phase 3: Score** | Run per-platform rubric, detect blockers | -| **Phase 4: Recommend** | Choose uplift target, per-platform recommendations | -| **Phase 5: Report** | Print full assessment report | - -**Minimum starting state:** At least one `skills/*/SKILL.md` with frontmatter, or any platform manifest file. - ---- - -## Phase 1: Detect - -### Step 1.1: Scan and Infer - -```pseudocode -DETECT(plugin_path): - computed.sources = scan_metadata_sources(plugin_path) # D1 - IF len(computed.sources) == 0: - DISPLAY "No recognisable plugin signals found in {plugin_path}." - EXIT - - computed.canonical = elect_canonical(computed.sources) # D2 - computed.metadata = build_metadata_model(computed.sources) # D3 - print_inference_summary(computed.metadata, computed.canonical) # D4 - computed.shape = classify_shape(computed.sources) # D5 -``` - ---- - -## Phase 2: Inventory - -### Step 2.1: Check Platform Manifests - -Check all 10 manifest paths across 6 platforms, recording `{ platform, path, status }`: - -```pseudocode -INVENTORY_MANIFESTS(computed): - manifest_checks = [ - { platform: "claude-code", path: ".claude-plugin/plugin.json" }, - { platform: "claude-code", path: ".claude-plugin/marketplace.json" }, - { platform: "cursor", path: ".cursor-plugin/plugin.json" }, - { platform: "gemini-cli", path: "gemini-extension.json" }, - { platform: "gemini-cli", path: "GEMINI.md" }, - { platform: "opencode", path: ".opencode/plugins/" + computed.metadata.name + ".js" }, - { platform: "codex", path: ".codex-plugin/plugin.json" }, - { platform: "codex", path: ".agents/plugins/marketplace.json" }, - { platform: "copilot-cli", path: "package.json" }, - { platform: "copilot-cli", path: ".github/copilot-instructions.md" } - ] - - computed.manifest_results = [] - FOR check IN manifest_checks: - status = IF file_exists(plugin_path + "/" + check.path) THEN "PRESENT" ELSE "MISSING" - computed.manifest_results.append({ platform: check.platform, path: check.path, status: status }) -``` - -### Step 2.2: Check Context Files - -```pseudocode -INVENTORY_CONTEXT_FILES(computed): - context_checks = [ - "CLAUDE.md", - "AGENTS.md", - "GEMINI.md", - ".github/copilot-instructions.md", - ".codex/INSTALL.md" - ] - - computed.context_results = [] - FOR path IN context_checks: - status = IF file_exists(plugin_path + "/" + path) THEN "PRESENT" ELSE "MISSING" - computed.context_results.append({ path: path, status: status }) -``` - -### Step 2.3: Check Per-Skill Sidecars - -Use `skill.dir` (directory basename) — not `skill.name` (frontmatter value) — for path construction. - -```pseudocode -INVENTORY_SIDECARS(computed): - computed.skills = Glob(plugin_path + "/skills/*/SKILL.md") - sidecar_files = ["copilot-tools.md", "codex-tools.md", "gemini-tools.md"] - computed.sidecar_results = [] - - FOR skill IN computed.skills: - FOR sidecar IN sidecar_files: - target = "skills/" + skill.dir + "/references/" + sidecar - status = IF file_exists(plugin_path + "/" + target) THEN "PRESENT" ELSE "MISSING" - computed.sidecar_results.append({ skill: skill.dir, file: sidecar, status: status }) -``` - -### Step 2.4: Check Hooks - -```pseudocode -INVENTORY_HOOKS(computed): - hook_checks = [ - "hooks/hooks.json", - "hooks/hooks-cursor.json", - ".github/hooks/", - "hooks/run-hook.cmd" - ] - - computed.hook_results = [] - FOR path IN hook_checks: - status = IF file_exists(plugin_path + "/" + path) THEN "PRESENT" ELSE "MISSING" - computed.hook_results.append({ path: path, status: status }) -``` - -### Step 2.5: Check Frontmatter Compatibility - -```pseudocode -INVENTORY_FRONTMATTER(computed): - computed.frontmatter_results = [] - FOR skill IN computed.skills: - frontmatter = parse_yaml_frontmatter(skill.content) - IF frontmatter.name AND frontmatter.description: - status = "COMPATIBLE" - ELSE: - status = "MISSING FRONTMATTER" - computed.frontmatter_results.append({ skill: skill.dir, status: status }) -``` - -### Step 2.6: Check Session-Start Injection - -See `lib/patterns/injection-checks.md` for the 8-component verification. - -```pseudocode -INVENTORY_INJECTION(computed): - using_path = "skills/using-" + computed.metadata.name + "/SKILL.md" - IF NOT file_exists(plugin_path + "/" + using_path): - computed.injection_status = "NOT CONFIGURED" - RETURN - - computed.injection_results = check_injection_components(computed) - computed.injection_summary = compute_injection_summary(computed.injection_results) -``` - -### Step 2.7: Check Context File Completeness - -```pseudocode -INVENTORY_CONTEXT_COMPLETENESS(computed): - computed.completeness_issues = [] - - IF file_exists(plugin_path + "/GEMINI.md"): - content = Read(plugin_path + "/GEMINI.md") - FOR skill IN computed.skills: - IF "@./skills/" + skill.dir + "/SKILL.md" NOT IN content: - computed.completeness_issues.append("GEMINI.md missing @include for " + skill.dir) - IF "@./skills/" + skill.dir + "/references/gemini-tools.md" NOT IN content: - computed.completeness_issues.append("GEMINI.md missing gemini-tools include for " + skill.dir) - ELSE: - computed.completeness_issues.append("GEMINI.md: MISSING — cannot check includes") - - IF file_exists(plugin_path + "/AGENTS.md"): - content = Read(plugin_path + "/AGENTS.md") - FOR skill IN computed.skills: - IF "skills/" + skill.dir + "/SKILL.md" NOT IN content: - computed.completeness_issues.append("AGENTS.md missing reference for " + skill.dir) - ELSE: - computed.completeness_issues.append("AGENTS.md: MISSING — cannot check skill references") - - # Copilot-specific - computed.copilot_checks = [ - Glob(plugin_path + "/.github/instructions/*.instructions.md"), - Glob(plugin_path + "/.github/agents/*.agent.md") - ] - - # Gemini-specific - computed.gemini_checks = [ - Glob(plugin_path + "/commands/*.toml"), - Glob(plugin_path + "/policies/*.toml") - ] - - # MCP configs - computed.mcp_checks = [] - FOR path IN [".mcp.json", "mcp.json"]: - IF file_exists(plugin_path + "/" + path): - computed.mcp_checks.append({ path: path, status: "PRESENT" }) -``` - ---- - -## Phase 3: Score - -### Step 3.1: Run Per-Platform Rubric - -See `lib/patterns/rubric-framework.md` for category definitions and scoring scale (0-3 per category, max 21 per platform). - -```pseudocode -SCORE(computed): - platforms = ["claude-code", "cursor", "gemini-cli", "opencode", "copilot-cli", "codex"] - - FOR platform IN platforms: - rules = load_platform_rules(platform) # from lib/patterns/platforms/ - computed.scores[platform] = {} - FOR category IN rules.categories: - computed.scores[platform][category.name] = evaluate(category, computed) - computed.scores[platform].total = sum(computed.scores[platform][c] for c IN rules.categories) - computed.scores[platform].band = classify_band(computed.scores[platform].total) -``` - -### Step 3.2: Detect Blockers - -```pseudocode -DETECT_BLOCKERS(computed): - computed.blockers = [] - - # Critical: no trustworthy metadata - IF all metadata fields derived from hard fallbacks only: - computed.blockers.append({ - severity: "critical", - description: "No trustworthy metadata source — all fields from hard fallbacks" - }) - - # Major: unresolved tool assumptions - FOR skill IN computed.skills: - IF skill references platform-specific tools AND skill.dir not in sidecar_results with status PRESENT: - computed.blockers.append({ - severity: "major", - description: "Unresolved tool assumptions in skills/" + skill.dir - }) - - # Major: hook env hard-coding - FOR hook_file IN ["hooks/hooks.json", "hooks/hooks-cursor.json"]: - IF file_exists(hook_file): - content = Read(plugin_path + "/" + hook_file) - IF "CLAUDE_PLUGIN_ROOT" IN content AND env_branching_absent(content): - computed.blockers.append({ - severity: "major", - description: "Hook env hard-coding in " + hook_file + " (no env branching)" - }) - - # Major: docs/structure mismatch - IF install_docs_reference_paths_that_dont_exist(computed): - computed.blockers.append({ - severity: "major", - description: "Install docs describe paths that don't exist in repo" - }) - - # Minor: GEMINI.md import gaps - gemini_gaps = [i for i IN computed.completeness_issues if "GEMINI.md missing" IN i] - IF len(gemini_gaps) > 0: - computed.blockers.append({ - severity: "minor", - description: "GEMINI.md import gaps: " + str(len(gemini_gaps)) + " missing includes" - }) -``` - ---- - -## Phase 4: Recommend - -### Step 4.1: Choose Uplift Target - -```pseudocode -RECOMMEND_TARGET(computed): - IF computed.shape == "bare-skill-repo": - IF len(computed.skills) <= 3: - computed.recommendation = "skill-first" - ELSE: - computed.recommendation = "full-portable-plugin" - - ELIF computed.shape == "single-platform-plugin": - computed.recommendation = "full-portable-plugin" - - ELIF computed.shape == "multi-platform-source": - computed.recommendation = "full-portable-plugin" - - ELIF computed.shape == "curated-distribution": - computed.recommendation = "curated-note-only" - - ELSE: - computed.recommendation = "full-portable-plugin" -``` - -### Step 4.2: Choose Codex Path - -```pseudocode -RECOMMEND_CODEX(computed): - IF ".codex-plugin/plugin.json" PRESENT in computed.manifest_results: - computed.codex_rec = "native-plugin-packaging" - ELIF computed.shape == "bare-skill-repo" AND len(computed.skills) > 0: - computed.codex_rec = "native-skill-discovery" - ELIF computed.shape IN ["single-platform-plugin", "multi-platform-source"]: - computed.codex_rec = "native-plugin-packaging" - ELIF computed.shape == "curated-distribution": - computed.codex_rec = "curated-package-note" - ELSE: - computed.codex_rec = "native-plugin-packaging" -``` - -### Step 4.3: Per-Platform Action Summary - -```pseudocode -SUMMARISE_ACTIONS(computed): - computed.actions = {} - FOR platform IN platforms: - band = computed.scores[platform].band - IF band == "strong": - computed.actions[platform] = "No action required" - ELIF band == "viable": - computed.actions[platform] = "Minor gaps — review category detail" - ELIF band == "partial": - computed.actions[platform] = "Significant gaps — uplift recommended" - ELSE: # weak - computed.actions[platform] = "Full uplift required" -``` - ---- - -## Phase 5: Report - -### Step 5.1: Print Assessment - -```text -# Portability Assessment: {name} v{version} - -## Repo Shape -{shape} -Metadata inferred from: {canonical.path} - -## Platform Scores -| Platform | Score | Band | Action | -|-------------|-------|---------|------------------------------------| -| claude-code | X/21 | {band} | {action} | -| cursor | X/21 | {band} | {action} | -| gemini-cli | X/21 | {band} | {action} | -| opencode | X/21 | {band} | {action} | -| copilot-cli | X/21 | {band} | {action} | -| codex | X/21 | {band} | {action} | - -### Per-Platform Detail - -#### {platform} -| Category | Score | -|---------------------|-------| -| Manifest packaging | X/3 | -| Skill compatibility | X/3 | -| Context delivery | X/3 | -| Hook portability | X/3 | -| Tool mapping | X/3 | -| Install readiness | X/3 | -| Runtime adapters | X/3 | -| **Total** | X/21 | - -(repeat for each platform) - -## Blockers -{severity}: {description} -(one entry per blocker; "None detected." if empty) - -## Uplift Recommendation -Target: {recommendation} -Codex path: {codex_rec} - -## Required Artifacts -(per platform where band != strong: list missing artifacts) - -### {platform} — {band} -- {missing artifact path} -- ... - -## Session-Start Injection -{status} -(IF configured: component-by-component status table) - -## Summary -Run the uplifting-a-plugin skill to generate all missing artifacts automatically. -``` - ---- - -## State Flow - -```text -Phase 1 Phase 2 Phase 3 -───────────────────────────────────────────────────────────── -computed computed.manifest_results computed.scores[platform] - .sources .context_results .blockers - .canonical .sidecar_results - .metadata .hook_results - .shape .frontmatter_results - .injection_results - .completeness_issues - .copilot_checks - .gemini_checks - .mcp_checks - -Phase 4 Phase 5 -────────────────────────────── -computed Report - .recommendation (displayed) - .codex_rec - .actions -``` - ---- - -## Reference Documentation - -- **Detection Algorithm:** `lib/patterns/detection-algorithm.md` (shared) -- **Rubric Framework:** `lib/patterns/rubric-framework.md` -- **Platform Rules:** `lib/patterns/platforms/claude-code.md` -- **Platform Rules:** `lib/patterns/platforms/cursor.md` -- **Platform Rules:** `lib/patterns/platforms/gemini-cli.md` -- **Platform Rules:** `lib/patterns/platforms/opencode.md` -- **Platform Rules:** `lib/patterns/platforms/copilot-cli.md` -- **Platform Rules:** `lib/patterns/platforms/codex.md` -- **Injection Checks:** `lib/patterns/injection-checks.md` - ---- - -## Related Skills - -- **Uplift plugin:** `skills/uplifting-a-plugin/SKILL.md` diff --git a/skills/assessing-plugin-portability/references/copilot-tools.md b/skills/assessing-plugin-portability/references/copilot-tools.md deleted file mode 100644 index 604a011..0000000 --- a/skills/assessing-plugin-portability/references/copilot-tools.md +++ /dev/null @@ -1 +0,0 @@ -See [lib/references/copilot-tools.md](../../lib/references/copilot-tools.md) for the full Copilot CLI tool mapping. diff --git a/skills/plugin-portability/SKILL.md b/skills/plugin-portability/SKILL.md new file mode 100644 index 0000000..7cc0de9 --- /dev/null +++ b/skills/plugin-portability/SKILL.md @@ -0,0 +1,279 @@ +--- +name: plugin-portability +description: > + Use when you need to assess or uplift a plugin for multi-platform portability. + Gauges intent upfront (diagnostic or uplift, which platforms, uplift target), + then runs shared detection, inventory, and condition-driven scoring across all + platforms. For uplift, generates missing artifacts with fixes: annotations + linked to rubric conditions. Supports incremental uplift for viable+ platforms. + Platforms: Claude Code, Cursor, Gemini CLI, Codex, Antigravity, OpenClaw. +allowed-tools: Read, Write, Edit, Bash, Glob, Grep +--- + +# Plugin Portability + +Assess or uplift a plugin for multi-platform portability. Single entry point for both modes. + +**Input:** `plugin_path` (string, required) -- path to plugin root directory. +**Output:** Assessment report; if uplift mode, also generated artifacts with `fixes:` annotations. + +> **External references:** +> `lib/patterns/detection-algorithm.md` | `lib/patterns/inventory.md` | `lib/patterns/rubric-framework.md` +> `lib/patterns/platforms/*.yaml` | `lib/references/platform-mappings.md` | `lib/patterns/manifest-generation.md` +> `lib/patterns/hook-merging.md` | `lib/patterns/bootstrapping.md` | `lib/patterns/injection-checks.md` +> `lib/templates/install-docs/` | `lib/templates/manifests/` | `lib/templates/context-files/` + +--- + +## Overview + +| Phase | Description | Runs | +|-------|-------------|------| +| **0a: Intent** | Q1 (mode) + Q2 (platforms) | Always | +| **1: Detect** | Scan metadata, elect canonical, classify shape | Always | +| **2: Inventory** | Discover all assets, manifests, sidecars, hooks, injection | Always | +| **0b: Uplift Target** | Q3 (shape-derived recommendation, user confirms) | Uplift only | +| **3: Score** | Full condition-driven rubric per platform | Always | +| **4: Report** | Per-platform scores, blockers, uplift strategy | Always; **assess mode STOPS here** | +| **5: Generate** | Manifests, context files, sidecars | Uplift only | +| **6: Port** | Hook adaptation across platforms | Uplift only | +| **7: Document** | Install docs per platform | Uplift only | +| **8: Bootstrap** | Session-start injection (opt-in) | Uplift only | +| **9: Summary** | Files created, merged, skipped, manual actions | Uplift only | + +--- + +## Phase 0a: Intent + +Runs BEFORE any file scanning. Two structured questions. + +```pseudocode +INTENT_UPFRONT(): + # Q1: Mode + mode = AskUserQuestion( + question: "Assess only (diagnostic, read-only) or Uplift (generate missing artifacts)?", + header: "Mode", + options: [ + { label: "Assess", description: "Score portability across platforms. Read-only, no changes." }, + { label: "Uplift", description: "Generate missing platform artifacts to close portability gaps." } + ], + multiSelect: false + ) + + # Q2: Platforms + platforms = AskUserQuestion( + question: "Which platforms to target?", + header: "Platforms", + options: [ + { label: "All platforms", description: "Claude Code, Cursor, Gemini, Codex, Antigravity, OpenClaw" }, + { label: "Select platforms", description: "Choose specific platforms to assess or uplift" } + ], + multiSelect: false + ) + + IF platforms == "Select platforms": + platforms = AskUserQuestion( + question: "Select target platforms:", + header: "Platforms", + options: [ + { label: "Claude Code", description: "Reference platform" }, + { label: "Cursor", description: "VS Code fork with rules, hooks, MCP" }, + { label: "Gemini CLI", description: "Google CLI with @ includes and settings-based hooks" }, + { label: "Codex", description: "OpenAI CLI with TOML agents and spawn_agent" }, + { label: "Antigravity", description: "Google VS Code fork, OpenVSX, .agents/skills/" }, + { label: "OpenClaw", description: "TypeScript gateway with plugin SDK hooks" } + ], + multiSelect: true + ) + ELSE: + platforms = ["claude-code", "cursor", "gemini-cli", "codex", "antigravity", "openclaw"] + + RETURN { mode, platforms } +``` + +--- + +## Phase 1: Detect + +Follow `lib/patterns/detection-algorithm.md`. + +```pseudocode +DETECT(plugin_path): + computed.sources = scan_metadata_sources(plugin_path) + IF len(computed.sources) == 0: DISPLAY "No plugin signals found."; EXIT + computed.canonical = elect_canonical(computed.sources) + computed.metadata = build_metadata_model(computed.sources) + computed.shape = classify_shape(computed.sources) + print_inference_summary(computed.metadata, computed.canonical) +``` + +--- + +## Phase 2: Inventory + +Follow `lib/patterns/inventory.md`. Populates: + +- `computed.skills`, `computed.agents`, `computed.commands`, `computed.hooks` +- `computed.manifest_results`, `computed.context_results`, `computed.sidecar_results` +- `computed.frontmatter_results`, `computed.hook_results`, `computed.injection_results` +- `computed.existing_files` (for conflict detection during uplift) + +--- + +## Phase 0b: Uplift Target + +Runs AFTER detection (needs shape). Uplift mode only. + +```pseudocode +INTENT_UPLIFT_TARGET(computed): + IF intent.mode != "uplift": RETURN + + # Derive recommendation from shape + IF computed.shape == "bare-skill-repo" AND len(computed.skills) <= 3: + recommended = "skill-first" + reason = "Bare skill repo with " + str(len(computed.skills)) + " skills" + ELIF computed.shape == "curated-distribution": + recommended = "curated-note-only" + reason = "Curated distribution (marketplace, no source skills)" + ELSE: + recommended = "full-portable-plugin" + reason = computed.shape + " with " + str(len(computed.skills)) + " skills" + + # Q3: Build options with "(Recommended)" suffix on derived choice + options = [ + { label: "Skill-first", description: "Sidecars, tool mapping, context files only." }, + { label: "Full portable plugin", description: "Manifests, context, hooks, install docs -- everything." }, + { label: "Curated note only", description: "Documentation only. No generated artifacts." } + ] + FOR opt IN options: + IF opt.label.lower().startswith(recommended.replace("-", " ")): + opt.label += " (Recommended)" + options = [opt] + [o for o in options if o != opt] + + computed.uplift_target = AskUserQuestion( + question: "Repo detected as: " + reason + ". What level of uplift?", + header: "Uplift target", + options: options, + multiSelect: false + ) +``` + +--- + +## Phase 3: Score + +Always runs full scoring. References `lib/patterns/rubric-framework.md` and `lib/patterns/platforms/*.yaml`. + +```pseudocode +SCORE(computed, platforms): + FOR platform IN platforms: + rubric = load_yaml("lib/patterns/platforms/" + platform + ".yaml") + FOR category IN rubric.categories: + FOR condition IN category.conditions: + passed = jit_evaluate(condition, computed) + results[condition.id] = { passed, type: condition.type } + category.score = compute_category_score(category, results) + computed.scores[platform] = { categories, band, percentage, results, failing } + computed.blockers = detect_blockers(computed) + + # Auto-derive per-platform depth (uplift mode only) + IF intent.mode == "uplift": + FOR platform IN platforms: + IF computed.scores[platform].band IN ["strong", "viable"]: + computed.recommendation_for[platform] = "incremental" + ELSE: + computed.recommendation_for[platform] = "full" +``` + +**Two-layer uplift:** Shape target (Phase 0b, user-confirmed) controls WHAT categories. Per-platform depth (auto from scores) controls HOW MUCH. + +--- + +## Phase 4: Report + +```pseudocode +REPORT(computed, intent): + DISPLAY "## Repo Shape: " + computed.shape + DISPLAY metadata summary table + + FOR platform IN intent.platforms: + score = computed.scores[platform] + DISPLAY "## " + platform + " -- " + score.band + " (" + score.percentage + "%)" + FOR category IN score.categories: + DISPLAY "### " + category.name + ": " + category.score + "/3" + FOR condition IN category.conditions: + DISPLAY ("pass" IF results[condition.id].passed ELSE "FAIL") + " " + condition.id + + DISPLAY blockers + + IF intent.mode == "uplift": + DISPLAY uplift strategy per platform + artifacts to generate + IF intent.mode == "assess": + STOP +``` + +--- + +## Phases 5-9: Uplift + +### Allowed categories by uplift target + +```pseudocode +ALLOWED_CATEGORIES = { + "skill-first": ["2_skills", "3_context", "5_toolmap", "6_install"], + "full-portable-plugin": ["1_manifest", "2_skills", "3_context", "4_hooks", + "5_toolmap", "6_install", "7_runtime"], + "curated-note-only": ["6_install"] +} +``` + +### Template action types + +- **create** -- `template: manifests/foo.tmpl` -- render to target if file absent +- **merge** -- `template: manifests/foo.tmpl?merge` -- update existing file with missing fields +- **none** -- `template: null` -- assessment-only, reported as manual action + +### Phase 5: Generate + +```pseudocode +allowed = ALLOWED_CATEGORIES[computed.uplift_target] + +FOR platform IN intent.platforms: + FOR condition IN computed.scores[platform].failing: + IF condition.category NOT IN allowed: SKIP + IF NOT condition.template: + computed.manual_actions.append(condition); CONTINUE + + target_path = resolve_target_path(condition.template, platform) + action = parse_action(condition.template) # "create" or "merge" + + IF action == "create" AND NOT exists(target_path): + render(condition.template, computed.metadata) # fixes: {condition.id} + ELIF action == "merge" AND exists(target_path): + merge_update(condition.template, target_path) # fixes: {condition.id} + ELIF action == "create" AND exists(target_path): + computed.manual_actions.append(condition) # exists but fails -- manual review + ELSE: + render(condition.template, computed.metadata) # fixes: {condition.id} +``` + +### Phase 6: Port -- `lib/patterns/hook-merging.md` + +Skipped if `"4_hooks" NOT IN allowed`. + +### Phase 7: Document -- `lib/templates/install-docs/` + +Always runs (`"6_install"` is in all allowed sets). + +### Phase 8: Bootstrap -- `lib/patterns/bootstrapping.md` + +Skipped if `uplift_target == "curated-note-only"`. + +### Phase 9: Summary + +```pseudocode +SUMMARY(computed): + DISPLAY "## Files Created" -- computed.created_files + DISPLAY "## Files Updated" -- computed.merged_files + DISPLAY "## Manual Actions" -- computed.manual_actions (condition.id + check) +``` diff --git a/skills/assessing-plugin-portability/references/codex-tools.md b/skills/plugin-portability/references/codex-tools.md similarity index 100% rename from skills/assessing-plugin-portability/references/codex-tools.md rename to skills/plugin-portability/references/codex-tools.md diff --git a/skills/assessing-plugin-portability/references/gemini-tools.md b/skills/plugin-portability/references/gemini-tools.md similarity index 56% rename from skills/assessing-plugin-portability/references/gemini-tools.md rename to skills/plugin-portability/references/gemini-tools.md index dfdabb0..e4c4ed2 100644 --- a/skills/assessing-plugin-portability/references/gemini-tools.md +++ b/skills/plugin-portability/references/gemini-tools.md @@ -1 +1 @@ -See [lib/references/gemini-tools.md](../../lib/references/gemini-tools.md) for the full Gemini CLI tool mapping. +See [lib/references/gemini-tools.md](../../lib/references/gemini-tools.md) for the full Gemini tool mapping. diff --git a/skills/uplifting-a-plugin/SKILL.md b/skills/uplifting-a-plugin/SKILL.md deleted file mode 100644 index d43294b..0000000 --- a/skills/uplifting-a-plugin/SKILL.md +++ /dev/null @@ -1,580 +0,0 @@ ---- -name: uplifting-a-plugin -description: > - Use when you need to add multi-platform portability to any plugin. Accepts any - starting state — Claude, Cursor, Gemini, OpenCode, Copilot, Codex, or bare - SKILL.md files. Detects what exists, infers canonical metadata, generates every - missing platform artifact, ports hooks, produces install documentation, and - optionally configures session-start bootstrapping. -allowed-tools: Read, Write, Edit, Bash(readonly), Glob, Grep ---- - -# Uplifting a Plugin to Multi-Platform Portability - -Transform any plugin into a fully portable plugin. No platform is assumed. - -**Inputs:** - -- `plugin_path` (string, required) — Path to the plugin root directory. -- `platforms` (string, optional) — Comma-separated list of target platforms. If omitted, presents an interactive checklist. Valid: claude-code, cursor, gemini-cli, opencode, copilot-cli, codex, all. - -**Output:** Summary of created, skipped, and flagged files. - -> **Detection Algorithm:** `lib/patterns/detection-algorithm.md` -> **Manifest Schemas:** `lib/patterns/manifest-generation.md` -> **Manifest Templates:** `lib/templates/manifests/` -> **Context File Templates:** `lib/templates/context-files/` -> **Hook Merging:** `lib/patterns/hook-merging.md` -> **Bootstrapping:** `lib/patterns/bootstrapping.md` -> **Platform References:** `lib/references/copilot-tools.md`, `codex-tools.md`, `gemini-tools.md` -> **Hook Templates:** `lib/templates/hooks/session-start.sh`, `run-hook.cmd` -> **Install Doc Templates:** `lib/templates/install-docs/` - -## Overview - -| Phase | Description | -| ----- | ----------- | -| **Phase 1: Detect** | Scan metadata, elect canonical, build model, classify shape | -| **Phase 2: Inventory** | Discover assets, init tracking, check conflicts | -| **Phase 3: Recommend** | Recommend uplift target, confirm with user, select target platforms | -| **Phase 4: Generate** | Write missing manifests, context files, sidecars per platform | -| **Phase 5: Port** | Adapt hooks across platforms | -| **Phase 6: Document** | Generate install docs per platform in target repo | -| **Phase 7: Bootstrap** | Opt-in session-start injection | -| **Phase 8: Report** | Summary of created, skipped, flagged files | - -**Minimum starting state:** At least one `skills/*/SKILL.md` with `name` + `description` -frontmatter, or any platform manifest file. - -**Idempotent:** Running twice on the same repo produces no diff on the second run. - -## 1. Phase 1: Detect - -Run the shared detection algorithm. See `lib/patterns/detection-algorithm.md` for full detail. - -### 1.1 Scan and Infer - -```pseudocode -DETECT(plugin_path): - computed.sources = scan_metadata_sources(plugin_path) - - IF len(computed.sources) == 0: - DISPLAY "No recognisable plugin signals found in {plugin_path}." - DISPLAY "Provide at least one platform manifest or one skills/*/SKILL.md" - DISPLAY "with name and description frontmatter." - EXIT - - computed.canonical = elect_canonical(computed.sources) - computed.metadata = build_metadata_model(computed.sources) - computed.shape = classify_shape(computed.sources) - print_inference_summary(computed.metadata, computed.canonical) -``` - -## 2. Phase 2: Inventory - -### 2.1 Discover Assets - -```pseudocode -INVENTORY(plugin_path): - raw_skills = Glob(plugin_path + "/skills/*/SKILL.md") - computed.skills = [ - { path: s, dir: dirname(s), name: basename(dirname(s)) } - FOR s IN raw_skills - ] - computed.commands = Glob(plugin_path + "/commands/*.md") - computed.agents = Glob(plugin_path + "/agents/*.md") - computed.existing_hooks = read_json_if_exists(plugin_path + "/hooks/hooks.json") - - computed.created = [] # list of { path, platform } - computed.skipped = [] # list of { path, platform } - computed.flagged = [] # list of strings -``` - -### 2.2 Check Conflicts - -```pseudocode -CHECK_CONFLICTS(computed): - conflict_checks = [ - { path: ".claude-plugin/plugin.json", platform: "claude-code" }, - { path: ".claude-plugin/marketplace.json", platform: "claude-code" }, - { path: ".cursor-plugin/plugin.json", platform: "cursor" }, - { path: ".cursor-plugin/marketplace.json", platform: "cursor" }, - { path: ".codex-plugin/plugin.json", platform: "codex" }, - { path: ".agents/plugins/marketplace.json", platform: "codex" }, - { path: "gemini-extension.json", platform: "gemini-cli" }, - { path: "GEMINI.md", platform: "gemini-cli" }, - { path: "AGENTS.md", platform: "cross" }, - { path: "CLAUDE.md", platform: "claude-code" }, - { path: "package.json", platform: "opencode" }, - { path: ".opencode/plugins/" + computed.metadata.name + ".js", platform: "opencode" }, - { path: ".github/copilot-instructions.md", platform: "copilot-cli" }, - { path: "hooks/hooks-cursor.json", platform: "cursor" }, - ] - - FOR check IN conflict_checks: - IF file_exists(check.path): - computed.skipped.append({ path: check.path, platform: check.platform }) -``` - -## 3. Phase 3: Recommend - -Interactive uplift target recommendation and platform selection. Uses `computed.shape` -from Phase 1 to derive a recommendation, then asks the user to confirm and select -target platforms. - -### 3.1 Recommend and Confirm Uplift Target - -```pseudocode -RECOMMEND_AND_CONFIRM(computed): - # Derive recommendation from shape - IF computed.shape == "bare-skill-repo": - IF len(computed.skills) <= 3: - recommended = "skill-first" - rationale = "This repo has " + len(computed.skills) + " skill(s) and no platform manifests. Skill-first generates sidecars and context files without full plugin packaging." - ELSE: - recommended = "full-portable-plugin" - rationale = "This repo has " + len(computed.skills) + " skills. Full plugin packaging gives each platform a native manifest for better discoverability." - - ELIF computed.shape == "single-platform-plugin": - recommended = "full-portable-plugin" - rationale = "This repo already has one platform manifest. Full plugin packaging adds the remaining platforms." - - ELIF computed.shape == "multi-platform-source": - recommended = "full-portable-plugin" - rationale = "This repo already targets multiple platforms. Full plugin packaging fills the remaining gaps." - - ELIF computed.shape == "curated-distribution": - recommended = "curated-note-only" - rationale = "This repo is a marketplace distribution without upstream skills. Only install documentation and notes will be generated." - - ELSE: - recommended = "full-portable-plugin" - rationale = "Repo shape could not be classified. Defaulting to full plugin packaging." - - # If platforms input was provided, auto-confirm - IF inputs.platforms IS PROVIDED: - computed.uplift_target = recommended - RETURN - - # Present to user - DISPLAY "## Uplift Target" - DISPLAY "Shape: " + computed.shape - DISPLAY "Recommendation: **" + recommended + "**" - DISPLAY rationale - DISPLAY "" - DISPLAY "Options:" - DISPLAY " 1. skill-first — sidecars, context files, AGENTS.md only (no platform manifests)" - DISPLAY " 2. full-portable-plugin — all platform manifests + context files + sidecars + install docs" - DISPLAY " 3. curated-note-only — install notes only" - - response = ASK "Accept recommendation (" + recommended + "), or choose 1/2/3?" - - IF response confirms recommendation: - computed.uplift_target = recommended - ELSE: - computed.uplift_target = parse_choice(response) -``` - -### 3.2 Select Target Platforms - -```pseudocode -SELECT_PLATFORMS(computed): - all_platforms = ["claude-code", "cursor", "gemini-cli", "opencode", "copilot-cli", "codex"] - - # If platforms input was provided, use it directly - IF inputs.platforms IS PROVIDED: - IF inputs.platforms == "all": - computed.target_platforms = all_platforms - ELSE: - computed.target_platforms = parse_csv(inputs.platforms) - validate_platform_names(computed.target_platforms) - RETURN - - # Pre-select based on uplift target and existing state - IF computed.uplift_target == "skill-first": - preselected = all_platforms - ELIF computed.uplift_target == "curated-note-only": - preselected = [p FOR p IN all_platforms - IF any(s.platform == p FOR s IN computed.skipped)] - IF len(preselected) == 0: - preselected = all_platforms - ELSE: - preselected = all_platforms - - # Present checklist - DISPLAY "## Target Platforms" - DISPLAY "" - FOR p IN all_platforms: - marker = "[x]" IF p IN preselected ELSE "[ ]" - existing = " (manifest exists)" IF any(s.platform == p FOR s IN computed.skipped) ELSE "" - DISPLAY " " + marker + " " + p + existing - DISPLAY "" - - response = ASK "Confirm platforms, or list the ones you want (e.g. 'claude-code, cursor, gemini-cli')?" - - IF response confirms: - computed.target_platforms = preselected - ELSE: - computed.target_platforms = parse_platform_list(response) - - # Validate: at least one platform required - IF len(computed.target_platforms) == 0: - DISPLAY "At least one platform must be selected." - GOTO SELECT_PLATFORMS -``` - -### 3.3 Derive Codex Path - -```pseudocode -DERIVE_CODEX_PATH(computed): - IF "codex" NOT IN computed.target_platforms: - computed.codex_rec = None - RETURN - - IF computed.uplift_target == "skill-first": - computed.codex_rec = "native-skill-discovery" - ELIF computed.uplift_target == "curated-note-only": - computed.codex_rec = "curated-package-note" - ELSE: - computed.codex_rec = "native-plugin-packaging" -``` - -### 3.4 Early Exit for Curated-Note-Only - -```pseudocode -IF computed.uplift_target == "curated-note-only": - SKIP Phase 4 (Generate) - SKIP Phase 5 (Port) - RUN Phase 6 (Document) — install docs only, for selected platforms - SKIP Phase 7 (Bootstrap) - RUN Phase 8 (Report) - RETURN -``` - -## 4. Phase 4: Generate - -### 4.1 Write Platform Manifests - -Table-driven generation. See `lib/patterns/manifest-generation.md` for all schemas. - -```pseudocode -GENERATE_MANIFESTS(computed): - manifests = [ - { target: ".claude-plugin/plugin.json", platform: "claude-code", schema: "claude-plugin" }, - { target: ".claude-plugin/marketplace.json", platform: "claude-code", schema: "claude-marketplace" }, - { target: "CLAUDE.md", platform: "claude-code", schema: "claude-context" }, - { target: ".cursor-plugin/plugin.json", platform: "cursor", schema: "cursor-plugin" }, - { target: ".cursor-plugin/marketplace.json", platform: "cursor", schema: "cursor-marketplace", - condition: "is_multi_plugin_repo" }, - { target: "gemini-extension.json", platform: "gemini-cli", schema: "gemini-extension" }, - { target: "GEMINI.md", platform: "gemini-cli", schema: "gemini-context" }, - { target: "package.json", platform: "opencode", schema: "opencode-package" }, - { target: ".opencode/plugins/{{name}}.js", platform: "opencode", schema: "opencode-shim" }, - { target: ".codex-plugin/plugin.json", platform: "codex", schema: "codex-plugin", - condition: "computed.codex_rec == 'native-plugin-packaging'" }, - { target: ".agents/plugins/marketplace.json", platform: "codex", schema: "codex-marketplace", - condition: "computed.codex_rec == 'native-plugin-packaging'" }, - { target: "AGENTS.md", platform: "cross", schema: "agents-context" }, - { target: ".github/copilot-instructions.md", platform: "copilot-cli", schema: "copilot-instructions" }, - ] - - FOR manifest IN manifests: - # Skip if platform not targeted (cross-platform always included) - IF manifest.platform != "cross" AND manifest.platform NOT IN computed.target_platforms: - CONTINUE - - IF manifest.condition AND NOT eval(manifest.condition): - CONTINUE - - resolved = substitute(manifest.target, computed.metadata) - IF any(s.path == resolved FOR s IN computed.skipped): - CONTINUE - - # Skill-first: skip platform manifests, only generate context files - IF computed.uplift_target == "skill-first" AND is_manifest(manifest.schema): - CONTINUE - - template_path = schema_to_template_path(manifest.schema) - mode = schema_to_mode(manifest.schema) - template = Read(template_path) - - IF mode == "plain": - content = substitute(template, computed.metadata) - ELIF mode == "conditional": - content = render_with_conditionals(template, computed.metadata, computed) - ELIF mode == "builder": - content = render_with_builder(template, computed.metadata, computed) - - Write(resolved, content) - computed.created.append({ path: resolved, platform: manifest.platform }) -``` - -The `is_manifest()` predicate classifies schemas as packaging vs context: - -```pseudocode -MANIFEST_SCHEMAS = [ - "claude-plugin", "claude-marketplace", "cursor-plugin", - "gemini-extension", "opencode-package", "opencode-shim", "codex-plugin", - "codex-marketplace" -] -CONTEXT_SCHEMAS = [ - "claude-context", "gemini-context", "agents-context", "copilot-instructions" -] - -FUNCTION is_manifest(schema): - RETURN schema IN MANIFEST_SCHEMAS -``` - -Under `skill-first`, only context schemas are generated (plus sidecars). Under `full-portable-plugin`, both manifest and context schemas are generated. - -### 4.2 Seed Tool-Mapping Sidecars - -```pseudocode -GENERATE_SIDECARS(computed): - sidecar_platform_map = { - "copilot-tools.md": "copilot-cli", - "codex-tools.md": "codex", - "gemini-tools.md": "gemini-cli", - } - - FOR skill IN computed.skills: - FOR sidecar, platform IN sidecar_platform_map: - IF platform NOT IN computed.target_platforms: - CONTINUE - target = skill.dir + "/references/" + sidecar - IF NOT file_exists(target): - source = Read("lib/references/" + sidecar) - Write(target, source) - computed.created.append({ path: target, platform: platform }) -``` - -### 4.3 Validate npx Skills Frontmatter - -```pseudocode -VALIDATE_FRONTMATTER(computed): - FOR skill IN computed.skills: - frontmatter = parse_yaml_frontmatter(Read(skill.path)) - IF NOT frontmatter.name OR NOT frontmatter.description: - computed.flagged.append( - skill.path + " — missing frontmatter field(s). Add name: and description: in YAML frontmatter." - ) -``` - -Do NOT auto-write — frontmatter descriptions require human authorship. - -## 5. Phase 5: Port - -Adapt hooks from any source platform to all target platforms. -See `lib/patterns/hook-merging.md` for event mapping and merge logic. - -Hook porting is filtered by `computed.target_platforms`. Each subsection only -runs if its target platform is selected. - -### 5.1 Claude Code → Cursor Hooks - -```pseudocode -PORT_CURSOR_HOOKS(computed): - IF "cursor" NOT IN computed.target_platforms: - RETURN - IF any(s.path == "hooks/hooks-cursor.json" FOR s IN computed.skipped): - RETURN - IF computed.existing_hooks: - generate_cursor_hooks(computed.existing_hooks) - ELSE: - Write("hooks/hooks-cursor.json", '{ "version": 1, "hooks": {} }') - computed.created.append({ path: "hooks/hooks-cursor.json", platform: "cursor" }) -``` - -### 5.2 Claude Code → Copilot Hooks - -Copilot uses separate `bash` / `powershell` fields, no `matcher`, stored under `.github/hooks/`. - -```pseudocode -PORT_COPILOT_HOOKS(computed): - IF "copilot-cli" NOT IN computed.target_platforms: - RETURN - IF NOT computed.existing_hooks: - RETURN - copilot_hooks = adapt_hooks_copilot(computed.existing_hooks) # see hook-merging.md - FOR hook IN copilot_hooks: - target = ".github/hooks/" + hook.event + ".sh" - IF NOT file_exists(target): - Write(target, hook.bash) - computed.created.append({ path: target, platform: "copilot-cli" }) - win_target = ".github/hooks/" + hook.event + ".ps1" - IF NOT file_exists(win_target): - Write(win_target, hook.powershell) - computed.created.append({ path: win_target, platform: "copilot-cli" }) -``` - -### 5.3 Gemini Hook Guidance - -Gemini CLI hooks are configured interactively by users via `GEMINI.md` instructions — -they cannot be written as files. Capture guidance text to include in install docs. - -```pseudocode -GEMINI_HOOK_GUIDANCE(computed): - IF "gemini-cli" NOT IN computed.target_platforms: - RETURN - IF computed.existing_hooks: - computed.gemini_hook_text = render_gemini_hook_instructions(computed.existing_hooks) - ELSE: - computed.gemini_hook_text = NULL -``` - -### 5.4 Windows Support - -```pseudocode -PORT_WINDOWS_HOOKS(computed): - IF file_exists("lib/templates/hooks/run-hook.cmd"): - source = Read("lib/templates/hooks/run-hook.cmd") - Write("hooks/run-hook.cmd", source) - computed.created.append({ path: "hooks/run-hook.cmd", platform: "cross" }) -``` - -## 6. Phase 6: Document - -Generate install documentation for every platform that received artifacts. -See `lib/templates/install-docs/` for section templates. - -### 6.1 Determine Platforms With Artifacts - -```pseudocode -DETERMINE_PLATFORMS(computed): - platforms_with_artifacts = computed.target_platforms -``` - -### 6.2 Render Per-Platform Install Sections - -```pseudocode -RENDER_INSTALL_SECTIONS(computed, platforms_with_artifacts): - sections = {} - FOR platform IN platforms_with_artifacts: - template = Read("lib/templates/install-docs/" + platform + ".md") - sections[platform] = render(template, computed.metadata) - IF computed.gemini_hook_text: - sections["gemini-cli"] += "\n\n### Hook Setup\n\n" + computed.gemini_hook_text - RETURN sections -``` - -### 6.3 Write Install Docs - -```pseudocode -WRITE_INSTALL_DOCS(computed, sections, platforms_with_artifacts): - # Whole-repo note: only include when plugin has shared assets that require - # whole-repo install (hooks, session-start bootstrapping, root context files, - # or platform manifests). Bare skill repos without these can use npx skills. - has_shared_assets = ( - computed.existing_hooks - OR file_exists("skills/using-" + computed.metadata.name + "/SKILL.md") - OR any(file_exists(p) FOR p IN ["CLAUDE.md", "AGENTS.md", "GEMINI.md"]) - OR computed.uplift_target == "full-portable-plugin" - ) - IF has_shared_assets: - whole_repo_note = render(Read("lib/templates/install-docs/whole-repo-note.md"), computed.metadata) - ELSE: - whole_repo_note = "" - - fresh_install = "" - adding_platform = "" - FOR platform IN platforms_with_artifacts: - fresh_install += sections[platform] + "\n\n" - adding_tmpl = read_if_exists("lib/templates/install-docs/adding-platform/" + platform + ".md") - IF adding_tmpl: - adding_platform += render(adding_tmpl, computed.metadata) + "\n\n" - - content = "# Installation\n\n" - IF whole_repo_note: - content += whole_repo_note + "\n\n" - content += "## Fresh Install\n\n" + fresh_install - content += "## Adding Another Platform\n\n" - content += "Already have the repo cloned for one platform? Add others by pointing them at the same checkout.\n\n" - content += adding_platform - - Write("INSTALL.md", content) - computed.created.append({ path: "INSTALL.md", platform: "cross" }) - - # Platform-specific pointers (not full docs) - IF "copilot-cli" IN platforms_with_artifacts: - Write(".github/INSTALL.md", "See [INSTALL.md](../INSTALL.md) for installation instructions.\n") - computed.created.append({ path: ".github/INSTALL.md", platform: "copilot-cli" }) - - IF "codex" IN platforms_with_artifacts: - Write(".codex/INSTALL.md", "See [INSTALL.md](../INSTALL.md) for installation instructions.\n") - computed.created.append({ path: ".codex/INSTALL.md", platform: "codex" }) - - # Flag missing Installation and Publishing sections in README - IF file_exists("README.md"): - readme = Read("README.md") - IF "## Installation" NOT IN readme AND "## Install" NOT IN readme: - computed.flagged.append( - "README.md — no Installation section found. Add install instructions or link to INSTALL.md." - ) - IF "PUBLISHING.md" NOT IN readme: - computed.flagged.append( - "README.md — no link to PUBLISHING.md. Add a link so plugin authors can find publishing guidance." - ) -``` - -### 6.4 Write Publishing Docs - -```pseudocode -WRITE_PUBLISHING_DOCS(computed, platforms_with_artifacts): - header = render(Read("lib/templates/install-docs/publishing.md"), computed.metadata) - sections = "" - FOR platform IN platforms_with_artifacts: - template = read_if_exists("lib/templates/install-docs/publishing/" + platform + ".md") - IF template: - sections += render(template, computed.metadata) + "\n\n" - - IF sections: - content = header + "\n\n" + sections - Write("PUBLISHING.md", content) - computed.created.append({ path: "PUBLISHING.md", platform: "cross" }) -``` - -## 7. Phase 7: Bootstrap (opt-in) - -Session-start injection. See `lib/patterns/bootstrapping.md` for full generation logic. - -### 7.1 Prompt and Execute - -```pseudocode -BOOTSTRAP(computed): - IF file_exists("skills/using-" + computed.metadata.name + "/SKILL.md"): - computed.bootstrap_status = "already-configured" - RETURN - - response = ASK "Generate session-start bootstrapping hooks? (y/n)" - - IF response == "no": - computed.bootstrap_status = "declined" - RETURN - - generate_using_skill(computed) - generate_using_sidecars(computed) # filtered by target_platforms (same as Phase 4.2) - generate_session_start(computed) - generate_run_hook_cmd(computed) - - # Hook merging gated on platform targeting - IF "claude-code" IN computed.target_platforms: - merge_session_start_hooks_claude(computed) - IF "cursor" IN computed.target_platforms: - merge_session_start_hooks_cursor(computed) - - # Platform-specific enhancements - IF "opencode" IN computed.target_platforms: - enhance_opencode_plugin(computed) - IF "gemini-cli" IN computed.target_platforms: - update_gemini_md(computed) - - computed.bootstrap_status = "configured" -``` - -## 8. Phase 8: Report - -Emit the final uplift report. See `lib/patterns/report-template.md` for the full report format and state flow diagram. - -## Related Skills - -- **Assess portability:** `skills/assessing-plugin-portability/SKILL.md` diff --git a/skills/uplifting-a-plugin/references/codex-tools.md b/skills/uplifting-a-plugin/references/codex-tools.md deleted file mode 100644 index 395905a..0000000 --- a/skills/uplifting-a-plugin/references/codex-tools.md +++ /dev/null @@ -1 +0,0 @@ -See [lib/references/codex-tools.md](../../lib/references/codex-tools.md) for the full Codex tool mapping. diff --git a/skills/uplifting-a-plugin/references/copilot-tools.md b/skills/uplifting-a-plugin/references/copilot-tools.md deleted file mode 100644 index 604a011..0000000 --- a/skills/uplifting-a-plugin/references/copilot-tools.md +++ /dev/null @@ -1 +0,0 @@ -See [lib/references/copilot-tools.md](../../lib/references/copilot-tools.md) for the full Copilot CLI tool mapping. diff --git a/skills/uplifting-a-plugin/references/gemini-tools.md b/skills/uplifting-a-plugin/references/gemini-tools.md deleted file mode 100644 index dfdabb0..0000000 --- a/skills/uplifting-a-plugin/references/gemini-tools.md +++ /dev/null @@ -1 +0,0 @@ -See [lib/references/gemini-tools.md](../../lib/references/gemini-tools.md) for the full Gemini CLI tool mapping. diff --git a/skills/using-skill-portability/SKILL.md b/skills/using-skill-portability/SKILL.md index 51468ee..c2cb17f 100644 --- a/skills/using-skill-portability/SKILL.md +++ b/skills/using-skill-portability/SKILL.md @@ -9,18 +9,15 @@ This plugin provides the following skills: | Skill | Description | | ----- | ----------- | -| `uplifting-a-plugin` | Add multi-platform portability to any plugin. Accepts any starting state — Claude, Cursor, Gemini, OpenCode, Copilot, Codex, or bare SKILL.md files. Detects what exists, infers canonical metadata, generates every missing platform artifact, ports hooks, produces install documentation, and optionally configures session-start bootstrapping. | -| `assessing-plugin-portability` | Assess a plugin for multi-platform portability. Classifies repo shape, scores readiness per platform using a 7-category rubric, detects structural blockers, and recommends an uplift target. Read-only — makes no changes. | +| `plugin-portability` | Assess or uplift a plugin for multi-platform portability. Asks intent upfront (assess/uplift, platforms, uplift target), runs condition-driven scoring, and optionally generates missing artifacts. Platforms: Claude Code, Cursor, Gemini CLI, Codex, Antigravity, OpenClaw. | -## How to Invoke Skills +## How to Invoke -**Claude Code / Cursor:** Use the `Skill` tool with the skill name. +**Claude Code / Cursor:** Use the `Skill` tool with skill name `plugin-portability`. -**Copilot CLI:** Use the `skill` tool with the skill name. +**Gemini CLI:** Use the `activate_skill` tool with skill name `plugin-portability`. -**Gemini CLI:** Use the `activate_skill` tool with the skill name. - -**Codex / Other:** Skills are auto-discovered. Follow the SKILL.md instructions directly. +**Antigravity / OpenClaw / Codex:** Skills are auto-discovered. Follow the SKILL.md instructions directly. ## Tool Name Mapping diff --git a/skills/using-skill-portability/references/copilot-tools.md b/skills/using-skill-portability/references/copilot-tools.md deleted file mode 100644 index 604a011..0000000 --- a/skills/using-skill-portability/references/copilot-tools.md +++ /dev/null @@ -1 +0,0 @@ -See [lib/references/copilot-tools.md](../../lib/references/copilot-tools.md) for the full Copilot CLI tool mapping.