e972267a42
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.
44 lines
1.7 KiB
JavaScript
44 lines
1.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* @file Regenerates the repo-root `openapi.yaml` from the single source of
|
|
* truth — `createOpenApiSpec()` in `server/openapi.js`. The JSON spec served at
|
|
* `/api/openapi.json` and this committed YAML mirror are therefore always in
|
|
* sync: run `npm run openapi:yaml` after any spec change. Never hand-edit
|
|
* `openapi.yaml`.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const yaml = require("js-yaml");
|
|
const { createOpenApiSpec } = require("../server/openapi");
|
|
|
|
const OUT = path.join(__dirname, "..", "openapi.yaml");
|
|
|
|
/** The exact bytes `openapi.yaml` must contain for the current spec. Exported so
|
|
* the drift test can compare against the committed file without duplicating the
|
|
* dump options — a second copy of them would let the two definitions diverge,
|
|
* which is the very staleness this file exists to prevent. */
|
|
function render() {
|
|
const spec = createOpenApiSpec();
|
|
const body = yaml.dump(spec, {
|
|
lineWidth: -1, // don't wrap long strings (keeps descriptions/examples intact)
|
|
noRefs: true, // inline any shared object references for a portable document
|
|
sortKeys: false, // preserve authored key order
|
|
});
|
|
const header =
|
|
"# DO NOT EDIT BY HAND. Generated from server/openapi.js via `npm run openapi:yaml`.\n" +
|
|
"# This YAML mirrors the live spec served at GET /api/openapi.json.\n";
|
|
return { text: header + body, pathCount: Object.keys(spec.paths || {}).length };
|
|
}
|
|
|
|
function main() {
|
|
const { text, pathCount } = render();
|
|
fs.writeFileSync(OUT, text, "utf8");
|
|
console.log(`Wrote ${OUT} (${pathCount} paths, ${fs.statSync(OUT).size} bytes).`);
|
|
}
|
|
|
|
if (require.main === module) main();
|
|
|
|
module.exports = { render, OUT };
|