skills/docs/adr/005-dual-publish-plugin-architecture.md
dan a198b31add docs: clarify deployment strategy (beads local, tissue remote)
Local (skills, dotfiles): beads + our dual-publish
Remote (ops-jrz1 VPS): tissue + emes ecosystem

They coexist by environment, not replacing each other.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 16:06:45 -08:00

3.8 KiB

ADR-005: Dual-Publish Plugin Architecture

Status

Accepted (2026-01-09)

Context

Claude Code introduced an official plugin system (October 2025) with:

  • .claude-plugin/plugin.json for metadata
  • skills/ directory for auto-discovery
  • Marketplace distribution via /plugin install
  • Lifecycle hooks (SessionStart, PostToolUse, etc.)

We already had a working system:

  • SKILL.md files with YAML frontmatter
  • Nix deployment to ~/.claude/skills/
  • Cross-agent support (Gemini, OpenCode, Claude)
  • Lenses pattern for multi-pass code review

The Problem

  1. Claude's system is Claude-only - Gemini can't read Claude plugins (see skills-bo8: path restrictions)
  2. Our system lacks hooks - No lifecycle events, mechanical enforcement
  3. Distribution differs - Nix vs marketplace

Alternatives Considered

Option Pros Cons
Migrate fully to Claude plugins Native UX, hooks, marketplace Lose Gemini/OpenCode support
Keep Nix-only Cross-agent, system-level No hooks, no marketplace
Dual-publish (chosen) Best of both Maintenance overhead

Decision

Dual-publish: Maintain both systems in parallel.

Each skill has:

skills/my-skill/
├── .claude-plugin/
│   └── plugin.json        # Claude plugin metadata
├── skills/
│   └── my-skill.md        # Claude auto-discovery
├── SKILL.md               # Nix deployment (cross-agent)
└── README.md

The repo root has:

.claude-plugin/
└── marketplace.json       # Plugin registry for /plugin install

How It Works

Nix path (Gemini, OpenCode, system-level):

  1. deploy-skill.sh copies skill to dotfiles
  2. Home-manager deploys to ~/.claude/skills/
  3. Agents read SKILL.md directly

Claude plugin path:

  1. User adds marketplace: /plugin marketplace add dan/skills
  2. User installs: /plugin install orch@dan-skills
  3. Claude discovers skills/orch.md automatically

Content is identical - skills/orch.md is a copy of SKILL.md.

Marketplace Structure

{
  "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
  "name": "dan-skills",
  "version": "1.0.0",
  "description": "Dual-publish for cross-agent support",
  "owner": { "name": "dan" },
  "plugins": [
    { "name": "orch", "source": "./skills/orch" }
  ]
}

Consequences

Positive

  • Cross-agent support preserved - Gemini, OpenCode continue working
  • Claude features available - Hooks, marketplace, /plugin install
  • Gradual migration - Convert skills one at a time
  • No breaking changes - Existing Nix deployment unchanged

Negative

  • Duplication - SKILL.md and skills/<name>.md must stay in sync
  • Two mental models - Nix vs plugin system
  • Testing complexity - Must verify both paths work

Mitigations

  • Automation: Script to sync SKILL.mdskills/<name>.md
  • CI validation: Check files are identical
  • Documentation: docs/emes-conversion-guide.md

Deployment Environments

Environment Issue Tracker Plugin System
Local (skills, dotfiles) beads Our dual-publish
Remote (ops-jrz1 VPS) tissue emes ecosystem

We use beads locally, tissue on the remote. They coexist by environment.

  • skills-6x1: Epic for emes plugin architecture
  • skills-bo8: Gemini path restriction issue (motivates cross-agent need)
  • skills-1ks: Task to convert remaining skills
  • skills-17f: Forgejo access for distribution

References