diff --git a/packages/app/src/providers/catalog/plugin.test.ts b/packages/app/src/providers/catalog/plugin.test.ts new file mode 100644 index 0000000000..41a2f72a37 --- /dev/null +++ b/packages/app/src/providers/catalog/plugin.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, test } from "bun:test" +import type { PluginInfo } from "@opencode-ai/client" +import { pluginLabels } from "./plugin" + +describe("pluginLabels", () => { + test("omits built-in plugins", () => { + const plugins: PluginInfo[] = [ + { id: "opencode.internal", source: { type: "builtin" }, status: "active", tui: false }, + { id: "package-plugin", source: { type: "package", package: "example" }, status: "active", tui: false }, + { id: "local-plugin", source: { type: "local", path: "/tmp/plugin.ts" }, status: "active", tui: false }, + { id: "sdk-plugin", source: { type: "sdk" }, status: "active", tui: false }, + ] + + expect(pluginLabels(plugins)).toEqual(["package-plugin", "local-plugin", "sdk-plugin"]) + }) +}) diff --git a/packages/app/src/providers/catalog/plugin.ts b/packages/app/src/providers/catalog/plugin.ts index 2c4145656f..dd03c56b73 100644 --- a/packages/app/src/providers/catalog/plugin.ts +++ b/packages/app/src/providers/catalog/plugin.ts @@ -6,3 +6,7 @@ export function pluginLabel(plugin: PluginInfo) { if (plugin.source.type === "local") return plugin.source.path return plugin.source.type } + +export function pluginLabels(plugins: readonly PluginInfo[]) { + return plugins.filter((plugin) => plugin.source.type !== "builtin").map(pluginLabel) +} diff --git a/packages/app/src/settings/providers/extensions.tsx b/packages/app/src/settings/providers/extensions.tsx index ffce573025..70732a515c 100644 --- a/packages/app/src/settings/providers/extensions.tsx +++ b/packages/app/src/settings/providers/extensions.tsx @@ -6,7 +6,7 @@ import { useLanguage } from "@/runtime/i18n/language" import { useData } from "@/runtime/server/current" import { useServerSDK } from "@/runtime/server/client" import { useMcpToggle } from "@/providers/connect/mcp" -import { pluginLabel } from "@/providers/catalog/plugin" +import { pluginLabels } from "@/providers/catalog/plugin" import { ExternalLink } from "@/runtime/platform/external-link" import { InlineServerSelect } from "@/settings/server-select" import "@/settings/settings.css" @@ -45,9 +45,7 @@ export const SettingsExtensions: Component = () => { () => serverSdk.connection.status() === "connected", () => serverSdk.api.plugin.list().then((result) => result.data), ) - const plugins = createMemo(() => - (pluginList.latest ?? []).map((item) => ({ name: pluginLabel(item) })), - ) + const plugins = createMemo(() => pluginLabels(pluginList.latest ?? []).map((name) => ({ name }))) createEffect(() => { if (serverSdk.connection.status() !== "connected") return diff --git a/packages/app/src/settings/workspaces/project-extensions.tsx b/packages/app/src/settings/workspaces/project-extensions.tsx index a961eb2280..fe9c2029f6 100644 --- a/packages/app/src/settings/workspaces/project-extensions.tsx +++ b/packages/app/src/settings/workspaces/project-extensions.tsx @@ -7,7 +7,7 @@ import { useMcpToggle } from "@/providers/connect/mcp" import { useWorkspaceLocation } from "@/workspaces/location" import { useServerSDK } from "@/runtime/server/client" import { useData } from "@/runtime/server/current" -import { pluginLabel } from "@/providers/catalog/plugin" +import { pluginLabels } from "@/providers/catalog/plugin" import { ExternalLink } from "@/runtime/platform/external-link" type SkillItem = { @@ -102,10 +102,10 @@ export const ProjectSettingsExtensions: Component = () => { () => (serverSDK.connection.status() === "connected" ? directorySDK().directory : undefined), (directory) => serverSDK.api.plugin.list({ location: { directory } }).then((result) => result.data), ) - const globalPlugins = createMemo(() => (globalPluginList.latest ?? []).map(pluginLabel)) + const globalPlugins = createMemo(() => pluginLabels(globalPluginList.latest ?? [])) const projectPlugins = createMemo(() => { const shared = new Set(globalPlugins()) - return (projectPluginList.latest ?? []).map(pluginLabel).filter((name) => !shared.has(name)) + return pluginLabels(projectPluginList.latest ?? []).filter((name) => !shared.has(name)) }) const serverSkills = createMemo(() => data.location.skill.list() ?? []) diff --git a/packages/app/src/shell/status/body.tsx b/packages/app/src/shell/status/body.tsx index e4e322065f..d9d5aa47f3 100644 --- a/packages/app/src/shell/status/body.tsx +++ b/packages/app/src/shell/status/body.tsx @@ -6,7 +6,7 @@ import { useMcpToggle } from "@/providers/connect/mcp" import { useWorkspaceLocation } from "@/workspaces/location" import { useData } from "@/runtime/server/current" import { useServerSDK } from "@/runtime/server/client" -import { pluginLabel } from "@/providers/catalog/plugin" +import { pluginLabels } from "@/providers/catalog/plugin" const pluginEmptyMessage = (value: string, file: string): JSXElement => { const parts = value.split(file) @@ -39,7 +39,7 @@ export function StatusPopoverBody(props: { shown: boolean }) { () => (props.shown ? sdk().directory : undefined), (directory) => serverSDK.api.plugin.list({ location: { directory } }).then((result) => result.data), ) - const plugins = createMemo(() => (pluginList.latest ?? []).map(pluginLabel)) + const plugins = createMemo(() => pluginLabels(pluginList.latest ?? [])) const pluginCount = createMemo(() => plugins().length) const pluginEmpty = createMemo(() => pluginEmptyMessage(language.t("dialog.plugins.empty"), "opencode.json"))