Files
clawhub/packages/schema/dist/pluginCategories.js
T
Jason (Json)andPatrick Erichsen 4bc58e4939 feat: add catalog classification backfill (#2719)
* feat: add controlled catalog taxonomy and topics

* refactor: limit taxonomy input to publish surfaces

* fix: omit inactive official-first filter

* fix: preserve unsaved catalog metadata edits

* fix: enforce catalog taxonomy invariants

* fix: close catalog taxonomy review gaps

* fix: tolerate retired stored skill categories

* fix: bound catalog metadata filter scans

* fix: preserve comma-containing topic labels

* fix: tolerate retired stored plugin categories

* fix: ignore empty secret integration metadata

* fix: honor explicit categories in related skills

* fix: harden taxonomy rollout migration

* fix: harden taxonomy browse pagination

* fix: preserve skill topic recommendation fallback

* fix: scale curated skill category browse

* fix: preserve legacy plugin category filters

* fix: preserve taxonomy compatibility semantics

* fix: preserve legacy catalog browse links

* feat: make catalog metadata editing explicit

* test: update plugin manage context contract

* fix: address taxonomy review findings

* fix: preserve empty category publish flags

* fix: preserve catalog search and publish metadata

* chore: keep taxonomy migration operator-run

* docs: keep taxonomy migrations operator-run

* fix: reject inherited category aliases

* fix: preserve normalized topic search behavior

* test: cover full topic pagination cursors

* feat: add catalog classification backfill

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-06-17 17:58:26 -07:00

82 lines
3.1 KiB
JavaScript

import { resolvePluginCategories } from "./catalogMetadata.js";
export { isPluginCategorySlug, PLUGIN_CATEGORY_DEFINITIONS, PLUGIN_CATEGORY_SLUGS, } from "./catalogMetadata.js";
function isRecord(value) {
return Boolean(value && typeof value === "object" && !Array.isArray(value));
}
function hasValues(value) {
return Array.isArray(value) && value.length > 0;
}
function hasProperties(value) {
return isRecord(value) && Object.keys(value).length > 0;
}
export function inferPluginCategoriesFromManifest(manifest) {
if (!isRecord(manifest))
return [];
const categories = [];
const add = (category) => {
if (!categories.includes(category))
categories.push(category);
};
const kinds = Array.isArray(manifest.kind) ? manifest.kind : [manifest.kind];
const contracts = isRecord(manifest.contracts) ? manifest.contracts : {};
if (hasValues(manifest.channels))
add("channels");
if (hasValues(manifest.providers) || hasValues(manifest.cliBackends))
add("models");
if (kinds.includes("memory") || hasValues(contracts.embeddingProviders))
add("memory");
if (kinds.includes("context-engine") || hasValues(contracts.memoryEmbeddingProviders)) {
add("context");
}
if (hasValues(contracts.speechProviders) ||
hasValues(contracts.realtimeTranscriptionProviders) ||
hasValues(contracts.realtimeVoiceProviders) ||
hasValues(contracts.transcriptSourceProviders)) {
add("voice");
}
if (hasValues(contracts.mediaUnderstandingProviders) ||
hasValues(contracts.imageGenerationProviders) ||
hasValues(contracts.musicGenerationProviders) ||
hasValues(contracts.videoGenerationProviders)) {
add("media");
}
if (hasValues(contracts.webFetchProviders) || hasValues(contracts.webSearchProviders)) {
add("web");
}
if (hasValues(contracts.tools) || hasValues(manifest.commandAliases))
add("tools");
if (hasValues(contracts.embeddedExtensionFactories) ||
hasValues(contracts.agentToolResultMiddleware)) {
add("runtime");
}
if (hasValues(contracts.gatewayMethodDispatch))
add("gateway");
if (hasValues(contracts.externalAuthProviders) ||
hasProperties(manifest.secretProviderIntegrations)) {
add("security");
}
return categories.slice(0, 3);
}
export function derivePluginCategoryTags(input) {
if (input.family === "skill")
return [];
return resolvePluginCategories({
declared: input.categories,
inferred: input.inferredCategories ?? inferPluginCategoriesFromManifest(input.pluginManifest),
});
}
export function resolveStoredPluginCategories(input) {
if (input.family === "skill")
return [];
try {
const inferenceCurrent = Boolean(input.latestReleaseId) && input.latestReleaseId === input.inferredFromReleaseId;
return resolvePluginCategories({
declared: input.categories,
inferred: inferenceCurrent ? input.inferredCategories : undefined,
});
}
catch {
return resolvePluginCategories({});
}
}
//# sourceMappingURL=pluginCategories.js.map