fix(plugins): make the 10 focused plugins installable, finish the AgentHub rename

The ten `agenthub-*` plugins were listed by the marketplace but could never be
installed — two schema mistakes present since the entries were first authored
(57dc915), both invisible until you actually run `claude plugin install`:

- marketplace entries used `"path": "plugins/<name>"`; the CLI only accepts
  `"source"`, so every one failed with
  `This plugin's marketplace entry is invalid: source: Invalid input`
- each `plugin.json` carried npm's `"repository": { "type", "url" }` object;
  the schema wants a plain string, so the next attempt failed with
  `Validation errors: repository: Invalid input`

Verified by installing all 11 entries into a throwaway CLAUDE_CONFIG_DIR:
1/11 before, 11/11 after. `plugins-marketplace.test.js` was asserting the
broken shape, which is why CI stayed green — it now asserts `source` and
rejects `path`.

Marketplace renamed `claude-code-agent-monitor-plugins` -> `agenthub`, so the
install line is `agenthub@agenthub`. ARCHITECTURE.md documented a marketplace
name (`smartgift-claude-code-monitor`) that never existed in any revision.

The documented `/plugin marketplace add Smartgift-AI/Claude-Code-Monitor` also
never worked: the `owner/repo` shorthand resolves to github.com only, and this
repo is self-hosted on Gitea. Confirmed the CLI rejects `host/owner/repo` and
ignores GH_HOST / GITHUB_HOST / CLAUDE_CODE_MARKETPLACE_URL, so the full
https:// clone URL is the only supported form. Same GitHub-habit class fixed
elsewhere: raw.githubusercontent.com icon URLs (404, and the repo ships
favicon.svg not favicon.ico — the client had the same dead /favicon.ico),
a smartgift.github.io docs link (404), and Gitea `/blob/<ref>/` paths that
must be `/src/branch/<ref>/`.

Repository renamed to Smartgift-AI/AgentHub throughout. Gitea 301-redirects
the old name, so these were stale rather than broken; `docs/superpowers/`
keeps the old name because those are dated decision records.

Adds `repo-links.test.js` to guard both classes (it runs under the existing
pre-commit gate) plus an openapi.yaml drift check — the committed spec had
silently fallen a minor version behind package.json. `generate-openapi-yaml.js`
now exports `render()` so the test compares against one definition of the dump
options instead of a second copy.
This commit is contained in:
2026-09-10 11:03:14 +07:00
parent 876c6de6d2
commit e972267a42
40 changed files with 290 additions and 170 deletions
+9 -4
View File
@@ -79,16 +79,21 @@ describe("plugin marketplace", () => {
for (const entry of subdirEntries) {
describe(`entry: ${entry.name}`, () => {
it("has name, path, description, tags", () => {
it("has name, source, description, tags", () => {
assert.equal(typeof entry.name, "string");
assert.equal(entry.path, `plugins/${entry.name}`);
// `source`, not `path`: the `claude plugin install` CLI validates the
// entry against a schema that only knows `source`, so a `path` entry
// fails with "This plugin's marketplace entry is invalid: source:
// Invalid input" — the plugin is listed but uninstallable.
assert.equal(entry.source, `./plugins/${entry.name}`);
assert.equal(entry.path, undefined, "`path` is not a source key the CLI accepts");
assert.equal(typeof entry.description, "string");
assert.ok(entry.description.length > 20);
assert.ok(Array.isArray(entry.tags) && entry.tags.length > 0);
});
it("path exists on disk", () => {
assert.ok(fs.existsSync(path.join(REPO_ROOT, entry.path)));
it("source exists on disk", () => {
assert.ok(fs.existsSync(path.join(REPO_ROOT, entry.source)));
});
});
}