Skip to content

Switch in-repo plugins to per-subdirectory layout with sync script#118

Merged
shinpr merged 3 commits intomainfrom
chore/plugin-structure-workaround
Apr 30, 2026
Merged

Switch in-repo plugins to per-subdirectory layout with sync script#118
shinpr merged 3 commits intomainfrom
chore/plugin-structure-workaround

Conversation

@shinpr
Copy link
Copy Markdown
Owner

@shinpr shinpr commented Apr 30, 2026

Summary

  • Each in-repo plugin (dev-workflows, dev-workflows-frontend, dev-skills) now lives in its own subdirectory at the repo root, populated from the canonical top-level agents/ and skills/ directories by scripts/sync-plugins.mjs
  • A lefthook pre-commit hook keeps the subdirectories in sync and runs claude plugin validate locally; a GitHub Actions workflow guards drift in PRs
  • This is an explicit workaround for two open Claude Code regressions and is intended to be reverted once either is fixed upstream

Why

The previous flatten layout (#117, source: "./") sidestepped a v2.1.117 installer regression that drops symlinked content (anthropics/claude-code#53948), but exposed a separate latent bug: the agents / skills filter arrays in marketplace.json are silently ignored by the loader (anthropics/claude-code#13344). As a result dev-skills was loading every agent and recipe-* skill at the repo root, instead of only the 9 knowledge skills it declares.

Per-subdirectory sources sidestep both bugs by giving the loader a directory that physically contains only the intended subset. The agents / skills arrays in marketplace.json are kept and used as the spec for sync-plugins.mjs, so when upstream filtering is fixed we can collapse back to a single-source layout without re-deriving the per-plugin curation.

What changed

  • scripts/sync-plugins.mjs — reads marketplace.json, copies the listed agents/skills into each <plugin-name>/ subdirectory, and generates <plugin-name>/.claude-plugin/plugin.json. --check mode does the same in a temp directory and exits non-zero on drift. Cross-platform (no shell utilities); rejects path traversal, source typos that would overwrite canonical directories, and any symlink in the source tree
  • package.json + pnpm-lock.yaml + .nvmrc — Node >=22, pnpm, single dev dependency on lefthook
  • lefthook.yml — pre-commit runs pnpm sync (re-stages the generated subdirectories) and then claude plugin validate for marketplace + each plugin
  • .github/workflows/sync-check.yml — runs pnpm sync:check on PRs; SHA-pinned third-party actions, least-privilege contents: read
  • .claude-plugin/marketplace.jsonsource switched to ./<plugin-name>, strict restored to true, version bumped to 0.16.16
  • dev-workflows/, dev-workflows-frontend/, dev-skills/ — generated bundles
  • CONTRIBUTING.md — short note on the editing workflow

Verification

Verified with a clean install in an isolated HOME on macOS (Claude Code 2.1.123):

Plugin agents skills Expected
dev-workflows 20 20 20 / 20
dev-workflows-frontend 20 18 20 / 18
dev-skills 0 9 0 / 9 (no agent or recipe leakage)

claude plugin validate passes for marketplace.json and all three subdirectories. pnpm sync:check reports clean. Drift detection, malicious-path rejection, and internal-symlink rejection were all confirmed against synthetic inputs.

Test plan

  • CI sync-check workflow passes
  • Local install from this branch into a fresh HOME reports the expected agent/skill counts above
  • Editing a file under agents/ or skills/ and committing causes the lefthook hook to run pnpm sync and re-stage the generated subdirectory

shinpr and others added 3 commits May 1, 2026 06:02
Introduce package.json (Node >=22, pnpm), a sync script that mirrors
the canonical agents/skills directories into per-plugin subdirectories,
and a lefthook pre-commit job that runs sync + claude plugin validate.
A sync-check GitHub Actions workflow guards against drift in PRs.

This is preparation for the per-subdirectory plugin layout switch
in the following commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each in-repo plugin now lives in its own subdirectory (./dev-workflows,
./dev-workflows-frontend, ./dev-skills) populated by scripts/sync-plugins.mjs
from the canonical top-level agents/ and skills/ directories. The
marketplace.json source field points at each subdirectory and strict mode
is restored.

This works around two upstream Claude Code regressions in the loader:

* The plugin installer stopped copying symlinked content in v2.1.117
  (anthropics/claude-code#53948, still open) — which made the previous
  flatten that uses source: "./" the only viable single-source layout.
* The marketplace agents/skills filter arrays under source: "./" are
  silently ignored by the loader (anthropics/claude-code#13344, still
  open) — so dev-skills was loading every agent and recipe-* skill that
  existed at the repo root.

Switching to per-subdirectory sources sidesteps both issues by giving the
loader a directory that physically contains only the intended subset.
The marketplace.json arrays are kept and used as the spec for the sync
script, so when the upstream filter is fixed we can collapse back to a
single-source layout without re-deriving the per-plugin curation.

Verified with a clean install in an isolated HOME on macOS:

* dev-workflows: 20 agents / 20 skills
* dev-workflows-frontend: 20 agents / 18 skills
* dev-skills: 0 agents / 9 skills (no leakage)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Note that the bundled plugin subdirectories are generated, edits go to
the canonical agents/ and skills/ directories, and pnpm install wires up
the lefthook pre-commit hook that keeps the two in sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shinpr shinpr self-assigned this Apr 30, 2026
@shinpr shinpr merged commit a5ce193 into main Apr 30, 2026
1 check passed
@shinpr shinpr deleted the chore/plugin-structure-workaround branch April 30, 2026 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant