From 021d874ceb5d2a4ceb2702f5afc0811fdaee4734 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 14:51:08 +1000 Subject: [PATCH 01/43] docs: add platform API restructure design spec Defines a structured pseudocode library to replace the duplicated prose-based reference system (platform-mappings.md + 5 *-tools.md files) with typed PlatformSpec dictionaries, canonical operation enums, and deterministic lookup functions. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...6-04-27-platform-api-restructure-design.md | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-27-platform-api-restructure-design.md diff --git a/docs/superpowers/specs/2026-04-27-platform-api-restructure-design.md b/docs/superpowers/specs/2026-04-27-platform-api-restructure-design.md new file mode 100644 index 0000000..8e12494 --- /dev/null +++ b/docs/superpowers/specs/2026-04-27-platform-api-restructure-design.md @@ -0,0 +1,359 @@ +# Platform API Restructure Design + +## Goal + +Replace the duplicated prose-based reference system (`platform-mappings.md` + +5 `*-tools.md` files) with a structured pseudocode library: typed platform +specifications, canonical operation vocabulary, and deterministic lookup +functions. DRY, platform-agnostic, machine-readable. + +## Problem + +The current `lib/references/` directory has two overlapping representations of +the same data: + +1. **`platform-mappings.md`** (226 lines) — 13 cross-platform comparison tables +2. **5 `*-tools.md` files** (53–127 lines each) — per-platform narrative guides + +Tool name mappings, hook events, frontmatter stripping, MCP config, context +files, and model formats all appear in both. The `*-tools.md` files have grown +beyond tool mapping into full platform reference guides covering hooks, +subagents, manifests, and distribution — duplicating content from +`platform-mappings.md` Tables 3–13. + +The rubric YAML files reference `LOOKUP["table_name"]["platform"]` in 28 +pseudocode comments, but there is no actual lookup engine — the "single source +of truth" claim is aspirational. Both representations must be updated manually +and kept in sync. + +## Architecture + +### File structure + +``` +lib/references/ + platform-api.md # Type definitions, canonical enums, lookup functions + platforms/ + claude-code.md # PLATFORMS["claude-code"]: PlatformSpec = { ... } + gemini-cli.md # PLATFORMS["gemini-cli"]: PlatformSpec = { ... } + codex.md # PLATFORMS["codex"]: PlatformSpec = { ... } + cursor.md # PLATFORMS["cursor"]: PlatformSpec = { ... } + antigravity.md # PLATFORMS["antigravity"]: PlatformSpec = { ... } + openclaw.md # PLATFORMS["openclaw"]: PlatformSpec = { ... } +``` + +### Deleted files + +``` +lib/references/platform-mappings.md # replaced by platform-api.md + platforms/*.md +lib/references/gemini-tools.md # replaced by platforms/gemini-cli.md +lib/references/codex-tools.md # replaced by platforms/codex.md +lib/references/cursor-tools.md # replaced by platforms/cursor.md +lib/references/antigravity-tools.md # replaced by platforms/antigravity.md +lib/references/openclaw-tools.md # replaced by platforms/openclaw.md +``` + +### Relocated prose + +The current `codex-tools.md` contains usage patterns (subagent message framing, +environment detection, app finishing) that are not platform metadata. These move +to `lib/patterns/`: + +- Subagent message framing → `lib/patterns/subagent-dispatch.md` (new) +- Environment detection → `lib/patterns/subagent-dispatch.md` +- Codex app finishing → `lib/patterns/subagent-dispatch.md` + +## Type System + +### PlatformSpec + +```pseudocode +TYPE PlatformSpec = { + id: string, # "gemini-cli", "codex", etc. + display_name: string, + + # ── Tools ── + tools: Dict[Operation, ToolEntry], + extra_tools: List[{ name: string, purpose: string }], + + # ── Hooks ── + hooks: { + system: "file" | "sdk" | "none", + config_path: string | null, + event_case: "PascalCase" | "camelCase" | "snake_case" | null, + timeout_unit: "seconds" | "milliseconds" | null, + async_support: bool, + structure: "nested" | "flat" | "sdk" | null, + output_key: string | null, + events: Dict[CanonicalEvent, HookEvent], + extra_events: List[HookEvent], + }, + + # ── Context ── + context: { + primary_file: string, + secondary_files: List[string], + priority_note: string | null, + }, + + # ── Skills ── + skills: { + path: string, + agents_path: string, + invocation: string, + }, + + # ── Manifest ── + manifest: { + path: string | null, + required_fields: List[string], + }, + + # ── Frontmatter ── + frontmatter: { + strip: List[string], + keep: List[string], + model_format: string | null, + }, + + # ── MCP ── + mcp: { + config_path: string | null, + notes: string | null, + }, + + # ── Path Variables ── + paths: { + plugin_root: string | null, + hooks_scripts: string | null, + }, +} + +TYPE ToolEntry = { + name: string | null, # null = not supported + notes: string | null, +} + +TYPE HookEvent = { + name: string | null, # null = no equivalent + can_block: bool, + notes: string | null, +} +``` + +### Canonical Enums + +```pseudocode +Operation = ENUM( + "file.read", + "file.write", + "file.edit", + "shell.execute", + "search.content", + "search.files", + "subagent.dispatch", + "task.track", + "skill.invoke", + "web.search", + "web.fetch", + "user.ask", +) + +CanonicalEvent = ENUM( + "session.start", + "tool.before", + "tool.after", + "tool.after_failure", + "subagent.start", + "subagent.stop", + "compact.before", + "session.stop", + "prompt.before_submit", +) +``` + +## Lookup Functions + +Defined in `platform-api.md`. Every function is deterministic. + +```pseudocode +REGISTRY: Dict[string, PlatformSpec] = {} + # Populated by per-platform data files. + +# ── Core lookups ── + +FUNCTION tool_name(platform, op) + RETURNS the platform-native tool name for a canonical operation, or null. + LOOKUP REGISTRY[platform].tools[op].name + +FUNCTION hook_event(platform, event) + RETURNS the platform-native hook event name, or null. + LOOKUP REGISTRY[platform].hooks.events[event].name + +FUNCTION hook_can_block(platform, event) + RETURNS whether the hook for this event can block execution. + entry = REGISTRY[platform].hooks.events[event] + RETURN entry.name IS NOT null AND entry.can_block + +# ── Bulk queries ── + +FUNCTION supported_tools(platform) + RETURNS list of canonical operations this platform supports. + FOR EACH op, entry IN REGISTRY[platform].tools: + INCLUDE op WHERE entry.name IS NOT null + +FUNCTION unsupported_tools(platform) + RETURNS list of canonical operations this platform cannot perform. + FOR EACH op, entry IN REGISTRY[platform].tools: + INCLUDE op WHERE entry.name IS null + +FUNCTION has_hooks(platform) + RETURN REGISTRY[platform].hooks.system != "none" + +FUNCTION strip_fields(platform) + RETURN REGISTRY[platform].frontmatter.strip + +# ── Cross-platform ── + +FUNCTION platforms_supporting(op) + RETURNS all platform IDs that support a given operation. + FOR EACH pid, spec IN REGISTRY: + INCLUDE pid WHERE spec.tools[op].name IS NOT null + +FUNCTION tool_mapping_table(op) + RETURNS { platform_id: tool_name } for one operation across all platforms. + Replaces old platform-mappings.md Table 2 rows. + FOR EACH pid, spec IN REGISTRY: + EMIT pid → spec.tools[op].name + +FUNCTION diff_from(source, target) + RETURNS what changes when porting from source to target platform. + src = REGISTRY[source] + tgt = REGISTRY[target] + renamed_tools: + FOR EACH op IN Operation + WHERE src.tools[op].name != tgt.tools[op].name + AND tgt.tools[op].name IS NOT null: + EMIT op → { from: src.tools[op].name, to: tgt.tools[op].name } + lost_tools: + FOR EACH op IN Operation + WHERE src.tools[op].name IS NOT null + AND tgt.tools[op].name IS null: + EMIT op + strip_fields: tgt.frontmatter.strip + model_format: tgt.frontmatter.model_format +``` + +## Consumer Migration + +### GEMINI.md @includes + +**Before:** +``` +@./lib/references/gemini-tools.md +@./lib/references/codex-tools.md +@./lib/references/cursor-tools.md +@./lib/references/antigravity-tools.md +@./lib/references/openclaw-tools.md +``` + +**After:** +``` +@./lib/references/platform-api.md +@./lib/references/platforms/gemini-cli.md +@./lib/references/platforms/codex.md +@./lib/references/platforms/cursor.md +@./lib/references/platforms/antigravity.md +@./lib/references/platforms/openclaw.md +``` + +### AGENTS.md pointers + +Update the tool references section to list the new paths and describe the +lookup function API. + +### Rubric YAML conditions + +All 28 `LOOKUP["table_name"]["platform"]` comments become function calls: + +**Before:** +```yaml +condition: | + # LOOKUP["tool_mapping"]["codex"]["Edit"] = "apply_patch" + sidecars = glob("**/codex-tools.md") + assert len(sidecars) > 0, "No codex-tools.md found" +``` + +**After:** +```yaml +condition: | + # tool_name("codex", "file.edit") = "apply_patch" + tool_ref = find_platform_spec("codex") + assert tool_ref IS NOT null, "No Codex platform spec found" +``` + +The specific LOOKUP-to-function mappings: + +| Old LOOKUP | New function call | +|------------|-------------------| +| `LOOKUP["tool_mapping"]["platform"]["Read"]` | `tool_name(platform, "file.read")` | +| `LOOKUP["tool_mapping"]["platform"]["Edit"]` | `tool_name(platform, "file.edit")` | +| `LOOKUP["tool_mapping"]["platform"]["Task"]` | `tool_name(platform, "subagent.dispatch")` | +| `LOOKUP["tool_mapping"]["platform"]["TodoWrite"]` | `tool_name(platform, "task.track")` | +| `LOOKUP["manifest_required_fields"]["platform"]` | `REGISTRY[platform].manifest.required_fields` | +| `LOOKUP["field_stripping"]["platform"]` | `strip_fields(platform)` | +| `LOOKUP["hook_events"]["platform"]` | `hook_event(platform, event)` | +| `LOOKUP["model_mapping"]["platform"]` | `REGISTRY[platform].frontmatter.model_format` | +| `LOOKUP["path_variables"]["platform"]` | `REGISTRY[platform].paths.plugin_root` | +| `LOOKUP["hook_format_rules"]["platform"]` | `REGISTRY[platform].hooks.*` (individual fields) | +| `LOOKUP["agent_output_format"]["platform"]` | `REGISTRY[platform].skills.agents_path` + `REGISTRY[platform].frontmatter.*` | + +### CI workflow + +Update `.github/workflows/ci.yml` to check for the new file paths: + +```yaml +# Before: checks lib/references/{codex,gemini,cursor,antigravity,openclaw}-tools.md +# After: checks lib/references/platforms/{codex,gemini-cli,cursor,antigravity,openclaw}.md +``` + +### Patterns + +- `lib/patterns/inventory.md` — update `sidecar_files` list to reference + `platforms/*.md` +- `lib/patterns/injection-checks.md` — update Component 2 to reference + `lib/references/platforms/gemini-cli.md` +- `lib/patterns/bootstrapping.md` — update sidecar references to shared + platform specs + +### Prose relocation + +Move these sections from `codex-tools.md` to a new +`lib/patterns/subagent-dispatch.md`: + +1. **Named agent dispatch** — how to map named Claude Code agent types to + Codex's generic `spawn_agent` with worker roles +2. **Message framing** — the XML-tag wrapping pattern for instruction adherence +3. **Environment detection** — git worktree/detached-HEAD checks +4. **Codex app finishing** — what to do when sandbox blocks branch/push + +These are usage patterns consumed by skill pseudocode, not platform metadata. + +## Scope + +### In scope + +- Create `lib/references/platform-api.md` (type system + functions) +- Create 6 `lib/references/platforms/*.md` files (one per platform) +- Create `lib/patterns/subagent-dispatch.md` (relocated prose) +- Delete 6 old files from `lib/references/` +- Update all consumers: GEMINI.md, AGENTS.md, CLAUDE.md, 6 rubric YAMLs, + CI workflow, 3 pattern files, CONTRIBUTING.md, rubric-framework.md +- Update reconciliation matrix + +### Out of scope + +- Changing the rubric scoring logic (only updating references/comments) +- Changing the skill SKILL.md pseudocode (only updating external references) +- Research doc updates (`docs/platforms/*.md` are the upstream source, unchanged) +- Template changes (templates reference patterns, not the reference files directly) From 8a54caa7743a086812c526f98b3916ab20d67dd5 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:12:38 +1000 Subject: [PATCH 02/43] fix: correct OpenClaw publishing template commands and registry URL ClawHub URL was clawhub.dev (wrong), corrected to clawhub.ai. Auth/publish commands updated from fabricated `openclaw auth login` / `openclaw plugins publish` to actual ClawHub CLI (`clawhub login`, `clawhub package publish`). Install syntax corrected to `openclaw plugins install clawhub:`. Found by Codex adversarial review of the platform API restructure spec. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/reconciliation-matrix.md | 59 ++++++++++++++++++- .../install-docs/publishing/openclaw.md | 20 ++++--- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/docs/reconciliation-matrix.md b/docs/reconciliation-matrix.md index 94806fb..d3737d7 100644 --- a/docs/reconciliation-matrix.md +++ b/docs/reconciliation-matrix.md @@ -198,6 +198,36 @@ Track every platform-specific claim in the plugin against researched facts in | Skill discovery paths | Per platform | Source plugin glob correct; platform paths handled by rubrics | Correct | | Hook discovery | Per platform | Codex hooks now exist | Fixed (hook-merging.md updated) | +### publishing-and-discoverability.md + +| Claim | Current | Research says | Status | +|-------|---------|---------------|--------| +| Claude Code publishing | Git repos, marketplace, team distribution | Confirmed: marketplace add, extraKnownMarketplaces | Correct | +| Cursor publishing | cursor.com/marketplace, /add-plugin | Confirmed: marketplace submission, /add-plugin install | Correct | +| Gemini CLI publishing | geminicli.com/extensions, extensions install | Confirmed: gallery, extensions install command | Correct | +| Codex skills publishing | $skill-installer, GitHub repos | Confirmed: $skill-installer, standard skill paths | Fixed (removed unverifiable .curated/.experimental claims) | +| Codex plugins publishing | marketplace add, not yet public | Confirmed: codex plugin marketplace add | Correct | +| Antigravity publishing | Claimed antigravity.dev/plugins marketplace | No official marketplace exists; distribution via git repos and npm installers | Fixed (rewrote section) | +| Antigravity install commands | Claimed `antigravity plugin add` | No such CLI command; install by copying to .agents/skills/ | Fixed (rewrote section) | +| OpenClaw registry | Claimed openclaw.dev/registry | Registry is ClawHub at clawhub.ai | Fixed (corrected URL and name) | +| OpenClaw install commands | Claimed `openclaw install ` | Actual: `openclaw plugins install clawhub:` or `openclaw skills install ` | Fixed (corrected syntax) | +| OpenClaw manifest requirement | Claimed "no manifest required" | `openclaw.plugin.json` with id and configSchema IS required for native plugins | Fixed (added requirement) | + +### injection-checks.md + +| Claim | Current | Research says | Status | +|-------|---------|---------------|--------| +| Component 2 sidecar path | Was `skills/using-{{name}}/references/gemini-tools.md` | Per-skill sidecars deleted in #11; shared refs at `lib/references/` | Fixed (updated to shared path) | +| Component 5 hooks.json SessionStart | Claude Code PascalCase | Correct | Correct | +| Component 6 hooks-cursor.json sessionStart | Cursor camelCase | Correct | Correct | +| Component 7 GEMINI.md ordering | First @./skills/ include | Correct (lib/references/ includes don't match skills/ regex) | Correct | + +### report-template.md + +| Claim | Current | Research says | Status | +|-------|---------|---------------|--------| +| Report structure | Phase-based computed fields | No platform-specific claims — purely structural | Correct | + --- ## 4. Templates (`lib/templates/`) @@ -246,16 +276,31 @@ Track every platform-specific claim in the plugin against researched facts in --- -## 5. Skill Logic (`skills/plugin-portability/SKILL.md`) +## 5. Skill Logic (`skills/`) + +### skills/plugin-portability/SKILL.md | Section | Claim | Research says | Status | |---------|-------|---------------|--------| | Phase 0a | Platform list (6) | Correct set | Correct | +| Phase 0a | Platform descriptions (Cursor, Gemini, Codex, Antigravity, OpenClaw) | All 5 descriptions accurate | Correct | | Phase 0b | Shape detection → uplift target | No platform-specific claims | Correct | -| Phase 3 | Loads rubric YAMLs per platform | Correct mechanism | Correct | +| Phase 3 | Loads rubric YAMLs per platform | Correct mechanism and file paths | Correct | | Phase 5 | ALLOWED_CATEGORIES by shape | Category names match rubric-framework.md (verified Tier 2) | Correct | +| Phase 5 | Template action types (create, merge, none) | No platform-specific claims | Correct | | Phase 6 | Hook porting (skips if 4_hooks not allowed) | Codex now has hooks — hook-merging.md updated | Fixed | | Phase 6 | References hook-merging.md | hook-merging.md now covers Codex | Fixed | +| Phase 7 | References lib/templates/install-docs/ | Correct path | Correct | +| Phase 8 | References lib/patterns/bootstrapping.md | Correct path | Correct | + +### skills/using-skill-portability/SKILL.md + +| Claim | Current | Research says | Status | +|-------|---------|---------------|--------| +| Claude Code / Cursor invocation | `Skill` tool | Correct for both | Correct | +| Gemini CLI invocation | `activate_skill` tool | Correct | Correct | +| Antigravity / OpenClaw / Codex | "Skills are auto-discovered" | Antigravity: semantic match auto-activate. Codex: native loading. OpenClaw: prompt injection. All correct. | Correct | +| Tool reference pointer | `lib/references/` | Correct shared path | Correct | --- @@ -327,6 +372,16 @@ in `docs/research/per-platform-context-loading.md` (#12). 24. ~~**cursor-tools.md**: Subagent support not documented~~ Fixed — added full subagent section 25. ~~**antigravity-tools.md + Table 2**: Tool names claimed "same as Claude"~~ Fixed — all 13 tools have different names (view_file, run_command, grep_search, etc.) +### Fixed (Tier 4 — patterns pseudocode and skills) + +26. ~~**publishing-and-discoverability.md**: Antigravity section fabricated marketplace URL and CLI commands~~ Fixed — rewritten with git/npm distribution, copy-based install +27. ~~**publishing-and-discoverability.md**: OpenClaw section wrong registry URL, wrong CLI syntax, false "no manifest" claim~~ Fixed — corrected to ClawHub, proper install commands, manifest requirement +28. ~~**injection-checks.md**: Component 2 referenced deleted per-skill sidecar path~~ Fixed — updated to shared `lib/references/gemini-tools.md` +29. ~~**publishing-and-discoverability.md**: Codex skills claimed unverifiable `.curated/`/`.experimental/` folders~~ Fixed — simplified to verified paths +30. ~~**publishing/openclaw.md template**: Wrong ClawHub URL (clawhub.dev → clawhub.ai), wrong CLI commands~~ Fixed — corrected to clawhub CLI and proper install syntax + ### All verification complete Zero "Needs review", "Missing", or known gap items remain. + +Full coverage: references (Tier 1), rubrics/patterns (Tier 2), templates/install docs (Tier 3), patterns pseudocode and skills (Tier 4). diff --git a/lib/templates/install-docs/publishing/openclaw.md b/lib/templates/install-docs/publishing/openclaw.md index 9700756..4ff261b 100644 --- a/lib/templates/install-docs/publishing/openclaw.md +++ b/lib/templates/install-docs/publishing/openclaw.md @@ -1,21 +1,23 @@ ## OpenClaw -Public registries: [ClawHub](https://clawhub.dev) (curated) and npm (open). +Public registries: [ClawHub](https://clawhub.ai) (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 +1. Ensure `openclaw/openclaw.plugin.json` manifest is present with required fields (`id`, `configSchema`) +2. Install the ClawHub CLI: `npm i -g clawhub` +3. Authenticate: `clawhub login` +4. Publish: `clawhub package publish your-org/{{name}}` +5. Submissions are reviewed before appearing in the registry ### Publishing to npm -1. Add an npm-compatible `package.json` to the `openclaw/` directory +1. Add an npm-compatible `package.json` with `openclaw.extensions` and `openclaw.compat` 2. Publish: `npm publish --access public` -3. Users install with: `openclaw plugins install @org/openclaw-{{name}}` +3. Users install with: `openclaw plugins install @org/{{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}}` +- Browse [ClawHub](https://clawhub.ai) or search npm for `{{name}}` +- Install via ClawHub: `openclaw plugins install clawhub:{{name}}` +- Install via npm: `openclaw plugins install @org/{{name}}` From ea84bb5332267f6fc0611650dd3b70f27386af6b Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:18:12 +1000 Subject: [PATCH 03/43] docs: add platform API restructure implementation plan 14 tasks covering: platform-api.md creation, 6 PlatformSpec files, subagent-dispatch.md prose relocation, consumer updates (GEMINI.md, AGENTS.md, CLAUDE.md, 6 rubric YAMLs, CI, patterns, CONTRIBUTING), and old file deletion. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-04-27-platform-api-restructure.md | 1758 +++++++++++++++++ 1 file changed, 1758 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-27-platform-api-restructure.md diff --git a/docs/superpowers/plans/2026-04-27-platform-api-restructure.md b/docs/superpowers/plans/2026-04-27-platform-api-restructure.md new file mode 100644 index 0000000..814a284 --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-platform-api-restructure.md @@ -0,0 +1,1758 @@ +# Platform API Restructure 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 duplicated prose-based reference files with a structured pseudocode library of typed PlatformSpec dictionaries and deterministic lookup functions. + +**Architecture:** A shared `platform-api.md` defines the type system, canonical enums, and lookup functions. Six per-platform data files in `platforms/` each declare a complete PlatformSpec dictionary. All consumers (GEMINI.md, AGENTS.md, rubric YAMLs, pattern files, CI) are updated to reference the new paths and use the new lookup vocabulary. + +**Tech Stack:** Markdown with pseudocode blocks. No runtime code. + +--- + +## File Structure + +**Create:** +- `lib/references/platform-api.md` — type system, enums, lookup functions +- `lib/references/platforms/claude-code.md` — REGISTRY["claude-code"] spec +- `lib/references/platforms/gemini-cli.md` — REGISTRY["gemini-cli"] spec +- `lib/references/platforms/codex.md` — REGISTRY["codex"] spec +- `lib/references/platforms/cursor.md` — REGISTRY["cursor"] spec +- `lib/references/platforms/antigravity.md` — REGISTRY["antigravity"] spec +- `lib/references/platforms/openclaw.md` — REGISTRY["openclaw"] spec +- `lib/patterns/subagent-dispatch.md` — relocated prose from codex-tools.md + +**Modify:** +- `GEMINI.md` — update @includes +- `AGENTS.md` — update tool reference pointers +- `CLAUDE.md` — update accuracy constraint reference +- `lib/rubrics/claude-code.yaml` — LOOKUP → function call comments +- `lib/rubrics/codex.yaml` — LOOKUP → function call comments + file references +- `lib/rubrics/gemini-cli.yaml` — LOOKUP → function call comments + file references +- `lib/rubrics/cursor.yaml` — LOOKUP → function call comments +- `lib/rubrics/antigravity.yaml` — LOOKUP → function call comments + file references +- `lib/rubrics/openclaw.yaml` — LOOKUP → function call comments + file references +- `lib/rubrics/rubric-framework.md` — update lookup table pointer +- `lib/patterns/inventory.md` — update sidecar_files list +- `lib/patterns/injection-checks.md` — update Component 2 path +- `lib/patterns/bootstrapping.md` — update sidecar references +- `.github/workflows/ci.yml` — update file existence checks +- `CONTRIBUTING.md` — update skill authoring guidance + +**Delete:** +- `lib/references/platform-mappings.md` +- `lib/references/gemini-tools.md` +- `lib/references/codex-tools.md` +- `lib/references/cursor-tools.md` +- `lib/references/antigravity-tools.md` +- `lib/references/openclaw-tools.md` + +--- + +### Task 1: Create platform-api.md + +**Files:** +- Create: `lib/references/platform-api.md` + +- [ ] **Step 1: Create the directory** + +```bash +ls lib/references/ +``` + +Confirm directory exists (it does — contains the old files). + +- [ ] **Step 2: Write platform-api.md** + +Write `lib/references/platform-api.md` with this content: + +````markdown +# Platform API + +Structured type system and deterministic lookup functions for cross-platform +portability. Each platform declares a `PlatformSpec` dictionary in +`lib/references/platforms/.md`. This file defines the schema and API. + +--- + +## Types + +```pseudocode +TYPE ToolEntry = { + name: string | null, # platform-native tool name; null = not supported + notes: string | null, # brief clarification when needed +} + +TYPE HookEvent = { + name: string | null, # platform-native event name; null = no equivalent + can_block: bool, # whether this hook can block execution + notes: string | null, +} + +TYPE PlatformSpec = { + id: string, # "gemini-cli", "codex", "cursor", "antigravity", "openclaw", "claude-code" + display_name: string, + + # ── Tools ── + tools: Dict[Operation, ToolEntry], + extra_tools: List[{ name: string, purpose: string }], + + # ── Hooks ── + hooks: { + system: "file" | "sdk" | "none", + config_path: string | null, + event_case: "PascalCase" | "camelCase" | "snake_case" | null, + timeout_unit: "seconds" | "milliseconds" | null, + async_support: bool, + structure: "nested" | "flat" | "sdk" | null, + output_key: string | null, + events: Dict[CanonicalEvent, HookEvent], + extra_events: List[HookEvent], + }, + + # ── Context ── + context: { + primary_file: string, + secondary_files: List[string], + priority_note: string | null, + }, + + # ── Skills ── + skills: { + path: string, + agents_path: string, + invocation: string, + }, + + # ── Manifest ── + manifest: { + path: string | null, + required_fields: List[string], + }, + + # ── Frontmatter ── + frontmatter: { + strip: List[string], + keep: List[string], + model_format: string | null, + }, + + # ── MCP ── + mcp: { + config_path: string | null, + notes: string | null, + }, + + # ── Path Variables ── + paths: { + plugin_root: string | null, + hooks_scripts: string | null, + }, +} +``` + +--- + +## Canonical Enums + +```pseudocode +Operation = ENUM( + "file.read", + "file.write", + "file.edit", + "shell.execute", + "search.content", + "search.files", + "subagent.dispatch", + "task.track", + "skill.invoke", + "web.search", + "web.fetch", + "user.ask", +) + +CanonicalEvent = ENUM( + "session.start", + "tool.before", + "tool.after", + "tool.after_failure", + "subagent.start", + "subagent.stop", + "compact.before", + "session.stop", + "prompt.before_submit", +) +``` + +--- + +## Registry + +```pseudocode +REGISTRY: Dict[string, PlatformSpec] = {} + # Populated by per-platform data files in lib/references/platforms/. +``` + +--- + +## Lookup Functions + +```pseudocode +# ── Core lookups ── + +FUNCTION tool_name(platform, op) + RETURNS the platform-native tool name for a canonical operation, or null. + LOOKUP REGISTRY[platform].tools[op].name + +FUNCTION hook_event(platform, event) + RETURNS the platform-native hook event name, or null. + LOOKUP REGISTRY[platform].hooks.events[event].name + +FUNCTION hook_can_block(platform, event) + RETURNS whether the hook for this event can block execution. + entry = REGISTRY[platform].hooks.events[event] + RETURN entry.name IS NOT null AND entry.can_block + +# ── Bulk queries ── + +FUNCTION supported_tools(platform) + RETURNS list of canonical operations this platform supports. + FOR EACH op, entry IN REGISTRY[platform].tools: + INCLUDE op WHERE entry.name IS NOT null + +FUNCTION unsupported_tools(platform) + RETURNS list of canonical operations this platform cannot perform. + FOR EACH op, entry IN REGISTRY[platform].tools: + INCLUDE op WHERE entry.name IS null + +FUNCTION has_hooks(platform) + RETURN REGISTRY[platform].hooks.system != "none" + +FUNCTION strip_fields(platform) + RETURN REGISTRY[platform].frontmatter.strip + +# ── Cross-platform ── + +FUNCTION platforms_supporting(op) + RETURNS all platform IDs that support a given operation. + FOR EACH pid, spec IN REGISTRY: + INCLUDE pid WHERE spec.tools[op].name IS NOT null + +FUNCTION tool_mapping_table(op) + RETURNS { platform_id: tool_name } for one operation across all platforms. + FOR EACH pid, spec IN REGISTRY: + EMIT pid -> spec.tools[op].name + +FUNCTION diff_from(source, target) + RETURNS what changes when porting from source to target platform. + src = REGISTRY[source] + tgt = REGISTRY[target] + renamed_tools: + FOR EACH op IN Operation + WHERE src.tools[op].name != tgt.tools[op].name + AND tgt.tools[op].name IS NOT null: + EMIT op -> { from: src.tools[op].name, to: tgt.tools[op].name } + lost_tools: + FOR EACH op IN Operation + WHERE src.tools[op].name IS NOT null + AND tgt.tools[op].name IS null: + EMIT op + strip_fields: tgt.frontmatter.strip + model_format: tgt.frontmatter.model_format +``` +```` + +- [ ] **Step 3: Verify the file** + +```bash +wc -l lib/references/platform-api.md +``` + +Expected: ~155 lines. + +- [ ] **Step 4: Commit** + +```bash +git add lib/references/platform-api.md +git commit -m "feat: create platform-api.md with type system and lookup functions" +``` + +--- + +### Task 2: Create claude-code.md platform spec + +**Files:** +- Create: `lib/references/platforms/claude-code.md` + +- [ ] **Step 1: Create the platforms directory** + +```bash +mkdir -p lib/references/platforms +``` + +- [ ] **Step 2: Write claude-code.md** + +Write `lib/references/platforms/claude-code.md` with this content: + +````markdown +# Claude Code Platform Specification + +```pseudocode +REGISTRY["claude-code"] = { + + id: "claude-code", + display_name: "Claude Code", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: null }, + "file.write": { name: "Write", notes: null }, + "file.edit": { name: "Edit", notes: null }, + "shell.execute": { name: "Bash", notes: null }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: "Agent", notes: "also Task for background dispatch" }, + "task.track": { name: "TodoWrite", notes: null }, + "skill.invoke": { name: "Skill", notes: null }, + "web.search": { name: "WebSearch", notes: null }, + "web.fetch": { name: "WebFetch", notes: null }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [ + { name: "Monitor", purpose: "run background command, stream output lines back" }, + { name: "NotebookEdit", purpose: "modify Jupyter notebook cells" }, + { name: "LSP", purpose: "code intelligence: jump to def, find refs, type errors" }, + { name: "EnterWorktree", purpose: "create/switch to git worktree" }, + { name: "ExitWorktree", purpose: "return to main worktree" }, + { name: "CronCreate", purpose: "schedule recurring/one-shot prompt" }, + { name: "TaskCreate", purpose: "create task in session checklist" }, + { name: "ToolSearch", purpose: "search and load deferred tools" }, + ], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "hooks/hooks.json", + event_case: "PascalCase", + timeout_unit: "seconds", + async_support: true, + structure: "nested", + output_key: "hookSpecificOutput.additionalContext", + + events: { + "session.start": { name: "SessionStart", can_block: false, notes: null }, + "tool.before": { name: "PreToolUse", can_block: true, notes: null }, + "tool.after": { name: "PostToolUse", can_block: false, notes: null }, + "tool.after_failure": { name: "PostToolUseFailure", can_block: false, notes: null }, + "subagent.start": { name: "SubagentStart", can_block: false, notes: null }, + "subagent.stop": { name: "SubagentStop", can_block: false, notes: null }, + "compact.before": { name: "PreCompact", can_block: false, notes: null }, + "session.stop": { name: "Stop", can_block: false, notes: null }, + "prompt.before_submit": { name: "UserPromptSubmit", can_block: false, notes: null }, + }, + + extra_events: [], + }, + + # ── Context ── + + context: { + primary_file: "CLAUDE.md", + secondary_files: [], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents/", + invocation: "Skill tool", + }, + + # ── Manifest ── + + manifest: { + path: ".claude-plugin/plugin.json", + required_fields: ["name", "version", "description", "author.name", "author.email"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: [], + keep: ["disable-model-invocation", "allowed-tools", "user-invocable"], + model_format: "claude-shortname", + # opus, sonnet, haiku + }, + + # ── MCP ── + + mcp: { + config_path: ".mcp.json", + notes: "dot-prefixed; supports resources and tools", + }, + + # ── Path Variables ── + + paths: { + plugin_root: "${CLAUDE_PLUGIN_ROOT}", + hooks_scripts: "/hooks/scripts/", + }, +} +``` +```` + +- [ ] **Step 3: Commit** + +```bash +git add lib/references/platforms/claude-code.md +git commit -m "feat: create claude-code.md platform spec" +``` + +--- + +### Task 3: Create gemini-cli.md platform spec + +**Files:** +- Create: `lib/references/platforms/gemini-cli.md` + +- [ ] **Step 1: Write gemini-cli.md** + +Write `lib/references/platforms/gemini-cli.md` with this content: + +````markdown +# Gemini CLI Platform Specification + +```pseudocode +REGISTRY["gemini-cli"] = { + + id: "gemini-cli", + display_name: "Gemini CLI", + + # ── Tools ── + + tools: { + "file.read": { name: "read_file", notes: null }, + "file.write": { name: "write_file", notes: null }, + "file.edit": { name: "replace", notes: null }, + "shell.execute": { name: "run_shell_command", notes: null }, + "search.content": { name: "grep_search", notes: null }, + "search.files": { name: "glob", notes: null }, + "subagent.dispatch": { name: "@agent-name", notes: "mention in prompt or automatic routing" }, + "task.track": { name: "write_todos", notes: null }, + "skill.invoke": { name: "activate_skill", notes: null }, + "web.search": { name: "google_web_search", notes: null }, + "web.fetch": { name: "web_fetch", notes: null }, + "user.ask": { name: "ask_user", notes: null }, + }, + + extra_tools: [ + { name: "read_many_files", purpose: "read multiple files at once (triggered by @path)" }, + { name: "list_directory", purpose: "list files and subdirectories" }, + { name: "save_memory", purpose: "persist facts to GEMINI.md across sessions" }, + { name: "get_internal_docs", purpose: "access Gemini CLI own documentation" }, + { name: "complete_task", purpose: "subagent-only: finalize and return result" }, + { name: "enter_plan_mode", purpose: "switch to read-only research mode" }, + { name: "exit_plan_mode", purpose: "leave plan mode" }, + { name: "browser_agent", purpose: "experimental web browser automation" }, + ], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "settings.json or extension manifest hooks field", + event_case: "PascalCase", + timeout_unit: "milliseconds", + async_support: false, + structure: "nested", + output_key: "hookSpecificOutput", + + events: { + "session.start": { name: "SessionStart", can_block: false, notes: null }, + "tool.before": { name: "BeforeTool", can_block: true, notes: "can rewrite args" }, + "tool.after": { name: "AfterTool", can_block: true, notes: "supports tail calls" }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: "PreCompress", can_block: false, notes: null }, + "session.stop": { name: "AfterAgent", can_block: false, notes: null }, + "prompt.before_submit": { name: null, can_block: false, notes: null }, + }, + + extra_events: [ + { name: "BeforeModel", can_block: true, notes: "before LLM request" }, + { name: "AfterModel", can_block: true, notes: "after LLM response" }, + { name: "BeforeToolSelection", can_block: true, notes: "filter available tools" }, + { name: "Notification", can_block: false, notes: "system notifications" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "GEMINI.md", + secondary_files: [], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents/", + invocation: "activate_skill tool", + }, + + # ── Manifest ── + + manifest: { + path: "gemini-extension.json", + required_fields: ["name", "version", "description", "contextFileName"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["disable-model-invocation", "allowed-tools", "user-invocable"], + keep: [], + model_format: "platform-native", + # gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash-lite + }, + + # ── MCP ── + + mcp: { + config_path: "gemini-extension.json -> mcpServers", + notes: "extension-bundled MCP servers", + }, + + # ── Path Variables ── + + paths: { + plugin_root: "${extensionPath}${/}", + hooks_scripts: "/scripts/", + }, +} +``` +```` + +- [ ] **Step 2: Commit** + +```bash +git add lib/references/platforms/gemini-cli.md +git commit -m "feat: create gemini-cli.md platform spec" +``` + +--- + +### Task 4: Create codex.md platform spec + +**Files:** +- Create: `lib/references/platforms/codex.md` + +- [ ] **Step 1: Write codex.md** + +Write `lib/references/platforms/codex.md` with this content: + +````markdown +# Codex Platform Specification + +```pseudocode +REGISTRY["codex"] = { + + id: "codex", + display_name: "Codex", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: "built-in, no named tool in some contexts" }, + "file.write": { name: "Write", notes: "uses apply_patch internally" }, + "file.edit": { name: "apply_patch", notes: "unified file write mechanism" }, + "shell.execute": { name: "Bash", notes: "sandboxed execution" }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: "spawn_agent", notes: "generic roles: default, worker, explorer" }, + "task.track": { name: "update_plan", notes: null }, + "skill.invoke": { name: null, notes: "skills load natively via $skill-name" }, + "web.search": { name: "WebSearch", notes: "live or cached mode" }, + "web.fetch": { name: null, notes: "no direct equivalent; use MCP" }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [ + { name: "report_agent_job_result", purpose: "worker result reporting for CSV batch jobs" }, + ], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "hooks.json or config.toml [hooks]", + event_case: "PascalCase", + timeout_unit: "seconds", + async_support: false, + structure: "nested", + output_key: "permissionDecision / decision", + + events: { + "session.start": { name: "SessionStart", can_block: false, notes: null }, + "tool.before": { name: "PreToolUse", can_block: true, notes: "permissionDecision: deny" }, + "tool.after": { name: "PostToolUse", can_block: true, notes: "decision: block replaces output" }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: null, can_block: false, notes: null }, + "session.stop": { name: "Stop", can_block: true, notes: "decision: block continues session" }, + "prompt.before_submit": { name: "UserPromptSubmit", can_block: false, notes: "matcher ignored" }, + }, + + extra_events: [ + { name: "PermissionRequest", can_block: true, notes: "controls approval flow; no Claude Code equivalent" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [".codex/INSTALL.md"], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: ".agents/skills/", + agents_path: ".codex/agents/", + invocation: "native loading via $skill-name", + }, + + # ── Manifest ── + + manifest: { + path: ".codex-plugin/plugin.json", + required_fields: ["name", "version", "description"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["disable-model-invocation", "allowed-tools", "user-invocable"], + keep: [], + model_format: "codex-native", + # gpt-5.4, gpt-5.4-mini + }, + + # ── MCP ── + + mcp: { + config_path: ".mcp.json or config.toml [mcp]", + notes: "supports stdio and SSE transports", + }, + + # ── Path Variables ── + + paths: { + plugin_root: null, + hooks_scripts: null, + }, +} +``` + +### Codex-specific notes + +- Hooks require `codex_hooks = true` feature flag in `config.toml`. +- Subagent dispatch requires `multi_agent = true` feature flag. +- See `lib/patterns/subagent-dispatch.md` for message framing and named + agent dispatch patterns. +- Default hook timeout: 600 seconds (vs Claude Code's 60 seconds). +```` + +- [ ] **Step 2: Commit** + +```bash +git add lib/references/platforms/codex.md +git commit -m "feat: create codex.md platform spec" +``` + +--- + +### Task 5: Create cursor.md platform spec + +**Files:** +- Create: `lib/references/platforms/cursor.md` + +- [ ] **Step 1: Write cursor.md** + +Write `lib/references/platforms/cursor.md` with this content: + +````markdown +# Cursor Platform Specification + +```pseudocode +REGISTRY["cursor"] = { + + id: "cursor", + display_name: "Cursor", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: null }, + "file.write": { name: "Write", notes: null }, + "file.edit": { name: "Edit", notes: null }, + "shell.execute": { name: "Bash", notes: null }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: "Agent", notes: "also Task; full subagent support" }, + "task.track": { name: "TodoWrite", notes: null }, + "skill.invoke": { name: "Skill", notes: "/add-plugin installs, skills load natively" }, + "web.search": { name: "WebSearch", notes: null }, + "web.fetch": { name: "WebFetch", notes: null }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "hooks/hooks-cursor.json", + event_case: "camelCase", + timeout_unit: "seconds", + async_support: false, + structure: "flat", + output_key: "additional_context", + + events: { + "session.start": { name: "sessionStart", can_block: false, notes: null }, + "tool.before": { name: "preToolUse", can_block: true, notes: null }, + "tool.after": { name: "postToolUse", can_block: false, notes: null }, + "tool.after_failure": { name: "postToolUseFailure", can_block: false, notes: null }, + "subagent.start": { name: "subagentStart", can_block: true, notes: null }, + "subagent.stop": { name: "subagentStop", can_block: false, notes: "can trigger followup_message" }, + "compact.before": { name: "preCompact", can_block: false, notes: null }, + "session.stop": { name: "stop", can_block: false, notes: "can trigger followup_message" }, + "prompt.before_submit": { name: "beforeSubmitPrompt", can_block: true, notes: null }, + }, + + extra_events: [ + { name: "sessionEnd", can_block: false, notes: "fire-and-forget" }, + { name: "beforeShellExecution", can_block: true, notes: "before shell command" }, + { name: "afterShellExecution", can_block: false, notes: "after shell command" }, + { name: "beforeMCPExecution", can_block: true, notes: "before MCP tool call" }, + { name: "afterMCPExecution", can_block: false, notes: "after MCP tool call" }, + { name: "beforeReadFile", can_block: true, notes: "before file read" }, + { name: "afterFileEdit", can_block: false, notes: "after file edit" }, + { name: "afterAgentResponse", can_block: false, notes: "after assistant message" }, + { name: "afterAgentThought", can_block: false, notes: "after thinking block" }, + { name: "beforeTabFileRead", can_block: true, notes: "Tab: before file read" }, + { name: "afterTabFileEdit", can_block: false, notes: "Tab: after file edit" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [".cursor/rules/*.mdc"], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents/", + invocation: "Skill tool", + }, + + # ── Manifest ── + + manifest: { + path: ".cursor-plugin/plugin.json", + required_fields: ["name", "displayName", "description", "version", "author"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["allowed-tools", "user-invocable"], + keep: ["disable-model-invocation"], + model_format: "inherit", + # always "inherit" — defers to user's model selection + }, + + # ── MCP ── + + mcp: { + config_path: "mcp.json", + notes: "no dot prefix; no MCP Resources support", + }, + + # ── Path Variables ── + + paths: { + plugin_root: "${CURSOR_PLUGIN_ROOT}", + hooks_scripts: "/scripts/", + }, +} +``` + +### Cursor-specific notes + +- Cursor hooks use `"version": 1` at top level of hooks-cursor.json. +- Flat hook structure: no nested `hooks[]` array; each entry has `event`, + `matcher`, `command` at top level. +- Custom agents: `.cursor/agents/*.md` (project) or `~/.cursor/agents/` + (global). Frontmatter fields: name, description, model, readonly, + is_background. +- Built-in subagents: explore, bash, browser. +- Async subagents available in Cursor 2.5+. +```` + +- [ ] **Step 2: Commit** + +```bash +git add lib/references/platforms/cursor.md +git commit -m "feat: create cursor.md platform spec" +``` + +--- + +### Task 6: Create antigravity.md platform spec + +**Files:** +- Create: `lib/references/platforms/antigravity.md` + +- [ ] **Step 1: Write antigravity.md** + +Write `lib/references/platforms/antigravity.md` with this content: + +````markdown +# Antigravity Platform Specification + +```pseudocode +REGISTRY["antigravity"] = { + + id: "antigravity", + display_name: "Antigravity", + + # ── Tools ── + + tools: { + "file.read": { name: "view_file", notes: null }, + "file.write": { name: "write_to_file", notes: null }, + "file.edit": { name: "replace_file_content", notes: "also multi_replace_file_content" }, + "shell.execute": { name: "run_command", notes: "PowerShell on Windows" }, + "search.content": { name: "grep_search", notes: "ripgrep-based" }, + "search.files": { name: "find_by_name", notes: "fd with glob patterns" }, + "subagent.dispatch": { name: null, notes: "browser_subagent for browser tasks only" }, + "task.track": { name: null, notes: null }, + "skill.invoke": { name: null, notes: "skills auto-activate via semantic matching" }, + "web.search": { name: "search_web", notes: "with citations" }, + "web.fetch": { name: "read_url_content", notes: null }, + "user.ask": { name: null, notes: null }, + }, + + extra_tools: [ + { name: "codebase_search", purpose: "semantic code search (not pattern-based)" }, + { name: "search_in_file", purpose: "semantic search within a specific file" }, + { name: "view_code_item", purpose: "view specific code node/function by name" }, + { name: "view_file_outline", purpose: "show file structure/outline" }, + { name: "view_content_chunk", purpose: "view document chunks by position" }, + { name: "list_dir", purpose: "list directory contents" }, + { name: "command_status", purpose: "check status of background terminal commands" }, + { name: "read_terminal", purpose: "read terminal output by process ID" }, + { name: "send_command_input", purpose: "send stdin to running processes" }, + { name: "generate_image", purpose: "create or edit images from text prompts" }, + { name: "list_resources", purpose: "show available MCP server resources" }, + { name: "read_resource", purpose: "retrieve MCP resource contents" }, + { name: "browser_subagent", purpose: "browser automation (click, scroll, type, screenshots, recording)" }, + ], + + # ── Hooks ── + + hooks: { + system: "none", + config_path: null, + event_case: null, + timeout_unit: null, + async_support: false, + structure: null, + output_key: null, + + events: { + "session.start": { name: null, can_block: false, notes: null }, + "tool.before": { name: null, can_block: false, notes: null }, + "tool.after": { name: null, can_block: false, notes: null }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: null, can_block: false, notes: null }, + "session.stop": { name: null, can_block: false, notes: null }, + "prompt.before_submit": { name: null, can_block: false, notes: null }, + }, + + extra_events: [], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [".agents/rules/*.md"], + priority_note: "GEMINI.md is also loaded if present (Antigravity-native)", + }, + + # ── Skills ── + + skills: { + path: ".agents/skills/", + agents_path: ".agent/rules/", + invocation: "auto-discover via semantic matching", + }, + + # ── Manifest ── + + manifest: { + path: "package.json", + required_fields: ["name", "displayName", "version", "description", "publisher"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["model", "tools", "disable-model-invocation", "allowed-tools", "user-invocable"], + keep: [], + model_format: null, + # model field stripped entirely + }, + + # ── MCP ── + + mcp: { + config_path: null, + notes: "MCP configured via Antigravity settings UI, not file-based", + }, + + # ── Path Variables ── + + paths: { + plugin_root: null, + hooks_scripts: null, + }, +} +``` + +### Antigravity-specific notes + +- ALL tool names differ from Claude Code (every single one is renamed). +- No hook system at all. +- Antigravity uses Workflows (`.agents/workflows/*.md`) for slash-command + style invocation instead of `user-invocable` frontmatter. +- Skills auto-activate via semantic matching against descriptions. +- Legacy path `.agent/` (singular) also works but `.agents/` (plural) + is preferred. +```` + +- [ ] **Step 2: Commit** + +```bash +git add lib/references/platforms/antigravity.md +git commit -m "feat: create antigravity.md platform spec" +``` + +--- + +### Task 7: Create openclaw.md platform spec + +**Files:** +- Create: `lib/references/platforms/openclaw.md` + +- [ ] **Step 1: Write openclaw.md** + +Write `lib/references/platforms/openclaw.md` with this content: + +````markdown +# OpenClaw Platform Specification + +```pseudocode +REGISTRY["openclaw"] = { + + id: "openclaw", + display_name: "OpenClaw", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: null }, + "file.write": { name: "Write", notes: null }, + "file.edit": { name: "Edit", notes: null }, + "shell.execute": { name: "Bash", notes: null }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: null, notes: "agents declared in agents.list[] manifest config" }, + "task.track": { name: null, notes: null }, + "skill.invoke": { name: null, notes: "skills load natively into prompt" }, + "web.search": { name: "WebSearch", notes: null }, + "web.fetch": { name: "WebFetch", notes: null }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [], + + # ── Hooks ── + + hooks: { + system: "sdk", + config_path: null, + event_case: "snake_case", + timeout_unit: null, + async_support: true, + structure: "sdk", + output_key: null, + + events: { + "session.start": { name: "gateway:startup", can_block: false, notes: null }, + "tool.before": { name: "before_tool_call", can_block: true, notes: "{ block: true } is terminal" }, + "tool.after": { name: "after_tool_call", can_block: false, notes: null }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: "session:compact:before", can_block: false, notes: null }, + "session.stop": { name: null, can_block: false, notes: null }, + "prompt.before_submit": { name: null, can_block: false, notes: null }, + }, + + extra_events: [ + { name: "tool_result_persist", can_block: false, notes: "tool result persistence" }, + { name: "llm_input", can_block: false, notes: "before LLM call; requires allowConversationAccess" }, + { name: "llm_output", can_block: false, notes: "after LLM produces output" }, + { name: "message_received", can_block: false, notes: "inbound message; typed threadId" }, + { name: "message_sent", can_block: false, notes: "outbound message" }, + { name: "message_sending", can_block: true, notes: "{ cancel: true } is terminal" }, + { name: "before_agent_finalize", can_block: false, notes: "requires allowConversationAccess" }, + { name: "agent_end", can_block: false, notes: "requires allowConversationAccess" }, + { name: "before_model_resolve", can_block: false, notes: "model switching" }, + { name: "before_compaction", can_block: false, notes: null }, + { name: "after_compaction", can_block: false, notes: null }, + { name: "before_install", can_block: true, notes: "{ block: true } is terminal" }, + { name: "command", can_block: false, notes: "slash command issued" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [], + priority_note: "also loads SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, MEMORY.md (user workspace files, not plugin output)", + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents.list[] in manifest", + invocation: "native loading into prompt", + }, + + # ── Manifest ── + + manifest: { + path: "openclaw.plugin.json", + required_fields: ["id", "configSchema"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["disable-model-invocation", "allowed-tools"], + keep: [], + model_format: "provider/model", + # anthropic/claude-opus-4-6, anthropic/claude-sonnet-4-5, anthropic/claude-haiku-4-5 + }, + + # ── MCP ── + + mcp: { + config_path: "openclaw.plugin.json -> mcp block", + notes: "embedded in manifest", + }, + + # ── Path Variables ── + + paths: { + plugin_root: null, + hooks_scripts: null, + }, +} +``` + +### OpenClaw-specific notes + +- Hooks are TypeScript SDK-based: `api.registerHook(event, handler)` or + `api.on(event, handler)`. No file-based hook config. +- Non-bundled conversation hooks require + `plugins.entries..hooks.allowConversationAccess=true`. +- Full plugins also need `package.json` with `openclaw.extensions` and + `openclaw.compat`. +- Auto-detects Claude, Codex, and Cursor bundle layouts. +```` + +- [ ] **Step 2: Commit** + +```bash +git add lib/references/platforms/openclaw.md +git commit -m "feat: create openclaw.md platform spec" +``` + +--- + +### Task 8: Create subagent-dispatch.md + +**Files:** +- Create: `lib/patterns/subagent-dispatch.md` +- Source: `lib/references/codex-tools.md:24-109` (relocate prose) + +- [ ] **Step 1: Write subagent-dispatch.md** + +Write `lib/patterns/subagent-dispatch.md` with the content from `lib/references/codex-tools.md` sections "Subagent dispatch requires multi-agent support", "Named agent dispatch", "Environment Detection", and "Codex App Finishing": + +```markdown +# Subagent Dispatch Patterns + +Cross-platform patterns for dispatching subagents from skills that use +Claude Code's `Task` or `Agent` tools. Referenced by rubric conditions and +skill pseudocode. + +--- + +## Codex: Named Agent Dispatch + +Claude Code skills reference named agent types like `superpowers:code-reviewer`. +Codex does not have a named agent registry — `spawn_agent` creates generic agents +from built-in roles (`default`, `explorer`, `worker`). + +When a skill says to dispatch a named agent type: + +1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's + local prompt template like `code-quality-reviewer-prompt.md`) +2. Read the prompt content +3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.) +4. Spawn a `worker` agent with the filled content as the `message` + +| Skill instruction | Codex equivalent | +| ----------------- | ---------------- | +| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content | +| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt | + +### Message framing + +The `message` parameter is user-level input, not a system prompt. Structure it +for maximum instruction adherence: + + Your task is to perform the following. Follow the instructions below exactly. + + + [filled prompt content from the agent's .md file] + + + Execute this now. Output ONLY the structured response following the format + specified in the instructions above. + +- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...") +- Wrap instructions in XML tags — the model treats tagged blocks as authoritative +- End with an explicit execution directive to prevent summarization of the instructions + +### When this workaround can be removed + +This approach compensates for Codex's plugin system not yet supporting an `agents` +field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the +plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and +skills can dispatch named agent types directly. + +--- + +## Codex: Multi-Agent Feature Flag + +Subagent dispatch requires the multi-agent feature flag: + + # ~/.codex/config.toml + [features] + multi_agent = true + +This enables `spawn_agent`, `wait`, and `close_agent`. + +--- + +## Environment Detection + +Skills that create worktrees or finish branches should detect their +environment with read-only git commands before proceeding: + + GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P) + GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P) + BRANCH=$(git branch --show-current) + +- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation) +- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox) + +See `using-git-worktrees` Step 0 and `finishing-a-development-branch` +Step 1 for how each skill uses these signals. + +--- + +## Codex App Finishing + +When the sandbox blocks branch/push operations (detached HEAD in an +externally managed worktree), the agent commits all work and informs +the user to use the App's native controls: + +- **"Create branch"** — names the branch, then commit/push/PR via App UI +- **"Hand off to local"** — transfers work to the user's local checkout + +The agent can still run tests, stage files, and output suggested branch +names, commit messages, and PR descriptions for the user to copy. +``` + +- [ ] **Step 2: Commit** + +```bash +git add lib/patterns/subagent-dispatch.md +git commit -m "feat: create subagent-dispatch.md with relocated Codex patterns" +``` + +--- + +### Task 9: Update context files (GEMINI.md, AGENTS.md, CLAUDE.md) + +**Files:** +- Modify: `GEMINI.md:7-16` +- Modify: `AGENTS.md:28-45` +- Modify: `CLAUDE.md:17` + +- [ ] **Step 1: Update GEMINI.md** + +Replace the Tool References section (lines 10-16): + +**Before:** +```markdown +## Tool References + +@./lib/references/gemini-tools.md +@./lib/references/codex-tools.md +@./lib/references/cursor-tools.md +@./lib/references/antigravity-tools.md +@./lib/references/openclaw-tools.md +``` + +**After:** +```markdown +## Platform API + +@./lib/references/platform-api.md +@./lib/references/platforms/gemini-cli.md +@./lib/references/platforms/codex.md +@./lib/references/platforms/cursor.md +@./lib/references/platforms/antigravity.md +@./lib/references/platforms/openclaw.md +``` + +- [ ] **Step 2: Update AGENTS.md** + +Replace the tool references section (lines 28-45): + +**Before:** +```markdown +See `lib/references/` for platform-specific tool mapping tables: +- `codex-tools.md` — Codex (spawn_agent, update_plan, message framing) +- `gemini-tools.md` — Gemini CLI (read_file, replace, run_shell_command, etc.) +- `cursor-tools.md` — Cursor (same names, different hooks/model/context) +- `antigravity-tools.md` — Antigravity (same names, stripped frontmatter) +- `openclaw-tools.md` — OpenClaw (agents.list[], no TodoWrite/Skill, SDK hooks) + +## Platform Accuracy Constraint + +Every platform-specific claim in this repo must be consistent with the researched +platform reference docs. Before changing any file that makes platform-specific +claims, cross-reference: + +1. **Research docs** — `docs/platforms/*.md` (sourced, with inline citations) +2. **Reconciliation matrix** — `docs/reconciliation-matrix.md` (tracks known + discrepancies and their fix status) +3. **Canonical lookup tables** — `lib/references/platform-mappings.md` (single + source of truth consumed by rubrics) +``` + +**After:** +```markdown +Platform-specific tool names, hooks, manifests, and frontmatter rules are defined +as structured `PlatformSpec` dictionaries in `lib/references/platforms/`. The type +system and lookup functions are in `lib/references/platform-api.md`. + +Use `tool_name(platform, op)` for tool mappings, `hook_event(platform, event)` +for hook events, and `strip_fields(platform)` for frontmatter stripping. + +## Platform Accuracy Constraint + +Every platform-specific claim in this repo must be consistent with the researched +platform reference docs. Before changing any file that makes platform-specific +claims, cross-reference: + +1. **Research docs** — `docs/platforms/*.md` (sourced, with inline citations) +2. **Reconciliation matrix** — `docs/reconciliation-matrix.md` (tracks known + discrepancies and their fix status) +3. **Platform API** — `lib/references/platform-api.md` and `lib/references/platforms/*.md` + (structured PlatformSpec dictionaries consumed by rubrics) +``` + +- [ ] **Step 3: Update CLAUDE.md** + +Replace line 17: + +**Before:** +```markdown +3. **Canonical lookup tables** — `lib/references/platform-mappings.md` (single + source of truth consumed by rubrics) +``` + +**After:** +```markdown +3. **Platform API** — `lib/references/platform-api.md` and `lib/references/platforms/*.md` + (structured PlatformSpec dictionaries consumed by rubrics) +``` + +- [ ] **Step 4: Commit** + +```bash +git add GEMINI.md AGENTS.md CLAUDE.md +git commit -m "refactor: update context files to reference platform API" +``` + +--- + +### Task 10: Update rubric YAML files + +**Files:** +- Modify: `lib/rubrics/claude-code.yaml` +- Modify: `lib/rubrics/codex.yaml` +- Modify: `lib/rubrics/gemini-cli.yaml` +- Modify: `lib/rubrics/cursor.yaml` +- Modify: `lib/rubrics/antigravity.yaml` +- Modify: `lib/rubrics/openclaw.yaml` +- Modify: `lib/rubrics/rubric-framework.md` + +All changes are comment/reference updates. No scoring logic changes. + +- [ ] **Step 1: Update all LOOKUP comments to function calls** + +Apply these replacements across all 6 YAML files. Each replacement is a comment text change inside a `check:` or `condition:` block. + +**Pattern 1 — manifest_required_fields:** +Replace all instances of: +``` +LOOKUP["manifest_required_fields"][""] +``` +With: +``` +REGISTRY[""].manifest.required_fields +``` + +**Pattern 2 — field_stripping:** +Replace all instances of: +``` +LOOKUP["field_stripping"][""] +``` +With: +``` +strip_fields("") +``` + +**Pattern 3 — tool_mapping:** +Replace all instances of: +``` +LOOKUP["tool_mapping"][""][""] +``` +With the canonical form, e.g.: +``` +tool_name("", "file.edit") # for Edit +tool_name("", "subagent.dispatch") # for Task +tool_name("", "task.track") # for TodoWrite +``` + +**Pattern 4 — hook_events:** +Replace all instances of: +``` +LOOKUP["hook_events"][""] +``` +With: +``` +REGISTRY[""].hooks.events +``` + +**Pattern 5 — model_mapping:** +Replace all instances of: +``` +LOOKUP["model_mapping"][""] +``` +With: +``` +REGISTRY[""].frontmatter.model_format +``` + +**Pattern 6 — path_variables:** +Replace: +``` +LOOKUP["path_variables"]["cursor"] +``` +With: +``` +REGISTRY["cursor"].paths.plugin_root +``` + +**Pattern 7 — hook_format_rules:** +Replace: +``` +LOOKUP["hook_format_rules"]["cursor"] +``` +With: +``` +REGISTRY["cursor"].hooks.output_key +``` + +**Pattern 8 — agent_output_format:** +Replace: +``` +LOOKUP["agent_output_format"]["gemini"] +``` +With: +``` +REGISTRY["gemini-cli"].skills.agents_path +``` + +**Pattern 9 — tool_names:** +Replace: +``` +LOOKUP["tool_names"]["claude-code"] +``` +With: +``` +supported_tools("claude-code") +``` + +- [ ] **Step 2: Update file references in rubric conditions** + +In `codex.yaml`, replace all references to `codex-tools.md`: +- `sidecars = glob("**/codex-tools.md")` → `spec = find_file("lib/references/platforms/codex.md") OR glob("**/codex.md")` +- `"No codex-tools.md found"` → `"No Codex platform spec found"` +- `references/codex-tools.md` → `lib/references/platforms/codex.md` +- Add reference to `lib/patterns/subagent-dispatch.md` where message framing is mentioned + +In `gemini-cli.yaml`, replace all references to `gemini-tools.md`: +- `"gemini-tools.md" in content` → `"platforms/gemini-cli.md" in content` (for GEMINI.md @include check) +- `sidecars = glob("**/gemini-tools.md")` → `spec = find_file("lib/references/platforms/gemini-cli.md") OR glob("**/gemini-cli.md")` +- `"No gemini-tools.md found"` → `"No Gemini CLI platform spec found"` +- `skills/{name}/references/gemini-tools.md` → `lib/references/platforms/gemini-cli.md` + +In `antigravity.yaml`, replace: +- `antigravity-tools.md reference` → `lib/references/platforms/antigravity.md` + +In `openclaw.yaml`, replace: +- `openclaw-tools.md` references → `lib/references/platforms/openclaw.md` + +In `cursor.yaml`, replace: +- `cursor-tools.md reference` → `lib/references/platforms/cursor.md` + +- [ ] **Step 3: Update rubric-framework.md** + +Replace line 5: +``` +Lookup tables are in `lib/references/platform-mappings.md`. +``` +With: +``` +Platform specs are in `lib/references/platforms/*.md`. Lookup functions are in `lib/references/platform-api.md`. +``` + +Replace line 144 reference: +``` +Missing subagent translation | Minor | Skills dispatch via `Task`/`Agent` but no codex-tools or gemini-tools sidecar +``` +With: +``` +Missing subagent translation | Minor | Skills dispatch via `Task`/`Agent` but no platform spec documents the mapping +``` + +- [ ] **Step 4: Commit** + +```bash +git add lib/rubrics/*.yaml lib/rubrics/rubric-framework.md +git commit -m "refactor: update rubric LOOKUP comments to platform API function calls" +``` + +--- + +### Task 11: Update pattern files + +**Files:** +- Modify: `lib/patterns/inventory.md:65-88` +- Modify: `lib/patterns/injection-checks.md:13,33` +- Modify: `lib/patterns/bootstrapping.md:76-87,273,281` + +- [ ] **Step 1: Update inventory.md** + +Replace the `sidecar_files` list and surrounding logic (lines 65-88): + +**Before:** +```pseudocode + sidecar_files = ["codex-tools.md", "gemini-tools.md", "cursor-tools.md", + "antigravity-tools.md", "openclaw-tools.md"] +``` + +**After:** +```pseudocode + platform_spec_files = ["codex.md", "gemini-cli.md", "cursor.md", + "antigravity.md", "openclaw.md"] +``` + +Update the bare-skill branch (lines 69-75): +```pseudocode + IF computed.shape IN ["bare-skill-repo", "skill-first"]: + FOR skill IN computed.skills: + FOR spec_file IN platform_spec_files: + target = "skills/" + skill.dir + "/references/" + spec_file + status = IF file_exists(plugin_path + "/" + target) THEN "PRESENT" ELSE "MISSING" + computed.sidecar_results.append({ skill: skill.dir, file: spec_file, status: status }) +``` + +Update the plugin branch (lines 77-88): +```pseudocode + ELIF computed.shape == "full-portable-plugin": + shared_paths = ["lib/references/platforms/", "references/platforms/", "lib/references/"] + FOR spec_file IN platform_spec_files: + found = false + FOR shared IN shared_paths: + IF file_exists(plugin_path + "/" + shared + spec_file): + found = true + computed.sidecar_results.append({ skill: "(shared)", file: shared + spec_file, status: "PRESENT" }) + BREAK + IF NOT found: + computed.sidecar_results.append({ skill: "(shared)", file: spec_file, status: "MISSING" }) +``` + +Also update the `sidecar_platform` helper (line 158): +``` +| `sidecar_platform(file)` | `"gemini-tools.md" → "gemini-cli"`, `"codex-tools.md" → "codex"` | +``` +Replace with: +``` +| `spec_platform(file)` | `"gemini-cli.md" → "gemini-cli"`, `"codex.md" → "codex"` | +``` + +- [ ] **Step 2: Update injection-checks.md** + +Replace Component 2 in the table (line 13): +``` +| 2 | `lib/references/gemini-tools.md` | File exists (shared tool reference) | PRESENT / MISSING | +``` +With: +``` +| 2 | `lib/references/platforms/gemini-cli.md` | File exists (platform spec) | PRESENT / MISSING | +``` + +Replace the pseudocode check (line 33): +``` + results.append(check_file_exists("lib/references/gemini-tools.md")) +``` +With: +``` + results.append(check_file_exists("lib/references/platforms/gemini-cli.md")) +``` + +- [ ] **Step 3: Update bootstrapping.md** + +Replace lines 82-83: +``` + - using-{{name}}/references/codex-tools.md + - using-{{name}}/references/gemini-tools.md +``` +With: +``` + # Per-skill sidecars are no longer generated. Shared platform specs + # in lib/references/platforms/ are loaded via context file @includes. +``` + +Replace line 273: +``` + @./skills/using-{{name}}/references/gemini-tools.md +``` +With: +``` + @./lib/references/platforms/gemini-cli.md +``` + +Replace line 281 reference to "gemini-tools.md sidecar" with "platform spec". + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/inventory.md lib/patterns/injection-checks.md lib/patterns/bootstrapping.md +git commit -m "refactor: update pattern files to reference platform specs" +``` + +--- + +### Task 12: Update CI, CONTRIBUTING.md + +**Files:** +- Modify: `.github/workflows/ci.yml:86-88` +- Modify: `CONTRIBUTING.md:40` + +- [ ] **Step 1: Update CI workflow** + +Replace lines 86-88: + +**Before:** +```yaml + echo "" + echo "=== Shared Tool References ===" + for sidecar in codex-tools.md gemini-tools.md cursor-tools.md antigravity-tools.md openclaw-tools.md; do + check_file "lib/references/${sidecar}" + done +``` + +**After:** +```yaml + echo "" + echo "=== Platform Specs ===" + check_file "lib/references/platform-api.md" + for spec in codex.md gemini-cli.md cursor.md antigravity.md openclaw.md claude-code.md; do + check_file "lib/references/platforms/${spec}" + done +``` + +- [ ] **Step 2: Update CONTRIBUTING.md** + +Replace line 40: +``` +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`). +``` +With: +``` +See existing skills in `skills/` for examples. Platform-specific tool mappings are in `lib/references/platforms/`. See `lib/references/platform-api.md` for the type system and lookup functions. +``` + +- [ ] **Step 3: Commit** + +```bash +git add .github/workflows/ci.yml CONTRIBUTING.md +git commit -m "refactor: update CI checks and CONTRIBUTING for platform specs" +``` + +--- + +### Task 13: Delete old files and update reconciliation matrix + +**Files:** +- Delete: `lib/references/platform-mappings.md` +- Delete: `lib/references/gemini-tools.md` +- Delete: `lib/references/codex-tools.md` +- Delete: `lib/references/cursor-tools.md` +- Delete: `lib/references/antigravity-tools.md` +- Delete: `lib/references/openclaw-tools.md` +- Modify: `docs/reconciliation-matrix.md` + +- [ ] **Step 1: Delete old reference files** + +```bash +git rm lib/references/platform-mappings.md +git rm lib/references/gemini-tools.md +git rm lib/references/codex-tools.md +git rm lib/references/cursor-tools.md +git rm lib/references/antigravity-tools.md +git rm lib/references/openclaw-tools.md +``` + +- [ ] **Step 2: Verify new files exist** + +```bash +ls lib/references/platform-api.md +ls lib/references/platforms/ +``` + +Expected: `platform-api.md` present, `platforms/` contains 6 `.md` files. + +- [ ] **Step 3: Run CI checks locally** + +```bash +# Verify all platform specs exist +for spec in codex.md gemini-cli.md cursor.md antigravity.md openclaw.md claude-code.md; do + test -f "lib/references/platforms/${spec}" && echo "OK: ${spec}" || echo "FAIL: ${spec}" +done + +# Verify platform-api.md exists +test -f lib/references/platform-api.md && echo "OK: platform-api.md" || echo "FAIL: platform-api.md" + +# Verify no old files remain +for old in platform-mappings.md gemini-tools.md codex-tools.md cursor-tools.md antigravity-tools.md openclaw-tools.md; do + test -f "lib/references/${old}" && echo "FAIL: ${old} still exists" || echo "OK: ${old} removed" +done +``` + +Expected: All OK, no FAIL. + +- [ ] **Step 4: Update reconciliation matrix** + +Add a new section to `docs/reconciliation-matrix.md` noting the restructure. +The old Sections 1 and 6 (which referenced the deleted files) should note that +the content has migrated to the platform API structure. + +- [ ] **Step 5: Commit** + +```bash +git add -A lib/references/ docs/reconciliation-matrix.md +git commit -m "refactor: delete old reference files, complete platform API migration + +Removes platform-mappings.md and 5 *-tools.md files, replaced by +platform-api.md + 6 structured PlatformSpec files in platforms/." +``` + +--- + +### Task 14: Verify and clean up + +- [ ] **Step 1: Grep for stale references** + +```bash +grep -rn 'platform-mappings\|gemini-tools\.md\|codex-tools\.md\|cursor-tools\.md\|antigravity-tools\.md\|openclaw-tools\.md' --include='*.md' --include='*.yaml' --include='*.yml' --include='*.json' | grep -v 'docs/superpowers/' | grep -v 'docs/reconciliation' | grep -v '.git/' | grep -v 'CHANGELOG' +``` + +Expected: No output (all references updated). + +- [ ] **Step 2: Fix any remaining references** + +If grep finds stale references, update them to point to the new paths. + +- [ ] **Step 3: Run markdown lint** + +```bash +npx markdownlint-cli2 "lib/references/**/*.md" +``` + +Fix any lint issues. + +- [ ] **Step 4: Final commit if needed** + +```bash +git add -A +git commit -m "chore: fix remaining stale references to old platform files" +``` From 3715ee262ad98aa1c444f39eaca2e58c3cbd0721 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:24:26 +1000 Subject: [PATCH 04/43] feat: create platform-api.md with type system and lookup functions --- lib/references/platform-api.md | 193 +++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 lib/references/platform-api.md diff --git a/lib/references/platform-api.md b/lib/references/platform-api.md new file mode 100644 index 0000000..86e7ab9 --- /dev/null +++ b/lib/references/platform-api.md @@ -0,0 +1,193 @@ +# Platform API + +Structured type system and deterministic lookup functions for cross-platform +portability. Each platform declares a `PlatformSpec` dictionary in +`lib/references/platforms/.md`. This file defines the schema and API. + +--- + +## Types + +```pseudocode +TYPE ToolEntry = { + name: string | null, # platform-native tool name; null = not supported + notes: string | null, # brief clarification when needed +} + +TYPE HookEvent = { + name: string | null, # platform-native event name; null = no equivalent + can_block: bool, # whether this hook can block execution + notes: string | null, +} + +TYPE PlatformSpec = { + id: string, # "gemini-cli", "codex", "cursor", "antigravity", "openclaw", "claude-code" + display_name: string, + + # ── Tools ── + tools: Dict[Operation, ToolEntry], + extra_tools: List[{ name: string, purpose: string }], + + # ── Hooks ── + hooks: { + system: "file" | "sdk" | "none", + config_path: string | null, + event_case: "PascalCase" | "camelCase" | "snake_case" | null, + timeout_unit: "seconds" | "milliseconds" | null, + async_support: bool, + structure: "nested" | "flat" | "sdk" | null, + output_key: string | null, + events: Dict[CanonicalEvent, HookEvent], + extra_events: List[HookEvent], + }, + + # ── Context ── + context: { + primary_file: string, + secondary_files: List[string], + priority_note: string | null, + }, + + # ── Skills ── + skills: { + path: string, + agents_path: string, + invocation: string, + }, + + # ── Manifest ── + manifest: { + path: string | null, + required_fields: List[string], + }, + + # ── Frontmatter ── + frontmatter: { + strip: List[string], + keep: List[string], + model_format: string | null, + }, + + # ── MCP ── + mcp: { + config_path: string | null, + notes: string | null, + }, + + # ── Path Variables ── + paths: { + plugin_root: string | null, + hooks_scripts: string | null, + }, +} +``` + +--- + +## Canonical Enums + +```pseudocode +Operation = ENUM( + "file.read", + "file.write", + "file.edit", + "shell.execute", + "search.content", + "search.files", + "subagent.dispatch", + "task.track", + "skill.invoke", + "web.search", + "web.fetch", + "user.ask", +) + +CanonicalEvent = ENUM( + "session.start", + "tool.before", + "tool.after", + "tool.after_failure", + "subagent.start", + "subagent.stop", + "compact.before", + "session.stop", + "prompt.before_submit", +) +``` + +--- + +## Registry + +```pseudocode +REGISTRY: Dict[string, PlatformSpec] = {} + # Populated by per-platform data files in lib/references/platforms/. +``` + +--- + +## Lookup Functions + +```pseudocode +# ── Core lookups ── + +FUNCTION tool_name(platform, op) + RETURNS the platform-native tool name for a canonical operation, or null. + LOOKUP REGISTRY[platform].tools[op].name + +FUNCTION hook_event(platform, event) + RETURNS the platform-native hook event name, or null. + LOOKUP REGISTRY[platform].hooks.events[event].name + +FUNCTION hook_can_block(platform, event) + RETURNS whether the hook for this event can block execution. + entry = REGISTRY[platform].hooks.events[event] + RETURN entry.name IS NOT null AND entry.can_block + +# ── Bulk queries ── + +FUNCTION supported_tools(platform) + RETURNS list of canonical operations this platform supports. + FOR EACH op, entry IN REGISTRY[platform].tools: + INCLUDE op WHERE entry.name IS NOT null + +FUNCTION unsupported_tools(platform) + RETURNS list of canonical operations this platform cannot perform. + FOR EACH op, entry IN REGISTRY[platform].tools: + INCLUDE op WHERE entry.name IS null + +FUNCTION has_hooks(platform) + RETURN REGISTRY[platform].hooks.system != "none" + +FUNCTION strip_fields(platform) + RETURN REGISTRY[platform].frontmatter.strip + +# ── Cross-platform ── + +FUNCTION platforms_supporting(op) + RETURNS all platform IDs that support a given operation. + FOR EACH pid, spec IN REGISTRY: + INCLUDE pid WHERE spec.tools[op].name IS NOT null + +FUNCTION tool_mapping_table(op) + RETURNS { platform_id: tool_name } for one operation across all platforms. + FOR EACH pid, spec IN REGISTRY: + EMIT pid -> spec.tools[op].name + +FUNCTION diff_from(source, target) + RETURNS what changes when porting from source to target platform. + src = REGISTRY[source] + tgt = REGISTRY[target] + renamed_tools: + FOR EACH op IN Operation + WHERE src.tools[op].name != tgt.tools[op].name + AND tgt.tools[op].name IS NOT null: + EMIT op -> { from: src.tools[op].name, to: tgt.tools[op].name } + lost_tools: + FOR EACH op IN Operation + WHERE src.tools[op].name IS NOT null + AND tgt.tools[op].name IS null: + EMIT op + strip_fields: tgt.frontmatter.strip + model_format: tgt.frontmatter.model_format +``` From 8b3a425aca062dd7509350e19a74a5d7a882a82b Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:26:11 +1000 Subject: [PATCH 05/43] feat: create 6 PlatformSpec data files claude-code, gemini-cli, codex, cursor, antigravity, openclaw --- lib/references/platforms/antigravity.md | 124 ++++++++++++++++++++++++ lib/references/platforms/claude-code.md | 109 +++++++++++++++++++++ lib/references/platforms/codex.md | 112 +++++++++++++++++++++ lib/references/platforms/cursor.md | 123 +++++++++++++++++++++++ lib/references/platforms/gemini-cli.md | 114 ++++++++++++++++++++++ lib/references/platforms/openclaw.md | 124 ++++++++++++++++++++++++ 6 files changed, 706 insertions(+) create mode 100644 lib/references/platforms/antigravity.md create mode 100644 lib/references/platforms/claude-code.md create mode 100644 lib/references/platforms/codex.md create mode 100644 lib/references/platforms/cursor.md create mode 100644 lib/references/platforms/gemini-cli.md create mode 100644 lib/references/platforms/openclaw.md diff --git a/lib/references/platforms/antigravity.md b/lib/references/platforms/antigravity.md new file mode 100644 index 0000000..6a555f2 --- /dev/null +++ b/lib/references/platforms/antigravity.md @@ -0,0 +1,124 @@ +# Antigravity Platform Specification + +```pseudocode +REGISTRY["antigravity"] = { + + id: "antigravity", + display_name: "Antigravity", + + # ── Tools ── + + tools: { + "file.read": { name: "view_file", notes: null }, + "file.write": { name: "write_to_file", notes: null }, + "file.edit": { name: "replace_file_content", notes: "also multi_replace_file_content" }, + "shell.execute": { name: "run_command", notes: "PowerShell on Windows" }, + "search.content": { name: "grep_search", notes: "ripgrep-based" }, + "search.files": { name: "find_by_name", notes: "fd with glob patterns" }, + "subagent.dispatch": { name: null, notes: "browser_subagent for browser tasks only" }, + "task.track": { name: null, notes: null }, + "skill.invoke": { name: null, notes: "skills auto-activate via semantic matching" }, + "web.search": { name: "search_web", notes: "with citations" }, + "web.fetch": { name: "read_url_content", notes: null }, + "user.ask": { name: null, notes: null }, + }, + + extra_tools: [ + { name: "codebase_search", purpose: "semantic code search (not pattern-based)" }, + { name: "search_in_file", purpose: "semantic search within a specific file" }, + { name: "view_code_item", purpose: "view specific code node/function by name" }, + { name: "view_file_outline", purpose: "show file structure/outline" }, + { name: "view_content_chunk", purpose: "view document chunks by position" }, + { name: "list_dir", purpose: "list directory contents" }, + { name: "command_status", purpose: "check status of background terminal commands" }, + { name: "read_terminal", purpose: "read terminal output by process ID" }, + { name: "send_command_input", purpose: "send stdin to running processes" }, + { name: "generate_image", purpose: "create or edit images from text prompts" }, + { name: "list_resources", purpose: "show available MCP server resources" }, + { name: "read_resource", purpose: "retrieve MCP resource contents" }, + { name: "browser_subagent", purpose: "browser automation (click, scroll, type, screenshots, recording)" }, + ], + + # ── Hooks ── + + hooks: { + system: "none", + config_path: null, + event_case: null, + timeout_unit: null, + async_support: false, + structure: null, + output_key: null, + + events: { + "session.start": { name: null, can_block: false, notes: null }, + "tool.before": { name: null, can_block: false, notes: null }, + "tool.after": { name: null, can_block: false, notes: null }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: null, can_block: false, notes: null }, + "session.stop": { name: null, can_block: false, notes: null }, + "prompt.before_submit": { name: null, can_block: false, notes: null }, + }, + + extra_events: [], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [".agents/rules/*.md"], + priority_note: "GEMINI.md is also loaded if present (Antigravity-native)", + }, + + # ── Skills ── + + skills: { + path: ".agents/skills/", + agents_path: ".agent/rules/", + invocation: "auto-discover via semantic matching", + }, + + # ── Manifest ── + + manifest: { + path: "package.json", + required_fields: ["name", "displayName", "version", "description", "publisher"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["model", "tools", "disable-model-invocation", "allowed-tools", "user-invocable"], + keep: [], + model_format: null, + # model field stripped entirely + }, + + # ── MCP ── + + mcp: { + config_path: null, + notes: "MCP configured via Antigravity settings UI, not file-based", + }, + + # ── Path Variables ── + + paths: { + plugin_root: null, + hooks_scripts: null, + }, +} +``` + +### Antigravity-specific notes + +- ALL tool names differ from Claude Code (every single one is renamed). +- No hook system at all. +- Antigravity uses Workflows (`.agents/workflows/*.md`) for slash-command + style invocation instead of `user-invocable` frontmatter. +- Skills auto-activate via semantic matching against descriptions. +- Legacy path `.agent/` (singular) also works but `.agents/` (plural) + is preferred. diff --git a/lib/references/platforms/claude-code.md b/lib/references/platforms/claude-code.md new file mode 100644 index 0000000..835f50a --- /dev/null +++ b/lib/references/platforms/claude-code.md @@ -0,0 +1,109 @@ +# Claude Code Platform Specification + +```pseudocode +REGISTRY["claude-code"] = { + + id: "claude-code", + display_name: "Claude Code", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: null }, + "file.write": { name: "Write", notes: null }, + "file.edit": { name: "Edit", notes: null }, + "shell.execute": { name: "Bash", notes: null }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: "Agent", notes: "also Task for background dispatch" }, + "task.track": { name: "TodoWrite", notes: null }, + "skill.invoke": { name: "Skill", notes: null }, + "web.search": { name: "WebSearch", notes: null }, + "web.fetch": { name: "WebFetch", notes: null }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [ + { name: "Monitor", purpose: "run background command, stream output lines back" }, + { name: "NotebookEdit", purpose: "modify Jupyter notebook cells" }, + { name: "LSP", purpose: "code intelligence: jump to def, find refs, type errors" }, + { name: "EnterWorktree", purpose: "create/switch to git worktree" }, + { name: "ExitWorktree", purpose: "return to main worktree" }, + { name: "CronCreate", purpose: "schedule recurring/one-shot prompt" }, + { name: "TaskCreate", purpose: "create task in session checklist" }, + { name: "ToolSearch", purpose: "search and load deferred tools" }, + ], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "hooks/hooks.json", + event_case: "PascalCase", + timeout_unit: "seconds", + async_support: true, + structure: "nested", + output_key: "hookSpecificOutput.additionalContext", + + events: { + "session.start": { name: "SessionStart", can_block: false, notes: null }, + "tool.before": { name: "PreToolUse", can_block: true, notes: null }, + "tool.after": { name: "PostToolUse", can_block: false, notes: null }, + "tool.after_failure": { name: "PostToolUseFailure", can_block: false, notes: null }, + "subagent.start": { name: "SubagentStart", can_block: false, notes: null }, + "subagent.stop": { name: "SubagentStop", can_block: false, notes: null }, + "compact.before": { name: "PreCompact", can_block: false, notes: null }, + "session.stop": { name: "Stop", can_block: false, notes: null }, + "prompt.before_submit": { name: "UserPromptSubmit", can_block: false, notes: null }, + }, + + extra_events: [], + }, + + # ── Context ── + + context: { + primary_file: "CLAUDE.md", + secondary_files: [], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents/", + invocation: "Skill tool", + }, + + # ── Manifest ── + + manifest: { + path: ".claude-plugin/plugin.json", + required_fields: ["name", "version", "description", "author.name", "author.email"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: [], + keep: ["disable-model-invocation", "allowed-tools", "user-invocable"], + model_format: "claude-shortname", + # opus, sonnet, haiku + }, + + # ── MCP ── + + mcp: { + config_path: ".mcp.json", + notes: "dot-prefixed; supports resources and tools", + }, + + # ── Path Variables ── + + paths: { + plugin_root: "${CLAUDE_PLUGIN_ROOT}", + hooks_scripts: "/hooks/scripts/", + }, +} +``` diff --git a/lib/references/platforms/codex.md b/lib/references/platforms/codex.md new file mode 100644 index 0000000..0ccfd24 --- /dev/null +++ b/lib/references/platforms/codex.md @@ -0,0 +1,112 @@ +# Codex Platform Specification + +```pseudocode +REGISTRY["codex"] = { + + id: "codex", + display_name: "Codex", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: "built-in, no named tool in some contexts" }, + "file.write": { name: "Write", notes: "uses apply_patch internally" }, + "file.edit": { name: "apply_patch", notes: "unified file write mechanism" }, + "shell.execute": { name: "Bash", notes: "sandboxed execution" }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: "spawn_agent", notes: "generic roles: default, worker, explorer" }, + "task.track": { name: "update_plan", notes: null }, + "skill.invoke": { name: null, notes: "skills load natively via $skill-name" }, + "web.search": { name: "WebSearch", notes: "live or cached mode" }, + "web.fetch": { name: null, notes: "no direct equivalent; use MCP" }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [ + { name: "report_agent_job_result", purpose: "worker result reporting for CSV batch jobs" }, + ], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "hooks.json or config.toml [hooks]", + event_case: "PascalCase", + timeout_unit: "seconds", + async_support: false, + structure: "nested", + output_key: "permissionDecision / decision", + + events: { + "session.start": { name: "SessionStart", can_block: false, notes: null }, + "tool.before": { name: "PreToolUse", can_block: true, notes: "permissionDecision: deny" }, + "tool.after": { name: "PostToolUse", can_block: true, notes: "decision: block replaces output" }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: null, can_block: false, notes: null }, + "session.stop": { name: "Stop", can_block: true, notes: "decision: block continues session" }, + "prompt.before_submit": { name: "UserPromptSubmit", can_block: false, notes: "matcher ignored" }, + }, + + extra_events: [ + { name: "PermissionRequest", can_block: true, notes: "controls approval flow; no Claude Code equivalent" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [".codex/INSTALL.md"], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: ".agents/skills/", + agents_path: ".codex/agents/", + invocation: "native loading via $skill-name", + }, + + # ── Manifest ── + + manifest: { + path: ".codex-plugin/plugin.json", + required_fields: ["name", "version", "description"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["disable-model-invocation", "allowed-tools", "user-invocable"], + keep: [], + model_format: "codex-native", + # gpt-5.4, gpt-5.4-mini + }, + + # ── MCP ── + + mcp: { + config_path: ".mcp.json or config.toml [mcp]", + notes: "supports stdio and SSE transports", + }, + + # ── Path Variables ── + + paths: { + plugin_root: null, + hooks_scripts: null, + }, +} +``` + +### Codex-specific notes + +- Hooks require `codex_hooks = true` feature flag in `config.toml`. +- Subagent dispatch requires `multi_agent = true` feature flag. +- See `lib/patterns/subagent-dispatch.md` for message framing and named + agent dispatch patterns. +- Default hook timeout: 600 seconds (vs Claude Code's 60 seconds). diff --git a/lib/references/platforms/cursor.md b/lib/references/platforms/cursor.md new file mode 100644 index 0000000..c92a1f0 --- /dev/null +++ b/lib/references/platforms/cursor.md @@ -0,0 +1,123 @@ +# Cursor Platform Specification + +```pseudocode +REGISTRY["cursor"] = { + + id: "cursor", + display_name: "Cursor", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: null }, + "file.write": { name: "Write", notes: null }, + "file.edit": { name: "Edit", notes: null }, + "shell.execute": { name: "Bash", notes: null }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: "Agent", notes: "also Task; full subagent support" }, + "task.track": { name: "TodoWrite", notes: null }, + "skill.invoke": { name: "Skill", notes: "/add-plugin installs, skills load natively" }, + "web.search": { name: "WebSearch", notes: null }, + "web.fetch": { name: "WebFetch", notes: null }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "hooks/hooks-cursor.json", + event_case: "camelCase", + timeout_unit: "seconds", + async_support: false, + structure: "flat", + output_key: "additional_context", + + events: { + "session.start": { name: "sessionStart", can_block: false, notes: null }, + "tool.before": { name: "preToolUse", can_block: true, notes: null }, + "tool.after": { name: "postToolUse", can_block: false, notes: null }, + "tool.after_failure": { name: "postToolUseFailure", can_block: false, notes: null }, + "subagent.start": { name: "subagentStart", can_block: true, notes: null }, + "subagent.stop": { name: "subagentStop", can_block: false, notes: "can trigger followup_message" }, + "compact.before": { name: "preCompact", can_block: false, notes: null }, + "session.stop": { name: "stop", can_block: false, notes: "can trigger followup_message" }, + "prompt.before_submit": { name: "beforeSubmitPrompt", can_block: true, notes: null }, + }, + + extra_events: [ + { name: "sessionEnd", can_block: false, notes: "fire-and-forget" }, + { name: "beforeShellExecution", can_block: true, notes: "before shell command" }, + { name: "afterShellExecution", can_block: false, notes: "after shell command" }, + { name: "beforeMCPExecution", can_block: true, notes: "before MCP tool call" }, + { name: "afterMCPExecution", can_block: false, notes: "after MCP tool call" }, + { name: "beforeReadFile", can_block: true, notes: "before file read" }, + { name: "afterFileEdit", can_block: false, notes: "after file edit" }, + { name: "afterAgentResponse", can_block: false, notes: "after assistant message" }, + { name: "afterAgentThought", can_block: false, notes: "after thinking block" }, + { name: "beforeTabFileRead", can_block: true, notes: "Tab: before file read" }, + { name: "afterTabFileEdit", can_block: false, notes: "Tab: after file edit" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [".cursor/rules/*.mdc"], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents/", + invocation: "Skill tool", + }, + + # ── Manifest ── + + manifest: { + path: ".cursor-plugin/plugin.json", + required_fields: ["name", "displayName", "description", "version", "author"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["allowed-tools", "user-invocable"], + keep: ["disable-model-invocation"], + model_format: "inherit", + # always "inherit" — defers to user's model selection + }, + + # ── MCP ── + + mcp: { + config_path: "mcp.json", + notes: "no dot prefix; no MCP Resources support", + }, + + # ── Path Variables ── + + paths: { + plugin_root: "${CURSOR_PLUGIN_ROOT}", + hooks_scripts: "/scripts/", + }, +} +``` + +### Cursor-specific notes + +- Cursor hooks use `"version": 1` at top level of hooks-cursor.json. +- Flat hook structure: no nested `hooks[]` array; each entry has `event`, + `matcher`, `command` at top level. +- Custom agents: `.cursor/agents/*.md` (project) or `~/.cursor/agents/` + (global). Frontmatter fields: name, description, model, readonly, + is_background. +- Built-in subagents: explore, bash, browser. +- Async subagents available in Cursor 2.5+. diff --git a/lib/references/platforms/gemini-cli.md b/lib/references/platforms/gemini-cli.md new file mode 100644 index 0000000..15397e8 --- /dev/null +++ b/lib/references/platforms/gemini-cli.md @@ -0,0 +1,114 @@ +# Gemini CLI Platform Specification + +```pseudocode +REGISTRY["gemini-cli"] = { + + id: "gemini-cli", + display_name: "Gemini CLI", + + # ── Tools ── + + tools: { + "file.read": { name: "read_file", notes: null }, + "file.write": { name: "write_file", notes: null }, + "file.edit": { name: "replace", notes: null }, + "shell.execute": { name: "run_shell_command", notes: null }, + "search.content": { name: "grep_search", notes: null }, + "search.files": { name: "glob", notes: null }, + "subagent.dispatch": { name: "@agent-name", notes: "mention in prompt or automatic routing" }, + "task.track": { name: "write_todos", notes: null }, + "skill.invoke": { name: "activate_skill", notes: null }, + "web.search": { name: "google_web_search", notes: null }, + "web.fetch": { name: "web_fetch", notes: null }, + "user.ask": { name: "ask_user", notes: null }, + }, + + extra_tools: [ + { name: "read_many_files", purpose: "read multiple files at once (triggered by @path)" }, + { name: "list_directory", purpose: "list files and subdirectories" }, + { name: "save_memory", purpose: "persist facts to GEMINI.md across sessions" }, + { name: "get_internal_docs", purpose: "access Gemini CLI own documentation" }, + { name: "complete_task", purpose: "subagent-only: finalize and return result" }, + { name: "enter_plan_mode", purpose: "switch to read-only research mode" }, + { name: "exit_plan_mode", purpose: "leave plan mode" }, + { name: "browser_agent", purpose: "experimental web browser automation" }, + ], + + # ── Hooks ── + + hooks: { + system: "file", + config_path: "settings.json or extension manifest hooks field", + event_case: "PascalCase", + timeout_unit: "milliseconds", + async_support: false, + structure: "nested", + output_key: "hookSpecificOutput", + + events: { + "session.start": { name: "SessionStart", can_block: false, notes: null }, + "tool.before": { name: "BeforeTool", can_block: true, notes: "can rewrite args" }, + "tool.after": { name: "AfterTool", can_block: true, notes: "supports tail calls" }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: "PreCompress", can_block: false, notes: null }, + "session.stop": { name: "AfterAgent", can_block: false, notes: null }, + "prompt.before_submit": { name: null, can_block: false, notes: null }, + }, + + extra_events: [ + { name: "BeforeModel", can_block: true, notes: "before LLM request" }, + { name: "AfterModel", can_block: true, notes: "after LLM response" }, + { name: "BeforeToolSelection", can_block: true, notes: "filter available tools" }, + { name: "Notification", can_block: false, notes: "system notifications" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "GEMINI.md", + secondary_files: [], + priority_note: null, + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents/", + invocation: "activate_skill tool", + }, + + # ── Manifest ── + + manifest: { + path: "gemini-extension.json", + required_fields: ["name", "version", "description", "contextFileName"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["disable-model-invocation", "allowed-tools", "user-invocable"], + keep: [], + model_format: "platform-native", + # gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash-lite + }, + + # ── MCP ── + + mcp: { + config_path: "gemini-extension.json -> mcpServers", + notes: "extension-bundled MCP servers", + }, + + # ── Path Variables ── + + paths: { + plugin_root: "${extensionPath}${/}", + hooks_scripts: "/scripts/", + }, +} +``` diff --git a/lib/references/platforms/openclaw.md b/lib/references/platforms/openclaw.md new file mode 100644 index 0000000..879bac1 --- /dev/null +++ b/lib/references/platforms/openclaw.md @@ -0,0 +1,124 @@ +# OpenClaw Platform Specification + +```pseudocode +REGISTRY["openclaw"] = { + + id: "openclaw", + display_name: "OpenClaw", + + # ── Tools ── + + tools: { + "file.read": { name: "Read", notes: null }, + "file.write": { name: "Write", notes: null }, + "file.edit": { name: "Edit", notes: null }, + "shell.execute": { name: "Bash", notes: null }, + "search.content": { name: "Grep", notes: null }, + "search.files": { name: "Glob", notes: null }, + "subagent.dispatch": { name: null, notes: "agents declared in agents.list[] manifest config" }, + "task.track": { name: null, notes: null }, + "skill.invoke": { name: null, notes: "skills load natively into prompt" }, + "web.search": { name: "WebSearch", notes: null }, + "web.fetch": { name: "WebFetch", notes: null }, + "user.ask": { name: "AskUserQuestion", notes: null }, + }, + + extra_tools: [], + + # ── Hooks ── + + hooks: { + system: "sdk", + config_path: null, + event_case: "snake_case", + timeout_unit: null, + async_support: true, + structure: "sdk", + output_key: null, + + events: { + "session.start": { name: "gateway:startup", can_block: false, notes: null }, + "tool.before": { name: "before_tool_call", can_block: true, notes: "{ block: true } is terminal" }, + "tool.after": { name: "after_tool_call", can_block: false, notes: null }, + "tool.after_failure": { name: null, can_block: false, notes: null }, + "subagent.start": { name: null, can_block: false, notes: null }, + "subagent.stop": { name: null, can_block: false, notes: null }, + "compact.before": { name: "session:compact:before", can_block: false, notes: null }, + "session.stop": { name: null, can_block: false, notes: null }, + "prompt.before_submit": { name: null, can_block: false, notes: null }, + }, + + extra_events: [ + { name: "tool_result_persist", can_block: false, notes: "tool result persistence" }, + { name: "llm_input", can_block: false, notes: "before LLM call; requires allowConversationAccess" }, + { name: "llm_output", can_block: false, notes: "after LLM produces output" }, + { name: "message_received", can_block: false, notes: "inbound message; typed threadId" }, + { name: "message_sent", can_block: false, notes: "outbound message" }, + { name: "message_sending", can_block: true, notes: "{ cancel: true } is terminal" }, + { name: "before_agent_finalize", can_block: false, notes: "requires allowConversationAccess" }, + { name: "agent_end", can_block: false, notes: "requires allowConversationAccess" }, + { name: "before_model_resolve", can_block: false, notes: "model switching" }, + { name: "before_compaction", can_block: false, notes: null }, + { name: "after_compaction", can_block: false, notes: null }, + { name: "before_install", can_block: true, notes: "{ block: true } is terminal" }, + { name: "command", can_block: false, notes: "slash command issued" }, + ], + }, + + # ── Context ── + + context: { + primary_file: "AGENTS.md", + secondary_files: [], + priority_note: "also loads SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, MEMORY.md (user workspace files, not plugin output)", + }, + + # ── Skills ── + + skills: { + path: "skills/", + agents_path: "agents.list[] in manifest", + invocation: "native loading into prompt", + }, + + # ── Manifest ── + + manifest: { + path: "openclaw.plugin.json", + required_fields: ["id", "configSchema"], + }, + + # ── Frontmatter ── + + frontmatter: { + strip: ["disable-model-invocation", "allowed-tools"], + keep: [], + model_format: "provider/model", + # anthropic/claude-opus-4-6, anthropic/claude-sonnet-4-5, anthropic/claude-haiku-4-5 + }, + + # ── MCP ── + + mcp: { + config_path: "openclaw.plugin.json -> mcp block", + notes: "embedded in manifest", + }, + + # ── Path Variables ── + + paths: { + plugin_root: null, + hooks_scripts: null, + }, +} +``` + +### OpenClaw-specific notes + +- Hooks are TypeScript SDK-based: `api.registerHook(event, handler)` or + `api.on(event, handler)`. No file-based hook config. +- Non-bundled conversation hooks require + `plugins.entries..hooks.allowConversationAccess=true`. +- Full plugins also need `package.json` with `openclaw.extensions` and + `openclaw.compat`. +- Auto-detects Claude, Codex, and Cursor bundle layouts. From 2d40fcf5392d82e3f19454d83f0e7f0c53e8103f Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:26:59 +1000 Subject: [PATCH 06/43] feat: create subagent-dispatch.md with relocated Codex patterns --- lib/patterns/subagent-dispatch.md | 94 +++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 lib/patterns/subagent-dispatch.md diff --git a/lib/patterns/subagent-dispatch.md b/lib/patterns/subagent-dispatch.md new file mode 100644 index 0000000..3766d50 --- /dev/null +++ b/lib/patterns/subagent-dispatch.md @@ -0,0 +1,94 @@ +# Subagent Dispatch Patterns + +Cross-platform patterns for dispatching subagents from skills that use +Claude Code's `Task` or `Agent` tools. Referenced by rubric conditions and +skill pseudocode. + +--- + +## Codex: Named Agent Dispatch + +Claude Code skills reference named agent types like `superpowers:code-reviewer`. +Codex does not have a named agent registry — `spawn_agent` creates generic agents +from built-in roles (`default`, `explorer`, `worker`). + +When a skill says to dispatch a named agent type: + +1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's + local prompt template like `code-quality-reviewer-prompt.md`) +2. Read the prompt content +3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.) +4. Spawn a `worker` agent with the filled content as the `message` + +| Skill instruction | Codex equivalent | +| ----------------- | ---------------- | +| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content | +| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt | + +### Message framing + +The `message` parameter is user-level input, not a system prompt. Structure it +for maximum instruction adherence: + + Your task is to perform the following. Follow the instructions below exactly. + + + [filled prompt content from the agent's .md file] + + + Execute this now. Output ONLY the structured response following the format + specified in the instructions above. + +- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...") +- Wrap instructions in XML tags — the model treats tagged blocks as authoritative +- End with an explicit execution directive to prevent summarization of the instructions + +### When this workaround can be removed + +This approach compensates for Codex's plugin system not yet supporting an `agents` +field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the +plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and +skills can dispatch named agent types directly. + +--- + +## Codex: Multi-Agent Feature Flag + +Subagent dispatch requires the multi-agent feature flag: + + # ~/.codex/config.toml + [features] + multi_agent = true + +This enables `spawn_agent`, `wait`, and `close_agent`. + +--- + +## Environment Detection + +Skills that create worktrees or finish branches should detect their +environment with read-only git commands before proceeding: + + GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P) + GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P) + BRANCH=$(git branch --show-current) + +- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation) +- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox) + +See `using-git-worktrees` Step 0 and `finishing-a-development-branch` +Step 1 for how each skill uses these signals. + +--- + +## Codex App Finishing + +When the sandbox blocks branch/push operations (detached HEAD in an +externally managed worktree), the agent commits all work and informs +the user to use the App's native controls: + +- **"Create branch"** — names the branch, then commit/push/PR via App UI +- **"Hand off to local"** — transfers work to the user's local checkout + +The agent can still run tests, stage files, and output suggested branch +names, commit messages, and PR descriptions for the user to copy. From 8f60b8fe399208269cfd9463734d70761ae40029 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:28:00 +1000 Subject: [PATCH 07/43] refactor: update context files to reference platform API Co-Authored-By: Claude Sonnet 4.6 --- AGENTS.md | 16 ++++++++-------- CLAUDE.md | 4 ++-- GEMINI.md | 17 +++++++++-------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0f7a699..23083b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,12 +25,12 @@ 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: -- `codex-tools.md` — Codex (spawn_agent, update_plan, message framing) -- `gemini-tools.md` — Gemini CLI (read_file, replace, run_shell_command, etc.) -- `cursor-tools.md` — Cursor (same names, different hooks/model/context) -- `antigravity-tools.md` — Antigravity (same names, stripped frontmatter) -- `openclaw-tools.md` — OpenClaw (agents.list[], no TodoWrite/Skill, SDK hooks) +Platform-specific tool names, hooks, manifests, and frontmatter rules are defined +as structured `PlatformSpec` dictionaries in `lib/references/platforms/`. The type +system and lookup functions are in `lib/references/platform-api.md`. + +Use `tool_name(platform, op)` for tool mappings, `hook_event(platform, event)` +for hook events, and `strip_fields(platform)` for frontmatter stripping. ## Platform Accuracy Constraint @@ -41,8 +41,8 @@ claims, cross-reference: 1. **Research docs** — `docs/platforms/*.md` (sourced, with inline citations) 2. **Reconciliation matrix** — `docs/reconciliation-matrix.md` (tracks known discrepancies and their fix status) -3. **Canonical lookup tables** — `lib/references/platform-mappings.md` (single - source of truth consumed by rubrics) +3. **Platform API** — `lib/references/platform-api.md` and `lib/references/platforms/*.md` + (structured PlatformSpec dictionaries consumed by rubrics) If you find a conflict between these sources, trust the researched platform docs (they have citations). Update the reconciliation matrix when fixing discrepancies diff --git a/CLAUDE.md b/CLAUDE.md index 000ca3d..2ad351f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,8 +14,8 @@ claims, cross-reference: 1. **Research docs** — `docs/platforms/*.md` (sourced, with inline citations) 2. **Reconciliation matrix** — `docs/reconciliation-matrix.md` (tracks known discrepancies and their fix status) -3. **Canonical lookup tables** — `lib/references/platform-mappings.md` (single - source of truth consumed by rubrics) +3. **Platform API** — `lib/references/platform-api.md` and `lib/references/platforms/*.md` + (structured PlatformSpec dictionaries consumed by rubrics) If you find a conflict between these sources, trust the researched platform docs (they have citations). Update the reconciliation matrix when fixing discrepancies diff --git a/GEMINI.md b/GEMINI.md index 43ab27b..2243f6b 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -7,13 +7,14 @@ Make any plugin fully portable across all platforms. Accepts Claude Code, Cursor @./skills/using-skill-portability/SKILL.md @./skills/plugin-portability/SKILL.md -## Tool References +## Platform API -@./lib/references/gemini-tools.md -@./lib/references/codex-tools.md -@./lib/references/cursor-tools.md -@./lib/references/antigravity-tools.md -@./lib/references/openclaw-tools.md +@./lib/references/platform-api.md +@./lib/references/platforms/gemini-cli.md +@./lib/references/platforms/codex.md +@./lib/references/platforms/cursor.md +@./lib/references/platforms/antigravity.md +@./lib/references/platforms/openclaw.md ## Platform Accuracy Constraint @@ -24,8 +25,8 @@ claims, cross-reference: 1. **Research docs** — `docs/platforms/*.md` (sourced, with inline citations) 2. **Reconciliation matrix** — `docs/reconciliation-matrix.md` (tracks known discrepancies and their fix status) -3. **Canonical lookup tables** — `lib/references/platform-mappings.md` (single - source of truth consumed by rubrics) +3. **Platform API** — `lib/references/platform-api.md` and `lib/references/platforms/*.md` + (structured PlatformSpec dictionaries consumed by rubrics) If you find a conflict between these sources, trust the researched platform docs (they have citations). Update the reconciliation matrix when fixing discrepancies From 6b816284140d31eefd12dcbe25d8f85bdd01c691 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:32:53 +1000 Subject: [PATCH 08/43] refactor: update rubric LOOKUP comments to platform API function calls Replace LOOKUP["..."]["platform"] patterns with REGISTRY[platform].* property paths and strip_fields()/tool_name()/supported_tools() function calls throughout all 6 platform rubric YAMLs and rubric-framework.md. Also update *-tools.md file references to lib/references/platforms/*.md paths. Co-Authored-By: Claude Sonnet 4.6 --- lib/rubrics/antigravity.yaml | 8 ++++---- lib/rubrics/claude-code.yaml | 6 +++--- lib/rubrics/codex.yaml | 26 +++++++++++++------------- lib/rubrics/cursor.yaml | 16 ++++++++-------- lib/rubrics/gemini-cli.yaml | 26 +++++++++++++------------- lib/rubrics/openclaw.yaml | 16 ++++++++-------- lib/rubrics/rubric-framework.md | 4 ++-- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/lib/rubrics/antigravity.yaml b/lib/rubrics/antigravity.yaml index 9190623..cd2b1ca 100644 --- a/lib/rubrics/antigravity.yaml +++ b/lib/rubrics/antigravity.yaml @@ -24,7 +24,7 @@ categories: check: | if file_exists("package.json"): json = read_json("package.json") - for field in LOOKUP["manifest_required_fields"]["antigravity"]: + for field in REGISTRY["antigravity"].manifest.required_fields: assert field in json, f"Missing required field: {field}" # Required: name, displayName, version, description, publisher template: manifests/antigravity/package.json.tmpl?merge @@ -60,7 +60,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["field_stripping"]["antigravity"]: strip both fields + # strip_fields("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, \ @@ -170,9 +170,9 @@ categories: mapping content is reachable: IF shape == "full-portable-plugin": Via AGENTS.md tool mapping section, GEMINI.md @ includes, or - shared antigravity-tools.md reference + lib/references/platforms/antigravity.md reference ELIF shape IN ["bare-skill-repo", "skill-first"]: - Via per-skill references/antigravity-tools.md sidecar + Via per-skill references/platforms/antigravity.md sidecar - id: antigravity.5_toolmap.model_mapping.no_model_field type: checkable diff --git a/lib/rubrics/claude-code.yaml b/lib/rubrics/claude-code.yaml index c662087..88ce479 100644 --- a/lib/rubrics/claude-code.yaml +++ b/lib/rubrics/claude-code.yaml @@ -23,7 +23,7 @@ categories: points: 1 check: | json = read_json(".claude-plugin/plugin.json") - for field in LOOKUP["manifest_required_fields"]["claude-code"]: + for field in REGISTRY["claude-code"].manifest.required_fields: assert field_present(json, field) # Required: name, version, description, author.name, author.email template: manifests/claude-plugin/plugin.json.tmpl?merge @@ -110,7 +110,7 @@ categories: critical: true points: 1 check: | - builtin = LOOKUP["tool_names"]["claude-code"] + builtin = supported_tools("claude-code") # [Read, Write, Edit, Bash, Grep, Glob, Task, Agent, # TodoWrite, Skill, WebSearch, WebFetch] for skill in glob("skills/*/SKILL.md"): @@ -171,7 +171,7 @@ categories: points: 1 check: | hooks = read_json("hooks/hooks.json") - valid_events = LOOKUP["hook_events"]["claude-code"] + valid_events = REGISTRY["claude-code"].hooks.events # [SessionStart, PreToolUse, PostToolUse, PostToolUseFailure, # SubagentStart, SubagentStop, PreCompact, Stop, UserPromptSubmit] for event in hooks.keys(): diff --git a/lib/rubrics/codex.yaml b/lib/rubrics/codex.yaml index 99c70b4..399e639 100644 --- a/lib/rubrics/codex.yaml +++ b/lib/rubrics/codex.yaml @@ -36,7 +36,7 @@ categories: check: | if file_exists(".codex-plugin/plugin.json"): json = read_json(".codex-plugin/plugin.json") - for field in LOOKUP["manifest_required_fields"]["codex"]: + for field in REGISTRY["codex"].manifest.required_fields: assert field in json, f"Missing required field: {field}" # Required: name, version, description (Table 6) template: manifests/codex-plugin/plugin.json.tmpl?merge @@ -84,7 +84,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["field_stripping"]["codex"]: strip disable-model-invocation, allowed-tools + # strip_fields("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, \ @@ -109,8 +109,8 @@ categories: 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. + maps to Codex spawn_agent (tool_name("codex", "Task")). A lib/references/platforms/codex.md + reference or inline note must explain this mapping. - id: codex.2_skills.tool_refs.update_plan type: judgement @@ -120,7 +120,7 @@ categories: 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). + Codex update_plan (tool_name("codex", "TodoWrite")). 3_context: conditions: @@ -208,7 +208,7 @@ categories: IF shape IN ["bare-skill-repo", "skill-first"]: for skill_dir in glob_dirs("skills/*/") + glob_dirs(".agents/skills/*/"): name = basename(skill_dir) - skill_path = skill_dir + "/references/codex-tools.md" + skill_path = skill_dir + "/references/platforms/codex.md" assert file_exists(skill_path), \ f"{skill_path} sidecar missing" ELSE: @@ -222,7 +222,7 @@ categories: check: | IF shape == "full-portable-plugin": Verify Codex tool mapping content is reachable — either via - shared references/codex-tools.md, lib/references/codex-tools.md, + lib/references/platforms/codex.md, or documented in AGENTS.md directly. Must contain "spawn_agent" and "apply_patch" mappings. ELSE: @@ -234,12 +234,12 @@ categories: critical: true points: 1 check: | - sidecars = glob("**/codex-tools.md") - assert len(sidecars) > 0, "No codex-tools.md found" + sidecars = glob("**/platforms/codex.md") OR find_file("lib/references/platforms/codex.md") + assert len(sidecars) > 0, "No Codex platform spec found" found = false for sidecar in sidecars: if "spawn_agent" in read(sidecar): found = true - assert found, "No codex-tools.md documents Task/Agent → spawn_agent" + assert found, "No Codex platform spec documents Task/Agent → spawn_agent" - id: codex.5_toolmap.sidecar.update_plan_mapped type: checkable @@ -247,12 +247,12 @@ categories: critical: false points: 1 check: | - sidecars = glob("**/codex-tools.md") + sidecars = glob("**/platforms/codex.md") OR find_file("lib/references/platforms/codex.md") if len(sidecars) > 0: found = false for sidecar in sidecars: if "update_plan" in read(sidecar): found = true - assert found, "No codex-tools.md documents TodoWrite → update_plan" + assert found, "No Codex platform spec documents TodoWrite → update_plan" - id: codex.5_toolmap.model_mapping.no_claude_models type: checkable @@ -260,7 +260,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["model_mapping"]["codex"]: opus→gpt-5.4, sonnet/haiku→gpt-5.4-mini + # REGISTRY["codex"].frontmatter.model_format: 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) diff --git a/lib/rubrics/cursor.yaml b/lib/rubrics/cursor.yaml index 2970862..a9c1444 100644 --- a/lib/rubrics/cursor.yaml +++ b/lib/rubrics/cursor.yaml @@ -23,7 +23,7 @@ categories: points: 1 check: | json = read_json(".cursor-plugin/plugin.json") - for field in LOOKUP["manifest_required_fields"]["cursor"]: + for field in REGISTRY["cursor"].manifest.required_fields: assert field in json, f"Missing required field: {field}" # Required: name, displayName, description, version, author template: manifests/cursor-plugin/plugin.json.tmpl?merge @@ -84,7 +84,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["field_stripping"]["cursor"]: strip allowed-tools, keep disable-model-invocation + # strip_fields("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" @@ -168,7 +168,7 @@ categories: points: 1 check: | hooks = read_json("hooks/hooks-cursor.json") - valid_events = LOOKUP["hook_events"]["cursor"] + valid_events = REGISTRY["cursor"].hooks.events # Valid: sessionStart, preToolUse, postToolUse, postToolUseFailure, # subagentStart, subagentStop, preCompact, stop, beforeSubmitPrompt for entry in hooks: @@ -196,7 +196,7 @@ categories: 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} + # REGISTRY["cursor"].paths.plugin_root: ${CURSOR_PLUGIN_ROOT} - id: cursor.4_hooks.scripts.cross_platform type: checkable @@ -215,7 +215,7 @@ categories: critical: false points: 1 check: | - # LOOKUP["hook_format_rules"]["cursor"]: output key is additional_context + # REGISTRY["cursor"].hooks.output_key: output key is additional_context for script in glob("hooks/scripts/*"): content = read(script) if "hookSpecificOutput" in content or "additionalContext" in content: @@ -231,7 +231,7 @@ categories: check: | Cursor uses the same tool names as Claude Code — no tool mapping sidecar is strictly required. However, verify that AGENTS.md or - a cursor-tools.md reference documents any Cursor-specific + lib/references/platforms/cursor.md documents any Cursor-specific differences (hook format, model handling, MCP path). - id: cursor.5_toolmap.model_mapping.no_claude_models @@ -240,7 +240,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["model_mapping"]["cursor"]: all models map to "inherit" + # REGISTRY["cursor"].frontmatter.model_format: all models map to "inherit" claude_models = ["opus", "sonnet", "haiku"] for md in glob("agents/*.md") + glob("skills/*/SKILL.md"): fm = parse_frontmatter(md) @@ -258,7 +258,7 @@ categories: fm = parse_frontmatter(md) if "model" in fm: assert fm["model"] == "inherit", \ - f"{md}: model should be 'inherit' for Cursor (LOOKUP Table 1)" + f"{md}: model should be 'inherit' for Cursor (REGISTRY[\"cursor\"].frontmatter.model_format)" - id: cursor.5_toolmap.subagent_syntax.documented type: judgement diff --git a/lib/rubrics/gemini-cli.yaml b/lib/rubrics/gemini-cli.yaml index 3059db4..adf936c 100644 --- a/lib/rubrics/gemini-cli.yaml +++ b/lib/rubrics/gemini-cli.yaml @@ -22,7 +22,7 @@ categories: points: 1 check: | json = read_json("gemini-extension.json") - for field in LOOKUP["manifest_required_fields"]["gemini"]: + for field in REGISTRY["gemini-cli"].manifest.required_fields: assert field in json, f"Missing required field: {field}" # Required: name, version, description, contextFileName template: manifests/gemini-extension.json.tmpl?merge @@ -83,7 +83,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["field_stripping"]["gemini"]: strip both fields + # strip_fields("gemini-cli"): strip both fields for skill in glob("skills/*/SKILL.md"): fm = parse_frontmatter(skill) assert "disable-model-invocation" not in fm, \ @@ -108,7 +108,7 @@ categories: 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"). + instead (tool_name("gemini-cli", "Task") = "@agent-name"). Flag any raw Task/Agent tool invocations. 3_context: @@ -145,8 +145,8 @@ categories: # Only for full-portable-plugin shape IF shape == "full-portable-plugin": content = read("GEMINI.md") - assert "@" in content and "gemini-tools.md" in content, \ - "GEMINI.md must @ include a gemini-tools.md (shared or per-skill)" + assert "@" in content and "platforms/gemini-cli.md" in content, \ + "GEMINI.md must @ include a platforms/gemini-cli.md (shared or per-skill)" ELSE: SKIP # bare-skill — no GEMINI.md to check template: context-files/GEMINI.md.tmpl?merge @@ -181,7 +181,7 @@ categories: points: 1 check: | If hook guidance exists, verify it uses correct Gemini PascalCase event - names from LOOKUP["hook_events"]["gemini"]: + names from REGISTRY["gemini-cli"].hooks.events: SessionStart, BeforeTool, AfterTool, PreCompress, AfterAgent. Not Claude's PascalCase variants or Cursor's camelCase. @@ -197,8 +197,8 @@ categories: IF shape IN ["bare-skill-repo", "skill-first"]: 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" + assert file_exists(f"skills/{name}/references/platforms/gemini-cli.md"), \ + f"skills/{name}/references/platforms/gemini-cli.md sidecar missing" ELSE: SKIP # plugin shape — shared references checked below @@ -223,9 +223,9 @@ categories: critical: false points: 1 check: | - # Find all gemini-tools.md files (per-skill or shared) - sidecars = glob("**/gemini-tools.md") - assert len(sidecars) > 0, "No gemini-tools.md found anywhere" + # Find all gemini-cli platform spec files (per-skill or shared) + sidecars = glob("**/platforms/gemini-cli.md") OR find_file("lib/references/platforms/gemini-cli.md") + assert len(sidecars) > 0, "No Gemini platform spec found anywhere" for sidecar in sidecars: content = read(sidecar) assert "read_file" in content, f"{sidecar}: missing Read → read_file" @@ -238,7 +238,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["model_mapping"]["gemini"]: opus→gemini-2.5-pro, etc. + # REGISTRY["gemini-cli"].frontmatter.model_format: 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) @@ -339,7 +339,7 @@ categories: critical: false points: 1 check: | - # LOOKUP["agent_output_format"]["gemini"]: tools = ["*"] + # REGISTRY["gemini-cli"].skills.agents_path: tools = ["*"] for md in glob("agents/*.md"): fm = parse_frontmatter(md) if "tools" in fm: diff --git a/lib/rubrics/openclaw.yaml b/lib/rubrics/openclaw.yaml index 5a0272f..a5c119e 100644 --- a/lib/rubrics/openclaw.yaml +++ b/lib/rubrics/openclaw.yaml @@ -22,7 +22,7 @@ categories: points: 1 check: | json = read_json("openclaw.plugin.json") - for field in LOOKUP["manifest_required_fields"]["openclaw"]: + for field in REGISTRY["openclaw"].manifest.required_fields: assert field in json, f"Missing required field: {field}" # Required: id, configSchema template: manifests/openclaw/openclaw.plugin.json.tmpl?merge @@ -73,7 +73,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["field_stripping"]["openclaw"]: strip both fields + # strip_fields("openclaw"): strip both fields for skill in glob("skills/*/SKILL.md"): fm = parse_frontmatter(skill) assert "disable-model-invocation" not in fm, \ @@ -98,7 +98,7 @@ categories: 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"]). + agents.list[] in runtime config instead (tool_name("openclaw", "Task")). Also has no TodoWrite or Skill tool (mapped to N/A). Flag any unresolvable tool references. @@ -155,7 +155,7 @@ categories: points: 1 check: | If hook guidance exists, verify it uses correct OpenClaw snake_case - event names (see lib/references/openclaw-tools.md for full 15-event + event names (see lib/references/platforms/openclaw.md for full 15-event list): before_tool_call, after_tool_call, tool_result_persist, llm_input, llm_output, message_received, message_sent, before_agent_finalize, agent_end, before_model_resolve, @@ -182,8 +182,8 @@ categories: points: 1 check: | OpenClaw shares most tool names with Claude Code but has no - Task/Agent or TodoWrite tools. Verify that AGENTS.md or an - openclaw-tools.md reference documents these differences and + Task/Agent or TodoWrite tools. Verify that AGENTS.md or + lib/references/platforms/openclaw.md documents these differences and the agents.list[] manifest-based agent dispatch pattern. - id: openclaw.5_toolmap.model_mapping.no_claude_models @@ -192,7 +192,7 @@ categories: critical: true points: 1 check: | - # LOOKUP["model_mapping"]["openclaw"]: opus→anthropic/claude-opus-4-6, etc. + # REGISTRY["openclaw"].frontmatter.model_format: 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) @@ -227,7 +227,7 @@ categories: 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[]". + tool_name("openclaw", "Task") = "agents.list[]". 6_install: conditions: diff --git a/lib/rubrics/rubric-framework.md b/lib/rubrics/rubric-framework.md index c1842af..bd21f46 100644 --- a/lib/rubrics/rubric-framework.md +++ b/lib/rubrics/rubric-framework.md @@ -2,7 +2,7 @@ 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`. +Platform specs are in `lib/references/platforms/*.md`. Lookup functions are in `lib/references/platform-api.md`. --- @@ -141,7 +141,7 @@ Blockers override raw scores. A repo with a decent score may still have one crit | 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 | +| Missing subagent translation | Minor | Skills dispatch via `Task`/`Agent` but no platform spec documents the mapping | | Gemini import gaps | Minor | `GEMINI.md` exists but missing `@` includes for some skills | --- From 6087f047934ee0e3d04a4ce10bff1d62e8b47359 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:33:59 +1000 Subject: [PATCH 09/43] refactor: update CI checks and CONTRIBUTING for platform specs --- .github/workflows/ci.yml | 7 ++++--- CONTRIBUTING.md | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dfa0a54..8417ce4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,9 +82,10 @@ jobs: done echo "" - echo "=== Shared Tool References ===" - for sidecar in codex-tools.md gemini-tools.md cursor-tools.md antigravity-tools.md openclaw-tools.md; do - check_file "lib/references/${sidecar}" + echo "=== Platform Specs ===" + check_file "lib/references/platform-api.md" + for spec in codex.md gemini-cli.md cursor.md antigravity.md openclaw.md claude-code.md; do + check_file "lib/references/platforms/${spec}" done echo "" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f85279..ee6d354 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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`, `gemini-tools.md`, `antigravity-tools.md`, `openclaw-tools.md`). +See existing skills in `skills/` for examples. Platform-specific tool mappings are in `lib/references/platforms/`. See `lib/references/platform-api.md` for the type system and lookup functions. ## Pull Request Process From d0cec9492ff97b4d233b184531578a9163d0151d Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:34:45 +1000 Subject: [PATCH 10/43] refactor: update pattern files to reference platform specs Co-Authored-By: Claude Sonnet 4.6 --- lib/patterns/bootstrapping.md | 14 +++++--------- lib/patterns/injection-checks.md | 6 +++--- lib/patterns/inventory.md | 26 +++++++++++++------------- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/lib/patterns/bootstrapping.md b/lib/patterns/bootstrapping.md index 1b91029..a0186e7 100644 --- a/lib/patterns/bootstrapping.md +++ b/lib/patterns/bootstrapping.md @@ -78,12 +78,8 @@ Skills use Claude Code tool names. See each skill's `references/` directory for ``` mkdir -p /skills/using-{{name}}/references/ -Write the same two sidecar files as the skill sidecar generation step: - - using-{{name}}/references/codex-tools.md - - using-{{name}}/references/gemini-tools.md - -Source templates from lib/references/ (same templates used for other skills). -Substitutions: {{name}}, {{displayName}}, {{description}} as in other sidecars. + # Per-skill sidecars are no longer generated. Shared platform specs + # in lib/references/platforms/ are loaded via context file @includes. ``` --- @@ -270,7 +266,7 @@ END Regenerate GEMINI.md placing using-{{name}} first: @./skills/using-{{name}}/SKILL.md - @./skills/using-{{name}}/references/gemini-tools.md + @./lib/references/platforms/gemini-cli.md {{otherSkillIncludes}} {{agentIncludes}} {{commandIncludes}} @@ -278,7 +274,7 @@ Regenerate GEMINI.md placing using-{{name}} first: Where: - {{otherSkillIncludes}} = all skills except using-{{name}}, same format as the GEMINI.md generation step (one @./skills//SKILL.md line per - skill, with its gemini-tools.md sidecar if present) + skill, with its platform spec if present) - {{agentIncludes}} = agent includes in same format as original generation - {{commandIncludes}} = command includes in same format as original generation ``` @@ -299,7 +295,7 @@ ELSE IF skip_bootstrapping AND reason == "user declined" THEN ELSE "Session-start injection configured. Generated: - using-{{name}}/SKILL.md - - using-{{name}}/references/ (2 sidecars) + - using-{{name}}/references/ (shared platform specs from lib/references/platforms/) - hooks/session-start - hooks/run-hook.cmd - hooks/hooks.json (SessionStart entry merged) diff --git a/lib/patterns/injection-checks.md b/lib/patterns/injection-checks.md index 161df19..cd2e926 100644 --- a/lib/patterns/injection-checks.md +++ b/lib/patterns/injection-checks.md @@ -10,7 +10,7 @@ Only runs when `skills/using-/SKILL.md` exists. | # | Component | Check | Status Values | |---|-----------|-------|---------------| | 1 | `skills/using-{{name}}/SKILL.md` | File exists | PRESENT / MISSING | -| 2 | `skills/using-{{name}}/references/gemini-tools.md` | File exists | PRESENT / MISSING | +| 2 | `lib/references/platforms/gemini-cli.md` | File exists (platform spec) | PRESENT / MISSING | | 3 | `hooks/session-start` | File exists and is executable | PRESENT / MISSING | | 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 | @@ -29,8 +29,8 @@ CHECK_INJECTION_COMPONENTS(computed): # 1. using-skill SKILL.md results.append(check_file_exists("skills/using-" + name + "/SKILL.md")) - # 2. using-skill gemini sidecar - results.append(check_file_exists("skills/using-" + name + "/references/gemini-tools.md")) + # 2. shared gemini platform spec + results.append(check_file_exists("lib/references/platforms/gemini-cli.md")) # 3. session-start script path = "hooks/session-start" diff --git a/lib/patterns/inventory.md b/lib/patterns/inventory.md index 3479ad3..223ccc7 100644 --- a/lib/patterns/inventory.md +++ b/lib/patterns/inventory.md @@ -62,30 +62,30 @@ INVENTORY(plugin_path, computed): ## 2.4 Check Tool Reference Sidecars ## Shape-aware: bare-skill repos need per-skill sidecars (no context file to carry ## shared references). Plugin repos can use shared references via context files. - sidecar_files = ["codex-tools.md", "gemini-tools.md", "cursor-tools.md", - "antigravity-tools.md", "openclaw-tools.md"] + platform_spec_files = ["codex.md", "gemini-cli.md", "cursor.md", + "antigravity.md", "openclaw.md"] computed.sidecar_results = [] IF computed.shape IN ["bare-skill-repo", "skill-first"]: - # Bare skills need per-skill sidecars — no context file to carry shared refs + # Bare skills need per-skill spec files — no context file to carry shared refs FOR skill IN computed.skills: - FOR sidecar IN sidecar_files: - target = "skills/" + skill.dir + "/references/" + sidecar + FOR spec_file IN platform_spec_files: + target = "skills/" + skill.dir + "/references/" + spec_file status = IF file_exists(plugin_path + "/" + target) THEN "PRESENT" ELSE "MISSING" - computed.sidecar_results.append({ skill: skill.dir, file: sidecar, status: status }) + computed.sidecar_results.append({ skill: skill.dir, file: spec_file, status: status }) ELIF computed.shape == "full-portable-plugin": # Plugins have context files — check shared references instead - shared_paths = ["lib/references/", "references/"] - FOR sidecar IN sidecar_files: + shared_paths = ["lib/references/platforms/", "references/platforms/", "lib/references/"] + FOR spec_file IN platform_spec_files: found = false FOR shared IN shared_paths: - IF file_exists(plugin_path + "/" + shared + sidecar): + IF file_exists(plugin_path + "/" + shared + spec_file): found = true - computed.sidecar_results.append({ skill: "(shared)", file: shared + sidecar, status: "PRESENT" }) + computed.sidecar_results.append({ skill: "(shared)", file: shared + spec_file, status: "PRESENT" }) BREAK IF NOT found: - computed.sidecar_results.append({ skill: "(shared)", file: sidecar, status: "MISSING" }) + computed.sidecar_results.append({ skill: "(shared)", file: spec_file, status: "MISSING" }) ## 2.5 Check Frontmatter Compatibility ## name and description are required for all platforms. @@ -140,7 +140,7 @@ INVENTORY(plugin_path, computed): 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) }) + computed.existing_files.append({ path: p, platform: spec_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) }) @@ -155,5 +155,5 @@ INVENTORY(plugin_path, computed): |--------|-----------| | `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"` | +| `spec_platform(file)` | `"gemini-cli.md" → "gemini-cli"`, `"codex.md" → "codex"` | | `hook_platform(path)` | `"hooks.json" → "claude-code"`, `"hooks-cursor.json" → "cursor"` | From 34887bb9d56054439039d4a48473027a42bdefe8 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:35:40 +1000 Subject: [PATCH 11/43] refactor: delete old reference files, complete platform API migration Removes platform-mappings.md and 5 *-tools.md files, replaced by platform-api.md + 6 structured PlatformSpec files in platforms/. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/reconciliation-matrix.md | 9 +- lib/references/antigravity-tools.md | 75 --------- lib/references/codex-tools.md | 127 ---------------- lib/references/cursor-tools.md | 72 --------- lib/references/gemini-tools.md | 53 ------- lib/references/openclaw-tools.md | 90 ----------- lib/references/platform-mappings.md | 226 ---------------------------- 7 files changed, 8 insertions(+), 644 deletions(-) delete mode 100644 lib/references/antigravity-tools.md delete mode 100644 lib/references/codex-tools.md delete mode 100644 lib/references/cursor-tools.md delete mode 100644 lib/references/gemini-tools.md delete mode 100644 lib/references/openclaw-tools.md delete mode 100644 lib/references/platform-mappings.md diff --git a/docs/reconciliation-matrix.md b/docs/reconciliation-matrix.md index d3737d7..1adcd18 100644 --- a/docs/reconciliation-matrix.md +++ b/docs/reconciliation-matrix.md @@ -380,8 +380,15 @@ in `docs/research/per-platform-context-loading.md` (#12). 29. ~~**publishing-and-discoverability.md**: Codex skills claimed unverifiable `.curated/`/`.experimental/` folders~~ Fixed — simplified to verified paths 30. ~~**publishing/openclaw.md template**: Wrong ClawHub URL (clawhub.dev → clawhub.ai), wrong CLI commands~~ Fixed — corrected to clawhub CLI and proper install syntax +### Platform API Restructure + +31. ~~**lib/references/**: Duplicated prose-based reference system~~ Fixed — replaced `platform-mappings.md` + 5 `*-tools.md` files with structured `platform-api.md` (type system + lookup functions) and 6 `PlatformSpec` data files in `platforms/` +32. ~~**Rubric YAMLs**: 28 LOOKUP comments referenced deleted tables~~ Fixed — all converted to `tool_name()`, `hook_event()`, `strip_fields()`, `REGISTRY[platform].*` function calls +33. ~~**Consumer files**: GEMINI.md, AGENTS.md, CLAUDE.md, CI, patterns, CONTRIBUTING referenced old paths~~ Fixed — all updated to `lib/references/platform-api.md` and `lib/references/platforms/*.md` +34. ~~**codex-tools.md prose**: Subagent dispatch patterns buried in tool reference~~ Fixed — relocated to `lib/patterns/subagent-dispatch.md` + ### All verification complete Zero "Needs review", "Missing", or known gap items remain. -Full coverage: references (Tier 1), rubrics/patterns (Tier 2), templates/install docs (Tier 3), patterns pseudocode and skills (Tier 4). +Full coverage: references (Tier 1), rubrics/patterns (Tier 2), templates/install docs (Tier 3), patterns pseudocode and skills (Tier 4), platform API restructure. diff --git a/lib/references/antigravity-tools.md b/lib/references/antigravity-tools.md deleted file mode 100644 index 36c6594..0000000 --- a/lib/references/antigravity-tools.md +++ /dev/null @@ -1,75 +0,0 @@ -# Antigravity Tool Mapping - -Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: - -| Skill references | Antigravity equivalent | -| ---------------- | ---------------------- | -| `Read` (file reading) | `view_file` | -| `Write` (file creation) | `write_to_file` | -| `Edit` (file editing) | `replace_file_content` or `multi_replace_file_content` | -| `Bash` (run commands) | `run_command` | -| `Grep` (search file content) | `grep_search` | -| `Glob` (search files by name) | `find_by_name` | -| `Task` (dispatch subagent) | No general equivalent — `browser_subagent` for browser tasks only | -| `Agent` (dispatch subagent) | No general equivalent | -| `TodoWrite` (task tracking) | No equivalent | -| `Skill` tool (invoke a skill) | Skills auto-activate via semantic matching — no explicit tool | -| `WebSearch` (web search) | `search_web` | -| `WebFetch` (fetch URL) | `read_url_content` | -| `AskUserQuestion` (structured input) | No equivalent | - -## Additional Antigravity tools - -These tools are available in Antigravity but have no Claude Code equivalent: - -| Tool | Purpose | -| ---- | ------- | -| `codebase_search` | Semantic code search (not pattern-based like grep) | -| `search_in_file` | Semantic search within a specific file | -| `view_code_item` | View specific code node/function by name | -| `view_file_outline` | Show file structure/outline | -| `view_content_chunk` | View document chunks by position | -| `list_dir` | List directory contents | -| `command_status` | Check status of background terminal commands | -| `read_terminal` | Read terminal output by process ID | -| `send_command_input` | Send stdin to running processes | -| `generate_image` | Create or edit images from text prompts | -| `list_resources` | Show available MCP server resources | -| `read_resource` | Retrieve MCP resource contents | -| `browser_subagent` | Browser automation (click, scroll, type, screenshots, recording) | - -## Key Differences from Claude Code - -Antigravity uses completely different tool names from Claude Code and also diverges in these areas: - -### Frontmatter stripping - -All skill and agent frontmatter must have these fields **removed**: -- `model` — Antigravity does not support model selection; strip entirely -- `tools` — Antigravity does not support per-agent tool restrictions; strip entirely -- `disable-model-invocation` — not supported; strip -- `allowed-tools` — not supported; strip -- `user-invocable` — Antigravity uses Workflows for slash-command invocation, not skills; strip - -### Skill discovery paths - -- **Preferred**: `.agents/skills/*/SKILL.md` -- **Legacy**: `.agent/skills/*/SKILL.md` (also works but prefer plural `.agents/`) -- Standard `skills/` path is **not** auto-discovered - -### Context files - -- `AGENTS.md` has highest priority (universal standard) -- `GEMINI.md` is Antigravity-native, loaded if present -- Additional: `.agents/rules/*.md` for rule-based agent bodies - -### No hooks system - -Antigravity has no hook system. The `hooks/` directory and hook config files -are not used. Session-start scripts in `hooks/session-start` are auto-discovered -and executed at session start without any separate config. - -### Workflows - -Antigravity uses `.agents/workflows/` for slash-command style workflows -(Markdown format, not TOML). diff --git a/lib/references/codex-tools.md b/lib/references/codex-tools.md deleted file mode 100644 index c61d624..0000000 --- a/lib/references/codex-tools.md +++ /dev/null @@ -1,127 +0,0 @@ -# Codex Tool Mapping - -Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: - -| Skill references | Codex equivalent | -| ---------------- | ---------------- | -| `Read` (file reading) | `Read` | -| `Write` (file creation) | `Write` | -| `Edit` (file editing) | `apply_patch` | -| `Bash` (run commands) | `Bash` | -| `Grep` (search file content) | `Grep` | -| `Glob` (search files by name) | `Glob` | -| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) | -| `Agent` (dispatch subagent) | `spawn_agent` | -| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls | -| Task returns result | `wait` | -| Task completes automatically | `close_agent` to free slot | -| `TodoWrite` (task tracking) | `update_plan` | -| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions | -| `WebSearch` (web search) | `WebSearch` | -| `WebFetch` (fetch URL) | No direct equivalent — use MCP for URL fetching | -| `AskUserQuestion` (structured input) | `AskUserQuestion` | - -## Subagent dispatch requires multi-agent support - -Add to your Codex config (`~/.codex/config.toml`): - -```toml -[features] -multi_agent = true -``` - -This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`. - -## Named agent dispatch - -Claude Code skills reference named agent types like `superpowers:code-reviewer`. -Codex does not have a named agent registry — `spawn_agent` creates generic agents -from built-in roles (`default`, `explorer`, `worker`). - -When a skill says to dispatch a named agent type: - -1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's - local prompt template like `code-quality-reviewer-prompt.md`) -2. Read the prompt content -3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.) -4. Spawn a `worker` agent with the filled content as the `message` - -| Skill instruction | Codex equivalent | -| ----------------- | ---------------- | -| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content | -| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt | - -### Message framing - -The `message` parameter is user-level input, not a system prompt. Structure it -for maximum instruction adherence: - -```text -Your task is to perform the following. Follow the instructions below exactly. - - -[filled prompt content from the agent's .md file] - - -Execute this now. Output ONLY the structured response following the format -specified in the instructions above. -``` - -- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...") -- Wrap instructions in XML tags — the model treats tagged blocks as authoritative -- End with an explicit execution directive to prevent summarization of the instructions - -### When this workaround can be removed - -This approach compensates for Codex's plugin system not yet supporting an `agents` -field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the -plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and -skills can dispatch named agent types directly. - -## Environment Detection - -Skills that create worktrees or finish branches should detect their -environment with read-only git commands before proceeding: - -```bash -GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P) -GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P) -BRANCH=$(git branch --show-current) -``` - -- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation) -- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox) - -See `using-git-worktrees` Step 0 and `finishing-a-development-branch` -Step 1 for how each skill uses these signals. - -## Codex App Finishing - -When the sandbox blocks branch/push operations (detached HEAD in an -externally managed worktree), the agent commits all work and informs -the user to use the App's native controls: - -- **"Create branch"** — names the branch, then commit/push/PR via App UI -- **"Hand off to local"** — transfers work to the user's local checkout - -The agent can still run tests, stage files, and output suggested branch -names, commit messages, and PR descriptions for the user to copy. - -## Hooks - -Codex has a lifecycle hook system behind a feature flag. Enable it in config: - -```toml -# ~/.codex/config.toml or .codex/config.toml -[features] -codex_hooks = true -``` - -Codex hooks use the same JSON protocol and PascalCase event names as Claude -Code (`SessionStart`, `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`). -Codex also has a `PermissionRequest` event with no Claude Code equivalent. - -Hook config goes in `hooks.json` (same format as Claude Code) or inline -`[hooks]` tables in `config.toml`. Default timeout: 600 seconds. - -See `lib/patterns/hook-merging.md` for generation logic. diff --git a/lib/references/cursor-tools.md b/lib/references/cursor-tools.md deleted file mode 100644 index d2f8619..0000000 --- a/lib/references/cursor-tools.md +++ /dev/null @@ -1,72 +0,0 @@ -# Cursor Tool Mapping - -Skills use Claude Code tool names. Cursor uses the **same tool names** for most operations — no renaming needed. - -| Skill references | Cursor equivalent | -| ---------------- | ----------------- | -| `Read` (file reading) | `Read` | -| `Write` (file creation) | `Write` | -| `Edit` (file editing) | `Edit` | -| `Bash` (run commands) | `Bash` | -| `Grep` (search file content) | `Grep` | -| `Glob` (search files by name) | `Glob` | -| `Task` (dispatch subagent) | `Task` | -| `Agent` (dispatch subagent) | `Agent` | -| `TodoWrite` (task tracking) | `TodoWrite` | -| `Skill` tool (invoke a skill) | `Skill` — `/add-plugin` installs, skills load natively | -| `WebSearch` | `WebSearch` | -| `WebFetch` | `WebFetch` | -| `AskUserQuestion` | `AskUserQuestion` | - -## Key Differences from Claude Code - -Cursor shares Claude Code's tool names but diverges in these areas: - -### Model handling - -Cursor does not accept Claude model shortnames (`opus`, `sonnet`, `haiku`). All model references in agent and skill frontmatter must use `inherit` — Cursor defers to the user's selected model. - -### Hook format - -Cursor hooks go in `hooks/hooks-cursor.json` (not `hooks/hooks.json`): -- Event names are **camelCase** (`sessionStart`, `preToolUse`) not PascalCase -- **Flat structure** — no nested `hooks[]` array; each entry has `event`, `matcher`, `command` at top level -- Output key is `additional_context` (snake_case), not `hookSpecificOutput.additionalContext` -- No async hook support — strip `async: true` if present - -### Subagent support - -Cursor has full subagent support via the `Task` and `Agent` tools (same names as Claude Code). - -**Custom agents:** Markdown files with YAML frontmatter in `.cursor/agents/` (project) or `~/.cursor/agents/` (global). Also reads `.claude/agents/` and `.codex/agents/` for compatibility. - -| Field | Type | Default | Description | -| ----- | ---- | ------- | ----------- | -| `name` | string | from filename | Display name and identifier | -| `description` | string | — | Shown in Task tool hints for delegation decisions | -| `model` | string | `inherit` | `fast`, `inherit`, or specific model ID | -| `readonly` | boolean | `false` | Restricts write permissions | -| `is_background` | boolean | `false` | Runs without blocking parent | - -**Built-in subagents:** `explore` (codebase search), `bash` (shell commands), `browser` (MCP browser control). - -**Async subagents (Cursor 2.5+):** Background mode, parallel execution (multiple Task calls in one message), nested subagents. State written to `~/.cursor/subagents/`. - -### Context files - -- Primary: `AGENTS.md` (not `CLAUDE.md`) -- Additional: `.cursor/rules/*.mdc` with YAML frontmatter (`description`, `alwaysApply: true`) - -### Frontmatter fields - -- `disable-model-invocation` is **kept** (Cursor supports it natively) -- `allowed-tools` must be **stripped** (Claude-specific) - -### MCP configuration - -- Cursor uses `mcp.json` (no dot prefix), not `.mcp.json` -- Cursor does not support MCP Resources — strip any `resources` blocks - -### Path variables - -- Hook scripts should branch on `${CURSOR_PLUGIN_ROOT}` instead of `${CLAUDE_PLUGIN_ROOT}` diff --git a/lib/references/gemini-tools.md b/lib/references/gemini-tools.md deleted file mode 100644 index cd0dfe5..0000000 --- a/lib/references/gemini-tools.md +++ /dev/null @@ -1,53 +0,0 @@ -# Gemini CLI Tool Mapping - -Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent: - -| Skill references | Gemini CLI equivalent | -| ---------------- | --------------------- | -| `Read` (file reading) | `read_file` | -| `Write` (file creation) | `write_file` | -| `Edit` (file editing) | `replace` | -| `Bash` (run commands) | `run_shell_command` | -| `Grep` (search file content) | `grep_search` | -| `Glob` (search files by name) | `glob` | -| `TodoWrite` (task tracking) | `write_todos` | -| `Skill` tool (invoke a skill) | `activate_skill` | -| `WebSearch` (web search) | `google_web_search` | -| `WebFetch` (fetch URL) | `web_fetch` | -| `AskUserQuestion` (structured input) | `ask_user` | -| `Task` tool (dispatch subagent) | `@agent-name` in prompt (see [Subagent dispatch](#subagent-dispatch)) | - -## Subagent dispatch - -Gemini CLI has full subagent support. Use `@agent-name ` syntax in prompts or let the agent route automatically. - -| Skill references | Gemini CLI equivalent | -| ---------------- | --------------------- | -| `Task` tool (dispatch subagent) | `@agent-name` in prompt, or automatic routing | -| `Agent` tool (dispatch subagent) | `@agent-name` in prompt, or automatic routing | - -### Built-in agents - -- `generalist` — general-purpose with all tools -- `cli_help` — Gemini CLI features expert -- `codebase_investigator` — codebase exploration specialist - -### Custom agents - -Define custom agents as Markdown files with YAML frontmatter in `.gemini/agents/` (project), `~/.gemini/agents/` (user), or `agents/` in extensions. - -Subagents use `complete_task` to return results to the parent agent. - -## Additional Gemini CLI tools - -These tools are available in Gemini CLI but have no Claude Code equivalent: - -| Tool | Purpose | -| ---- | ------- | -| `read_many_files` | Read multiple files at once (triggered by `@path`) | -| `list_directory` | List files and subdirectories | -| `save_memory` | Persist facts to GEMINI.md across sessions | -| `get_internal_docs` | Access Gemini CLI's own documentation | -| `complete_task` | Subagent-only: finalize mission and return result | -| `enter_plan_mode` / `exit_plan_mode` | Switch to read-only research mode | -| `browser_agent` | Experimental web browser automation | diff --git a/lib/references/openclaw-tools.md b/lib/references/openclaw-tools.md deleted file mode 100644 index 025bcf3..0000000 --- a/lib/references/openclaw-tools.md +++ /dev/null @@ -1,90 +0,0 @@ -# OpenClaw Tool Mapping - -Skills use Claude Code tool names. OpenClaw shares most tool names but has significant differences for agent dispatch, task tracking, and skill invocation. - -| Skill references | OpenClaw equivalent | -| ---------------- | ------------------- | -| `Read` (file reading) | `Read` | -| `Write` (file creation) | `Write` | -| `Edit` (file editing) | `Edit` | -| `Bash` (run commands) | `Bash` | -| `Grep` (search file content) | `Grep` | -| `Glob` (search files by name) | `Glob` | -| `Task` (dispatch subagent) | `agents.list[]` — configured in manifest, not a tool | -| `Agent` (dispatch subagent) | `agents.list[]` — configured in manifest, not a tool | -| `TodoWrite` (task tracking) | No equivalent | -| `Skill` tool (invoke a skill) | No equivalent — skills load natively | -| `WebSearch` (web search) | `WebSearch` | -| `WebFetch` (fetch URL) | `WebFetch` | -| `AskUserQuestion` (structured input) | `AskUserQuestion` | - -## No Task/Agent tool - -OpenClaw does not have a Task or Agent dispatch tool. Instead, agents are -declared in the `agents.list[]` array of `openclaw.plugin.json`. Inter-agent -delegation is handled by the runtime based on manifest configuration. - -Skills that use `Task` or `Agent` for subagent dispatch need their patterns -documented in AGENTS.md so OpenClaw can route via its manifest-based system. - -## No TodoWrite or Skill tool - -OpenClaw has no equivalent to TodoWrite (task tracking) or Skill (skill -invocation). Skills that depend on these tools should provide fallback -instructions or note the limitation. - -## Key Differences from Claude Code - -### Model format - -OpenClaw uses `provider/model` format instead of Claude shortnames: -- `opus` → `anthropic/claude-opus-4-6` -- `sonnet` → `anthropic/claude-sonnet-4-5` -- `haiku` → `anthropic/claude-haiku-4-5` - -### Frontmatter stripping - -All skill and agent frontmatter must have these fields **removed**: -- `disable-model-invocation` — not supported -- `allowed-tools` — not supported - -### Hooks via TypeScript SDK - -OpenClaw hooks are **not file-based**. They use the TypeScript plugin SDK: -- Register handlers via `api.registerHook(eventName, handler)` or `api.on(eventName, handler)` -- Async handlers are supported -- Can register to multiple events: `api.registerHook(["event_a", "event_b"], handler)` - -Event names are **snake_case**: - -| Event | Description | Notes | -| ----- | ----------- | ----- | -| `before_tool_call` | Before agent tool executes | `{ block: true }` stops lower-priority handlers | -| `after_tool_call` | After tool execution | Observational | -| `tool_result_persist` | When tool result is persisted | — | -| `llm_input` | Before LLM call | Requires `allowConversationAccess` | -| `llm_output` | After LLM produces output | Requires `allowConversationAccess` | -| `message_received` | Inbound message | Typed `threadId` for routing | -| `message_sent` | Outbound message | — | -| `before_agent_finalize` | Before agent finalizes | Requires `allowConversationAccess` | -| `agent_end` | Agent run ends | Requires `allowConversationAccess` | -| `before_model_resolve` | Before model resolution | Model switching | -| `before_compaction` | Before context compaction | — | -| `after_compaction` | After context compaction | — | -| `before_install` | Before plugin install | `{ block: true }` is terminal | -| `command` | Slash command issued | — | -| `gateway:startup` | Plugin startup | — | - -If the source plugin has file-based hooks (e.g. Claude Code `hooks/` directory), -they must be wrapped as TypeScript handlers. - -### Manifest - -OpenClaw uses `openclaw.plugin.json` with required fields `id` and `configSchema`. -Full plugins also need `package.json` with `openclaw.extensions` and `openclaw.compat`. - -### Distribution - -- Primary: ClawHub registry (`openclaw plugins install `) -- Alternative: npm publishing -- OpenClaw auto-detects Claude, Codex, and Cursor bundle layouts diff --git a/lib/references/platform-mappings.md b/lib/references/platform-mappings.md deleted file mode 100644 index bb7acbb..0000000 --- a/lib/references/platform-mappings.md +++ /dev/null @@ -1,226 +0,0 @@ -# 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 | Read | Read | view_file | Read | -| Write | write_file | Write | Write | write_to_file | Write | -| Edit | replace | apply_patch | Edit | replace_file_content | Edit | -| Bash | run_shell_command | Bash | Bash | run_command | Bash | -| Grep | grep_search | Grep | Grep | grep_search | Grep | -| Glob | glob | Glob | Glob | find_by_name | Glob | -| Task | @agent-name | spawn_agent | Task | (N/A) | agents.list[] | -| Agent | @agent-name | spawn_agent | Agent | (N/A) | agents.list[] | -| TodoWrite | write_todos | update_plan | TodoWrite | (N/A) | (N/A) | -| Skill | activate_skill | (N/A) | Skill | (N/A — auto-activate) | (N/A) | -| WebSearch | google_web_search | WebSearch | WebSearch | search_web | WebSearch | -| WebFetch | web_fetch | (N/A — use MCP) | WebFetch | read_url_content | WebFetch | -| AskUserQuestion | ask_user | AskUserQuestion | AskUserQuestion | (N/A) | AskUserQuestion | - -**Rules**: -- Gemini renames most tools — see `lib/references/gemini-tools.md` for full details. -- Codex replaces Task/Agent with `spawn_agent` and TodoWrite with `update_plan`. - Codex has no Skill tool — skills load natively. -- Antigravity renames ALL tools — see `lib/references/antigravity-tools.md`. - Antigravity has no Task/Agent, TodoWrite, Skill, or AskUserQuestion equivalents. -- OpenClaw manages agents via `agents.list[]` in runtime config, not a tool. - OpenClaw has no TodoWrite or Skill tool equivalents. - ---- - -## Table 3: Hook Event Mapping - -| Claude Event | Cursor | Gemini | Codex | Antigravity | OpenClaw | -|---|---|---|---|---|---| -| SessionStart | sessionStart | SessionStart | SessionStart | N/A | gateway:startup (plugin SDK) | -| PreToolUse | preToolUse | BeforeTool | PreToolUse | N/A | before_tool_call (plugin SDK) | -| PostToolUse | postToolUse | AfterTool | PostToolUse | 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 | Stop | N/A | N/A | -| UserPromptSubmit | beforeSubmitPrompt | (N/A) | UserPromptSubmit | N/A | N/A | -| (N/A) | (N/A) | BeforeModel | (N/A) | N/A | N/A | -| (N/A) | (N/A) | AfterModel | (N/A) | N/A | N/A | -| (N/A) | (N/A) | BeforeToolSelection | (N/A) | N/A | N/A | -| (N/A) | (N/A) | Notification | (N/A) | N/A | N/A | -| (N/A) | (N/A) | (N/A) | PermissionRequest | N/A | N/A | - -**Rules**: -- Codex hooks require `codex_hooks = true` feature flag in `config.toml`. -- Codex `PermissionRequest` has no Claude Code equivalent — it controls approval flow. -- Antigravity has no hook system. -- Gemini hooks go in user `settings.json` or the extension manifest `hooks` field. -- Gemini has 4 platform-specific events not available on other platforms (BeforeModel, AfterModel, BeforeToolSelection, Notification). -- OpenClaw hooks use TypeScript plugin SDK (`api.registerHook()`), not file-based config. -- Cursor uses camelCase; Gemini and Codex use 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 | -| `user-invocable` | strip | strip | strip | strip | strip | - -**Rules**: -- Cursor keeps `disable-model-invocation` (supported natively). -- All platforms strip `allowed-tools` (Claude-specific). -- All platforms strip `user-invocable` — Antigravity uses Workflows for slash-command invocation instead. - ---- - -## 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 | Codex | OpenClaw | -|---|---|---|---|---|---| -| Event name case | PascalCase | camelCase | PascalCase | PascalCase | snake_case (SDK) | -| Timeout unit | seconds | seconds | milliseconds | seconds | N/A (SDK-managed) | -| Async support | yes (optional) | no (strip) | no (strip) | no (strip) | yes (async handlers) | -| Structure | nested (matcher → hooks[]) | flat (matcher at hook level) | settings.json or extension manifest `hooks` field | nested (same as Claude Code) | `api.registerHook()` (TypeScript) | -| Output key | `hookSpecificOutput.additionalContext` | `additional_context` | N/A | `permissionDecision` / `decision` (event-specific) | return value from handler | - -**Notes**: Antigravity has no hook system — 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 | - ---- - -## Table 10: Context File Names - -| Platform | Primary Context File | Secondary Context Files | -|---|---|---| -| Claude Code | `CLAUDE.md` | — | -| Cursor | `AGENTS.md` | `.cursor/rules/*.mdc` | -| Gemini | `GEMINI.md` | — | -| Codex | `AGENTS.md` | `.codex/INSTALL.md` | -| Antigravity | `AGENTS.md` | `GEMINI.md` (higher priority), `.agent/rules/*.md` | -| OpenClaw | `AGENTS.md` | — | - -**Notes**: -- `AGENTS.md` is the universal fallback — every platform except Claude Code reads it. -- Antigravity prioritises `GEMINI.md` over `AGENTS.md` when both exist. -- Cursor `.mdc` rules use YAML frontmatter (`description`, `alwaysApply`). - ---- - -## Table 11: Rules and Policies Format - -| Platform | Path | Format | Notes | -|---|---|---|---| -| Claude Code | — | — | No standalone rules format | -| Cursor | `.cursor/rules/*.mdc` | Markdown + YAML front | `alwaysApply: true` for global rules | -| Gemini | `policies/*.toml` | TOML | Optional policy constraints | -| Codex | — | — | Instructions in `AGENTS.md` only | -| Antigravity | `.agent/rules/*.md` | Markdown | Auto-discovered by runtime | -| OpenClaw | — | — | Configured in `openclaw.plugin.json` | - ---- - -## Table 12: Commands Format - -| Platform | Path | Format | Notes | -|---|---|---|---| -| Claude Code | `commands/` | Deprecated | Use skills instead | -| Cursor | `commands/` | Optional | Auto-discovered from manifest | -| Gemini | `commands/*.toml` | TOML | Named commands with descriptions | -| Codex | — | — | No standalone commands format | -| Antigravity | `.agents/workflows/` | Markdown | Slash-command workflows | -| OpenClaw | — | — | Defined in manifest | - ---- - -## Table 13: MCP Configuration - -| Platform | Config Path | Notes | -|---|---|---| -| Claude Code | `.mcp.json` | Dot-prefixed; supports resources and tools | -| Cursor | `mcp.json` | No dot prefix; no MCP Resources support | -| Gemini | `gemini-extension.json` → `mcpServers` | Extension-bundled MCP servers | -| Codex | `.mcp.json` or `config.toml [mcp]` | Supports stdio and SSE transports | -| Antigravity | — | MCP not supported via config file | -| OpenClaw | `openclaw.plugin.json` → `mcp` block | Embedded in manifest | From ec8c88c1b23b4190886cd07c070a60a3747a5827 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:36:31 +1000 Subject: [PATCH 12/43] chore: fix remaining stale references to old platform files Updates UPSTREAM.md re-seeding instructions and SKILL.md external references to point to the new platform API structure. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/templates/UPSTREAM.md | 10 +++------- skills/plugin-portability/SKILL.md | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/templates/UPSTREAM.md b/lib/templates/UPSTREAM.md index 508b384..94fd32a 100644 --- a/lib/templates/UPSTREAM.md +++ b/lib/templates/UPSTREAM.md @@ -6,13 +6,9 @@ Templates in `lib/templates/` are seeded from [superpowers](https://github.com/o 1. Install the new superpowers version: update your Claude Code plugin config to the new version. 2. Locate the new cache path: `~/.claude/plugins/cache/claude-plugins-official/superpowers//` -3. Update tool-mapping sidecars: - - ```bash - SP=~/.claude/plugins/cache/claude-plugins-official/superpowers/ - cp "$SP/skills/using-superpowers/references/codex-tools.md" lib/references/ - cp "$SP/skills/using-superpowers/references/gemini-tools.md" lib/references/ - ``` +3. Review platform specs in `lib/references/platforms/` against any tool + name changes in the new superpowers release. The structured `PlatformSpec` + dictionaries replace the old per-tool sidecar files. 4. Compare manifest templates in `lib/templates/manifests/` and context-file templates in `lib/templates/context-files/` against the new superpowers manifests (`.claude-plugin/`, `.cursor-plugin/`, `gemini-extension.json`, `GEMINI.md`, hooks files) and update accordingly. 5. Update this file with the new version pin. diff --git a/skills/plugin-portability/SKILL.md b/skills/plugin-portability/SKILL.md index d6202c6..14cfc5f 100644 --- a/skills/plugin-portability/SKILL.md +++ b/skills/plugin-portability/SKILL.md @@ -19,7 +19,7 @@ Assess or uplift a plugin for multi-platform portability. Single entry point for > **External references:** > `lib/patterns/detection-algorithm.md` | `lib/patterns/inventory.md` | `lib/rubrics/rubric-framework.md` -> `lib/rubrics/*.yaml` | `lib/references/platform-mappings.md` | `lib/patterns/manifest-generation.md` +> `lib/rubrics/*.yaml` | `lib/references/platform-api.md` | `lib/references/platforms/*.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/` From 77ceccfe9b51b07ee51af8cb2430088c652776bc Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 15:43:10 +1000 Subject: [PATCH 13/43] fix: remove unverified Codex community directory claims SkillsMP.com and Smithery.ai are not verified in the researched Codex platform docs. Only $skill-installer and direct GitHub URL install paths are documented. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../publishing-and-discoverability.md | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/lib/patterns/publishing-and-discoverability.md b/lib/patterns/publishing-and-discoverability.md index c1fe3cf..f57b0b8 100644 --- a/lib/patterns/publishing-and-discoverability.md +++ b/lib/patterns/publishing-and-discoverability.md @@ -9,10 +9,10 @@ How to get a plugin discovered and installed on each platform. Use this referenc | Claude Code | No (Git repos) | N/A | N/A | Share repo URL | 1. `/plugin marketplace add owner/repo` 2. `/plugin install name@marketplace` | | Cursor | cursor.com/marketplace | cursor.com/marketplace/publish | Manual, open-source only | Marketplace search or `/add-plugin` | `/add-plugin owner/repo` | | 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 (skills) | Community directories | GitHub repo | N/A | `$skill-installer` search | `$skill-installer ` | | Codex (plugins) | Not yet public | N/A | N/A | `codex plugin marketplace add` | Via marketplace | -| 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 ` | +| Antigravity | No (Git repos + npm) | N/A | N/A | Community collections | Copy to `.agents/skills/` | +| OpenClaw | ClawHub (clawhub.ai) | ClawHub CLI or npm | Curated | ClawHub search | `openclaw plugins install clawhub:` | ## Claude Code @@ -110,9 +110,7 @@ For repos that are mostly instructions with no plugin UI metadata needed. ### Publishing -- Submit a PR to `github.com/openai/skills` for inclusion in the curated catalog -- Skills go into `.curated/` (vetted) or `.experimental/` (community/developmental) -- Alternatively, publish as a standalone GitHub repo +- Publish as a standalone GitHub repo with `SKILL.md` frontmatter ### Requirements @@ -120,16 +118,11 @@ For repos that are mostly instructions with no plugin UI metadata needed. ### Discovery and install -- `$skill-installer ` — install from the curated catalog by name -- `$skill-installer install from the .experimental folder` +- `$skill-installer ` — install by name - `$skill-installer install ` — install by URL -- Manual: clone and symlink `skills/` to `~/.agents/skills/` +- Manual: copy or symlink to `~/.codex/skills//` or `~/.agents/skills//` -Skills install into `~/.codex/skills//` by default. - -### Third-party registries - -SkillsMP.com and Smithery.ai have emerged as community skill directories. +Skills install into `~/.codex/skills//` by default. Restart Codex after adding new skills. ## Codex — Plugin Packaging Path @@ -157,50 +150,64 @@ Public self-serve plugin publishing is "coming soon" per OpenAI docs. Currently, ## Antigravity -Plugin directory at [antigravity.dev/plugins](https://antigravity.dev/plugins/). +No official skill marketplace or registry. Distribution is via Git repositories, community npm installers, and manual sharing. ### Publishing -Plugins are published as GitHub repositories and submitted to the Antigravity plugin directory. Community members can review and rate plugins. +Skills are published as GitHub repositories. Community skill collections (e.g., `antigravity-awesome-skills` with 1,400+ skills) provide npm-based cross-platform installers. ### Requirements -- `AGENTS.md` context file describing the plugin -- `skills/*/SKILL.md` with standard frontmatter +- `.agents/skills//SKILL.md` with standard frontmatter +- `AGENTS.md` context file (optional but recommended) -Antigravity auto-discovers skills from the `skills/` directory and context from `AGENTS.md`. No platform-specific manifest is required. +Antigravity auto-discovers skills from `.agents/skills/` directories. No platform-specific manifest is required for skill distribution. ### Discovery and install -- Browse the directory at `antigravity.dev/plugins` -- Install: `antigravity plugin add ` -- Install from URL: `antigravity plugin add ` +- Community collections: [sickn33/antigravity-awesome-skills](https://github.com/sickn33/antigravity-awesome-skills) +- npm installer: `npx antigravity-awesome-skills` (installs to `~/.gemini/antigravity/skills/`) +- Manual: copy skill directory into `.agents/skills/` (workspace) or `~/.gemini/antigravity/skills/` (global) ### Local development -`antigravity plugin link ` for local development. Changes are picked up on next session start. +Copy or symlink skill directory into `.agents/skills/`. Changes are picked up on next session start. + +### VS Code extensions + +As a VS Code fork, Antigravity uses **OpenVSX** for IDE extensions (separate from the skill system). Install via `--install-extension `. ## OpenClaw -Plugin registry at [openclaw.dev/registry](https://openclaw.dev/registry/). +Public registry at [ClawHub](https://clawhub.ai). Also supports npm distribution. ### Publishing -Plugins are published by submitting a PR to the OpenClaw registry repository. Registry entries are curated and reviewed before inclusion. +Plugins are published via ClawHub CLI or npm: -### Requirements +```bash +npm i -g clawhub +clawhub login +clawhub package publish your-org/your-plugin +clawhub skill publish ./my-skill-pack +``` -- `AGENTS.md` context file describing the plugin -- `skills/*/SKILL.md` with standard frontmatter +Alternatively, publish to npm with `openclaw.extensions` in `package.json`. + +### Requirements -OpenClaw auto-discovers skills from the `skills/` directory and context from `AGENTS.md`. No platform-specific manifest is required. +- `openclaw.plugin.json` manifest with `id` and `configSchema` (required for native plugins) +- `package.json` with `openclaw.extensions` and `openclaw.compat` (required for npm distribution) +- `AGENTS.md` context file (optional but recommended) +- `skills/*/SKILL.md` with standard frontmatter (for skill-bearing plugins) ### Discovery and install -- Browse the registry at `openclaw.dev/registry` -- Install: `openclaw install ` -- Install from URL: `openclaw install ` +- ClawHub: `openclaw plugins install clawhub:` +- Skills: `openclaw skills install ` +- npm: `openclaw plugins install @org/plugin-name` +- Bare names check ClawHub first, then npm ### Local development -`openclaw link ` for local development. +`openclaw plugins install -l ./my-plugin` for linked local development. Or add to `plugins.load.paths` in `~/.openclaw/openclaw.json`. From c17c565d9070f950eca4b62c7b08a3ba33c29388 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 16:52:23 +1000 Subject: [PATCH 14/43] Add rubric check alignment design spec Defines how to align rubric check pseudocode with the Platform API: fix tool_name() calls, inline opaque functions, unify synonyms, and document the pseudocode/prose decision boundary. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...026-04-27-rubric-check-alignment-design.md | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md diff --git a/docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md b/docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md new file mode 100644 index 0000000..30ac2b2 --- /dev/null +++ b/docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md @@ -0,0 +1,136 @@ +# Rubric Check Alignment + +> **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:** Align all rubric `check:` pseudocode with the defined Platform API, eliminate undefined opaque functions, and document the pseudocode/prose boundary. + +**Architecture:** No new functions in platform-api.md — existing lookup functions already cover all platform-dependent needs. Opaque utility functions that hide 1-2 lines of simple logic get inlined as prose pseudocode. A new principles document codifies the decision boundary for future rubric authors. + +**Tech Stack:** YAML rubrics, pseudocode in platform-api.md, markdown principles doc. + +--- + +## Deliverables + +1. **`lib/patterns/pseudocode-principles.md`** (new) — Three-tier decision boundary for rubric check pseudocode +2. **6 rubric YAMLs** (edit) — Fix `tool_name()` args, fix `supported_tools()` comment, inline opaque functions, unify synonyms +3. **`lib/references/platform-api.md`** (no changes expected) — Existing functions are sufficient + +## Pseudocode Principles (new doc content) + +The principles doc establishes three tiers: + +### Tier 1: Define in platform-api.md + +Functions that look up platform-specific data from REGISTRY. These depend on +the PlatformSpec type system and canonical enums. Examples: `tool_name()`, +`supported_tools()`, `strip_fields()`, `hook_event()`, `has_hooks()`. + +**Test:** Does the function access `REGISTRY[platform]`? If yes, define it. + +### Tier 2: Self-evident primitives + +Generic filesystem, data, and parsing operations whose names carry their +semantics. No definition needed — any LLM performing JIT evaluation understands +them from the name alone. + +Canonical list (use these names, not synonyms): +- **Filesystem:** `file_exists`, `dir_exists`, `read`, `read_json`, `valid_json`, `glob`, `glob_dirs`, `find_first` +- **Parsing:** `parse_frontmatter`, `parse_toml`, `parse_toml_field` +- **Data:** `has_keys`, `len`, `basename`, `project_root`, `is_array`, `match` +- **Operators:** `in` for single-key membership, `not in` for absence + +Retired synonyms (do not use): +- `find_file` → use `find_first` +- `count` → use `len` +- `field_present(obj, field)` → use `field in obj` +- `has_key(obj, key)` → use `key in obj` + +### Tier 3: Inline as prose + +When logic is 1-2 lines and only appears in one or two checks, write it +inline rather than naming a function. Hiding simple intent behind a named +function obscures what the check actually does. + +Examples of inlining: +- `extract_tool_references(skill)` → `for tool_call in skill content:` +- `remaps_claude_tools(content)` → `content maps any tool in supported_tools("claude-code") to a different name` +- `has_agent_definitions(".")` → `glob("agents/*.md") is non-empty` +- `uses_mcp_servers(".")` → `file_exists(".mcp.json") or config references MCP servers` +- `contains_verification_guidance(content)` → `content includes steps to verify the plugin loaded` +- `skill_name_referenced(content, skill)` → `name from parse_frontmatter(skill) appears in content` +- `scan_plugin_structure(".")` / `content_matches_structure(content, structure)` → describe expected structure in prose + +## Rubric Changes + +### A. Fix `tool_name()` calls to use canonical Operations + +The `tool_name(platform, op)` function takes a canonical Operation enum value, +not a Claude Code tool name. Four calls pass tool names instead of operations: + +| File | Current | Corrected | +|------|---------|-----------| +| codex.yaml (2_skills.tool_refs.spawn_agent) | `tool_name("codex", "Task")` | `tool_name("codex", "subagent.dispatch")` | +| codex.yaml (2_skills.tool_refs.update_plan) | `tool_name("codex", "TodoWrite")` | `tool_name("codex", "task.track")` | +| gemini-cli.yaml (2_skills.tool_refs.no_task_tool) | `tool_name("gemini-cli", "Task")` | `tool_name("gemini-cli", "subagent.dispatch")` | +| openclaw.yaml (2_skills.tool_refs.no_unresolved) | `tool_name("openclaw", "Task")` | `tool_name("openclaw", "subagent.dispatch")` | + +### B. Fix `supported_tools()` comment + +In claude-code.yaml condition `claude.2_skills.tool_refs.builtin_only`, the +comment after `supported_tools("claude-code")` lists tool names: +``` +# [Read, Write, Edit, Bash, Grep, Glob, Task, Agent, TodoWrite, Skill, WebSearch, WebFetch] +``` + +Should list canonical operations: +``` +# [file.read, file.write, file.edit, shell.execute, search.content, search.files, +# subagent.dispatch, task.track, skill.invoke, web.search, web.fetch, user.ask] +``` + +### C. Inline opaque functions + +Seven functions in claude-code.yaml that hide simple logic behind names: + +| Current | Inlined replacement | +|---------|-------------------| +| `extract_tool_references(skill)` | `for tool_call in skill content:` | +| `remaps_claude_tools(content)` | `content maps any tool in supported_tools("claude-code") to a different name` | +| `scan_plugin_structure(".")` | Remove — the judgement prose already says what to check | +| `content_matches_structure(content, structure)` | `CLAUDE.md describes the actual plugin layout (skills, hooks, agents) accurately` | +| `has_agent_definitions(".")` | `glob("agents/*.md") is non-empty` | +| `uses_mcp_servers(".")` | `file_exists(".mcp.json") or config references MCP servers` | +| `contains_verification_guidance(content)` | `content includes steps to verify the plugin loaded correctly` | +| `skill_name_referenced(content, skill)` | `name from parse_frontmatter(skill) appears in content` | + +### D. Unify synonyms across all 6 rubrics + +| Retired | Replacement | Files affected | +|---------|-------------|---------------| +| `find_file(...)` | `find_first(...)` | codex.yaml, cursor.yaml, gemini-cli.yaml, openclaw.yaml | +| `count(...)` | `len(...)` | codex.yaml, cursor.yaml | +| `field_present(json, field)` | `field in json` | claude-code.yaml | +| `has_key(obj, key)` | `key in obj` | claude-code.yaml | + +`has_keys(obj, keys)` is retained — checking multiple keys at once is a distinct +operation from single-key membership. + +## What Does NOT Change + +- **platform-api.md** — No new functions. Existing `tool_name`, `supported_tools`, `strip_fields`, `hook_event`, `hook_can_block`, `has_hooks`, `unsupported_tools`, `platforms_supporting`, `tool_mapping_table`, `diff_from` are sufficient. +- **Self-evident primitives** — `file_exists`, `glob`, `read_json`, `parse_frontmatter`, `len`, `basename`, etc. remain undefined. Their names carry the semantics. +- **Judgement conditions** — These are prose by nature. No pseudocode alignment needed. +- **rubric-framework.md** — JIT section (line 153) already acknowledges primitives exist. Add a cross-reference to the new principles doc. + +## Verification + +After all edits: +1. `grep -rn 'tool_name.*"Task"' lib/rubrics/` returns zero matches +2. `grep -rn 'tool_name.*"TodoWrite"' lib/rubrics/` returns zero matches +3. `grep -rn 'find_file(' lib/rubrics/` returns zero matches +4. `grep -rn 'count(' lib/rubrics/` returns zero matches +5. `grep -rn 'field_present(' lib/rubrics/` returns zero matches +6. `grep -rn 'has_key(' lib/rubrics/` returns zero matches +7. `grep -rn 'extract_tool_references\|remaps_claude_tools\|scan_plugin_structure\|content_matches_structure\|has_agent_definitions\|uses_mcp_servers\|contains_verification_guidance\|skill_name_referenced' lib/rubrics/` returns zero matches +8. All `tool_name()` calls use canonical Operation enum values From eaa7c91268692a724f30a148ad70096556861ae6 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 17:47:01 +1000 Subject: [PATCH 15/43] Fix remap check semantics and update reconciliation matrix - remaps_claude_tools inline now resolves canonical ops to native names via tool_name() before checking for remapping (Codex review finding) - Reconciliation matrix: replace false "zero gaps" claim with open items 35-39 for rubric check alignment work Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/reconciliation-matrix.md | 13 ++++++++++--- .../2026-04-27-rubric-check-alignment-design.md | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/reconciliation-matrix.md b/docs/reconciliation-matrix.md index 1adcd18..f50eb78 100644 --- a/docs/reconciliation-matrix.md +++ b/docs/reconciliation-matrix.md @@ -387,8 +387,15 @@ in `docs/research/per-platform-context-loading.md` (#12). 33. ~~**Consumer files**: GEMINI.md, AGENTS.md, CLAUDE.md, CI, patterns, CONTRIBUTING referenced old paths~~ Fixed — all updated to `lib/references/platform-api.md` and `lib/references/platforms/*.md` 34. ~~**codex-tools.md prose**: Subagent dispatch patterns buried in tool reference~~ Fixed — relocated to `lib/patterns/subagent-dispatch.md` -### All verification complete +### Rubric Check Alignment -Zero "Needs review", "Missing", or known gap items remain. +35. **Rubric YAMLs**: `tool_name()` called with tool names instead of canonical Operations (4 instances) — Needs fix +36. **claude-code.yaml**: `supported_tools()` comment lists tool names not canonical ops — Needs fix +37. **claude-code.yaml**: 7 opaque utility functions should be inlined as prose pseudocode — Needs fix +38. **All rubrics**: Synonym duplication (`find_file`/`find_first`, `count`/`len`, `field_present`/`in`) — Needs fix +39. **lib/patterns/pseudocode-principles.md**: Decision boundary doc missing — Needs creation -Full coverage: references (Tier 1), rubrics/patterns (Tier 2), templates/install docs (Tier 3), patterns pseudocode and skills (Tier 4), platform API restructure. +### Verification status + +Tiers 1-4 and platform API restructure: complete, zero gaps. +Rubric check alignment (items 35-39): open, spec written, implementation pending. diff --git a/docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md b/docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md index 30ac2b2..be42a33 100644 --- a/docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md +++ b/docs/superpowers/specs/2026-04-27-rubric-check-alignment-design.md @@ -96,7 +96,7 @@ Seven functions in claude-code.yaml that hide simple logic behind names: | Current | Inlined replacement | |---------|-------------------| | `extract_tool_references(skill)` | `for tool_call in skill content:` | -| `remaps_claude_tools(content)` | `content maps any tool in supported_tools("claude-code") to a different name` | +| `remaps_claude_tools(content)` | `for op in supported_tools("claude-code"): name = tool_name("claude-code", op); if content maps name to a different name: fail` | | `scan_plugin_structure(".")` | Remove — the judgement prose already says what to check | | `content_matches_structure(content, structure)` | `CLAUDE.md describes the actual plugin layout (skills, hooks, agents) accurately` | | `has_agent_definitions(".")` | `glob("agents/*.md") is non-empty` | From afdb3d9eabb930f76234c084e46ad2bd280ff71d Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 21:47:24 +1000 Subject: [PATCH 16/43] Add rubric check alignment implementation plan 10 tasks: create principles doc, add framework cross-ref, fix 4 tool_name() calls, inline 7 opaque functions, unify 4 retired synonyms, update reconciliation matrix. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-04-27-rubric-check-alignment.md | 701 ++++++++++++++++++ 1 file changed, 701 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-27-rubric-check-alignment.md diff --git a/docs/superpowers/plans/2026-04-27-rubric-check-alignment.md b/docs/superpowers/plans/2026-04-27-rubric-check-alignment.md new file mode 100644 index 0000000..ec07e84 --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-rubric-check-alignment.md @@ -0,0 +1,701 @@ +# Rubric Check Alignment 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:** Align all rubric `check:` pseudocode with the defined Platform API — fix wrong `tool_name()` arguments, inline opaque functions, unify retired synonyms, and create the pseudocode principles doc. + +**Architecture:** Pure text edits across YAML rubrics and markdown files. No code, no tests, no new functions. One new markdown file (`pseudocode-principles.md`). Cross-reference added to `rubric-framework.md`. + +**Tech Stack:** YAML, Markdown, grep for verification. + +--- + +## File Map + +| File | Action | Responsibility | +|------|--------|---------------| +| `lib/patterns/pseudocode-principles.md` | Create | Three-tier decision boundary for check pseudocode | +| `lib/rubrics/rubric-framework.md` | Modify (line 154) | Add cross-reference to principles doc | +| `lib/rubrics/codex.yaml` | Modify (lines 52, 112, 123, 237, 250, 310) | Fix tool_name args, unify find_file→find_first, count→len | +| `lib/rubrics/gemini-cli.yaml` | Modify (lines 111, 227, 281) | Fix tool_name arg, unify find_file→find_first | +| `lib/rubrics/openclaw.yaml` | Modify (lines 101, 230, 240) | Fix tool_name args, unify find_file→find_first | +| `lib/rubrics/cursor.yaml` | Modify (lines 64, 281) | Unify count→len, find_file→find_first | +| `lib/rubrics/claude-code.yaml` | Modify (lines 27, 113-118, 139-142, 150-154, 194, 247-249, 293-296, 305-308, 315-318) | Fix supported_tools comment, inline 7 opaque functions, unify field_present→in, has_key→in | +| `lib/rubrics/antigravity.yaml` | No changes | No affected patterns found | + +--- + +### Task 1: Create pseudocode-principles.md + +**Files:** +- Create: `lib/patterns/pseudocode-principles.md` + +- [ ] **Step 1: Create the principles document** + +```markdown +# Pseudocode Principles + +Decision boundary for rubric `check:` pseudocode. Determines whether a +function needs a formal definition, is self-evident, or should be inlined. + +--- + +## Tier 1: Define in platform-api.md + +Functions that look up platform-specific data from REGISTRY. These depend on +the PlatformSpec type system and canonical enums. + +**Test:** Does the function access `REGISTRY[platform]`? If yes, define it. + +Defined functions: `tool_name`, `hook_event`, `hook_can_block`, +`supported_tools`, `unsupported_tools`, `has_hooks`, `strip_fields`, +`platforms_supporting`, `tool_mapping_table`, `diff_from`. + +--- + +## Tier 2: Self-evident primitives + +Generic operations whose names carry their semantics. No definition needed — +any LLM performing JIT evaluation understands them from the name alone. + +Canonical names (use these, not synonyms): + +- **Filesystem:** `file_exists`, `dir_exists`, `read`, `read_json`, `valid_json`, `glob`, `glob_dirs`, `find_first` +- **Parsing:** `parse_frontmatter`, `parse_toml`, `parse_toml_field` +- **Data:** `has_keys`, `len`, `basename`, `project_root`, `is_array`, `match` +- **Operators:** `in` for single-key membership, `not in` for absence + +Retired synonyms — do not use in new checks: + +| Retired | Use instead | +|---------|-------------| +| `find_file(...)` | `find_first(...)` | +| `count(...)` | `len(...)` | +| `field_present(obj, field)` | `field in obj` | +| `has_key(obj, key)` | `key in obj` | + +--- + +## Tier 3: Inline as prose + +When logic is 1-2 lines and only appears in one or two checks, write it +inline rather than naming a function. Hiding simple intent behind a named +function obscures what the check actually does. + +Inlining examples: + +| Instead of | Write | +|------------|-------| +| `extract_tool_references(skill)` | `for tool_call in skill content:` | +| `has_agent_definitions(".")` | `glob("agents/*.md") is non-empty` | +| `uses_mcp_servers(".")` | `file_exists(".mcp.json") or config references MCP servers` | +| `contains_verification_guidance(content)` | `content includes steps to verify the plugin loaded` | +| `skill_name_referenced(content, skill)` | `name from parse_frontmatter(skill) appears in content` | +``` + +- [ ] **Step 2: Verify the file was created** + +Run: `test -f lib/patterns/pseudocode-principles.md && echo OK` +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +git add lib/patterns/pseudocode-principles.md +git commit -m "Add pseudocode principles doc for rubric check authoring" +``` + +--- + +### Task 2: Add cross-reference in rubric-framework.md + +**Files:** +- Modify: `lib/rubrics/rubric-framework.md:153-154` + +- [ ] **Step 1: Add cross-reference after the JIT section's existing text** + +In `lib/rubrics/rubric-framework.md`, find this text at line 153: + +``` +each check. The pseudocode operations (`read_json`, `file_exists`, +`parse_frontmatter`, `glob`) map to read-only filesystem queries. +``` + +Replace with: + +``` +each check. The pseudocode operations (`read_json`, `file_exists`, +`parse_frontmatter`, `glob`) map to read-only filesystem queries. +See `lib/patterns/pseudocode-principles.md` for the full canonical +primitive list and the three-tier decision boundary (define / self-evident / inline). +``` + +- [ ] **Step 2: Verify** + +Run: `grep 'pseudocode-principles' lib/rubrics/rubric-framework.md` +Expected: line containing the cross-reference + +- [ ] **Step 3: Commit** + +```bash +git add lib/rubrics/rubric-framework.md +git commit -m "Add cross-reference to pseudocode principles in rubric framework" +``` + +--- + +### Task 3: Fix tool_name() calls in codex.yaml + +**Files:** +- Modify: `lib/rubrics/codex.yaml:112,123` + +- [ ] **Step 1: Fix spawn_agent check (line 112)** + +Find this text in `lib/rubrics/codex.yaml`: + +``` + maps to Codex spawn_agent (tool_name("codex", "Task")). A lib/references/platforms/codex.md +``` + +Replace with: + +``` + maps to Codex spawn_agent (tool_name("codex", "subagent.dispatch")). A lib/references/platforms/codex.md +``` + +- [ ] **Step 2: Fix update_plan check (line 123)** + +Find this text in `lib/rubrics/codex.yaml`: + +``` + Codex update_plan (tool_name("codex", "TodoWrite")). +``` + +Replace with: + +``` + Codex update_plan (tool_name("codex", "task.track")). +``` + +- [ ] **Step 3: Verify no tool_name("codex", "Task") or "TodoWrite" remain** + +Run: `grep -n 'tool_name.*"Task"\|tool_name.*"TodoWrite"' lib/rubrics/codex.yaml` +Expected: no output + +- [ ] **Step 4: Commit** + +```bash +git add lib/rubrics/codex.yaml +git commit -m "Fix tool_name() calls to use canonical operations in codex rubric" +``` + +--- + +### Task 4: Fix tool_name() call in gemini-cli.yaml + +**Files:** +- Modify: `lib/rubrics/gemini-cli.yaml:111` + +- [ ] **Step 1: Fix the tool_name call** + +Find this text in `lib/rubrics/gemini-cli.yaml`: + +``` + instead (tool_name("gemini-cli", "Task") = "@agent-name"). +``` + +Replace with: + +``` + instead (tool_name("gemini-cli", "subagent.dispatch") = "@agent-name"). +``` + +- [ ] **Step 2: Verify** + +Run: `grep -n 'tool_name.*"Task"' lib/rubrics/gemini-cli.yaml` +Expected: no output + +- [ ] **Step 3: Commit** + +```bash +git add lib/rubrics/gemini-cli.yaml +git commit -m "Fix tool_name() call to use canonical operation in gemini-cli rubric" +``` + +--- + +### Task 5: Fix tool_name() calls in openclaw.yaml + +**Files:** +- Modify: `lib/rubrics/openclaw.yaml:101,230` + +- [ ] **Step 1: Fix first tool_name call (line 101)** + +Find this text in `lib/rubrics/openclaw.yaml`: + +``` + agents.list[] in runtime config instead (tool_name("openclaw", "Task")). +``` + +Replace with: + +``` + agents.list[] in runtime config instead (tool_name("openclaw", "subagent.dispatch")). +``` + +- [ ] **Step 2: Fix second tool_name call (line 230)** + +Find this text in `lib/rubrics/openclaw.yaml`: + +``` + tool_name("openclaw", "Task") = "agents.list[]". +``` + +Replace with: + +``` + tool_name("openclaw", "subagent.dispatch") = "agents.list[]". +``` + +- [ ] **Step 3: Verify** + +Run: `grep -n 'tool_name.*"Task"' lib/rubrics/openclaw.yaml` +Expected: no output + +- [ ] **Step 4: Commit** + +```bash +git add lib/rubrics/openclaw.yaml +git commit -m "Fix tool_name() calls to use canonical operations in openclaw rubric" +``` + +--- + +### Task 6: Unify synonyms in codex.yaml + +**Files:** +- Modify: `lib/rubrics/codex.yaml:52,237,250,310` + +- [ ] **Step 1: Replace count() with len() (line 52)** + +Find: + +``` + if count(glob(".agents/skills/*/SKILL.md")) > 1: +``` + +Replace: + +``` + if len(glob(".agents/skills/*/SKILL.md")) > 1: +``` + +- [ ] **Step 2: Replace find_file() with find_first() (line 237)** + +Find: + +``` + sidecars = glob("**/platforms/codex.md") OR find_file("lib/references/platforms/codex.md") +``` + +Replace: + +``` + sidecars = glob("**/platforms/codex.md") OR find_first("lib/references/platforms/codex.md") +``` + +- [ ] **Step 3: Replace find_file() with find_first() (line 250)** + +Find: + +``` + sidecars = glob("**/platforms/codex.md") OR find_file("lib/references/platforms/codex.md") +``` + +Note: This is a second occurrence of the same string. Use `replace_all` or target the second instance. The two occurrences are in conditions `codex.5_toolmap.sidecar.spawn_agent_mapped` (line 237) and `codex.5_toolmap.sidecar.update_plan_mapped` (line 250). After Step 2 replaces the first, this step replaces the remaining one. + +Replace: + +``` + sidecars = glob("**/platforms/codex.md") OR find_file("lib/references/platforms/codex.md") +``` + +With: + +``` + sidecars = glob("**/platforms/codex.md") OR find_first("lib/references/platforms/codex.md") +``` + +- [ ] **Step 4: Replace find_file() with find_first() (line 310)** + +Find: + +``` + readme = find_file(["README.md", "INSTALL.md", ".codex/INSTALL.md"]) +``` + +Replace: + +``` + readme = find_first(["README.md", "INSTALL.md", ".codex/INSTALL.md"]) +``` + +- [ ] **Step 5: Verify no find_file or count remain** + +Run: `grep -n 'find_file\|count(' lib/rubrics/codex.yaml` +Expected: no output + +- [ ] **Step 6: Commit** + +```bash +git add lib/rubrics/codex.yaml +git commit -m "Unify synonyms in codex rubric: find_file→find_first, count→len" +``` + +--- + +### Task 7: Unify synonyms in cursor.yaml + +**Files:** +- Modify: `lib/rubrics/cursor.yaml:64,281` + +- [ ] **Step 1: Replace count() with len() (line 64)** + +Find: + +``` + if count(glob("skills/*/SKILL.md")) > 1 or dir_exists(".cursor-plugin/plugins/"): +``` + +Replace: + +``` + if len(glob("skills/*/SKILL.md")) > 1 or dir_exists(".cursor-plugin/plugins/"): +``` + +- [ ] **Step 2: Replace find_file() with find_first() (line 281)** + +Find: + +``` + readme = find_file(["README.md", "INSTALL.md"]) +``` + +Replace: + +``` + readme = find_first(["README.md", "INSTALL.md"]) +``` + +- [ ] **Step 3: Verify** + +Run: `grep -n 'find_file\|count(' lib/rubrics/cursor.yaml` +Expected: no output + +- [ ] **Step 4: Commit** + +```bash +git add lib/rubrics/cursor.yaml +git commit -m "Unify synonyms in cursor rubric: find_file→find_first, count→len" +``` + +--- + +### Task 8: Unify synonyms in gemini-cli.yaml and openclaw.yaml + +**Files:** +- Modify: `lib/rubrics/gemini-cli.yaml:227,281` +- Modify: `lib/rubrics/openclaw.yaml:240` + +- [ ] **Step 1: Replace find_file() in gemini-cli.yaml (line 227)** + +Find: + +``` + sidecars = glob("**/platforms/gemini-cli.md") OR find_file("lib/references/platforms/gemini-cli.md") +``` + +Replace: + +``` + sidecars = glob("**/platforms/gemini-cli.md") OR find_first("lib/references/platforms/gemini-cli.md") +``` + +- [ ] **Step 2: Replace find_file() in gemini-cli.yaml (line 281)** + +Find: + +``` + readme = find_file(["README.md", "INSTALL.md"]) +``` + +Replace: + +``` + readme = find_first(["README.md", "INSTALL.md"]) +``` + +- [ ] **Step 3: Replace find_file() in openclaw.yaml (line 240)** + +Find: + +``` + readme = find_file(["README.md", "INSTALL.md"]) +``` + +Replace: + +``` + readme = find_first(["README.md", "INSTALL.md"]) +``` + +- [ ] **Step 4: Verify** + +Run: `grep -rn 'find_file(' lib/rubrics/gemini-cli.yaml lib/rubrics/openclaw.yaml` +Expected: no output + +- [ ] **Step 5: Commit** + +```bash +git add lib/rubrics/gemini-cli.yaml lib/rubrics/openclaw.yaml +git commit -m "Unify find_file→find_first in gemini-cli and openclaw rubrics" +``` + +--- + +### Task 9: Fix supported_tools() comment and inline opaque functions in claude-code.yaml + +This is the largest task — all changes are in `lib/rubrics/claude-code.yaml`. + +**Files:** +- Modify: `lib/rubrics/claude-code.yaml:27,113-118,139-142,150-154,194,247-249,293-296,305-308,315-318` + +- [ ] **Step 1: Fix field_present → in (line 27)** + +Find: + +``` + assert field_present(json, field) +``` + +Replace: + +``` + assert field in json +``` + +- [ ] **Step 2: Fix supported_tools() comment and inline extract_tool_references (lines 113-119)** + +Find the entire check block for condition `claude.2_skills.tool_refs.builtin_only` (starting at line 112): + +``` + builtin = supported_tools("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 +``` + +Replace with: + +``` + builtin = supported_tools("claude-code") + # [file.read, file.write, file.edit, shell.execute, search.content, + # search.files, subagent.dispatch, task.track, skill.invoke, + # web.search, web.fetch, user.ask] + for skill in glob("skills/*/SKILL.md"): + for tool_call in skill content: + assert tool_call in builtin +``` + +- [ ] **Step 3: Inline skill_name_referenced (lines 139-142)** + +Find: + +``` + skills = glob("skills/*/SKILL.md") + content = read("CLAUDE.md") + for skill in skills: + assert skill_name_referenced(content, skill) +``` + +Replace: + +``` + skills = glob("skills/*/SKILL.md") + content = read("CLAUDE.md") + for skill in skills: + name = parse_frontmatter(skill)["name"] + assert name in content +``` + +- [ ] **Step 4: Inline scan_plugin_structure and content_matches_structure (lines 150-154)** + +Find: + +``` + content = read("CLAUDE.md") + structure = scan_plugin_structure(".") + assert content_matches_structure(content, structure) + # CLAUDE.md describes the actual plugin layout accurately +``` + +Replace: + +``` + content = read("CLAUDE.md") + CLAUDE.md describes the actual plugin layout (skills, hooks, agents) + accurately — no stale references to removed or renamed components. +``` + +- [ ] **Step 5: Fix has_key → key in (line 194)** + +Find: + +``` + assert has_key(config, "hooks") +``` + +Replace: + +``` + assert "hooks" in config +``` + +- [ ] **Step 6: Inline remaps_claude_tools (lines 247-249)** + +Find: + +``` + for s in sidecars: + content = read(s) + assert not remaps_claude_tools(content) +``` + +Replace: + +``` + for s in sidecars: + content = read(s) + for op in supported_tools("claude-code"): + name = tool_name("claude-code", op) + assert content does not map name to a different tool name +``` + +- [ ] **Step 7: Inline contains_verification_guidance (lines 293-296)** + +Find: + +``` + 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 +``` + +Replace: + +``` + readme = find_first(["README.md", "INSTALL.md", "docs/install.md"]) + content = read(readme) + assert content includes steps to verify the plugin loaded correctly +``` + +- [ ] **Step 8: Inline uses_mcp_servers (lines 305-308)** + +Find: + +``` + if uses_mcp_servers("."): + assert file_exists(".mcp.json") +``` + +Replace: + +``` + if file_exists(".mcp.json") or config references MCP servers: + assert file_exists(".mcp.json") +``` + +- [ ] **Step 9: Inline has_agent_definitions (lines 315-319)** + +Find: + +``` + # Only required if plugin defines agents + if has_agent_definitions("."): + agents = glob("agents/*.md") + assert len(agents) > 0 +``` + +Replace: + +``` + # Only required if plugin defines agents + if dir_exists("agents/"): + assert glob("agents/*.md") is non-empty + +- [ ] **Step 10: Verify all opaque functions are gone** + +Run: `grep -n 'extract_tool_references\|remaps_claude_tools\|scan_plugin_structure\|content_matches_structure\|has_agent_definitions\|uses_mcp_servers\|contains_verification_guidance\|skill_name_referenced\|field_present\|has_key(' lib/rubrics/claude-code.yaml` +Expected: no output + +- [ ] **Step 11: Commit** + +```bash +git add lib/rubrics/claude-code.yaml +git commit -m "Inline opaque functions and unify synonyms in claude-code rubric" +``` + +--- + +### Task 10: Final verification + +- [ ] **Step 1: Run all spec verification commands** + +```bash +echo "=== 1. tool_name Task ===" && grep -rn 'tool_name.*"Task"' lib/rubrics/ +echo "=== 2. tool_name TodoWrite ===" && grep -rn 'tool_name.*"TodoWrite"' lib/rubrics/ +echo "=== 3. find_file ===" && grep -rn 'find_file(' lib/rubrics/ +echo "=== 4. count( ===" && grep -rn 'count(' lib/rubrics/ +echo "=== 5. field_present ===" && grep -rn 'field_present(' lib/rubrics/ +echo "=== 6. has_key( ===" && grep -rn 'has_key(' lib/rubrics/ +echo "=== 7. opaque functions ===" && grep -rn 'extract_tool_references\|remaps_claude_tools\|scan_plugin_structure\|content_matches_structure\|has_agent_definitions\|uses_mcp_servers\|contains_verification_guidance\|skill_name_referenced' lib/rubrics/ +echo "=== 8. pseudocode-principles exists ===" && test -f lib/patterns/pseudocode-principles.md && echo OK +echo "=== 9. framework cross-ref ===" && grep 'pseudocode-principles' lib/rubrics/rubric-framework.md +``` + +Expected: checks 1-7 return no output, check 8 prints OK, check 9 prints the cross-reference line. + +- [ ] **Step 2: Update reconciliation matrix items 35-39 to fixed** + +In `docs/reconciliation-matrix.md`, find the Rubric Check Alignment section and strike through all five items: + +Find: + +``` +35. **Rubric YAMLs**: `tool_name()` called with tool names instead of canonical Operations (4 instances) — Needs fix +36. **claude-code.yaml**: `supported_tools()` comment lists tool names not canonical ops — Needs fix +37. **claude-code.yaml**: 7 opaque utility functions should be inlined as prose pseudocode — Needs fix +38. **All rubrics**: Synonym duplication (`find_file`/`find_first`, `count`/`len`, `field_present`/`in`) — Needs fix +39. **lib/patterns/pseudocode-principles.md**: Decision boundary doc missing — Needs creation +``` + +Replace: + +``` +35. ~~**Rubric YAMLs**: `tool_name()` called with tool names instead of canonical Operations (4 instances)~~ Fixed — all use canonical Operation enum values +36. ~~**claude-code.yaml**: `supported_tools()` comment lists tool names not canonical ops~~ Fixed — lists canonical operations +37. ~~**claude-code.yaml**: 7 opaque utility functions should be inlined as prose pseudocode~~ Fixed — all inlined +38. ~~**All rubrics**: Synonym duplication (`find_file`/`find_first`, `count`/`len`, `field_present`/`in`)~~ Fixed — unified to canonical names +39. ~~**lib/patterns/pseudocode-principles.md**: Decision boundary doc missing~~ Fixed — created with three-tier framework +``` + +- [ ] **Step 3: Commit** + +```bash +git add docs/reconciliation-matrix.md +git commit -m "Mark rubric check alignment items 35-39 as fixed in reconciliation matrix" +``` From 04f2bba735147b0764939c73c547048a6552d173 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 21:57:47 +1000 Subject: [PATCH 17/43] Add pseudocode principles doc for rubric check authoring --- lib/patterns/pseudocode-principles.md | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 lib/patterns/pseudocode-principles.md diff --git a/lib/patterns/pseudocode-principles.md b/lib/patterns/pseudocode-principles.md new file mode 100644 index 0000000..cb1d3a0 --- /dev/null +++ b/lib/patterns/pseudocode-principles.md @@ -0,0 +1,58 @@ +# Pseudocode Principles + +Decision boundary for rubric `check:` pseudocode. Determines whether a +function needs a formal definition, is self-evident, or should be inlined. + +--- + +## Tier 1: Define in platform-api.md + +Functions that look up platform-specific data from REGISTRY. These depend on +the PlatformSpec type system and canonical enums. + +**Test:** Does the function access `REGISTRY[platform]`? If yes, define it. + +Defined functions: `tool_name`, `hook_event`, `hook_can_block`, +`supported_tools`, `unsupported_tools`, `has_hooks`, `strip_fields`, +`platforms_supporting`, `tool_mapping_table`, `diff_from`. + +--- + +## Tier 2: Self-evident primitives + +Generic operations whose names carry their semantics. No definition needed — +any LLM performing JIT evaluation understands them from the name alone. + +Canonical names (use these, not synonyms): + +- **Filesystem:** `file_exists`, `dir_exists`, `read`, `read_json`, `valid_json`, `glob`, `glob_dirs`, `find_first` +- **Parsing:** `parse_frontmatter`, `parse_toml`, `parse_toml_field` +- **Data:** `has_keys`, `len`, `basename`, `project_root`, `is_array`, `match` +- **Operators:** `in` for single-key membership, `not in` for absence + +Retired synonyms — do not use in new checks: + +| Retired | Use instead | +|---------|-------------| +| `find_file(...)` | `find_first(...)` | +| `count(...)` | `len(...)` | +| `field_present(obj, field)` | `field in obj` | +| `has_key(obj, key)` | `key in obj` | + +--- + +## Tier 3: Inline as prose + +When logic is 1-2 lines and only appears in one or two checks, write it +inline rather than naming a function. Hiding simple intent behind a named +function obscures what the check actually does. + +Inlining examples: + +| Instead of | Write | +|------------|-------| +| `extract_tool_references(skill)` | `for tool_call in skill content:` | +| `has_agent_definitions(".")` | `glob("agents/*.md") is non-empty` | +| `uses_mcp_servers(".")` | `file_exists(".mcp.json") or config references MCP servers` | +| `contains_verification_guidance(content)` | `content includes steps to verify the plugin loaded` | +| `skill_name_referenced(content, skill)` | `name from parse_frontmatter(skill) appears in content` | From 7e64a4093594a5dda4ad57df8e3d75e2dfc4cc46 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 21:58:39 +1000 Subject: [PATCH 18/43] Add cross-reference to pseudocode principles in rubric framework Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/rubrics/rubric-framework.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rubrics/rubric-framework.md b/lib/rubrics/rubric-framework.md index bd21f46..3e8dc57 100644 --- a/lib/rubrics/rubric-framework.md +++ b/lib/rubrics/rubric-framework.md @@ -152,6 +152,8 @@ 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. +See `lib/patterns/pseudocode-principles.md` for the full canonical +primitive list and the three-tier decision boundary (define / self-evident / inline). 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 From 4f22c4f17d958ffa22796dc458c494ffd68040c2 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 21:58:41 +1000 Subject: [PATCH 19/43] Fix tool_name() calls to use canonical operations in rubrics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - codex: Task→subagent.dispatch, TodoWrite→task.track - gemini-cli: Task→subagent.dispatch - openclaw: Task→subagent.dispatch (2 instances) Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/rubrics/codex.yaml | 4 ++-- lib/rubrics/gemini-cli.yaml | 2 +- lib/rubrics/openclaw.yaml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rubrics/codex.yaml b/lib/rubrics/codex.yaml index 399e639..e242722 100644 --- a/lib/rubrics/codex.yaml +++ b/lib/rubrics/codex.yaml @@ -109,7 +109,7 @@ categories: 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 (tool_name("codex", "Task")). A lib/references/platforms/codex.md + maps to Codex spawn_agent (tool_name("codex", "subagent.dispatch")). A lib/references/platforms/codex.md reference or inline note must explain this mapping. - id: codex.2_skills.tool_refs.update_plan @@ -120,7 +120,7 @@ categories: 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 (tool_name("codex", "TodoWrite")). + Codex update_plan (tool_name("codex", "task.track")). 3_context: conditions: diff --git a/lib/rubrics/gemini-cli.yaml b/lib/rubrics/gemini-cli.yaml index adf936c..fad0f2a 100644 --- a/lib/rubrics/gemini-cli.yaml +++ b/lib/rubrics/gemini-cli.yaml @@ -108,7 +108,7 @@ categories: 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 (tool_name("gemini-cli", "Task") = "@agent-name"). + instead (tool_name("gemini-cli", "subagent.dispatch") = "@agent-name"). Flag any raw Task/Agent tool invocations. 3_context: diff --git a/lib/rubrics/openclaw.yaml b/lib/rubrics/openclaw.yaml index a5c119e..ae869ff 100644 --- a/lib/rubrics/openclaw.yaml +++ b/lib/rubrics/openclaw.yaml @@ -98,7 +98,7 @@ categories: 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 (tool_name("openclaw", "Task")). + agents.list[] in runtime config instead (tool_name("openclaw", "subagent.dispatch")). Also has no TodoWrite or Skill tool (mapped to N/A). Flag any unresolvable tool references. @@ -227,7 +227,7 @@ categories: 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 — - tool_name("openclaw", "Task") = "agents.list[]". + tool_name("openclaw", "subagent.dispatch") = "agents.list[]". 6_install: conditions: From c2845c5c00c4afe3bb62e33593ccdab0553ea663 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 21:59:12 +1000 Subject: [PATCH 20/43] Unify retired synonyms across all rubrics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit find_file→find_first, count→len in codex, cursor, gemini-cli, openclaw Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/rubrics/codex.yaml | 8 ++++---- lib/rubrics/cursor.yaml | 4 ++-- lib/rubrics/gemini-cli.yaml | 4 ++-- lib/rubrics/openclaw.yaml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/rubrics/codex.yaml b/lib/rubrics/codex.yaml index e242722..7b42fa2 100644 --- a/lib/rubrics/codex.yaml +++ b/lib/rubrics/codex.yaml @@ -49,7 +49,7 @@ categories: check: | # Only for native plugin path with multiple plugins if file_exists(".codex-plugin/plugin.json"): - if count(glob(".agents/skills/*/SKILL.md")) > 1: + if len(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 @@ -234,7 +234,7 @@ categories: critical: true points: 1 check: | - sidecars = glob("**/platforms/codex.md") OR find_file("lib/references/platforms/codex.md") + sidecars = glob("**/platforms/codex.md") OR find_first("lib/references/platforms/codex.md") assert len(sidecars) > 0, "No Codex platform spec found" found = false for sidecar in sidecars: @@ -247,7 +247,7 @@ categories: critical: false points: 1 check: | - sidecars = glob("**/platforms/codex.md") OR find_file("lib/references/platforms/codex.md") + sidecars = glob("**/platforms/codex.md") OR find_first("lib/references/platforms/codex.md") if len(sidecars) > 0: found = false for sidecar in sidecars: @@ -307,7 +307,7 @@ categories: critical: true points: 1 check: | - readme = find_file(["README.md", "INSTALL.md", ".codex/INSTALL.md"]) + readme = find_first(["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" diff --git a/lib/rubrics/cursor.yaml b/lib/rubrics/cursor.yaml index a9c1444..46df2ba 100644 --- a/lib/rubrics/cursor.yaml +++ b/lib/rubrics/cursor.yaml @@ -61,7 +61,7 @@ categories: points: 1 check: | # Only required for multi-plugin repos - if count(glob("skills/*/SKILL.md")) > 1 or dir_exists(".cursor-plugin/plugins/"): + if len(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 @@ -278,7 +278,7 @@ categories: critical: true points: 1 check: | - readme = find_file(["README.md", "INSTALL.md"]) + readme = find_first(["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" diff --git a/lib/rubrics/gemini-cli.yaml b/lib/rubrics/gemini-cli.yaml index fad0f2a..d9205a7 100644 --- a/lib/rubrics/gemini-cli.yaml +++ b/lib/rubrics/gemini-cli.yaml @@ -224,7 +224,7 @@ categories: points: 1 check: | # Find all gemini-cli platform spec files (per-skill or shared) - sidecars = glob("**/platforms/gemini-cli.md") OR find_file("lib/references/platforms/gemini-cli.md") + sidecars = glob("**/platforms/gemini-cli.md") OR find_first("lib/references/platforms/gemini-cli.md") assert len(sidecars) > 0, "No Gemini platform spec found anywhere" for sidecar in sidecars: content = read(sidecar) @@ -278,7 +278,7 @@ categories: critical: true points: 1 check: | - readme = find_file(["README.md", "INSTALL.md"]) + readme = find_first(["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" diff --git a/lib/rubrics/openclaw.yaml b/lib/rubrics/openclaw.yaml index ae869ff..e8c26d0 100644 --- a/lib/rubrics/openclaw.yaml +++ b/lib/rubrics/openclaw.yaml @@ -237,7 +237,7 @@ categories: critical: true points: 1 check: | - readme = find_file(["README.md", "INSTALL.md"]) + readme = find_first(["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" From 5132e6601884713a0dab4d3090aa1af2019797d4 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 22:00:39 +1000 Subject: [PATCH 21/43] Inline opaque functions and unify synonyms in claude-code rubric Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/rubrics/claude-code.yaml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/rubrics/claude-code.yaml b/lib/rubrics/claude-code.yaml index 88ce479..d88761a 100644 --- a/lib/rubrics/claude-code.yaml +++ b/lib/rubrics/claude-code.yaml @@ -24,7 +24,7 @@ categories: check: | json = read_json(".claude-plugin/plugin.json") for field in REGISTRY["claude-code"].manifest.required_fields: - assert field_present(json, field) + assert field in json # Required: name, version, description, author.name, author.email template: manifests/claude-plugin/plugin.json.tmpl?merge @@ -111,12 +111,12 @@ categories: points: 1 check: | builtin = supported_tools("claude-code") - # [Read, Write, Edit, Bash, Grep, Glob, Task, Agent, - # TodoWrite, Skill, WebSearch, WebFetch] + # [file.read, file.write, file.edit, shell.execute, search.content, + # search.files, subagent.dispatch, task.track, skill.invoke, + # web.search, web.fetch, user.ask] for skill in glob("skills/*/SKILL.md"): - tool_refs = extract_tool_references(skill) - for ref in tool_refs: - assert ref in builtin + for tool_call in skill content: + assert tool_call in builtin 3_context: conditions: @@ -138,7 +138,8 @@ categories: skills = glob("skills/*/SKILL.md") content = read("CLAUDE.md") for skill in skills: - assert skill_name_referenced(content, skill) + name = parse_frontmatter(skill)["name"] + assert name in content # CLAUDE.md should list or reference every skill template: context-files/CLAUDE.md.tmpl?merge @@ -149,9 +150,8 @@ categories: 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 + CLAUDE.md describes the actual plugin layout (skills, hooks, agents) + accurately — no stale references to removed or renamed components. 4_hooks: conditions: @@ -191,7 +191,7 @@ categories: for entry in config: assert has_keys(entry, ["hooks"]) else: - assert has_key(config, "hooks") + assert "hooks" in config - id: claude.4_hooks.scripts.cross_platform type: checkable @@ -246,7 +246,9 @@ categories: if len(sidecars) > 0: for s in sidecars: content = read(s) - assert not remaps_claude_tools(content) + for op in supported_tools("claude-code"): + name = tool_name("claude-code", op) + assert content does not map name to a different tool name 6_install: conditions: @@ -292,8 +294,7 @@ categories: 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 + assert content includes steps to verify the plugin loaded correctly 7_runtime: conditions: @@ -304,7 +305,7 @@ categories: points: 1 check: | # Only required if plugin uses MCP servers - if uses_mcp_servers("."): + if file_exists(".mcp.json") or config references MCP servers: assert file_exists(".mcp.json") - id: claude.7_runtime.agents.exists @@ -314,9 +315,8 @@ categories: points: 1 check: | # Only required if plugin defines agents - if has_agent_definitions("."): - agents = glob("agents/*.md") - assert len(agents) > 0 + if dir_exists("agents/"): + assert glob("agents/*.md") is non-empty - id: claude.7_runtime.agents.frontmatter type: checkable From 776af79396dc0a1ee314f888e15dae4fecdf3cc4 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 22:01:37 +1000 Subject: [PATCH 22/43] Mark rubric check alignment items 35-39 as fixed in reconciliation matrix All verification checks pass: zero stale tool_name() args, zero opaque functions, zero retired synonyms, pseudocode-principles doc created. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/reconciliation-matrix.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/reconciliation-matrix.md b/docs/reconciliation-matrix.md index f50eb78..0a1c6db 100644 --- a/docs/reconciliation-matrix.md +++ b/docs/reconciliation-matrix.md @@ -389,13 +389,12 @@ in `docs/research/per-platform-context-loading.md` (#12). ### Rubric Check Alignment -35. **Rubric YAMLs**: `tool_name()` called with tool names instead of canonical Operations (4 instances) — Needs fix -36. **claude-code.yaml**: `supported_tools()` comment lists tool names not canonical ops — Needs fix -37. **claude-code.yaml**: 7 opaque utility functions should be inlined as prose pseudocode — Needs fix -38. **All rubrics**: Synonym duplication (`find_file`/`find_first`, `count`/`len`, `field_present`/`in`) — Needs fix -39. **lib/patterns/pseudocode-principles.md**: Decision boundary doc missing — Needs creation +35. ~~**Rubric YAMLs**: `tool_name()` called with tool names instead of canonical Operations (4 instances)~~ Fixed — all use canonical Operation enum values +36. ~~**claude-code.yaml**: `supported_tools()` comment lists tool names not canonical ops~~ Fixed — lists canonical operations +37. ~~**claude-code.yaml**: 7 opaque utility functions should be inlined as prose pseudocode~~ Fixed — all inlined +38. ~~**All rubrics**: Synonym duplication (`find_file`/`find_first`, `count`/`len`, `field_present`/`in`)~~ Fixed — unified to canonical names +39. ~~**lib/patterns/pseudocode-principles.md**: Decision boundary doc missing~~ Fixed — created with three-tier framework ### Verification status -Tiers 1-4 and platform API restructure: complete, zero gaps. -Rubric check alignment (items 35-39): open, spec written, implementation pending. +All tiers, platform API restructure, and rubric check alignment: complete, zero gaps. From a4cb73caaaf821debbdc83e5f7eb875930be566f Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 22:11:00 +1000 Subject: [PATCH 23/43] Unify retired synonyms in pattern files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit directory_exists→dir_exists in manifest-generation.md count→len in injection-checks.md Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/injection-checks.md | 2 +- lib/patterns/manifest-generation.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/patterns/injection-checks.md b/lib/patterns/injection-checks.md index cd2e926..9e8897f 100644 --- a/lib/patterns/injection-checks.md +++ b/lib/patterns/injection-checks.md @@ -80,7 +80,7 @@ CHECK_INJECTION_COMPONENTS(computed): RETURN results COMPUTE_INJECTION_SUMMARY(results): - present = count(r for r in results if r.status == "PRESENT") + present = len(r for r in results if r.status == "PRESENT") total = len(results) IF present == total: RETURN "COMPLETE" diff --git a/lib/patterns/manifest-generation.md b/lib/patterns/manifest-generation.md index 730ca72..34a1038 100644 --- a/lib/patterns/manifest-generation.md +++ b/lib/patterns/manifest-generation.md @@ -22,9 +22,9 @@ Applies to: `cursor-plugin` RENDER_WITH_CONDITIONALS(template, metadata, computed): content = substitute(template, metadata) parsed = JSON.parse(content) - IF NOT directory_exists("agents/"): + IF NOT dir_exists("agents/"): delete parsed["agents"] - IF NOT directory_exists("commands/"): + IF NOT dir_exists("commands/"): delete parsed["commands"] IF NOT file_exists("hooks/hooks.json") AND NOT file_exists("hooks/hooks-cursor.json"): delete parsed["hooks"] From e87beb0e396ee3e01408d10243acb8e182a1275f Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 22:23:57 +1000 Subject: [PATCH 24/43] Add pattern deduplication design spec 9 hardcoded platform data instances across 5 pattern files to replace with REGISTRY lookups. 2 new reverse-lookup helpers for platform-api.md. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...2026-04-27-pattern-deduplication-design.md | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md diff --git a/docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md b/docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md new file mode 100644 index 0000000..cf40281 --- /dev/null +++ b/docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md @@ -0,0 +1,246 @@ +# Pattern File Platform Deduplication + +> **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 9 hardcoded platform data instances in pattern files with REGISTRY lookups, so platform facts are stated once (in PlatformSpec) and consumed everywhere. + +**Architecture:** Add 2 reverse-lookup helpers to platform-api.md. Replace inline tables and hardcoded lists in 5 pattern files with REGISTRY-derived expressions. Executable bash templates stay as-is — only pseudocode and prose commentary change. + +**Tech Stack:** Markdown pseudocode, YAML-adjacent structured data in platform-api.md. + +--- + +## New platform-api.md Functions + +Two reverse-lookup helpers needed by inventory.md: + +```pseudocode +FUNCTION platform_for_spec(filename) + RETURNS platform ID from a spec filename like "codex.md" → "codex" + FOR EACH pid IN REGISTRY: + IF filename == pid + ".md": RETURN pid + +FUNCTION platform_for_hooks(path) + RETURNS platform ID from a hooks file path + IF "cursor" IN path: RETURN "cursor" + RETURN "claude-code" +``` + +--- + +## Changes by File + +### hook-merging.md (3 changes) + +**1. Event Name Mapping table (lines 9-26)** + +Replace the 20-line cross-platform event mapping table and the Cursor-only / Codex-only event prose with a REGISTRY reference and lookup example. + +Current (lines 9-27): +``` +| Claude Code event | Cursor event | Codex event | Notes | +|---|---|---|---| +| `SessionStart` | `sessionStart` | `SessionStart` | | +[... 10 more rows ...] + +Cursor-only events (no Claude Code equivalent): +`sessionEnd`, `beforeShellExecution`, ... + +Codex-only events (no Claude Code equivalent): +`PermissionRequest` +``` + +Replace with: +``` +Event mappings are derived from REGISTRY. To get the platform-native name +for any canonical event: + + hook_event(platform, canonical_event) + +Example: hook_event("cursor", "session.start") → "sessionStart" + +For platform-specific events beyond the canonical set, see +REGISTRY[platform].hooks.extra_events. +``` + +**2. GENERATE_CODEX_HOOKS hardcoded event list (lines 102-103)** + +Current: +``` + codex_events = ["SessionStart", "PreToolUse", "PostToolUse", + "UserPromptSubmit", "Stop", "PermissionRequest"] +``` + +Replace with: +``` + codex_events = [entry.name FOR event, entry IN REGISTRY["codex"].hooks.events + WHERE entry.name IS NOT null] +``` + +**3. GENERATE_GEMINI_HOOK_GUIDANCE inline event map (lines 238-244)** + +Current: +``` + gemini_event_map = { + "SessionStart": "SessionStart", + "PreToolUse": "BeforeTool", + "PostToolUse": "AfterTool", + "PreCompact": "PreCompress", + "Stop": "AfterAgent", + } +``` + +Replace with: +``` + FOR canonical_event IN CanonicalEvent: + gemini_name = hook_event("gemini-cli", canonical_event) + IF gemini_name IS null: SKIP + claude_name = hook_event("claude-code", canonical_event) +``` + +And update the loop body (lines 250-265) to use `gemini_name` and +`claude_name` instead of looking up from the map. + +### inventory.md (3 changes) + +**4. manifest_checks hardcoded list (lines 28-38)** + +Current: +``` + 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" }, + ... + ] +``` + +Replace with: +``` + manifest_checks = [] + FOR pid, spec IN REGISTRY: + IF spec.manifest.path IS NOT null: + manifest_checks.append({ platform: pid, path: spec.manifest.path }) +``` + +Note: The current list includes marketplace paths (e.g. `.claude-plugin/marketplace.json`) +that are not in `REGISTRY[platform].manifest.path`. These need to be captured. Options: +- The marketplace paths can be derived from the manifest path's parent directory +- Or add a `marketplace_path` field check alongside the manifest check + +The simplest approach: keep a small supplementary list for marketplace paths that +aren't derivable from REGISTRY, since only 3 platforms have them and the paths +follow the convention `/marketplace.json`. + +**5. context_checks hardcoded list (lines 47-55)** + +Current: +``` + context_checks = [ + { platform: "claude-code", path: "CLAUDE.md" }, + { platform: "cursor", path: "AGENTS.md" }, + { platform: "gemini-cli", path: "GEMINI.md" }, + ... + ] +``` + +Replace with: +``` + context_checks = [] + FOR pid, spec IN REGISTRY: + context_checks.append({ platform: pid, path: spec.context.primary_file }) +``` + +Note: The current list has duplicate entries — `AGENTS.md` appears for cursor, +codex, antigravity, and openclaw. The REGISTRY-derived version naturally +deduplicates (each platform contributes one entry). The inventory loop that +consumes this list checks file existence per-platform, so duplicates were +harmless but unnecessary. + +**6. spec_platform() and hook_platform() helpers (lines 156-159)** + +Current: +``` +| `spec_platform(file)` | `"gemini-cli.md" → "gemini-cli"`, `"codex.md" → "codex"` | +| `hook_platform(path)` | `"hooks.json" → "claude-code"`, `"hooks-cursor.json" → "cursor"` | +``` + +Replace references in the pseudocode (line 143, 146) with calls to +`platform_for_spec(filename)` and `platform_for_hooks(path)` — the two +new functions added to platform-api.md. Update the Helper References table +to point to platform-api.md instead of defining inline. + +### injection-checks.md (2 changes) + +**7. Hardcoded "SessionStart" (line 52)** + +Current: +``` + IF content contains "SessionStart" AND content contains "session-start": +``` + +Replace with: +``` + event_name = hook_event("claude-code", "session.start") + IF content contains event_name AND content contains "session-start": +``` + +**8. Hardcoded "sessionStart" (line 62)** + +Current: +``` + IF content contains "sessionStart" AND content contains "session-start": +``` + +Replace with: +``` + event_name = hook_event("cursor", "session.start") + IF content contains event_name AND content contains "session-start": +``` + +### bootstrapping.md (1 change) + +**9. Hook output format comment (lines 128-130)** + +Current comment block: +``` +# Output context injection as JSON. +# Cursor hooks expect additional_context (snake_case). +# Claude Code hooks expect hookSpecificOutput.additionalContext (nested). +# Other platforms expect additionalContext (top-level, SDK standard). +``` + +Replace with: +``` +# Output format per platform — see REGISTRY[platform].hooks.output_key +# and REGISTRY[platform].hooks.structure for the authoritative mapping. +``` + +The bash code itself (lines 131-137) stays unchanged — it's executable +code, not pseudocode. + +--- + +## What Does NOT Change + +- **detection-algorithm.md** — `source_definitions` field lists serve metadata discovery (what *can* we extract?), not validation (what *must* be present?). Different purpose from `manifest.required_fields`. +- **manifest-generation.md** — Template rendering logic and builder functions. No platform data duplication. +- **publishing-and-discoverability.md** — Pure prose reference with no pseudocode. +- **report-template.md** — Template structure only. +- **subagent-dispatch.md** — Codex-specific patterns, already correct. +- **pseudocode-principles.md** — Meta-doc, no platform data. +- **Executable bash templates** — Real code stays as-is; only pseudocode and prose commentary change. + +--- + +## Verification + +After all edits: + +1. `grep -cP '^\| .*(SessionStart|PreToolUse|PostToolUse).*\|' lib/patterns/hook-merging.md` returns 0 (no inline mapping table rows) +2. `grep -n 'codex_events = \[' lib/patterns/hook-merging.md` shows REGISTRY-derived expression +3. `grep -n 'gemini_event_map' lib/patterns/hook-merging.md` returns 0 +4. `grep -c 'platform:.*path:' lib/patterns/inventory.md` returns 0 (no hardcoded check entries) +5. `grep -n 'spec_platform\|hook_platform' lib/patterns/inventory.md` returns 0 (replaced with platform_for_spec/platform_for_hooks) +6. `grep -n 'platform_for_spec\|platform_for_hooks' lib/references/platform-api.md` shows both new functions +7. Hardcoded event names in injection-checks.md replaced with hook_event() calls From f41695c8924b65f51c27db8361d67a121f36b879 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:19:00 +1000 Subject: [PATCH 25/43] Fix pattern deduplication spec per Codex adversarial review - Include extra_events in Codex hook allowlist derivation - Add marketplace_path to PlatformSpec.manifest instead of supplementary list - Derive context_checks from both primary_file and secondary_files - Add GEMINI.md to Antigravity secondary_files as prerequisite Co-Authored-By: Claude Opus 4.6 (1M context) --- ...2026-04-27-pattern-deduplication-design.md | 58 ++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md b/docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md index cf40281..c835668 100644 --- a/docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md +++ b/docs/superpowers/specs/2026-04-27-pattern-deduplication-design.md @@ -28,6 +28,41 @@ FUNCTION platform_for_hooks(path) --- +## PlatformSpec Prerequisites + +Before pattern file edits, update the type system and platform data: + +**1. Add `marketplace_path` to PlatformSpec.manifest in platform-api.md:** +``` +manifest: { + path: string | null, + marketplace_path: string | null, # NEW + required_fields: List[string], +} +``` + +Populate in platform spec files: +- claude-code: `".claude-plugin/marketplace.json"` +- cursor: `".cursor-plugin/marketplace.json"` +- codex: `".agents/plugins/marketplace.json"` +- gemini-cli, antigravity, openclaw: `null` + +**2. Add `GEMINI.md` to Antigravity secondary_files:** + +In `lib/references/platforms/antigravity.md`, change: +``` +secondary_files: [".agents/rules/*.md"], +``` +to: +``` +secondary_files: ["GEMINI.md", ".agents/rules/*.md"], +``` + +This moves `GEMINI.md` from prose-only `priority_note` to machine-readable +`secondary_files`, enabling REGISTRY-derived context checks to capture it. + +--- + ## Changes by File ### hook-merging.md (3 changes) @@ -75,6 +110,7 @@ Replace with: ``` codex_events = [entry.name FOR event, entry IN REGISTRY["codex"].hooks.events WHERE entry.name IS NOT null] + + [entry.name FOR entry IN REGISTRY["codex"].hooks.extra_events] ``` **3. GENERATE_GEMINI_HOOK_GUIDANCE inline event map (lines 238-244)** @@ -121,16 +157,11 @@ Replace with: FOR pid, spec IN REGISTRY: IF spec.manifest.path IS NOT null: manifest_checks.append({ platform: pid, path: spec.manifest.path }) + IF spec.manifest.marketplace_path IS NOT null: + manifest_checks.append({ platform: pid, path: spec.manifest.marketplace_path }) ``` -Note: The current list includes marketplace paths (e.g. `.claude-plugin/marketplace.json`) -that are not in `REGISTRY[platform].manifest.path`. These need to be captured. Options: -- The marketplace paths can be derived from the manifest path's parent directory -- Or add a `marketplace_path` field check alongside the manifest check - -The simplest approach: keep a small supplementary list for marketplace paths that -aren't derivable from REGISTRY, since only 3 platforms have them and the paths -follow the convention `/marketplace.json`. +See PlatformSpec Prerequisites section above for the `marketplace_path` addition. **5. context_checks hardcoded list (lines 47-55)** @@ -149,13 +180,14 @@ Replace with: context_checks = [] FOR pid, spec IN REGISTRY: context_checks.append({ platform: pid, path: spec.context.primary_file }) + FOR secondary IN spec.context.secondary_files: + context_checks.append({ platform: pid, path: secondary }) ``` -Note: The current list has duplicate entries — `AGENTS.md` appears for cursor, -codex, antigravity, and openclaw. The REGISTRY-derived version naturally -deduplicates (each platform contributes one entry). The inventory loop that -consumes this list checks file existence per-platform, so duplicates were -harmless but unnecessary. +This captures Antigravity's `GEMINI.md` requirement (stored in `secondary_files`) +and any future secondary context files. The current hardcoded list explicitly +includes both `AGENTS.md` and `GEMINI.md` for Antigravity — the REGISTRY-derived +version preserves this via `secondary_files`. **6. spec_platform() and hook_platform() helpers (lines 156-159)** From 23dfcc6b4fefcca5f5a0e94d222b5f61310c9c41 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:21:50 +1000 Subject: [PATCH 26/43] Add pattern deduplication implementation plan 9 tasks: PlatformSpec prerequisites, 2 new API functions, 5 pattern file edits, verification + reconciliation matrix update. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../plans/2026-04-27-pattern-deduplication.md | 751 ++++++++++++++++++ 1 file changed, 751 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-27-pattern-deduplication.md diff --git a/docs/superpowers/plans/2026-04-27-pattern-deduplication.md b/docs/superpowers/plans/2026-04-27-pattern-deduplication.md new file mode 100644 index 0000000..41cd4fb --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-pattern-deduplication.md @@ -0,0 +1,751 @@ +# Pattern Deduplication 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 9 hardcoded platform data instances in pattern files with REGISTRY lookups, adding 2 new API functions and 1 new PlatformSpec field. + +**Architecture:** Prerequisites first (PlatformSpec changes), then pattern file edits in dependency order, then verification. All edits are text replacements in markdown pseudocode — no executable code changes. + +**Tech Stack:** Markdown, pseudocode. + +--- + +## File Map + +| File | Action | What changes | +|------|--------|-------------| +| `lib/references/platform-api.md` | Modify | Add `marketplace_path` to manifest type, add 2 reverse-lookup functions | +| `lib/references/platforms/claude-code.md` | Modify | Add `marketplace_path` field | +| `lib/references/platforms/cursor.md` | Modify | Add `marketplace_path` field | +| `lib/references/platforms/codex.md` | Modify | Add `marketplace_path` field | +| `lib/references/platforms/gemini-cli.md` | Modify | Add `marketplace_path: null` | +| `lib/references/platforms/antigravity.md` | Modify | Add `marketplace_path: null`, add GEMINI.md to secondary_files | +| `lib/references/platforms/openclaw.md` | Modify | Add `marketplace_path: null` | +| `lib/patterns/hook-merging.md` | Modify | Replace event mapping table, codex event list, gemini event map | +| `lib/patterns/inventory.md` | Modify | Replace manifest_checks, context_checks, helper references | +| `lib/patterns/injection-checks.md` | Modify | Replace hardcoded event names with hook_event() calls | +| `lib/patterns/bootstrapping.md` | Modify | Replace hook output format comment | +| `docs/reconciliation-matrix.md` | Modify | Add items for pattern deduplication | + +--- + +### Task 1: Add marketplace_path to PlatformSpec type + +**Files:** +- Modify: `lib/references/platform-api.md:58-62` + +- [ ] **Step 1: Add marketplace_path field to the manifest type** + +In `lib/references/platform-api.md`, find: + +``` + # ── Manifest ── + manifest: { + path: string | null, + required_fields: List[string], + }, +``` + +Replace with: + +``` + # ── Manifest ── + manifest: { + path: string | null, + marketplace_path: string | null, + required_fields: List[string], + }, +``` + +- [ ] **Step 2: Verify** + +Run: `grep 'marketplace_path' lib/references/platform-api.md` +Expected: one line containing `marketplace_path` + +- [ ] **Step 3: Commit** + +```bash +git add lib/references/platform-api.md +git commit -m "Add marketplace_path to PlatformSpec.manifest type" +``` + +--- + +### Task 2: Add reverse-lookup functions to platform-api.md + +**Files:** +- Modify: `lib/references/platform-api.md:191-193` (after last function) + +- [ ] **Step 1: Add the two new functions** + +In `lib/references/platform-api.md`, find the last line of the file (line 193, which is the closing triple-backtick of the Lookup Functions code block): + +```` + strip_fields: tgt.frontmatter.strip + model_format: tgt.frontmatter.model_format +``` +```` + +Replace with: + +```` + strip_fields: tgt.frontmatter.strip + model_format: tgt.frontmatter.model_format + +# ── Reverse lookups ── + +FUNCTION platform_for_spec(filename) + RETURNS platform ID from a spec filename like "codex.md" → "codex". + FOR EACH pid IN REGISTRY: + IF filename == pid + ".md": RETURN pid + +FUNCTION platform_for_hooks(path) + RETURNS platform ID from a hooks file path. + IF "cursor" IN path: RETURN "cursor" + RETURN "claude-code" +``` +```` + +- [ ] **Step 2: Verify** + +Run: `grep -c 'FUNCTION platform_for' lib/references/platform-api.md` +Expected: `2` + +- [ ] **Step 3: Commit** + +```bash +git add lib/references/platform-api.md +git commit -m "Add platform_for_spec and platform_for_hooks reverse-lookup functions" +``` + +--- + +### Task 3: Populate marketplace_path in all 6 platform spec files + +**Files:** +- Modify: `lib/references/platforms/claude-code.md` +- Modify: `lib/references/platforms/cursor.md` +- Modify: `lib/references/platforms/codex.md` +- Modify: `lib/references/platforms/gemini-cli.md` +- Modify: `lib/references/platforms/antigravity.md` +- Modify: `lib/references/platforms/openclaw.md` + +- [ ] **Step 1: claude-code.md — add marketplace_path** + +Find: + +``` + manifest: { + path: ".claude-plugin/plugin.json", + required_fields: ["name", "version", "description", "author.name", "author.email"], + }, +``` + +Replace with: + +``` + manifest: { + path: ".claude-plugin/plugin.json", + marketplace_path: ".claude-plugin/marketplace.json", + required_fields: ["name", "version", "description", "author.name", "author.email"], + }, +``` + +- [ ] **Step 2: cursor.md — add marketplace_path** + +Find: + +``` + manifest: { + path: ".cursor-plugin/plugin.json", + required_fields: ["name", "displayName", "description", "version", "author"], + }, +``` + +Replace with: + +``` + manifest: { + path: ".cursor-plugin/plugin.json", + marketplace_path: ".cursor-plugin/marketplace.json", + required_fields: ["name", "displayName", "description", "version", "author"], + }, +``` + +- [ ] **Step 3: codex.md — add marketplace_path** + +Find: + +``` + manifest: { + path: ".codex-plugin/plugin.json", + required_fields: ["name", "version", "description"], + }, +``` + +Replace with: + +``` + manifest: { + path: ".codex-plugin/plugin.json", + marketplace_path: ".agents/plugins/marketplace.json", + required_fields: ["name", "version", "description"], + }, +``` + +- [ ] **Step 4: gemini-cli.md — add marketplace_path (null)** + +Find: + +``` + manifest: { + path: "gemini-extension.json", + required_fields: ["name", "version", "description", "contextFileName"], + }, +``` + +Replace with: + +``` + manifest: { + path: "gemini-extension.json", + marketplace_path: null, + required_fields: ["name", "version", "description", "contextFileName"], + }, +``` + +- [ ] **Step 5: antigravity.md — add marketplace_path (null) and add GEMINI.md to secondary_files** + +Find: + +``` + manifest: { + path: "package.json", + required_fields: ["name", "displayName", "version", "description", "publisher"], + }, +``` + +Replace with: + +``` + manifest: { + path: "package.json", + marketplace_path: null, + required_fields: ["name", "displayName", "version", "description", "publisher"], + }, +``` + +Also find: + +``` + secondary_files: [".agents/rules/*.md"], +``` + +Replace with: + +``` + secondary_files: ["GEMINI.md", ".agents/rules/*.md"], +``` + +- [ ] **Step 6: openclaw.md — add marketplace_path (null)** + +Find: + +``` + manifest: { + path: "openclaw.plugin.json", + required_fields: ["id", "configSchema"], + }, +``` + +Replace with: + +``` + manifest: { + path: "openclaw.plugin.json", + marketplace_path: null, + required_fields: ["id", "configSchema"], + }, +``` + +- [ ] **Step 7: Verify** + +Run: `grep -c 'marketplace_path' lib/references/platforms/*.md` +Expected: each file shows 1 + +Run: `grep 'secondary_files' lib/references/platforms/antigravity.md` +Expected: contains `"GEMINI.md"` + +- [ ] **Step 8: Commit** + +```bash +git add lib/references/platforms/*.md +git commit -m "Add marketplace_path to all platform specs, GEMINI.md to Antigravity secondary_files" +``` + +--- + +### Task 4: Replace event mapping table and codex event list in hook-merging.md + +**Files:** +- Modify: `lib/patterns/hook-merging.md:7-26,102-103` + +- [ ] **Step 1: Replace event name mapping section (lines 7-26)** + +Find: + +``` +## Event Name Mapping + +| Claude Code event | Cursor event | Codex event | Notes | +|---|---|---|---| +| `SessionStart` | `sessionStart` | `SessionStart` | | +| `PreToolUse` | `preToolUse` | `PreToolUse` | | +| `PostToolUse` | `postToolUse` | `PostToolUse` | | +| `PostToolUseFailure` | `postToolUseFailure` | (N/A) | | +| `SubagentStart` | `subagentStart` | (N/A) | | +| `SubagentStop` | `subagentStop` | (N/A) | | +| `PreCompact` | `preCompact` | (N/A) | | +| `Stop` | `stop` | `Stop` | | +| `UserPromptSubmit` | `beforeSubmitPrompt` | `UserPromptSubmit` | | +| (N/A) | (N/A) | `PermissionRequest` | Codex-only | + +Cursor-only events (no Claude Code equivalent): +`sessionEnd`, `beforeShellExecution`, `afterShellExecution`, `beforeMCPExecution`, `afterMCPExecution`, `beforeReadFile`, `afterFileEdit`, `afterAgentResponse`, `afterAgentThought` + +Codex-only events (no Claude Code equivalent): +`PermissionRequest` +``` + +Replace with: + +``` +## Event Name Mapping + +Event mappings are derived from REGISTRY. To get the platform-native name +for any canonical event: + + hook_event(platform, canonical_event) + +Example: hook_event("cursor", "session.start") → "sessionStart" + +For platform-specific events beyond the canonical set, see +REGISTRY[platform].hooks.extra_events. +``` + +- [ ] **Step 2: Replace codex_events hardcoded list (lines 102-103)** + +Find: + +``` + codex_events = ["SessionStart", "PreToolUse", "PostToolUse", + "UserPromptSubmit", "Stop", "PermissionRequest"] +``` + +Replace with: + +``` + codex_events = [entry.name FOR event, entry IN REGISTRY["codex"].hooks.events + WHERE entry.name IS NOT null] + + [entry.name FOR entry IN REGISTRY["codex"].hooks.extra_events] +``` + +- [ ] **Step 3: Verify** + +Run: `grep -cP '^\| .*(SessionStart|PreToolUse|PostToolUse).*\|' lib/patterns/hook-merging.md` +Expected: `0` + +Run: `grep -n 'codex_events' lib/patterns/hook-merging.md` +Expected: shows REGISTRY-derived expression + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/hook-merging.md +git commit -m "Replace event mapping table and codex event list with REGISTRY lookups" +``` + +--- + +### Task 5: Replace gemini event map in hook-merging.md + +**Files:** +- Modify: `lib/patterns/hook-merging.md:237-265` + +- [ ] **Step 1: Replace the gemini event map and loop** + +Find: + +``` +GENERATE_GEMINI_HOOK_GUIDANCE(claude_hooks): + gemini_event_map = { + "SessionStart": "SessionStart", + "PreToolUse": "BeforeTool", + "PostToolUse": "AfterTool", + "PreCompact": "PreCompress", + "Stop": "AfterAgent", + } + + guidance = "### Gemini CLI Hook Configuration\n\n" + guidance += "Add the following to your `~/.gemini/settings.json`:\n\n" + guidance += "```json\n{\n \"hooks\": {\n" + + FOR event, entries IN claude_hooks.hooks: + IF event NOT IN gemini_event_map: + SKIP + gemini_event = gemini_event_map[event] +``` + +Replace with: + +``` +GENERATE_GEMINI_HOOK_GUIDANCE(claude_hooks): + guidance = "### Gemini CLI Hook Configuration\n\n" + guidance += "Add the following to your `~/.gemini/settings.json`:\n\n" + guidance += "```json\n{\n \"hooks\": {\n" + + FOR canonical_event IN CanonicalEvent: + gemini_name = hook_event("gemini-cli", canonical_event) + IF gemini_name IS null: SKIP + claude_name = hook_event("claude-code", canonical_event) + IF claude_name NOT IN claude_hooks.hooks: SKIP + entries = claude_hooks.hooks[claude_name] +``` + +- [ ] **Step 2: Update the loop body variable reference** + +Find (in the line after the replacement above): + +``` + FOR entry IN entries: + guidance += ' "' + gemini_event + '": [{\n' +``` + +Replace with: + +``` + FOR entry IN entries: + guidance += ' "' + gemini_name + '": [{\n' +``` + +- [ ] **Step 3: Verify** + +Run: `grep -n 'gemini_event_map' lib/patterns/hook-merging.md` +Expected: no output + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/hook-merging.md +git commit -m "Replace gemini event map with REGISTRY-derived hook_event() lookups" +``` + +--- + +### Task 6: Replace hardcoded lists in inventory.md + +**Files:** +- Modify: `lib/patterns/inventory.md:26-60,143,146,152-159` + +- [ ] **Step 1: Replace manifest_checks (lines 26-38)** + +Find: + +``` + ## 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" }, + ] +``` + +Replace with: + +``` + ## 2.2 Check Platform Manifests + ## Derive paths from REGISTRY. Record { platform, path, status }. + manifest_checks = [] + FOR pid, spec IN REGISTRY: + IF spec.manifest.path IS NOT null: + manifest_checks.append({ platform: pid, path: spec.manifest.path }) + IF spec.manifest.marketplace_path IS NOT null: + manifest_checks.append({ platform: pid, path: spec.manifest.marketplace_path }) +``` + +- [ ] **Step 2: Replace context_checks (lines 45-55)** + +Find: + +``` + ## 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" }, + ] +``` + +Replace with: + +``` + ## 2.3 Check Context Files + ## Derive from REGISTRY primary_file + secondary_files. + context_checks = [] + FOR pid, spec IN REGISTRY: + context_checks.append({ platform: pid, path: spec.context.primary_file }) + FOR secondary IN spec.context.secondary_files: + context_checks.append({ platform: pid, path: secondary }) +``` + +- [ ] **Step 3: Replace spec_platform and hook_platform calls (lines 143, 146)** + +Find: + +``` + computed.existing_files.append({ path: p, platform: spec_platform(r.file) }) +``` + +Replace with: + +``` + computed.existing_files.append({ path: p, platform: platform_for_spec(r.file) }) +``` + +Find: + +``` + computed.existing_files.append({ path: r.path, platform: hook_platform(r.path) }) +``` + +Replace with: + +``` + computed.existing_files.append({ path: r.path, platform: platform_for_hooks(r.path) }) +``` + +- [ ] **Step 4: Update Helper References table (lines 152-159)** + +Find: + +``` +## Helper References + +| Helper | Defined in | +|--------|-----------| +| `parse_yaml_frontmatter` | inline — read between `---` markers | +| `check_injection_components` | `lib/patterns/injection-checks.md` | +| `spec_platform(file)` | `"gemini-cli.md" → "gemini-cli"`, `"codex.md" → "codex"` | +| `hook_platform(path)` | `"hooks.json" → "claude-code"`, `"hooks-cursor.json" → "cursor"` | +``` + +Replace with: + +``` +## Helper References + +| Helper | Defined in | +|--------|-----------| +| `parse_yaml_frontmatter` | inline — read between `---` markers | +| `check_injection_components` | `lib/patterns/injection-checks.md` | +| `platform_for_spec(file)` | `lib/references/platform-api.md` | +| `platform_for_hooks(path)` | `lib/references/platform-api.md` | +``` + +- [ ] **Step 5: Verify** + +Run: `grep -c 'platform:.*path:' lib/patterns/inventory.md` +Expected: `0` + +Run: `grep -n 'spec_platform\|hook_platform' lib/patterns/inventory.md` +Expected: no output + +Run: `grep -n 'platform_for_spec\|platform_for_hooks' lib/patterns/inventory.md` +Expected: two lines (one each) + +- [ ] **Step 6: Commit** + +```bash +git add lib/patterns/inventory.md +git commit -m "Replace hardcoded manifest/context lists and helpers with REGISTRY lookups" +``` + +--- + +### Task 7: Replace hardcoded event names in injection-checks.md + +**Files:** +- Modify: `lib/patterns/injection-checks.md:50-67` + +- [ ] **Step 1: Replace SessionStart check (lines 50-57)** + +Find: + +``` + # 5. hooks.json SessionStart entry + IF file_exists("hooks/hooks.json"): + content = Read("hooks/hooks.json") + IF content contains "SessionStart" AND content contains "session-start": + results.append({ component: "hooks/hooks.json (SessionStart)", status: "PRESENT" }) + ELSE: + results.append({ component: "hooks/hooks.json (SessionStart)", status: "MISSING" }) + ELSE: + results.append({ component: "hooks/hooks.json (SessionStart)", status: "MISSING" }) +``` + +Replace with: + +``` + # 5. hooks.json SessionStart entry + claude_event = hook_event("claude-code", "session.start") + IF file_exists("hooks/hooks.json"): + content = Read("hooks/hooks.json") + IF content contains claude_event AND content contains "session-start": + results.append({ component: "hooks/hooks.json (" + claude_event + ")", status: "PRESENT" }) + ELSE: + results.append({ component: "hooks/hooks.json (" + claude_event + ")", status: "MISSING" }) + ELSE: + results.append({ component: "hooks/hooks.json (" + claude_event + ")", status: "MISSING" }) +``` + +- [ ] **Step 2: Replace sessionStart check (lines 59-67)** + +Find: + +``` + # 6. hooks-cursor.json sessionStart entry + IF file_exists("hooks/hooks-cursor.json"): + content = Read("hooks/hooks-cursor.json") + IF content contains "sessionStart" AND content contains "session-start": + results.append({ component: "hooks/hooks-cursor.json (sessionStart)", status: "PRESENT" }) + ELSE: + results.append({ component: "hooks/hooks-cursor.json (sessionStart)", status: "MISSING" }) + ELSE: + results.append({ component: "hooks/hooks-cursor.json (sessionStart)", status: "MISSING" }) +``` + +Replace with: + +``` + # 6. hooks-cursor.json sessionStart entry + cursor_event = hook_event("cursor", "session.start") + IF file_exists("hooks/hooks-cursor.json"): + content = Read("hooks/hooks-cursor.json") + IF content contains cursor_event AND content contains "session-start": + results.append({ component: "hooks/hooks-cursor.json (" + cursor_event + ")", status: "PRESENT" }) + ELSE: + results.append({ component: "hooks/hooks-cursor.json (" + cursor_event + ")", status: "MISSING" }) + ELSE: + results.append({ component: "hooks/hooks-cursor.json (" + cursor_event + ")", status: "MISSING" }) +``` + +- [ ] **Step 3: Verify** + +Run: `grep -n '"SessionStart"\|"sessionStart"' lib/patterns/injection-checks.md` +Expected: no output (all replaced with hook_event() calls) + +- [ ] **Step 4: Commit** + +```bash +git add lib/patterns/injection-checks.md +git commit -m "Replace hardcoded hook event names with hook_event() lookups" +``` + +--- + +### Task 8: Replace hook output comment in bootstrapping.md + +**Files:** +- Modify: `lib/patterns/bootstrapping.md:127-130` + +- [ ] **Step 1: Replace the comment block** + +Find: + +``` +# Output context injection as JSON. +# Cursor hooks expect additional_context (snake_case). +# Claude Code hooks expect hookSpecificOutput.additionalContext (nested). +# Other platforms expect additionalContext (top-level, SDK standard). +``` + +Replace with: + +``` +# Output format per platform — see REGISTRY[platform].hooks.output_key +# and REGISTRY[platform].hooks.structure for the authoritative mapping. +``` + +- [ ] **Step 2: Verify** + +Run: `grep -n 'additional_context (snake_case)\|hookSpecificOutput.additionalContext (nested)' lib/patterns/bootstrapping.md` +Expected: no output + +- [ ] **Step 3: Commit** + +```bash +git add lib/patterns/bootstrapping.md +git commit -m "Replace inline hook output format comment with REGISTRY reference" +``` + +--- + +### Task 9: Final verification and reconciliation matrix + +- [ ] **Step 1: Run all verification commands** + +```bash +echo "=== 1. No event mapping table rows ===" && grep -cP '^\| .*(SessionStart|PreToolUse|PostToolUse).*\|' lib/patterns/hook-merging.md ; echo "=== 2. codex_events REGISTRY ===" && grep -n 'codex_events' lib/patterns/hook-merging.md ; echo "=== 3. No gemini_event_map ===" && grep -n 'gemini_event_map' lib/patterns/hook-merging.md ; echo "=== 4. No hardcoded manifest checks ===" && grep -c 'platform:.*path:' lib/patterns/inventory.md ; echo "=== 5. No old helpers ===" && grep -n 'spec_platform\|hook_platform' lib/patterns/inventory.md ; echo "=== 6. New functions in API ===" && grep -n 'platform_for_spec\|platform_for_hooks' lib/references/platform-api.md ; echo "=== 7. No hardcoded event names in injection-checks ===" && grep -n '"SessionStart"\|"sessionStart"' lib/patterns/injection-checks.md ; echo "=== 8. marketplace_path in all specs ===" && grep -c 'marketplace_path' lib/references/platforms/*.md ; echo "=== DONE ===" +``` + +Expected: checks 1,3,4,5,7 return 0/no output; check 2 shows REGISTRY expression; check 6 shows 2 functions; check 8 shows 1 per file. + +- [ ] **Step 2: Add reconciliation matrix items** + +In `docs/reconciliation-matrix.md`, find: + +``` +### Verification status + +All tiers, platform API restructure, and rubric check alignment: complete, zero gaps. +``` + +Replace with: + +``` +### Pattern Deduplication + +40. ~~**hook-merging.md**: 20-line event mapping table duplicated REGISTRY data~~ Fixed — replaced with hook_event() reference +41. ~~**hook-merging.md**: Hardcoded codex_events list~~ Fixed — derived from REGISTRY canonical + extra_events +42. ~~**hook-merging.md**: Inline gemini_event_map dictionary~~ Fixed — replaced with hook_event() lookups +43. ~~**inventory.md**: Hardcoded manifest_checks list~~ Fixed — derived from REGISTRY manifest.path + marketplace_path +44. ~~**inventory.md**: Hardcoded context_checks list~~ Fixed — derived from REGISTRY primary_file + secondary_files +45. ~~**inventory.md**: Inline spec_platform/hook_platform helpers~~ Fixed — replaced with platform_for_spec/platform_for_hooks from platform-api.md +46. ~~**injection-checks.md**: Hardcoded "SessionStart"/"sessionStart" strings~~ Fixed — replaced with hook_event() calls +47. ~~**bootstrapping.md**: Inline hook output format documentation~~ Fixed — references REGISTRY[platform].hooks.output_key +48. ~~**PlatformSpec**: Missing marketplace_path field~~ Fixed — added to type and all 6 platform specs +49. ~~**antigravity.md**: GEMINI.md in priority_note prose, not secondary_files~~ Fixed — added to secondary_files + +### Verification status + +All tiers, platform API restructure, rubric check alignment, and pattern deduplication: complete, zero gaps. +``` + +- [ ] **Step 3: Commit** + +```bash +git add docs/reconciliation-matrix.md +git commit -m "Mark pattern deduplication items 40-49 as fixed in reconciliation matrix" +``` From 5a68aa7036ad984b13763978e5a9762cfc08a27b Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:32:04 +1000 Subject: [PATCH 27/43] Add marketplace_path to PlatformSpec and reverse-lookup functions - marketplace_path field in manifest type - platform_for_spec() and platform_for_hooks() for inventory lookups Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/references/platform-api.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/references/platform-api.md b/lib/references/platform-api.md index 86e7ab9..a788d71 100644 --- a/lib/references/platform-api.md +++ b/lib/references/platform-api.md @@ -57,8 +57,9 @@ TYPE PlatformSpec = { # ── Manifest ── manifest: { - path: string | null, - required_fields: List[string], + path: string | null, + marketplace_path: string | null, + required_fields: List[string], }, # ── Frontmatter ── @@ -190,4 +191,16 @@ FUNCTION diff_from(source, target) EMIT op strip_fields: tgt.frontmatter.strip model_format: tgt.frontmatter.model_format + +# ── Reverse lookups ── + +FUNCTION platform_for_spec(filename) + RETURNS platform ID from a spec filename like "codex.md" → "codex". + FOR EACH pid IN REGISTRY: + IF filename == pid + ".md": RETURN pid + +FUNCTION platform_for_hooks(path) + RETURNS platform ID from a hooks file path. + IF "cursor" IN path: RETURN "cursor" + RETURN "claude-code" ``` From 7b8fae0004bdb565271038b8d3ae86913fa2edcc Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:32:54 +1000 Subject: [PATCH 28/43] Add marketplace_path to all platform specs, GEMINI.md to Antigravity secondary_files Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/references/platforms/antigravity.md | 3 ++- lib/references/platforms/claude-code.md | 1 + lib/references/platforms/codex.md | 1 + lib/references/platforms/cursor.md | 1 + lib/references/platforms/gemini-cli.md | 1 + lib/references/platforms/openclaw.md | 1 + 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/references/platforms/antigravity.md b/lib/references/platforms/antigravity.md index 6a555f2..4df428b 100644 --- a/lib/references/platforms/antigravity.md +++ b/lib/references/platforms/antigravity.md @@ -69,7 +69,7 @@ REGISTRY["antigravity"] = { context: { primary_file: "AGENTS.md", - secondary_files: [".agents/rules/*.md"], + secondary_files: ["GEMINI.md", ".agents/rules/*.md"], priority_note: "GEMINI.md is also loaded if present (Antigravity-native)", }, @@ -85,6 +85,7 @@ REGISTRY["antigravity"] = { manifest: { path: "package.json", + marketplace_path: null, required_fields: ["name", "displayName", "version", "description", "publisher"], }, diff --git a/lib/references/platforms/claude-code.md b/lib/references/platforms/claude-code.md index 835f50a..41aee95 100644 --- a/lib/references/platforms/claude-code.md +++ b/lib/references/platforms/claude-code.md @@ -80,6 +80,7 @@ REGISTRY["claude-code"] = { manifest: { path: ".claude-plugin/plugin.json", + marketplace_path: ".claude-plugin/marketplace.json", required_fields: ["name", "version", "description", "author.name", "author.email"], }, diff --git a/lib/references/platforms/codex.md b/lib/references/platforms/codex.md index 0ccfd24..b1ef7bc 100644 --- a/lib/references/platforms/codex.md +++ b/lib/references/platforms/codex.md @@ -75,6 +75,7 @@ REGISTRY["codex"] = { manifest: { path: ".codex-plugin/plugin.json", + marketplace_path: ".agents/plugins/marketplace.json", required_fields: ["name", "version", "description"], }, diff --git a/lib/references/platforms/cursor.md b/lib/references/platforms/cursor.md index c92a1f0..326a69d 100644 --- a/lib/references/platforms/cursor.md +++ b/lib/references/platforms/cursor.md @@ -83,6 +83,7 @@ REGISTRY["cursor"] = { manifest: { path: ".cursor-plugin/plugin.json", + marketplace_path: ".cursor-plugin/marketplace.json", required_fields: ["name", "displayName", "description", "version", "author"], }, diff --git a/lib/references/platforms/gemini-cli.md b/lib/references/platforms/gemini-cli.md index 15397e8..917afb0 100644 --- a/lib/references/platforms/gemini-cli.md +++ b/lib/references/platforms/gemini-cli.md @@ -85,6 +85,7 @@ REGISTRY["gemini-cli"] = { manifest: { path: "gemini-extension.json", + marketplace_path: null, required_fields: ["name", "version", "description", "contextFileName"], }, diff --git a/lib/references/platforms/openclaw.md b/lib/references/platforms/openclaw.md index 879bac1..d8f4260 100644 --- a/lib/references/platforms/openclaw.md +++ b/lib/references/platforms/openclaw.md @@ -85,6 +85,7 @@ REGISTRY["openclaw"] = { manifest: { path: "openclaw.plugin.json", + marketplace_path: null, required_fields: ["id", "configSchema"], }, From eb9c6ffa90e9d39595dec8be8050ac3f32a184bb Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:34:14 +1000 Subject: [PATCH 29/43] Replace hardcoded event data with REGISTRY lookups in hook-merging.md Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/hook-merging.md | 52 +++++++++++++----------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/lib/patterns/hook-merging.md b/lib/patterns/hook-merging.md index ecef4a1..ac36530 100644 --- a/lib/patterns/hook-merging.md +++ b/lib/patterns/hook-merging.md @@ -6,24 +6,15 @@ Event name mapping and merge logic for porting hooks across platforms. ## Event Name Mapping -| Claude Code event | Cursor event | Codex event | Notes | -|---|---|---|---| -| `SessionStart` | `sessionStart` | `SessionStart` | | -| `PreToolUse` | `preToolUse` | `PreToolUse` | | -| `PostToolUse` | `postToolUse` | `PostToolUse` | | -| `PostToolUseFailure` | `postToolUseFailure` | (N/A) | | -| `SubagentStart` | `subagentStart` | (N/A) | | -| `SubagentStop` | `subagentStop` | (N/A) | | -| `PreCompact` | `preCompact` | (N/A) | | -| `Stop` | `stop` | `Stop` | | -| `UserPromptSubmit` | `beforeSubmitPrompt` | `UserPromptSubmit` | | -| (N/A) | (N/A) | `PermissionRequest` | Codex-only | - -Cursor-only events (no Claude Code equivalent): -`sessionEnd`, `beforeShellExecution`, `afterShellExecution`, `beforeMCPExecution`, `afterMCPExecution`, `beforeReadFile`, `afterFileEdit`, `afterAgentResponse`, `afterAgentThought` - -Codex-only events (no Claude Code equivalent): -`PermissionRequest` +Event mappings are derived from REGISTRY. To get the platform-native name +for any canonical event: + + hook_event(platform, canonical_event) + +Example: hook_event("cursor", "session.start") → "sessionStart" + +For platform-specific events beyond the canonical set, see +REGISTRY[platform].hooks.extra_events. --- @@ -99,8 +90,9 @@ GENERATE_CODEX_HOOKS(plugin_path): // The install docs handle that (see lib/templates/install-docs/codex.md). // Check for unmapped events - codex_events = ["SessionStart", "PreToolUse", "PostToolUse", - "UserPromptSubmit", "Stop", "PermissionRequest"] + codex_events = [entry.name FOR event, entry IN REGISTRY["codex"].hooks.events + WHERE entry.name IS NOT null] + + [entry.name FOR entry IN REGISTRY["codex"].hooks.extra_events] flags = [] for event in source.hooks: @@ -235,25 +227,19 @@ For standalone use, it generates guidance text for install docs. ```pseudocode GENERATE_GEMINI_HOOK_GUIDANCE(claude_hooks): - gemini_event_map = { - "SessionStart": "SessionStart", - "PreToolUse": "BeforeTool", - "PostToolUse": "AfterTool", - "PreCompact": "PreCompress", - "Stop": "AfterAgent", - } - guidance = "### Gemini CLI Hook Configuration\n\n" guidance += "Add the following to your `~/.gemini/settings.json`:\n\n" guidance += "```json\n{\n \"hooks\": {\n" - FOR event, entries IN claude_hooks.hooks: - IF event NOT IN gemini_event_map: - SKIP - gemini_event = gemini_event_map[event] + FOR canonical_event IN CanonicalEvent: + gemini_name = hook_event("gemini-cli", canonical_event) + IF gemini_name IS null: SKIP + claude_name = hook_event("claude-code", canonical_event) + IF claude_name NOT IN claude_hooks.hooks: SKIP + entries = claude_hooks.hooks[claude_name] FOR entry IN entries: - guidance += ' "' + gemini_event + '": [{\n' + guidance += ' "' + gemini_name + '": [{\n' IF entry.matcher: guidance += ' "matcher": "' + entry.matcher + '",\n' guidance += ' "sequential": true,\n' From ddbdc7f56ec1bb411e14f96b20e579a6b3d0356a Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:35:41 +1000 Subject: [PATCH 30/43] Replace hardcoded manifest/context lists and helpers with REGISTRY lookups Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/inventory.md | 43 ++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/lib/patterns/inventory.md b/lib/patterns/inventory.md index 223ccc7..f16de73 100644 --- a/lib/patterns/inventory.md +++ b/lib/patterns/inventory.md @@ -24,18 +24,13 @@ INVENTORY(plugin_path, computed): 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" }, - ] + ## Derive paths from REGISTRY. Record { platform, path, status }. + manifest_checks = [] + FOR pid, spec IN REGISTRY: + IF spec.manifest.path IS NOT null: + manifest_checks.append({ platform: pid, path: spec.manifest.path }) + IF spec.manifest.marketplace_path IS NOT null: + manifest_checks.append({ platform: pid, path: spec.manifest.marketplace_path }) computed.manifest_results = [] FOR check IN manifest_checks: @@ -43,16 +38,12 @@ INVENTORY(plugin_path, computed): 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" }, - ] + ## Derive from REGISTRY primary_file + secondary_files. + context_checks = [] + FOR pid, spec IN REGISTRY: + context_checks.append({ platform: pid, path: spec.context.primary_file }) + FOR secondary IN spec.context.secondary_files: + context_checks.append({ platform: pid, path: secondary }) computed.context_results = [] FOR check IN context_checks: @@ -140,10 +131,10 @@ INVENTORY(plugin_path, computed): FOR r IN computed.sidecar_results: IF r.status == "PRESENT": p = "skills/" + r.skill + "/references/" + r.file - computed.existing_files.append({ path: p, platform: spec_platform(r.file) }) + computed.existing_files.append({ path: p, platform: platform_for_spec(r.file) }) FOR r IN computed.hook_results: IF r.status == "PRESENT": - computed.existing_files.append({ path: r.path, platform: hook_platform(r.path) }) + computed.existing_files.append({ path: r.path, platform: platform_for_hooks(r.path) }) # Any file that already exists will be skipped during generation (idempotent). computed.skipped = computed.existing_files @@ -155,5 +146,5 @@ INVENTORY(plugin_path, computed): |--------|-----------| | `parse_yaml_frontmatter` | inline — read between `---` markers | | `check_injection_components` | `lib/patterns/injection-checks.md` | -| `spec_platform(file)` | `"gemini-cli.md" → "gemini-cli"`, `"codex.md" → "codex"` | -| `hook_platform(path)` | `"hooks.json" → "claude-code"`, `"hooks-cursor.json" → "cursor"` | +| `platform_for_spec(file)` | `lib/references/platform-api.md` | +| `platform_for_hooks(path)` | `lib/references/platform-api.md` | From a8aeb723ee0a9e8243386f5d57538bd80d3cc8c8 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:36:22 +1000 Subject: [PATCH 31/43] Replace hardcoded hook event names with hook_event() lookups in injection-checks Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/injection-checks.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/patterns/injection-checks.md b/lib/patterns/injection-checks.md index 9e8897f..1b8f5b7 100644 --- a/lib/patterns/injection-checks.md +++ b/lib/patterns/injection-checks.md @@ -47,24 +47,26 @@ CHECK_INJECTION_COMPONENTS(computed): results.append({ component: path, status: "MISSING" }) # 5. hooks.json SessionStart entry + claude_event = hook_event("claude-code", "session.start") IF file_exists("hooks/hooks.json"): content = Read("hooks/hooks.json") - IF content contains "SessionStart" AND content contains "session-start": - results.append({ component: "hooks/hooks.json (SessionStart)", status: "PRESENT" }) + IF content contains claude_event AND content contains "session-start": + results.append({ component: "hooks/hooks.json (" + claude_event + ")", status: "PRESENT" }) ELSE: - results.append({ component: "hooks/hooks.json (SessionStart)", status: "MISSING" }) + results.append({ component: "hooks/hooks.json (" + claude_event + ")", status: "MISSING" }) ELSE: - results.append({ component: "hooks/hooks.json (SessionStart)", status: "MISSING" }) + results.append({ component: "hooks/hooks.json (" + claude_event + ")", status: "MISSING" }) # 6. hooks-cursor.json sessionStart entry + cursor_event = hook_event("cursor", "session.start") IF file_exists("hooks/hooks-cursor.json"): content = Read("hooks/hooks-cursor.json") - IF content contains "sessionStart" AND content contains "session-start": - results.append({ component: "hooks/hooks-cursor.json (sessionStart)", status: "PRESENT" }) + IF content contains cursor_event AND content contains "session-start": + results.append({ component: "hooks/hooks-cursor.json (" + cursor_event + ")", status: "PRESENT" }) ELSE: - results.append({ component: "hooks/hooks-cursor.json (sessionStart)", status: "MISSING" }) + results.append({ component: "hooks/hooks-cursor.json (" + cursor_event + ")", status: "MISSING" }) ELSE: - results.append({ component: "hooks/hooks-cursor.json (sessionStart)", status: "MISSING" }) + results.append({ component: "hooks/hooks-cursor.json (" + cursor_event + ")", status: "MISSING" }) # 7. GEMINI.md ordering IF file_exists("GEMINI.md"): From e573cf0539d2a7e3e3d5979ed8a9d611713f5967 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:36:22 +1000 Subject: [PATCH 32/43] Replace inline hook output format comment with REGISTRY reference Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/bootstrapping.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/patterns/bootstrapping.md b/lib/patterns/bootstrapping.md index a0186e7..92213ef 100644 --- a/lib/patterns/bootstrapping.md +++ b/lib/patterns/bootstrapping.md @@ -124,10 +124,8 @@ escape_for_json() { using_escaped=$(escape_for_json "$using_content") session_context="\nThis plugin uses the superpowers portability pattern.\n\n${using_escaped}\n" -# Output context injection as JSON. -# Cursor hooks expect additional_context (snake_case). -# Claude Code hooks expect hookSpecificOutput.additionalContext (nested). -# Other platforms expect additionalContext (top-level, SDK standard). +# Output format per platform — see REGISTRY[platform].hooks.output_key +# and REGISTRY[platform].hooks.structure for the authoritative mapping. if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then printf '{\n "additional_context": "%s"\n}\n' "$session_context" elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then From 932d046eaac21a0c2525d94f66ad8c0c24d0ea6f Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:37:08 +1000 Subject: [PATCH 33/43] Mark pattern deduplication items 40-49 as fixed in reconciliation matrix Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/reconciliation-matrix.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/reconciliation-matrix.md b/docs/reconciliation-matrix.md index 0a1c6db..88cd528 100644 --- a/docs/reconciliation-matrix.md +++ b/docs/reconciliation-matrix.md @@ -395,6 +395,19 @@ in `docs/research/per-platform-context-loading.md` (#12). 38. ~~**All rubrics**: Synonym duplication (`find_file`/`find_first`, `count`/`len`, `field_present`/`in`)~~ Fixed — unified to canonical names 39. ~~**lib/patterns/pseudocode-principles.md**: Decision boundary doc missing~~ Fixed — created with three-tier framework +### Pattern Deduplication + +40. ~~**hook-merging.md**: 20-line event mapping table duplicated REGISTRY data~~ Fixed — replaced with hook_event() reference +41. ~~**hook-merging.md**: Hardcoded codex_events list~~ Fixed — derived from REGISTRY canonical + extra_events +42. ~~**hook-merging.md**: Inline gemini_event_map dictionary~~ Fixed — replaced with hook_event() lookups +43. ~~**inventory.md**: Hardcoded manifest_checks list~~ Fixed — derived from REGISTRY manifest.path + marketplace_path +44. ~~**inventory.md**: Hardcoded context_checks list~~ Fixed — derived from REGISTRY primary_file + secondary_files +45. ~~**inventory.md**: Inline spec_platform/hook_platform helpers~~ Fixed — replaced with platform_for_spec/platform_for_hooks from platform-api.md +46. ~~**injection-checks.md**: Hardcoded "SessionStart"/"sessionStart" strings~~ Fixed — replaced with hook_event() calls +47. ~~**bootstrapping.md**: Inline hook output format documentation~~ Fixed — references REGISTRY[platform].hooks.output_key +48. ~~**PlatformSpec**: Missing marketplace_path field~~ Fixed — added to type and all 6 platform specs +49. ~~**antigravity.md**: GEMINI.md in priority_note prose, not secondary_files~~ Fixed — added to secondary_files + ### Verification status -All tiers, platform API restructure, and rubric check alignment: complete, zero gaps. +All tiers, platform API restructure, rubric check alignment, and pattern deduplication: complete, zero gaps. From 590846aa7b42ee0e42c664937588fd1f7aa376b3 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:46:21 +1000 Subject: [PATCH 34/43] Add shape and template registries design spec Uplift Target Registry (3 entries) and Template Registry (10 entries) in lib/references/ with concept-aligned directories. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...-04-27-shape-template-registries-design.md | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-27-shape-template-registries-design.md diff --git a/docs/superpowers/specs/2026-04-27-shape-template-registries-design.md b/docs/superpowers/specs/2026-04-27-shape-template-registries-design.md new file mode 100644 index 0000000..9625d76 --- /dev/null +++ b/docs/superpowers/specs/2026-04-27-shape-template-registries-design.md @@ -0,0 +1,309 @@ +# Shape & Template Registries + +> **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:** Create two new structured registries — Uplift Targets and Templates — following the same pattern as the Platform API, to eliminate scattered inline data and enable deterministic lookups. + +**Architecture:** Each registry gets its own directory under `lib/references/` with a `registry.md` file containing the type definition, data entries, and lookup functions. Consumer files replace inline data with registry lookups. + +**Tech Stack:** Markdown pseudocode. + +--- + +## File Structure + +``` +lib/references/ + platform-api.md # existing + platforms/ # existing + uplift-targets/ + registry.md # NEW: UpliftTarget type + data + functions + templates/ + registry.md # NEW: TemplateEntry type + data + functions +``` + +--- + +## Part 1: Uplift Target Registry + +### New file: `lib/references/uplift-targets/registry.md` + +Contains the type, 3 data entries, and 2 lookup functions. + +#### Type + +```pseudocode +TYPE UpliftTarget = { + id: string, + description: string, + allowed_categories: List[string], + sidecar_strategy: "per-skill" | "shared" | "none", +} +``` + +#### Data + +```pseudocode +UPLIFT_TARGETS: Dict[string, UpliftTarget] = { + "skill-first": { + id: "skill-first", + description: "Sidecars, tool mapping, context files only.", + allowed_categories: ["2_skills", "3_context", "5_toolmap", "6_install"], + sidecar_strategy: "per-skill", + }, + "full-portable-plugin": { + id: "full-portable-plugin", + description: "Manifests, context, hooks, install docs — everything.", + allowed_categories: ["1_manifest", "2_skills", "3_context", "4_hooks", + "5_toolmap", "6_install", "7_runtime"], + sidecar_strategy: "shared", + }, + "curated-note-only": { + id: "curated-note-only", + description: "Documentation only. No generated artifacts.", + allowed_categories: ["6_install"], + sidecar_strategy: "none", + }, +} +``` + +#### Functions + +```pseudocode +FUNCTION allowed_categories(uplift_target) + RETURNS list of category IDs this target generates artifacts for. + RETURN UPLIFT_TARGETS[uplift_target].allowed_categories + +FUNCTION sidecar_strategy(uplift_target) + RETURNS how tool-mapping sidecars are organized for this target. + RETURN UPLIFT_TARGETS[uplift_target].sidecar_strategy +``` + +### Consumer changes + +**1. SKILL.md Phase 0b (lines 143-158) — Option list** + +Current: Inline list of 3 options with hardcoded labels and descriptions. + +Replace option construction with: + +```pseudocode + options = [] + FOR id, target IN UPLIFT_TARGETS: + options.append({ label: title_case(id), description: target.description }) +``` + +The `(Recommended)` suffix logic and reordering stays — it uses `recommended` +which is derived from the shape classification (3-line IF/ELIF/ELSE, unchanged). + +**2. SKILL.md Phase 5 (lines 222-228) — ALLOWED_CATEGORIES** + +Current: + +```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"] +} +``` + +Replace with: + +```pseudocode +allowed = allowed_categories(computed.uplift_target) +``` + +Line 239 (`allowed = ALLOWED_CATEGORIES[computed.uplift_target]`) becomes +redundant and is removed. + +**3. SKILL.md Phase 5 skip checks (lines 262, 266)** + +Current: + +``` +Skipped if `"4_hooks" NOT IN allowed`. +Always runs (`"6_install"` is in all allowed sets). +``` + +These are prose comments referencing `allowed` — they stay as-is since `allowed` +is still the variable name. + +**4. inventory.md (lines 60, 68) — Sidecar strategy branching** + +Current: + +```pseudocode + IF computed.shape IN ["bare-skill-repo", "skill-first"]: + # per-skill sidecars + ELIF computed.shape == "full-portable-plugin": + # shared sidecars +``` + +This check currently uses `computed.shape` (detected shape), but it should +use the uplift target's sidecar_strategy. After Phase 0b sets +`computed.uplift_target`, inventory can use: + +```pseudocode + strategy = sidecar_strategy(computed.uplift_target) + IF strategy == "per-skill": + # per-skill sidecars + ELIF strategy == "shared": + # shared sidecars +``` + +**Note:** In assess-only mode (no uplift target selected), inventory still runs. +The sidecar check needs a default. Derive it from shape: + +```pseudocode + IF computed.uplift_target IS NOT null: + strategy = sidecar_strategy(computed.uplift_target) + ELSE: + # Assess mode: infer from shape + IF computed.shape IN ["bare-skill-repo"]: + strategy = "per-skill" + ELSE: + strategy = "shared" +``` + +--- + +## Part 2: Template Registry + +### New file: `lib/references/templates/registry.md` + +Contains the type, 10 data entries, and 2 lookup functions. + +#### Type + +```pseudocode +TYPE TemplateEntry = { + schema: string, + platform: string, + mode: "plain" | "conditional" | "builder", + template_path: string, + target_path: string, +} +``` + +#### Data + +```pseudocode +TEMPLATE_REGISTRY: List[TemplateEntry] = [ + { schema: "claude-plugin", platform: "claude-code", mode: "plain", + template_path: "lib/templates/manifests/claude-plugin/plugin.json.tmpl", + target_path: ".claude-plugin/plugin.json" }, + + { schema: "claude-marketplace", platform: "claude-code", mode: "plain", + template_path: "lib/templates/manifests/claude-plugin/marketplace.json.tmpl", + target_path: ".claude-plugin/marketplace.json" }, + + { schema: "claude-context", platform: "claude-code", mode: "plain", + template_path: "lib/templates/context-files/CLAUDE.md.tmpl", + target_path: "CLAUDE.md" }, + + { schema: "cursor-plugin", platform: "cursor", mode: "conditional", + template_path: "lib/templates/manifests/cursor-plugin/plugin.json.tmpl", + target_path: ".cursor-plugin/plugin.json" }, + + { schema: "cursor-marketplace", platform: "cursor", mode: "plain", + template_path: "lib/templates/manifests/cursor-plugin/marketplace.json.tmpl", + target_path: ".cursor-plugin/marketplace.json" }, + + { schema: "gemini-extension", platform: "gemini-cli", mode: "plain", + template_path: "lib/templates/manifests/gemini-extension.json.tmpl", + target_path: "gemini-extension.json" }, + + { schema: "gemini-context", platform: "gemini-cli", mode: "builder", + template_path: "lib/templates/context-files/GEMINI.md.tmpl", + target_path: "GEMINI.md" }, + + { schema: "agents-context", platform: "all", mode: "builder", + template_path: "lib/templates/context-files/AGENTS.md.tmpl", + target_path: "AGENTS.md" }, + + { schema: "codex-plugin", platform: "codex", mode: "plain", + template_path: "lib/templates/manifests/codex-plugin/plugin.json.tmpl", + target_path: ".codex-plugin/plugin.json" }, + + { schema: "codex-marketplace", platform: "codex", mode: "plain", + template_path: "lib/templates/manifests/codex-plugin/marketplace.json.tmpl", + target_path: ".agents/plugins/marketplace.json" }, +] +``` + +#### Functions + +```pseudocode +FUNCTION template_for_schema(schema) + RETURNS the TemplateEntry for a given schema name. + FOR entry IN TEMPLATE_REGISTRY: + IF entry.schema == schema: RETURN entry + +FUNCTION templates_for_platform(platform) + RETURNS all TemplateEntries for a given platform. + FOR entry IN TEMPLATE_REGISTRY: + IF entry.platform == platform OR entry.platform == "all": + INCLUDE entry +``` + +### Consumer changes + +**1. manifest-generation.md — Schema-to-Template Mapping table (lines 64-78)** + +Current prose table: + +``` +| Schema | Mode | Template file | +|--------|------|---------------| +| claude-plugin | Plain | `lib/templates/manifests/claude-plugin/plugin.json.tmpl` | +[... 9 more rows ...] +``` + +Replace with a reference: + +``` +Schema-to-template mappings are defined in `lib/references/templates/registry.md`. +Use `template_for_schema(schema)` to look up template path, target path, and +rendering mode for a given schema. +``` + +The per-schema prose sections (claude-plugin, cursor-plugin, etc.) stay — they +document conditional logic, field notes, and rendering details that don't belong +in the registry. + +**2. manifest-generation.md — Rendering mode dispatch** + +The `RENDER_WITH_CONDITIONALS` and `RENDER_WITH_BUILDER` functions stay as-is. +The dispatch logic can use `template_for_schema(schema).mode` to select which +renderer to call. + +**3. SKILL.md Phase 5 — resolve_target_path** + +`resolve_target_path(condition.template, platform)` can look up +`template_for_schema(schema).target_path` from the registry instead of +re-deriving it. + +--- + +## What Does NOT Change + +- **detection-algorithm.md** — `CLASSIFY_SHAPE` is algorithm logic, not lookup data +- **Shape→target recommendation** in SKILL.md Phase 0b — 3-line IF/ELIF/ELSE stays inline +- **Rendering mode implementations** — `RENDER_WITH_CONDITIONALS`, `RENDER_WITH_BUILDER` are algorithms +- **Per-schema documentation sections** in manifest-generation.md — field notes, conditional logic docs +- **platform-api.md** — No changes; uplift targets and templates are separate concepts + +--- + +## Verification + +After all edits: + +1. `test -f lib/references/uplift-targets/registry.md && echo OK` — registry exists +2. `test -f lib/references/templates/registry.md && echo OK` — registry exists +3. `grep -n 'ALLOWED_CATEGORIES' skills/plugin-portability/SKILL.md` returns 0 — replaced with `allowed_categories()` +4. `grep -n 'allowed_categories\|sidecar_strategy' skills/plugin-portability/SKILL.md` shows lookup calls +5. `grep -cP '^\| .*(claude-plugin|cursor-plugin|gemini-extension).*\|' lib/patterns/manifest-generation.md` returns 0 — prose table replaced with registry reference +6. `grep -n 'template_for_schema\|templates_for_platform' lib/patterns/manifest-generation.md` shows lookup calls From e6f8ce5a46183959abd76c0336d37c7470d69a5e Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Mon, 27 Apr 2026 23:51:24 +1000 Subject: [PATCH 35/43] Fix template registry per Codex adversarial review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add missing Antigravity and OpenClaw template entries (10→12) - Add template_for_path() lookup matching rubric template contract - Add completeness verification checks Co-Authored-By: Claude Opus 4.6 (1M context) --- ...-04-27-shape-template-registries-design.md | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/specs/2026-04-27-shape-template-registries-design.md b/docs/superpowers/specs/2026-04-27-shape-template-registries-design.md index 9625d76..357decf 100644 --- a/docs/superpowers/specs/2026-04-27-shape-template-registries-design.md +++ b/docs/superpowers/specs/2026-04-27-shape-template-registries-design.md @@ -173,7 +173,7 @@ The sidecar check needs a default. Derive it from shape: ### New file: `lib/references/templates/registry.md` -Contains the type, 10 data entries, and 2 lookup functions. +Contains the type, 12 data entries, and 3 lookup functions. #### Type @@ -230,6 +230,14 @@ TEMPLATE_REGISTRY: List[TemplateEntry] = [ { schema: "codex-marketplace", platform: "codex", mode: "plain", template_path: "lib/templates/manifests/codex-plugin/marketplace.json.tmpl", target_path: ".agents/plugins/marketplace.json" }, + + { schema: "antigravity-package", platform: "antigravity", mode: "plain", + template_path: "lib/templates/manifests/antigravity/package.json.tmpl", + target_path: "package.json" }, + + { schema: "openclaw-plugin", platform: "openclaw", mode: "plain", + template_path: "lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl", + target_path: "openclaw.plugin.json" }, ] ``` @@ -241,6 +249,14 @@ FUNCTION template_for_schema(schema) FOR entry IN TEMPLATE_REGISTRY: IF entry.schema == schema: RETURN entry +FUNCTION template_for_path(template_ref) + RETURNS the TemplateEntry matching a rubric template reference. + Strips "?merge" suffix and prepends "lib/templates/" if needed. + normalized = strip_suffix(template_ref, "?merge") + full_path = "lib/templates/" + normalized + FOR entry IN TEMPLATE_REGISTRY: + IF entry.template_path == full_path: RETURN entry + FUNCTION templates_for_platform(platform) RETURNS all TemplateEntries for a given platform. FOR entry IN TEMPLATE_REGISTRY: @@ -248,6 +264,11 @@ FUNCTION templates_for_platform(platform) INCLUDE entry ``` +**Note on rubric contract:** Rubric conditions carry `condition.template` values +like `manifests/gemini-extension.json.tmpl?merge` — a path relative to +`lib/templates/` with an optional `?merge` action suffix. `template_for_path()` +bridges this contract to the registry by normalizing the reference. + ### Consumer changes **1. manifest-generation.md — Schema-to-Template Mapping table (lines 64-78)** @@ -281,9 +302,9 @@ renderer to call. **3. SKILL.md Phase 5 — resolve_target_path** -`resolve_target_path(condition.template, platform)` can look up -`template_for_schema(schema).target_path` from the registry instead of -re-deriving it. +`resolve_target_path(condition.template, platform)` can use +`template_for_path(condition.template).target_path` from the registry instead +of re-deriving the target path. The `?merge` suffix is stripped by the lookup. --- @@ -306,4 +327,6 @@ After all edits: 3. `grep -n 'ALLOWED_CATEGORIES' skills/plugin-portability/SKILL.md` returns 0 — replaced with `allowed_categories()` 4. `grep -n 'allowed_categories\|sidecar_strategy' skills/plugin-portability/SKILL.md` shows lookup calls 5. `grep -cP '^\| .*(claude-plugin|cursor-plugin|gemini-extension).*\|' lib/patterns/manifest-generation.md` returns 0 — prose table replaced with registry reference -6. `grep -n 'template_for_schema\|templates_for_platform' lib/patterns/manifest-generation.md` shows lookup calls +6. `grep -n 'template_for_schema\|template_for_path\|templates_for_platform' lib/patterns/manifest-generation.md` shows lookup calls +7. Template registry has 12 entries — one per `.tmpl` file in `lib/templates/manifests/` + 2 context-file entries +8. Every non-null `template:` value in `lib/rubrics/*.yaml` resolves via `template_for_path()` From 46e1258eb4d60718635ee3b28ab05eec703f68c8 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:07:18 +1000 Subject: [PATCH 36/43] Add shape and template registries implementation plan 8 tasks: 2 registry files, 3 SKILL.md consumer edits, inventory sidecar strategy, manifest-generation table, reconciliation matrix. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-04-28-shape-template-registries.md | 560 ++++++++++++++++++ 1 file changed, 560 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-28-shape-template-registries.md diff --git a/docs/superpowers/plans/2026-04-28-shape-template-registries.md b/docs/superpowers/plans/2026-04-28-shape-template-registries.md new file mode 100644 index 0000000..61bcdf0 --- /dev/null +++ b/docs/superpowers/plans/2026-04-28-shape-template-registries.md @@ -0,0 +1,560 @@ +# Shape & Template Registries 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:** Create Uplift Target and Template registries in `lib/references/`, then update all consumer files to use registry lookups instead of inline data. + +**Architecture:** Two new registry files (type + data + functions each), then consumer edits in SKILL.md, inventory.md, and manifest-generation.md. Each task is self-contained. + +**Tech Stack:** Markdown pseudocode. + +--- + +## File Map + +| File | Action | What changes | +|------|--------|-------------| +| `lib/references/uplift-targets/registry.md` | Create | UpliftTarget type, 3 entries, 2 functions | +| `lib/references/templates/registry.md` | Create | TemplateEntry type, 12 entries, 3 functions | +| `skills/plugin-portability/SKILL.md` | Modify | Phase 0b options, Phase 5 ALLOWED_CATEGORIES, resolve_target_path | +| `lib/patterns/inventory.md` | Modify | Sidecar strategy branching | +| `lib/patterns/manifest-generation.md` | Modify | Schema-to-Template table → registry reference | +| `docs/reconciliation-matrix.md` | Modify | Add items for registries | + +--- + +### Task 1: Create Uplift Target Registry + +**Files:** +- Create: `lib/references/uplift-targets/registry.md` + +- [ ] **Step 1: Create the directory and registry file** + +Create `lib/references/uplift-targets/registry.md` with this exact content: + +````markdown +# Uplift Target Registry + +Structured data and lookup functions for uplift target behavior. +Each target defines which rubric categories it generates artifacts for +and how tool-mapping sidecars are organized. + +--- + +## Type + +```pseudocode +TYPE UpliftTarget = { + id: string, + description: string, + allowed_categories: List[string], + sidecar_strategy: "per-skill" | "shared" | "none", +} +``` + +--- + +## Data + +```pseudocode +UPLIFT_TARGETS: Dict[string, UpliftTarget] = { + "skill-first": { + id: "skill-first", + description: "Sidecars, tool mapping, context files only.", + allowed_categories: ["2_skills", "3_context", "5_toolmap", "6_install"], + sidecar_strategy: "per-skill", + }, + "full-portable-plugin": { + id: "full-portable-plugin", + description: "Manifests, context, hooks, install docs — everything.", + allowed_categories: ["1_manifest", "2_skills", "3_context", "4_hooks", + "5_toolmap", "6_install", "7_runtime"], + sidecar_strategy: "shared", + }, + "curated-note-only": { + id: "curated-note-only", + description: "Documentation only. No generated artifacts.", + allowed_categories: ["6_install"], + sidecar_strategy: "none", + }, +} +``` + +--- + +## Lookup Functions + +```pseudocode +FUNCTION allowed_categories(uplift_target) + RETURNS list of category IDs this target generates artifacts for. + RETURN UPLIFT_TARGETS[uplift_target].allowed_categories + +FUNCTION sidecar_strategy(uplift_target) + RETURNS how tool-mapping sidecars are organized for this target. + RETURN UPLIFT_TARGETS[uplift_target].sidecar_strategy +``` +```` + +- [ ] **Step 2: Verify** + +Run: `test -f lib/references/uplift-targets/registry.md && echo OK` +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +git add lib/references/uplift-targets/registry.md +git commit -m "Create Uplift Target Registry with 3 entries and 2 lookup functions" +``` + +--- + +### Task 2: Create Template Registry + +**Files:** +- Create: `lib/references/templates/registry.md` + +- [ ] **Step 1: Create the directory and registry file** + +Create `lib/references/templates/registry.md` with this exact content: + +````markdown +# Template Registry + +Structured data and lookup functions for manifest and context-file templates. +Maps schema names to template paths, target paths, platforms, and rendering modes. + +--- + +## Type + +```pseudocode +TYPE TemplateEntry = { + schema: string, + platform: string, + mode: "plain" | "conditional" | "builder", + template_path: string, + target_path: string, +} +``` + +--- + +## Data + +```pseudocode +TEMPLATE_REGISTRY: List[TemplateEntry] = [ + { schema: "claude-plugin", platform: "claude-code", mode: "plain", + template_path: "lib/templates/manifests/claude-plugin/plugin.json.tmpl", + target_path: ".claude-plugin/plugin.json" }, + + { schema: "claude-marketplace", platform: "claude-code", mode: "plain", + template_path: "lib/templates/manifests/claude-plugin/marketplace.json.tmpl", + target_path: ".claude-plugin/marketplace.json" }, + + { schema: "claude-context", platform: "claude-code", mode: "plain", + template_path: "lib/templates/context-files/CLAUDE.md.tmpl", + target_path: "CLAUDE.md" }, + + { schema: "cursor-plugin", platform: "cursor", mode: "conditional", + template_path: "lib/templates/manifests/cursor-plugin/plugin.json.tmpl", + target_path: ".cursor-plugin/plugin.json" }, + + { schema: "cursor-marketplace", platform: "cursor", mode: "plain", + template_path: "lib/templates/manifests/cursor-plugin/marketplace.json.tmpl", + target_path: ".cursor-plugin/marketplace.json" }, + + { schema: "gemini-extension", platform: "gemini-cli", mode: "plain", + template_path: "lib/templates/manifests/gemini-extension.json.tmpl", + target_path: "gemini-extension.json" }, + + { schema: "gemini-context", platform: "gemini-cli", mode: "builder", + template_path: "lib/templates/context-files/GEMINI.md.tmpl", + target_path: "GEMINI.md" }, + + { schema: "agents-context", platform: "all", mode: "builder", + template_path: "lib/templates/context-files/AGENTS.md.tmpl", + target_path: "AGENTS.md" }, + + { schema: "codex-plugin", platform: "codex", mode: "plain", + template_path: "lib/templates/manifests/codex-plugin/plugin.json.tmpl", + target_path: ".codex-plugin/plugin.json" }, + + { schema: "codex-marketplace", platform: "codex", mode: "plain", + template_path: "lib/templates/manifests/codex-plugin/marketplace.json.tmpl", + target_path: ".agents/plugins/marketplace.json" }, + + { schema: "antigravity-package", platform: "antigravity", mode: "plain", + template_path: "lib/templates/manifests/antigravity/package.json.tmpl", + target_path: "package.json" }, + + { schema: "openclaw-plugin", platform: "openclaw", mode: "plain", + template_path: "lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl", + target_path: "openclaw.plugin.json" }, +] +``` + +--- + +## Lookup Functions + +```pseudocode +FUNCTION template_for_schema(schema) + RETURNS the TemplateEntry for a given schema name. + FOR entry IN TEMPLATE_REGISTRY: + IF entry.schema == schema: RETURN entry + +FUNCTION template_for_path(template_ref) + RETURNS the TemplateEntry matching a rubric template reference. + Strips "?merge" suffix and prepends "lib/templates/" if needed. + normalized = strip_suffix(template_ref, "?merge") + full_path = "lib/templates/" + normalized + FOR entry IN TEMPLATE_REGISTRY: + IF entry.template_path == full_path: RETURN entry + +FUNCTION templates_for_platform(platform) + RETURNS all TemplateEntries for a given platform. + FOR entry IN TEMPLATE_REGISTRY: + IF entry.platform == platform OR entry.platform == "all": + INCLUDE entry +``` +```` + +- [ ] **Step 2: Verify** + +Run: `test -f lib/references/templates/registry.md && echo OK` +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +git add lib/references/templates/registry.md +git commit -m "Create Template Registry with 12 entries and 3 lookup functions" +``` + +--- + +### Task 3: Update SKILL.md Phase 0b — derive options from registry + +**Files:** +- Modify: `skills/plugin-portability/SKILL.md:142-151` + +- [ ] **Step 1: Replace the hardcoded options list** + +Find: + +``` + # 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] +``` + +Replace with: + +``` + # Q3: Build options from UPLIFT_TARGETS registry + options = [] + FOR id, target IN UPLIFT_TARGETS: + options.append({ label: title_case(id), description: target.description }) + FOR opt IN options: + IF opt.label.lower().startswith(recommended.replace("-", " ")): + opt.label += " (Recommended)" + options = [opt] + [o for o in options if o != opt] +``` + +- [ ] **Step 2: Verify** + +Run: `grep -n 'Sidecars, tool mapping, context files only' skills/plugin-portability/SKILL.md` +Expected: no output (description now comes from registry) + +- [ ] **Step 3: Commit** + +```bash +git add skills/plugin-portability/SKILL.md +git commit -m "Derive Phase 0b uplift options from UPLIFT_TARGETS registry" +``` + +--- + +### Task 4: Update SKILL.md Phase 5 — replace ALLOWED_CATEGORIES + +**Files:** +- Modify: `skills/plugin-portability/SKILL.md:219-239` + +- [ ] **Step 1: Replace the ALLOWED_CATEGORIES block and its lookup** + +Find: + +``` +### 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"] +} +``` +``` + +Replace with: + +``` +### Allowed categories by uplift target + +Derived from `lib/references/uplift-targets/registry.md`. +``` + +- [ ] **Step 2: Replace the ALLOWED_CATEGORIES lookup in Phase 5** + +Find: + +``` +allowed = ALLOWED_CATEGORIES[computed.uplift_target] +``` + +Replace with: + +``` +allowed = allowed_categories(computed.uplift_target) +``` + +- [ ] **Step 3: Verify** + +Run: `grep -n 'ALLOWED_CATEGORIES' skills/plugin-portability/SKILL.md` +Expected: no output + +Run: `grep -n 'allowed_categories' skills/plugin-portability/SKILL.md` +Expected: one line with the function call + +- [ ] **Step 4: Commit** + +```bash +git add skills/plugin-portability/SKILL.md +git commit -m "Replace ALLOWED_CATEGORIES with allowed_categories() registry lookup" +``` + +--- + +### Task 5: Update SKILL.md Phase 5 — resolve_target_path + +**Files:** +- Modify: `skills/plugin-portability/SKILL.md:247` + +- [ ] **Step 1: Update resolve_target_path to use template registry** + +Find: + +``` + target_path = resolve_target_path(condition.template, platform) +``` + +Replace with: + +``` + target_path = template_for_path(condition.template).target_path +``` + +- [ ] **Step 2: Update external references to include new registries** + +Find: + +``` +> `lib/rubrics/*.yaml` | `lib/references/platform-api.md` | `lib/references/platforms/*.md` | `lib/patterns/manifest-generation.md` +``` + +Replace with: + +``` +> `lib/rubrics/*.yaml` | `lib/references/platform-api.md` | `lib/references/platforms/*.md` | `lib/patterns/manifest-generation.md` +> `lib/references/uplift-targets/registry.md` | `lib/references/templates/registry.md` +``` + +- [ ] **Step 3: Verify** + +Run: `grep -n 'resolve_target_path' skills/plugin-portability/SKILL.md` +Expected: no output + +Run: `grep -n 'template_for_path' skills/plugin-portability/SKILL.md` +Expected: one line + +- [ ] **Step 4: Commit** + +```bash +git add skills/plugin-portability/SKILL.md +git commit -m "Use template_for_path() for target path resolution, add registry references" +``` + +--- + +### Task 6: Update inventory.md — sidecar strategy branching + +**Files:** +- Modify: `lib/patterns/inventory.md:60-68` + +- [ ] **Step 1: Replace shape-based branching with strategy-based branching** + +Find: + +``` + IF computed.shape IN ["bare-skill-repo", "skill-first"]: + # Bare skills need per-skill spec files — no context file to carry shared refs + FOR skill IN computed.skills: + FOR spec_file IN platform_spec_files: + target = "skills/" + skill.dir + "/references/" + spec_file + status = IF file_exists(plugin_path + "/" + target) THEN "PRESENT" ELSE "MISSING" + computed.sidecar_results.append({ skill: skill.dir, file: spec_file, status: status }) + + ELIF computed.shape == "full-portable-plugin": +``` + +Replace with: + +``` + IF computed.uplift_target IS NOT null: + strategy = sidecar_strategy(computed.uplift_target) + ELSE: + IF computed.shape IN ["bare-skill-repo"]: + strategy = "per-skill" + ELSE: + strategy = "shared" + + IF strategy == "per-skill": + # Per-skill spec files — no context file to carry shared refs + FOR skill IN computed.skills: + FOR spec_file IN platform_spec_files: + target = "skills/" + skill.dir + "/references/" + spec_file + status = IF file_exists(plugin_path + "/" + target) THEN "PRESENT" ELSE "MISSING" + computed.sidecar_results.append({ skill: skill.dir, file: spec_file, status: status }) + + ELIF strategy == "shared": +``` + +- [ ] **Step 2: Verify** + +Run: `grep -n 'sidecar_strategy' lib/patterns/inventory.md` +Expected: one line with the function call + +Run: `grep -n '"bare-skill-repo", "skill-first"' lib/patterns/inventory.md` +Expected: no output (old shape list gone) + +- [ ] **Step 3: Commit** + +```bash +git add lib/patterns/inventory.md +git commit -m "Replace shape-based sidecar branching with sidecar_strategy() lookup" +``` + +--- + +### Task 7: Update manifest-generation.md — replace prose table + +**Files:** +- Modify: `lib/patterns/manifest-generation.md:64-78` + +- [ ] **Step 1: Replace the Schema-to-Template Mapping table** + +Find: + +``` +## Schema-to-Template Mapping + +| Schema | Mode | Template file | +|--------|------|---------------| +| claude-plugin | Plain | `lib/templates/manifests/claude-plugin/plugin.json.tmpl` | +| claude-marketplace | Plain | `lib/templates/manifests/claude-plugin/marketplace.json.tmpl` | +| claude-context | Plain | `lib/templates/context-files/CLAUDE.md.tmpl` | +| cursor-plugin | Conditional | `lib/templates/manifests/cursor-plugin/plugin.json.tmpl` | +| cursor-marketplace | Plain | `lib/templates/manifests/cursor-plugin/marketplace.json.tmpl` | +| 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` | +| codex-plugin | Plain | `lib/templates/manifests/codex-plugin/plugin.json.tmpl` | +| codex-marketplace | Plain | `lib/templates/manifests/codex-plugin/marketplace.json.tmpl` | +``` + +Replace with: + +``` +## Schema-to-Template Mapping + +Schema-to-template mappings are defined in `lib/references/templates/registry.md`. +Use `template_for_schema(schema)` to look up template path, target path, and +rendering mode for a given schema. Use `template_for_path(template_ref)` to +resolve a rubric `condition.template` value to its registry entry. +``` + +- [ ] **Step 2: Verify** + +Run: `grep -cP '^\| .*(claude-plugin|cursor-plugin|gemini-extension).*\|' lib/patterns/manifest-generation.md` +Expected: `0` + +Run: `grep -n 'template_for_schema\|template_for_path' lib/patterns/manifest-generation.md` +Expected: two lines + +- [ ] **Step 3: Commit** + +```bash +git add lib/patterns/manifest-generation.md +git commit -m "Replace Schema-to-Template prose table with registry reference" +``` + +--- + +### Task 8: Final verification and reconciliation matrix + +- [ ] **Step 1: Run all verification commands** + +```bash +echo "=== 1. uplift-targets registry ===" && test -f lib/references/uplift-targets/registry.md && echo OK +echo "=== 2. templates registry ===" && test -f lib/references/templates/registry.md && echo OK +echo "=== 3. No ALLOWED_CATEGORIES ===" && grep -n 'ALLOWED_CATEGORIES' skills/plugin-portability/SKILL.md +echo "=== 4. Registry lookups in SKILL.md ===" && grep -n 'allowed_categories\|sidecar_strategy\|template_for_path' skills/plugin-portability/SKILL.md +echo "=== 5. No prose table ===" && grep -cP '^\| .*(claude-plugin|cursor-plugin|gemini-extension).*\|' lib/patterns/manifest-generation.md +echo "=== 6. Registry refs in manifest-generation ===" && grep -n 'template_for_schema\|template_for_path' lib/patterns/manifest-generation.md +echo "=== 7. Template count ===" && grep -c 'schema:' lib/references/templates/registry.md +echo "=== DONE ===" +``` + +Expected: checks 1-2 print OK, check 3 returns nothing, check 4 shows lookup calls, check 5 returns 0, check 6 shows references, check 7 returns 12. + +- [ ] **Step 2: Add reconciliation matrix items** + +In `docs/reconciliation-matrix.md`, find: + +``` +### Verification status + +All tiers, platform API restructure, rubric check alignment, and pattern deduplication: complete, zero gaps. +``` + +Replace with: + +``` +### Shape & Template Registries + +50. ~~**SKILL.md Phase 0b**: Hardcoded uplift option labels/descriptions~~ Fixed — derived from UPLIFT_TARGETS registry +51. ~~**SKILL.md Phase 5**: Inline ALLOWED_CATEGORIES dict~~ Fixed — replaced with allowed_categories() lookup +52. ~~**SKILL.md Phase 5**: resolve_target_path re-derives target paths~~ Fixed — uses template_for_path().target_path +53. ~~**inventory.md**: Shape-based sidecar branching~~ Fixed — uses sidecar_strategy() lookup +54. ~~**manifest-generation.md**: 10-row Schema-to-Template prose table~~ Fixed — references lib/references/templates/registry.md + +### Verification status + +All tiers, platform API restructure, rubric check alignment, pattern deduplication, and shape/template registries: complete, zero gaps. +``` + +- [ ] **Step 3: Commit** + +```bash +git add docs/reconciliation-matrix.md +git commit -m "Mark shape and template registry items 50-54 as fixed in reconciliation matrix" +``` From 8d78ca263d025333711d3a19fda3af1ca83ce90a Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:10:21 +1000 Subject: [PATCH 37/43] Create Uplift Target and Template registries - uplift-targets/registry.md: 3 entries, 2 lookup functions - templates/registry.md: 12 entries, 3 lookup functions Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/references/templates/registry.md | 99 +++++++++++++++++++++++ lib/references/uplift-targets/registry.md | 60 ++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 lib/references/templates/registry.md create mode 100644 lib/references/uplift-targets/registry.md diff --git a/lib/references/templates/registry.md b/lib/references/templates/registry.md new file mode 100644 index 0000000..0cabd86 --- /dev/null +++ b/lib/references/templates/registry.md @@ -0,0 +1,99 @@ +# Template Registry + +Structured data and lookup functions for manifest and context-file templates. +Maps schema names to template paths, target paths, platforms, and rendering modes. + +--- + +## Type + +```pseudocode +TYPE TemplateEntry = { + schema: string, + platform: string, + mode: "plain" | "conditional" | "builder", + template_path: string, + target_path: string, +} +``` + +--- + +## Data + +```pseudocode +TEMPLATE_REGISTRY: List[TemplateEntry] = [ + { schema: "claude-plugin", platform: "claude-code", mode: "plain", + template_path: "lib/templates/manifests/claude-plugin/plugin.json.tmpl", + target_path: ".claude-plugin/plugin.json" }, + + { schema: "claude-marketplace", platform: "claude-code", mode: "plain", + template_path: "lib/templates/manifests/claude-plugin/marketplace.json.tmpl", + target_path: ".claude-plugin/marketplace.json" }, + + { schema: "claude-context", platform: "claude-code", mode: "plain", + template_path: "lib/templates/context-files/CLAUDE.md.tmpl", + target_path: "CLAUDE.md" }, + + { schema: "cursor-plugin", platform: "cursor", mode: "conditional", + template_path: "lib/templates/manifests/cursor-plugin/plugin.json.tmpl", + target_path: ".cursor-plugin/plugin.json" }, + + { schema: "cursor-marketplace", platform: "cursor", mode: "plain", + template_path: "lib/templates/manifests/cursor-plugin/marketplace.json.tmpl", + target_path: ".cursor-plugin/marketplace.json" }, + + { schema: "gemini-extension", platform: "gemini-cli", mode: "plain", + template_path: "lib/templates/manifests/gemini-extension.json.tmpl", + target_path: "gemini-extension.json" }, + + { schema: "gemini-context", platform: "gemini-cli", mode: "builder", + template_path: "lib/templates/context-files/GEMINI.md.tmpl", + target_path: "GEMINI.md" }, + + { schema: "agents-context", platform: "all", mode: "builder", + template_path: "lib/templates/context-files/AGENTS.md.tmpl", + target_path: "AGENTS.md" }, + + { schema: "codex-plugin", platform: "codex", mode: "plain", + template_path: "lib/templates/manifests/codex-plugin/plugin.json.tmpl", + target_path: ".codex-plugin/plugin.json" }, + + { schema: "codex-marketplace", platform: "codex", mode: "plain", + template_path: "lib/templates/manifests/codex-plugin/marketplace.json.tmpl", + target_path: ".agents/plugins/marketplace.json" }, + + { schema: "antigravity-package", platform: "antigravity", mode: "plain", + template_path: "lib/templates/manifests/antigravity/package.json.tmpl", + target_path: "package.json" }, + + { schema: "openclaw-plugin", platform: "openclaw", mode: "plain", + template_path: "lib/templates/manifests/openclaw/openclaw.plugin.json.tmpl", + target_path: "openclaw.plugin.json" }, +] +``` + +--- + +## Lookup Functions + +```pseudocode +FUNCTION template_for_schema(schema) + RETURNS the TemplateEntry for a given schema name. + FOR entry IN TEMPLATE_REGISTRY: + IF entry.schema == schema: RETURN entry + +FUNCTION template_for_path(template_ref) + RETURNS the TemplateEntry matching a rubric template reference. + Strips "?merge" suffix and prepends "lib/templates/" if needed. + normalized = strip_suffix(template_ref, "?merge") + full_path = "lib/templates/" + normalized + FOR entry IN TEMPLATE_REGISTRY: + IF entry.template_path == full_path: RETURN entry + +FUNCTION templates_for_platform(platform) + RETURNS all TemplateEntries for a given platform. + FOR entry IN TEMPLATE_REGISTRY: + IF entry.platform == platform OR entry.platform == "all": + INCLUDE entry +``` diff --git a/lib/references/uplift-targets/registry.md b/lib/references/uplift-targets/registry.md new file mode 100644 index 0000000..df69726 --- /dev/null +++ b/lib/references/uplift-targets/registry.md @@ -0,0 +1,60 @@ +# Uplift Target Registry + +Structured data and lookup functions for uplift target behavior. +Each target defines which rubric categories it generates artifacts for +and how tool-mapping sidecars are organized. + +--- + +## Type + +```pseudocode +TYPE UpliftTarget = { + id: string, + description: string, + allowed_categories: List[string], + sidecar_strategy: "per-skill" | "shared" | "none", +} +``` + +--- + +## Data + +```pseudocode +UPLIFT_TARGETS: Dict[string, UpliftTarget] = { + "skill-first": { + id: "skill-first", + description: "Sidecars, tool mapping, context files only.", + allowed_categories: ["2_skills", "3_context", "5_toolmap", "6_install"], + sidecar_strategy: "per-skill", + }, + "full-portable-plugin": { + id: "full-portable-plugin", + description: "Manifests, context, hooks, install docs — everything.", + allowed_categories: ["1_manifest", "2_skills", "3_context", "4_hooks", + "5_toolmap", "6_install", "7_runtime"], + sidecar_strategy: "shared", + }, + "curated-note-only": { + id: "curated-note-only", + description: "Documentation only. No generated artifacts.", + allowed_categories: ["6_install"], + sidecar_strategy: "none", + }, +} +``` + +--- + +## Lookup Functions + +```pseudocode +FUNCTION allowed_categories(uplift_target) + RETURNS list of category IDs this target generates artifacts for. + RETURN UPLIFT_TARGETS[uplift_target].allowed_categories + +FUNCTION sidecar_strategy(uplift_target) + RETURNS how tool-mapping sidecars are organized for this target. + RETURN UPLIFT_TARGETS[uplift_target].sidecar_strategy +``` From 8d5f178d6a1a6cb8580ef2efd5679dc2f2e88393 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:11:23 +1000 Subject: [PATCH 38/43] Update SKILL.md to use uplift target and template registries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Phase 0b: derive options from UPLIFT_TARGETS - Phase 5: ALLOWED_CATEGORIES → allowed_categories() - Phase 5: resolve_target_path → template_for_path().target_path - External references: add registry paths Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/plugin-portability/SKILL.md | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/skills/plugin-portability/SKILL.md b/skills/plugin-portability/SKILL.md index 14cfc5f..84de559 100644 --- a/skills/plugin-portability/SKILL.md +++ b/skills/plugin-portability/SKILL.md @@ -20,6 +20,7 @@ Assess or uplift a plugin for multi-platform portability. Single entry point for > **External references:** > `lib/patterns/detection-algorithm.md` | `lib/patterns/inventory.md` | `lib/rubrics/rubric-framework.md` > `lib/rubrics/*.yaml` | `lib/references/platform-api.md` | `lib/references/platforms/*.md` | `lib/patterns/manifest-generation.md` +> `lib/references/uplift-targets/registry.md` | `lib/references/templates/registry.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/` @@ -139,12 +140,10 @@ INTENT_UPLIFT_TARGET(computed): 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." } - ] + # Q3: Build options from UPLIFT_TARGETS registry + options = [] + FOR id, target IN UPLIFT_TARGETS: + options.append({ label: title_case(id), description: target.description }) FOR opt IN options: IF opt.label.lower().startswith(recommended.replace("-", " ")): opt.label += " (Recommended)" @@ -218,14 +217,7 @@ REPORT(computed, intent): ### 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"] -} -``` +Derived from `lib/references/uplift-targets/registry.md`. ### Template action types @@ -236,7 +228,7 @@ ALLOWED_CATEGORIES = { ### Phase 5: Generate ```pseudocode -allowed = ALLOWED_CATEGORIES[computed.uplift_target] +allowed = allowed_categories(computed.uplift_target) FOR platform IN intent.platforms: FOR condition IN computed.scores[platform].failing: @@ -244,7 +236,7 @@ FOR platform IN intent.platforms: IF NOT condition.template: computed.manual_actions.append(condition); CONTINUE - target_path = resolve_target_path(condition.template, platform) + target_path = template_for_path(condition.template).target_path action = parse_action(condition.template) # "create" or "merge" IF action == "create" AND NOT exists(target_path): From 923b7453c05255ab975cdb4f8dfe80225f5e887e Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:12:07 +1000 Subject: [PATCH 39/43] Replace shape-based sidecar branching with sidecar_strategy() lookup Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/inventory.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/patterns/inventory.md b/lib/patterns/inventory.md index f16de73..f7cd586 100644 --- a/lib/patterns/inventory.md +++ b/lib/patterns/inventory.md @@ -57,15 +57,23 @@ INVENTORY(plugin_path, computed): "antigravity.md", "openclaw.md"] computed.sidecar_results = [] - IF computed.shape IN ["bare-skill-repo", "skill-first"]: - # Bare skills need per-skill spec files — no context file to carry shared refs + IF computed.uplift_target IS NOT null: + strategy = sidecar_strategy(computed.uplift_target) + ELSE: + IF computed.shape IN ["bare-skill-repo"]: + strategy = "per-skill" + ELSE: + strategy = "shared" + + IF strategy == "per-skill": + # Per-skill spec files — no context file to carry shared refs FOR skill IN computed.skills: FOR spec_file IN platform_spec_files: target = "skills/" + skill.dir + "/references/" + spec_file status = IF file_exists(plugin_path + "/" + target) THEN "PRESENT" ELSE "MISSING" computed.sidecar_results.append({ skill: skill.dir, file: spec_file, status: status }) - ELIF computed.shape == "full-portable-plugin": + ELIF strategy == "shared": # Plugins have context files — check shared references instead shared_paths = ["lib/references/platforms/", "references/platforms/", "lib/references/"] FOR spec_file IN platform_spec_files: From 36d4afd03bd2feeb8a2cb284fa738d1e4063d9be Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:12:07 +1000 Subject: [PATCH 40/43] Replace Schema-to-Template prose table with registry reference Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/manifest-generation.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/patterns/manifest-generation.md b/lib/patterns/manifest-generation.md index 34a1038..a471324 100644 --- a/lib/patterns/manifest-generation.md +++ b/lib/patterns/manifest-generation.md @@ -63,18 +63,10 @@ RENDER_WITH_BUILDER(template, metadata, computed): ## Schema-to-Template Mapping -| Schema | Mode | Template file | -|--------|------|---------------| -| claude-plugin | Plain | `lib/templates/manifests/claude-plugin/plugin.json.tmpl` | -| claude-marketplace | Plain | `lib/templates/manifests/claude-plugin/marketplace.json.tmpl` | -| claude-context | Plain | `lib/templates/context-files/CLAUDE.md.tmpl` | -| cursor-plugin | Conditional | `lib/templates/manifests/cursor-plugin/plugin.json.tmpl` | -| cursor-marketplace | Plain | `lib/templates/manifests/cursor-plugin/marketplace.json.tmpl` | -| 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` | -| codex-plugin | Plain | `lib/templates/manifests/codex-plugin/plugin.json.tmpl` | -| codex-marketplace | Plain | `lib/templates/manifests/codex-plugin/marketplace.json.tmpl` | +Schema-to-template mappings are defined in `lib/references/templates/registry.md`. +Use `template_for_schema(schema)` to look up template path, target path, and +rendering mode for a given schema. Use `template_for_path(template_ref)` to +resolve a rubric `condition.template` value to its registry entry. --- From b27ea2fd39f62190bf45f15af773481e67c17941 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:12:45 +1000 Subject: [PATCH 41/43] Mark shape and template registry items 50-54 as fixed in reconciliation matrix Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/reconciliation-matrix.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/reconciliation-matrix.md b/docs/reconciliation-matrix.md index 88cd528..264c52b 100644 --- a/docs/reconciliation-matrix.md +++ b/docs/reconciliation-matrix.md @@ -408,6 +408,14 @@ in `docs/research/per-platform-context-loading.md` (#12). 48. ~~**PlatformSpec**: Missing marketplace_path field~~ Fixed — added to type and all 6 platform specs 49. ~~**antigravity.md**: GEMINI.md in priority_note prose, not secondary_files~~ Fixed — added to secondary_files +### Shape & Template Registries + +50. ~~**SKILL.md Phase 0b**: Hardcoded uplift option labels/descriptions~~ Fixed — derived from UPLIFT_TARGETS registry +51. ~~**SKILL.md Phase 5**: Inline ALLOWED_CATEGORIES dict~~ Fixed — replaced with allowed_categories() lookup +52. ~~**SKILL.md Phase 5**: resolve_target_path re-derives target paths~~ Fixed — uses template_for_path().target_path +53. ~~**inventory.md**: Shape-based sidecar branching~~ Fixed — uses sidecar_strategy() lookup +54. ~~**manifest-generation.md**: 10-row Schema-to-Template prose table~~ Fixed — references lib/references/templates/registry.md + ### Verification status -All tiers, platform API restructure, rubric check alignment, and pattern deduplication: complete, zero gaps. +All tiers, platform API restructure, rubric check alignment, pattern deduplication, and shape/template registries: complete, zero gaps. From 08ebb5743fcde6c7c351df1c19fc6b57e194c8b3 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:19:47 +1000 Subject: [PATCH 42/43] Remove hardcoded Target/Template paths from manifest-generation sections Per-schema sections now document only rendering notes (conditional logic, field requirements). Target paths and template locations are looked up via template_for_schema() from the Template Registry. Added antigravity-package and openclaw-plugin sections for completeness. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/manifest-generation.md | 54 ++++++++++------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/lib/patterns/manifest-generation.md b/lib/patterns/manifest-generation.md index a471324..805b8de 100644 --- a/lib/patterns/manifest-generation.md +++ b/lib/patterns/manifest-generation.md @@ -70,36 +70,30 @@ resolve a rubric `condition.template` value to its registry entry. --- -## claude-plugin +Each section below documents schema-specific rendering notes. Target paths +and template file locations are in the registry — use +`template_for_schema("schema-name")` to look them up. -**Target:** `.claude-plugin/plugin.json` +## claude-plugin Create `.claude-plugin/` directory if needed. `{{keywords}}` is a JSON array literal (e.g. `["ai", "skills"]`). -> **Template:** `lib/templates/manifests/claude-plugin/plugin.json.tmpl` - --- ## claude-marketplace -**Target:** `.claude-plugin/marketplace.json` - -> **Template:** `lib/templates/manifests/claude-plugin/marketplace.json.tmpl` +No special rendering notes. --- ## claude-context -**Target:** `CLAUDE.md` - -> **Template:** `lib/templates/context-files/CLAUDE.md.tmpl` +No special rendering notes. --- ## cursor-plugin -**Target:** `.cursor-plugin/plugin.json` - Create `.cursor-plugin/` directory if needed. **Conditional logic:** Omit the `"agents"` key if `agents/` doesn't exist. Omit the `"commands"` key if `commands/` doesn't exist. Omit `"hooks"` if no hooks config exists. Omit `"mcpServers"` if no `mcp.json` exists. @@ -108,24 +102,16 @@ Cursor auto-discovers components from default directories (`skills/`, `rules/`, **Note:** `displayName` is not part of the official Cursor manifest schema. Use `name` (kebab-case identifier) and `description` for display purposes. -> **Template:** `lib/templates/manifests/cursor-plugin/plugin.json.tmpl` +--- ## cursor-marketplace -**Target:** `.cursor-plugin/marketplace.json` - Only generated for multi-plugin repositories. Lists all plugins with their source paths, descriptions, and optional metadata (category, tags, logo). -> **Template:** `lib/templates/manifests/cursor-plugin/marketplace.json.tmpl` - --- ## gemini-extension -**Target:** `gemini-extension.json` - -> **Template:** `lib/templates/manifests/gemini-extension.json.tmpl` - ### Required fields `name` (kebab-case, matches npm conventions), `version` (semver). @@ -151,40 +137,36 @@ Use `${extensionPath}` for absolute paths, `${workspacePath}` for workspace-rela ## gemini-context -**Target:** `GEMINI.md` - Build the include blocks from the skills/agents/commands lists inventoried during discovery. The file contains only `@` include directives and no other prose. -> **Template:** `lib/templates/context-files/GEMINI.md.tmpl` - --- ## agents-context -**Target:** `AGENTS.md` - Build skill bullet list for `{{skillIncludes}}` and command bullet list for `{{commandIncludes}}` (omit the entire Commands section if no commands exist). -> **Template:** `lib/templates/context-files/AGENTS.md.tmpl` - --- ## codex-plugin -**Target:** `.codex-plugin/plugin.json` - Create `.codex-plugin/` directory if needed. Only generated when Codex recommendation is `native-plugin-packaging`. -> **Template:** `lib/templates/manifests/codex-plugin/plugin.json.tmpl` - --- ## codex-marketplace -**Target:** `.agents/plugins/marketplace.json` - Create `.agents/plugins/` directory if needed. Only generated when Codex recommendation is `native-plugin-packaging`. For single-plugin upstream repos, this manifest points to the repo root with `source.path: "./"`. -> **Template:** `lib/templates/manifests/codex-plugin/marketplace.json.tmpl` +--- + +## antigravity-package + +No special rendering notes. + +--- + +## openclaw-plugin + +No special rendering notes. From d32e7814c96cce57db7ee439a72731b6ad53b835 Mon Sep 17 00:00:00 2001 From: Nathaniel Ramm Date: Tue, 28 Apr 2026 00:28:12 +1000 Subject: [PATCH 43/43] Rewrite manifest-generation sections as pseudocode generation functions Each per-schema section is now a GENERATE() function that: - Looks up paths via template_for_schema() from the Template Registry - Dispatches to the correct rendering mode (plain/conditional/builder) - Documents schema-specific logic as inline comments All rendering notes preserved: conditional key omission, Gemini optional fields, variable substitution, codex native-plugin-packaging gates, etc. Added antigravity-package and openclaw-plugin sections. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/patterns/manifest-generation.md | 204 ++++++++++++++++++---------- 1 file changed, 136 insertions(+), 68 deletions(-) diff --git a/lib/patterns/manifest-generation.md b/lib/patterns/manifest-generation.md index 805b8de..9d08811 100644 --- a/lib/patterns/manifest-generation.md +++ b/lib/patterns/manifest-generation.md @@ -70,103 +70,171 @@ resolve a rubric `condition.template` value to its registry entry. --- -Each section below documents schema-specific rendering notes. Target paths -and template file locations are in the registry — use -`template_for_schema("schema-name")` to look them up. +## Per-Schema Generation -## claude-plugin +Each section below is a generation function. Paths come from the Template +Registry via `template_for_schema()`. Rendering mode is dispatched per the +entry's `mode` field. -Create `.claude-plugin/` directory if needed. `{{keywords}}` is a JSON array literal (e.g. `["ai", "skills"]`). +### claude-plugin ---- - -## claude-marketplace - -No special rendering notes. - ---- - -## claude-context - -No special rendering notes. - ---- - -## cursor-plugin - -Create `.cursor-plugin/` directory if needed. - -**Conditional logic:** Omit the `"agents"` key if `agents/` doesn't exist. Omit the `"commands"` key if `commands/` doesn't exist. Omit `"hooks"` if no hooks config exists. Omit `"mcpServers"` if no `mcp.json` exists. - -Cursor auto-discovers components from default directories (`skills/`, `rules/`, `agents/`, `commands/`, `hooks/hooks.json`, `mcp.json`). Only specify explicit paths when overriding defaults. - -**Note:** `displayName` is not part of the official Cursor manifest schema. Use `name` (kebab-case identifier) and `description` for display purposes. - ---- +```pseudocode +GENERATE(schema="claude-plugin", metadata, computed): + entry = template_for_schema("claude-plugin") + mkdir_p(dirname(entry.target_path)) -## cursor-marketplace + # {{keywords}} must be a JSON array literal (e.g. ["ai", "skills"]) + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) +``` -Only generated for multi-plugin repositories. Lists all plugins with their source paths, descriptions, and optional metadata (category, tags, logo). +### claude-marketplace ---- +```pseudocode +GENERATE(schema="claude-marketplace", metadata, computed): + entry = template_for_schema("claude-marketplace") + mkdir_p(dirname(entry.target_path)) + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) +``` -## gemini-extension +### claude-context -### Required fields +```pseudocode +GENERATE(schema="claude-context", metadata, computed): + entry = template_for_schema("claude-context") + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) +``` -`name` (kebab-case, matches npm conventions), `version` (semver). +### cursor-plugin -### Optional fields (conditional generation) +```pseudocode +GENERATE(schema="cursor-plugin", metadata, computed): + entry = template_for_schema("cursor-plugin") + mkdir_p(dirname(entry.target_path)) + + content = RENDER_WITH_CONDITIONALS(entry.template_path, metadata, computed) + write(entry.target_path, content) + + # Cursor auto-discovers components from default directories (skills/, + # rules/, agents/, commands/, hooks/hooks.json, mcp.json). Only specify + # explicit paths when overriding defaults. + # + # displayName is not part of the official Cursor manifest schema. + # Use name (kebab-case identifier) and description for display purposes. +``` -| Field | Include when | Value | -|-------|-------------|-------| -| `description` | Always | From computed metadata | -| `contextFileName` | Always | `"GEMINI.md"` | -| `mcpServers` | Source plugin has `.mcp.json` | Map of server configs with `${extensionPath}` variables | -| `hooksDir` | Hooks exist in non-default path | Relative path (default `hooks/` is auto-discovered) | -| `skillsDir` | Skills exist in non-default path | Relative path (default `skills/` is auto-discovered) | -| `settings` | Plugin requires user configuration | Array of `{ name, description, envVar, sensitive? }` | -| `plan` | Plugin uses planning artifacts | `{ "directory": "" }` | -| `excludeTools` | Plugin restricts dangerous operations | Array of tool exclusion strings | +### cursor-marketplace -### Variable substitution +```pseudocode +GENERATE(schema="cursor-marketplace", metadata, computed): + # Only for multi-plugin repositories + IF len(computed.skills) <= 1 AND NOT dir_exists(".cursor-plugin/plugins/"): SKIP -Use `${extensionPath}` for absolute paths, `${workspacePath}` for workspace-relative, `${/}` for OS path separator. + entry = template_for_schema("cursor-marketplace") + mkdir_p(dirname(entry.target_path)) + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) ---- + # Each entry lists source path, description, and optional metadata + # (category, tags, logo). +``` -## gemini-context +### gemini-extension -Build the include blocks from the skills/agents/commands lists inventoried during discovery. The file contains only `@` include directives and no other prose. +```pseudocode +GENERATE(schema="gemini-extension", metadata, computed): + entry = template_for_schema("gemini-extension") + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) + + # Required fields: name (kebab-case), version (semver). + # + # Optional fields (include conditionally): + # description — always, from computed metadata + # contextFileName — always "GEMINI.md" + # mcpServers — when source has .mcp.json; use ${extensionPath} vars + # hooksDir — when hooks exist in non-default path + # skillsDir — when skills exist in non-default path + # settings — when plugin requires user config + # Array of { name, description, envVar, sensitive? } + # plan — when plugin uses planning artifacts + # { "directory": "" } + # excludeTools — when plugin restricts dangerous operations + # + # Variable substitution: + # ${extensionPath} — absolute path to extension + # ${workspacePath} — workspace-relative path + # ${/} — OS path separator +``` ---- +### gemini-context -## agents-context +```pseudocode +GENERATE(schema="gemini-context", metadata, computed): + entry = template_for_schema("gemini-context") + content = RENDER_WITH_BUILDER(entry.template_path, metadata, computed) + write(entry.target_path, content) -Build skill bullet list for `{{skillIncludes}}` and command bullet list for `{{commandIncludes}}` (omit the entire Commands section if no commands exist). + # Output contains only @ include directives and no other prose. +``` ---- +### agents-context -## codex-plugin +```pseudocode +GENERATE(schema="agents-context", metadata, computed): + entry = template_for_schema("agents-context") + content = RENDER_WITH_BUILDER(entry.template_path, metadata, computed) + write(entry.target_path, content) -Create `.codex-plugin/` directory if needed. Only generated when Codex recommendation is `native-plugin-packaging`. + # {{skillIncludes}} — bullet list of skills + # {{commandIncludes}} — bullet list of commands (omit section if none) +``` ---- +### codex-plugin -## codex-marketplace +```pseudocode +GENERATE(schema="codex-plugin", metadata, computed): + # Only for native-plugin-packaging recommendation + IF computed.codex_rec != "native-plugin-packaging": SKIP + + entry = template_for_schema("codex-plugin") + mkdir_p(dirname(entry.target_path)) + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) +``` -Create `.agents/plugins/` directory if needed. Only generated when Codex recommendation is `native-plugin-packaging`. +### codex-marketplace -For single-plugin upstream repos, this manifest points to the repo root with `source.path: "./"`. +```pseudocode +GENERATE(schema="codex-marketplace", metadata, computed): + # Only for native-plugin-packaging recommendation + IF computed.codex_rec != "native-plugin-packaging": SKIP ---- + entry = template_for_schema("codex-marketplace") + mkdir_p(dirname(entry.target_path)) + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) -## antigravity-package + # For single-plugin upstream repos, this manifest points to the repo + # root with source.path: "./" +``` -No special rendering notes. +### antigravity-package ---- +```pseudocode +GENERATE(schema="antigravity-package", metadata, computed): + entry = template_for_schema("antigravity-package") + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) +``` -## openclaw-plugin +### openclaw-plugin -No special rendering notes. +```pseudocode +GENERATE(schema="openclaw-plugin", metadata, computed): + entry = template_for_schema("openclaw-plugin") + content = RENDER_PLAIN(entry.template_path, metadata) + write(entry.target_path, content) +```