Skip to content

feat(plugin): ship the Claude plugin from a generated plugin/ folder - #1207

Merged
boshu2 merged 8 commits into
mainfrom
feat/plugin-subfolder-projection
Oct 10, 2026
Merged

boshu2 merged 8 commits into
mainfrom
feat/plugin-subfolder-projection

Conversation

@boshu2

@boshu2 boshu2 commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Why

The plugin marketplace validator (main @ f9c4b1c) raised 24 holds with one cause: the plugin folder was the repository root, so the screen walked 2,764 files (Go CLI, evals, docs, CI). Holds included BINARIES_NOT_INSPECTED (>512 files), UNREAD_ASSET_REFERENCED, MCP_FORWARDS_CREDENTIAL_ENV (release.yml), RUNTIME_FETCH_EXEC (README, CHANGELOG, .github, cli tests), ICON_MISSING and ARCHIVE_SYMLINK_SKIPPED. The plugin format has no file-exclusion mechanism; the portal's "Plugin path" field scans only the named folder.

Design

  • plugin/ is the plugin folder; .claude-plugin/marketplace.json now has "source": "./plugin".
  • plugin/{skills,hooks,agents,workflows}/ are generated by scripts/regen-plugin-tree.sh from the canonical root trees, which stay put (scripts, tests and ao skills link keep reading skills/).
  • The copy follows git ls-files (tracked + untracked-not-ignored) and skips developer-only tests/, *.bats and Python caches.
  • The generator refuses symlinks, non-image files over 256 KiB, binary non-image files, and more than 512 files, printing the paths; --check reports drift and is wired into scripts/regen-all.sh (both modes), so always.regen-all gates it.
  • Hand-owned in plugin/: .claude-plugin/plugin.json (moved from root; release version lives here), .claude-plugin/icon.png (1024x1024, 286 KB, from docs/assets/logo.svg), and a short README.md with no fetch-and-run commands or images.

Result: 262 files, no symlinks; the only file over 256 KiB is the icon PNG.

Behavior change: bin/ no longer ships

The manifest reference lists bin/ as a plugin component: its files go on the Bash PATH, and claude.ai and Cowork do not install a plugin that has one. The root-folder plugin was therefore putting bin/factory and bin/ralph on users' PATH. They are operator tools (factory reads a private ~/.config/factory), so the projection leaves them out. Noted in CHANGELOG Unreleased.

Consumers updated

  • cli/cmd/ao/version_manifest_parity_test.go: root marker is now .claude-plugin/marketplace.json; surface is plugin/.claude-plugin/plugin.json (a mutation to 9.9.9 fails the test).
  • .githooks/pre-commit, scripts/ci-local-release.sh, tests/docs/validate-doc-release.sh, images/claude/verify.sh, scripts/validate-manifests.sh, tests/run-all.sh, tests/skills/test-runtime-claude-code-smoke.sh
  • Fixtures: tests/scripts/explicit-skill-requests.bats, tests/scripts/test-codex-plugin-metadata-schema.sh
  • schemas/plugin-manifest.v1.schema.json: adds icon (additionalProperties: false)
  • .github/CODEOWNERS (/plugin/), doc links in docs/MIGRATION.md and docs/contracts/multi-runtime-tier-charter.md, CHANGELOG (both copies)
  • skills/doc/scripts/audit-oss-docs.sh: removed an unused variable (SC2034) that shell.shellcheck-changed flagged on the new copy
  • Unchanged on purpose: evals/skills-rpi/** copies .claude-plugin/ (still present with marketplace.json; ao never reads plugin.json); historical docs/releases/** and docs/plans/**.

Installs after this

  • Claude Code: claude plugin marketplace add boshu2/agentops then claude plugin install agentops@agentops-marketplace are unchanged. A marketplace's relative source resolves from the marketplace root, so ./plugin is valid. An existing install should pick up the new layout on its next marketplace update; that upgrade path was not exercised here.
  • Codex: unchanged. Codex reads plugins/marketplace.json (path: "./") and root .codex-plugin/plugin.json (skills: "./skills"), neither of which this PR touches.

Checks

  • bash scripts/regen-plugin-tree.sh && bash scripts/regen-plugin-tree.sh --check: regenerated 262 files, then "plugin/ is current". Failure paths also exercised: a hand-edited copy -> --check rc=1 with a diff summary; a symlink in workflows/ -> refused; a 300 KB file -> refused.
  • bash scripts/regen-all.sh --check: all 8 steps pass, including "Claude plugin folder".
  • find plugin -type l empty; find plugin -type f | wc -l = 262; find plugin -type f -size +256k = only plugin/.claude-plugin/icon.png.
  • claude plugin validate plugin: passed with 2 warnings (unquoted ${CLAUDE_PLUGIN_ROOT} in hooks/hooks.json, pre-existing; --strict fails on them). claude plugin validate .: marketplace passed.
  • shellcheck -S warning on the new script and every edited script: clean.
  • cd cli && go build ./... && go vet ./... && go test ./cmd/ao/...: pass.
  • bats tests/scripts/explicit-skill-requests.bats 5/5; test-codex-plugin-metadata-schema.sh 2/2; Claude runtime smoke 8/8; images/claude/verify.sh OK; validate-manifests.sh and validate-doc-release.sh pass.
  • ao gate check --full (ao built from this branch): 65/66 pass. go.lint fails on cmd/skill-frontmatter-json/main.go (untouched): the local golangci-lint cannot read Go 1.27.2 export data ("export data version 5 is greater than maximum supported version 4") after the toolchain bump in chore(deps): update go toolchain directive to v1.27.2 #1201. Unrelated to this change.
  • Local install smoke with a scratch CLAUDE_CONFIG_DIR: marketplace add from the worktree path, install, details shows 29 skills, 4 agents, 1 hook; the install cache holds exactly the 262 files of plugin/, workflows included. Uninstalled afterwards.

Not verified here

  • The portal's security screens run only on submission; resubmit with Plugin path plugin.
  • Workflow .js files still ship, so COMMAND_SCRIPT_NOT_FOLLOWED stays a reviewer hold by design.
  • Dead links in the shipped copy: skills/skill-eval/SKILL.md and two of its references link to repo evals/, scripts/ and docs/ paths that are outside the plugin folder.

boshu2 added 4 commits October 9, 2026 18:59
The plugin marketplace validator flagged research's allowed-tools line
(ALLOWED_TOOLS_BROAD for unscoped Bash, ALLOWED_TOOLS_UNSCOPED_WRITE for
unscoped Write). No step in the skill needs unprompted execution, so the
grant is removed and research uses the normal permission prompts like
the other 28 skills.
scripts/regen-plugin-tree.sh projects skills/, hooks/, agents/ and
workflows/ into plugin/ from git's file list, skipping developer-only
tests/, *.bats and caches. It refuses symlinks, files over 256 KiB, non-image
binaries and more than 512 files, and --check reports drift. regen-all.sh
runs it in both modes.
The marketplace screened the whole repository because the plugin folder was
the repo root: 2,764 files, most of them never loaded. plugin/ now holds only
the loaded components (skills, agents, the policy hook dispatcher, workflows),
the manifest moved to plugin/.claude-plugin/plugin.json, and a 1024 px
listing icon. The marketplace entry points at ./plugin; install commands are
unchanged.

bin/factory and bin/ralph no longer ship: a plugin bin/ lands on the Bash
PATH and blocks claude.ai and Cowork installs.

Consumers repointed: version parity test, pre-commit version warning,
ci-local-release, validate-doc-release, images/claude/verify.sh,
validate-manifests, run-all, the Claude runtime smoke, two fixture tests, the
manifest schema (icon), CODEOWNERS and two doc links. Codex is untouched.
GIT_ORIGIN was assigned and never read (shellcheck SC2034). The new plugin/
copy made shell.shellcheck-changed report it.
boshu2 added 4 commits October 9, 2026 19:10
The pinned v2.13.1 (x/tools 0.49.0) cannot read go1.27 export data
("export data version 5 is greater than maximum supported version 4"),
so the go.lint gate failed on every PR after the toolchain bump in #1201.
v2.14.0 ships x/tools 0.50.0 and lints the tree clean (0 findings).
claude plugin validate warned that the unquoted placeholder can split on a
path containing spaces. Regenerated plugin/ after #1205 and #1206 merged.
@github-actions github-actions Bot added the hooks label Oct 10, 2026
@boshu2
boshu2 merged commit 407c70b into main Oct 10, 2026
10 checks passed
@boshu2
boshu2 deleted the feat/plugin-subfolder-projection branch October 10, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant