GitHub, unlike Gitea, doesn't create a repo on first push, so a mirror
pointed at a nonexistent GitHub repo would register successfully but
fail every sync. Check for the target repo via gh/MCP/REST API and
create it if missing, falling back to a clear manual-creation warning
when no capability is available. Bumps plugin version to 1.1.0.
Claude Code's plugin/marketplace schemas require author to be an object
({"name": ...}), not a bare string — installing any plugin from this
marketplace failed validation ("author: Invalid input: expected object,
received string"). Convert every plugins/*/plugin.json author field
accordingly, and add a build-time check so this can't regress.
Also propagate each plugin's description/author into the generated
marketplace.json entries (not just its own nested plugin.json), matching
Anthropic's official marketplace shape, so the Discover UI shows a
description before install instead of a blank "Plugin details" pane.
Adds a mirror-it skill that registers a Gitea push mirror via `tea api`
against the push_mirrors endpoint, using Gitea's actual
remote_address/remote_username/remote_password fields (never embedding
credentials in the mirror URL). Credentials can come from the
environment or fall back to a repo-root .env file, and all shell
snippets are POSIX-compatible so the skill isn't tied to any one shell.
Also ignores .env* files repo-wide.
Lets a plugin.json declare an `external` target->source map instead of
carrying local content, so the generator emits that Claude Code
marketplace source (e.g. a GitHub repo) verbatim rather than a local
dist path. Adds caveman and ponytail as external entries, installed
straight from their upstream repos with nothing vendored or copied.
Claude Code-only for now — agy has no equivalent external-source
mechanism, and both plugins' hooks/MCP assume Claude Code's runtime.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YVWWAnvR5TDQzmfY3RXG9
Split the GitHub/Gitea MCP servers out of core-essentials into their own
github-mcp/gitea-mcp plugins, so a user can install either, both, or
neither independently instead of always getting both bundled together.
Add an optional "targets" field, honored at both levels:
- plugin.json: restricts the whole plugin (including any hooks/mcp/
rules/commands/agents it bundles) to specific targets.
- a skill's own SKILL.md frontmatter: restricts just that one skill,
independent of its plugin's sibling skills (narrowed to, never wider
than, the plugin's own targets).
Omitting it (the default everywhere) means every target in manifest.yaml,
so this is fully backward compatible. A plugin left with no content at
all for a given target is now skipped entirely for that target rather
than emitting an empty output directory / marketplace entry.
Applies this to the actual motivating case: delegate-agy only makes sense
run from Claude Code (it shells out to `agy`), so it's now marked
targets: [claude-code] and no longer ships into dist/agy at all — it was
previously being installed into agy for no reason.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YVWWAnvR5TDQzmfY3RXG9
Testing against a live agy session confirmed the plugins.json "entries"
mechanism documented for agy does not reliably surface a plugins/<name>/
bundle as a plugin: hooks and MCP servers inside it are never loaded, and
only a stray skill occasionally surfaces via agy's generic skill-walk
("No plugins are currently loaded or discoverable in the active context").
Add scripts/install_agy.py, which instead pushes a generated dist/agy
into the locations confirmed to actually work: symlinks each skill into
~/.gemini/config/skills/<name>/ (or a project's .agents/skills/ with
--project) and merges each plugin's mcpServers/hooks into the shared
global mcp_config.json/hooks.json without touching unrelated entries.
Supports --uninstall to cleanly remove exactly what was added.
generate_plugins.py's --install-local now shells out to it for the agy
side instead of printing the broken plugins.json instructions.
Also fixes plugins/core-essentials/mcp.json, which had been hand-added
directly under the repo root (never discovered by the generator) with
invalid JSON (missing the outer object) and no env-var indirection for
its GitHub/Gitea tokens; moved under the plugin and wired up with
${VAR} placeholders instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YVWWAnvR5TDQzmfY3RXG9
Replace the skills-only SSoT (skills/<name>/SKILL.md) with plugins/<name>/,
where a plugin can bundle skills, MCP servers, lifecycle hooks, rules,
commands, and agents. scripts/generate_plugins.py now discovers plugins
from one or more layered --source roots and emits real per-target trees
(dist/claude-code/** + .claude-plugin/marketplace.json, dist/agy/**),
translating hooks.json/mcp.json between Claude Code's and agy's actual
schemas instead of only fanning out flat skill lists.
Adds a private-overlay build path (--source/--private-repo/--out/
--install-local) so a second, non-public repo (PII, tokens, personal-only
plugins) can extend or override the public plugin set without either repo
touching the other's history — private builds default to a gitignored
dist-private/ and never land in tracked output.
Drops install.sh and descriptions.json: both only ever covered skills and
pre-date the native plugin marketplace; both tools now install the plugin
marketplace the documented way (Claude Code's /plugin marketplace add,
agy's plugins.json entries). CI path filters move to plugins/**, and the
workflow gains workflow_dispatch for a manual re-trigger.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YVWWAnvR5TDQzmfY3RXG9