Remove local suffix from adapter display names

This commit is contained in:
Paperclip Cody committed 2026-07-03 02:42:59 +00:00
1 parent bac7307ec4
commit 492c88a0b4
25 files changed
+106 -48

No files matched your search

+1 -1
View File
@@ -54,7 +54,7 @@ my-adapter/ # external plugin
```ts
export const type = "my_agent"; // snake_case, globally unique
export const label = "My Agent (local)";
export const label = "My Agent";
export const models = [
{ id: "model-a", label: "Model A" },
];
+1 -1
View File
@@ -116,7 +116,7 @@ The plugin loader calls `createServerAdapter()` from your package root. This fun
```ts
export const type = "my_adapter"; // snake_case, globally unique
export const label = "My Agent (local)";
export const label = "My Agent";
export const models = [
{ id: "model-a", label: "Model A" },
+1 -1
View File
@@ -1,7 +1,7 @@
import type { AdapterModel } from "@paperclipai/adapter-utils";
export const type = "acpx_local";
export const label = "ACPX (local)";
export const label = "ACPX";
export const DEFAULT_ACPX_LOCAL_AGENT = "claude";
export const DEFAULT_ACPX_LOCAL_MODE = "persistent";
+1 -1
View File
@@ -1,7 +1,7 @@
import type { AdapterModelProfileDefinition } from "@paperclipai/adapter-utils";
export const type = "claude_local";
export const label = "Claude Code (local)";
export const label = "Claude Code";
export const SANDBOX_INSTALL_COMMAND = "npm install -g @anthropic-ai/claude-code";
+1 -1
View File
@@ -1,7 +1,7 @@
import type { AdapterModelProfileDefinition } from "@paperclipai/adapter-utils";
export const type = "codex_local";
export const label = "Codex (local)";
export const label = "Codex";
export const SANDBOX_INSTALL_COMMAND = "npm install -g @openai/codex";
+1 -1
View File
@@ -1,7 +1,7 @@
import type { AdapterModelProfileDefinition } from "@paperclipai/adapter-utils";
export const type = "cursor";
export const label = "Cursor CLI (local)";
export const label = "Cursor CLI";
// Cursor CLI is not distributed as an npm package — the official install
// path is the upstream installer script at cursor.com/install. Other adapters
+1 -1
View File
@@ -4,7 +4,7 @@ import {
} from "@paperclipai/adapter-utils";
export const type = "gemini_local";
export const label = "Gemini CLI (local)";
export const label = "Gemini CLI";
export const SANDBOX_INSTALL_COMMAND = buildSandboxNpmInstallCommand("@google/gemini-cli");
+1 -1
View File
@@ -1,5 +1,5 @@
export const type = "grok_local";
export const label = "Grok Build (local)";
export const label = "Grok Build";
export const DEFAULT_GROK_LOCAL_MODEL = "grok-build";
@@ -1,7 +1,7 @@
import type { AdapterModelProfileDefinition } from "@paperclipai/adapter-utils";
export const type = "opencode_local";
export const label = "OpenCode (local)";
export const label = "OpenCode";
// Use OpenCode's official installer instead of `npm install -g opencode-ai`.
// The npm package reifies four large Linux x64 prebuilt-binary subpackages
+1 -1
View File
@@ -1,7 +1,7 @@
import type { AdapterModelProfileDefinition } from "@paperclipai/adapter-utils";
export const type = "pi_local";
export const label = "Pi (local)";
export const label = "Pi";
export const SANDBOX_INSTALL_COMMAND = "npm install -g @earendil-works/pi-coding-agent@0.74.0";
+1 -1
View File
@@ -4,7 +4,7 @@ import { SchemaConfigFields } from "../schema-config-fields";
export const acpxLocalUIAdapter: UIAdapterModule = {
type: "acpx_local",
label: "ACPX (local)",
label: "ACPX",
parseStdoutLine: parseAcpxStdoutLine,
ConfigFields: SchemaConfigFields,
buildAdapterConfig: buildAcpxLocalConfig,
@@ -0,0 +1,45 @@
import { describe, expect, it } from "vitest";
import { getAdapterDisplay, getAdapterLabel, getAdapterLabels } from "./adapter-display-registry";
describe("adapter display registry", () => {
it("uses user-facing labels without the legacy local qualifier for built-in adapters", () => {
expect(getAdapterLabel("codex_local")).toBe("Codex");
expect(getAdapterLabel("claude_local")).toBe("Claude Code");
expect(getAdapterLabel("acpx_local")).toBe("ACPX");
expect(getAdapterLabel("cursor")).toBe("Cursor");
expect(getAdapterLabel("gemini_local")).toBe("Gemini CLI");
expect(getAdapterLabel("grok_local")).toBe("Grok Build");
expect(getAdapterLabel("hermes_local")).toBe("Hermes");
expect(getAdapterLabel("opencode_local")).toBe("OpenCode");
expect(getAdapterLabel("pi_local")).toBe("Pi");
expect(getAdapterLabels()).toMatchObject({
codex_local: "Codex",
claude_local: "Claude Code",
acpx_local: "ACPX",
cursor: "Cursor",
gemini_local: "Gemini CLI",
grok_local: "Grok Build",
hermes_local: "Hermes",
opencode_local: "OpenCode",
pi_local: "Pi",
});
});
it("drops local suffixes for unknown plugin adapter labels", () => {
expect(getAdapterLabel("droid_local")).toBe("Droid");
expect(getAdapterDisplay("droid_local")).toMatchObject({
label: "Droid",
description: "External adapter",
});
});
it("keeps a gateway suffix for unknown plugin adapter labels", () => {
expect(getAdapterLabel("droid_gateway")).toBe("Droid (gateway)");
expect(getAdapterDisplay("droid_gateway")).toMatchObject({
label: "Droid (gateway)",
description: "External gateway adapter",
});
});
});
+29 -19
View File
@@ -21,13 +21,19 @@ import { OpenCodeLogoIcon } from "@/components/OpenCodeLogoIcon";
// Type suffix parsing
// ---------------------------------------------------------------------------
const TYPE_SUFFIXES: Record<string, string> = {
_local: "local",
// Suffixes stripped from type ids when deriving a human-readable label for
// unknown (plugin) adapter types. "_local" is a legacy qualifier from before
// first-class Environments and is never displayed; "_gateway" is re-appended
// as " (gateway)" to disambiguate gateway variants. Known adapters in
// `adapterDisplayMap` have final labels and never get a derived suffix.
const STRIPPED_TYPE_SUFFIXES = ["_local", "_gateway"] as const;
const DISPLAY_SUFFIXES: Record<string, string> = {
_gateway: "gateway",
};
function getTypeSuffix(type: string): string | null {
for (const [suffix, mode] of Object.entries(TYPE_SUFFIXES)) {
for (const [suffix, mode] of Object.entries(DISPLAY_SUFFIXES)) {
if (type.endsWith(suffix)) return mode;
}
return null;
@@ -55,57 +61,57 @@ export interface AdapterDisplayInfo {
const adapterDisplayMap: Record<string, AdapterDisplayInfo> = {
acpx_local: {
label: "ACPX",
description: "Experimental local ACPX multi-agent adapter",
description: "Experimental ACPX multi-agent harness",
icon: Bot,
experimental: true,
hideFromVisualSelection: true,
},
claude_local: {
label: "Claude Code",
description: "Local Claude agent",
description: "Claude Code CLI harness",
icon: Sparkles,
recommended: true,
},
codex_local: {
label: "Codex",
description: "Local Codex agent",
description: "Codex CLI harness",
icon: Code,
recommended: true,
},
gemini_local: {
label: "Gemini CLI",
description: "Local Gemini agent",
description: "Gemini CLI harness",
icon: Gem,
},
grok_local: {
label: "Grok Build",
description: "Local Grok Build agent",
description: "Grok Build harness",
icon: Bot,
},
hermes_gateway: {
label: "Hermes",
label: "Hermes (gateway)",
description: "Remote Hermes API server",
icon: Bot,
hideFromVisualSelection: true,
},
hermes_local: {
label: "Hermes",
description: "Local Hermes agent",
description: "Hermes harness",
icon: Bot,
},
opencode_local: {
label: "OpenCode",
description: "Local multi-provider agent",
description: "OpenCode multi-provider harness",
icon: OpenCodeLogoIcon,
},
pi_local: {
label: "Pi",
description: "Local Pi agent",
description: "Pi harness",
icon: Terminal,
},
cursor: {
label: "Cursor",
description: "Local Cursor agent",
description: "Cursor CLI harness",
icon: MousePointer2,
},
cursor_cloud: {
@@ -142,7 +148,7 @@ const adapterDisplayMap: Record<string, AdapterDisplayInfo> = {
function humanizeType(type: string): string {
// Strip known type suffixes so "droid_local" → "Droid", not "Droid Local"
let base = type;
for (const suffix of Object.keys(TYPE_SUFFIXES)) {
for (const suffix of STRIPPED_TYPE_SUFFIXES) {
if (base.endsWith(suffix)) {
base = base.slice(0, -suffix.length);
break;
@@ -152,16 +158,20 @@ function humanizeType(type: string): string {
}
export function getAdapterLabel(type: string): string {
const base = adapterDisplayMap[type]?.label ?? humanizeType(type);
return withSuffix(base, getTypeSuffix(type));
// Known labels are final — only unknown (plugin) types get a derived
// suffix, so labels like "OpenClaw Gateway" don't become
// "OpenClaw Gateway (gateway)".
const known = adapterDisplayMap[type];
if (known) return known.label;
return withSuffix(humanizeType(type), getTypeSuffix(type));
}
export function getAdapterLabels(): Record<string, string> {
const suffixed: Record<string, string> = {};
const labels: Record<string, string> = {};
for (const [type, info] of Object.entries(adapterDisplayMap)) {
suffixed[type] = withSuffix(info.label, getTypeSuffix(type));
labels[type] = info.label;
}
return suffixed;
return labels;
}
export function getAdapterDisplay(type: string): AdapterDisplayInfo {
+1 -1
View File
@@ -5,7 +5,7 @@ import { buildClaudeLocalConfig } from "@paperclipai/adapter-claude-local/ui";
export const claudeLocalUIAdapter: UIAdapterModule = {
type: "claude_local",
label: "Claude Code (local)",
label: "Claude Code",
parseStdoutLine: parseClaudeStdoutLine,
ConfigFields: ClaudeLocalConfigFields,
buildAdapterConfig: buildClaudeLocalConfig,
+1 -1
View File
@@ -5,7 +5,7 @@ import { buildCodexLocalConfig } from "@paperclipai/adapter-codex-local/ui";
export const codexLocalUIAdapter: UIAdapterModule = {
type: "codex_local",
label: "Codex (local)",
label: "Codex",
parseStdoutLine: parseCodexStdoutLine,
ConfigFields: CodexLocalConfigFields,
buildAdapterConfig: buildCodexLocalConfig,
+1 -1
View File
@@ -5,7 +5,7 @@ import { buildCursorLocalConfig } from "@paperclipai/adapter-cursor-local/ui";
export const cursorLocalUIAdapter: UIAdapterModule = {
type: "cursor",
label: "Cursor CLI (local)",
label: "Cursor CLI",
parseStdoutLine: parseCursorStdoutLine,
ConfigFields: CursorLocalConfigFields,
buildAdapterConfig: buildCursorLocalConfig,
+1 -1
View File
@@ -5,7 +5,7 @@ import { buildGeminiLocalConfig } from "@paperclipai/adapter-gemini-local/ui";
export const geminiLocalUIAdapter: UIAdapterModule = {
type: "gemini_local",
label: "Gemini CLI (local)",
label: "Gemini CLI",
parseStdoutLine: parseGeminiStdoutLine,
ConfigFields: GeminiLocalConfigFields,
buildAdapterConfig: buildGeminiLocalConfig,
+1 -1
View File
@@ -5,7 +5,7 @@ import { GrokLocalConfigFields } from "./config-fields";
export const grokLocalUIAdapter: UIAdapterModule = {
type: "grok_local",
label: "Grok Build (local)",
label: "Grok Build",
parseStdoutLine: parseGrokStdoutLine,
createStdoutParser: createGrokStdoutParser,
ConfigFields: GrokLocalConfigFields,
+1 -1
View File
@@ -5,7 +5,7 @@ import { buildOpenCodeLocalConfig } from "@paperclipai/adapter-opencode-local/ui
export const openCodeLocalUIAdapter: UIAdapterModule = {
type: "opencode_local",
label: "OpenCode (local)",
label: "OpenCode",
parseStdoutLine: parseOpenCodeStdoutLine,
ConfigFields: OpenCodeLocalConfigFields,
buildAdapterConfig: buildOpenCodeLocalConfig,
+1 -1
View File
@@ -5,7 +5,7 @@ import { buildPiLocalConfig } from "@paperclipai/adapter-pi-local/ui";
export const piLocalUIAdapter: UIAdapterModule = {
type: "pi_local",
label: "Pi (local)",
label: "Pi",
parseStdoutLine: parsePiStdoutLine,
ConfigFields: PiLocalConfigFields,
buildAdapterConfig: buildPiLocalConfig,
+4 -1
View File
@@ -72,7 +72,10 @@ function AdapterRow({
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<span className={cn("font-medium", adapter.disabled && "text-muted-foreground line-through")}>
{adapter.label || getAdapterLabel(adapter.type)}
{/* The server reports label = raw type id, so prefer the display
registry's human label; keep a real self-reported label if an
external adapter ever provides one. */}
{adapter.label && adapter.label !== adapter.type ? adapter.label : getAdapterLabel(adapter.type)}
</span>
<Badge variant="outline">{adapter.source === "external" ? "External" : "Built-in"}</Badge>
{adapter.source === "external" && (
+2 -2
View File
@@ -104,7 +104,7 @@ function installStorybookApiFixtures() {
return Response.json([
{
type: "claude_local",
label: "Claude Local",
label: "Claude Code",
source: "builtin",
modelsCount: 2,
loaded: true,
@@ -119,7 +119,7 @@ function installStorybookApiFixtures() {
},
{
type: "codex_local",
label: "Codex Local",
label: "Codex",
source: "builtin",
modelsCount: 3,
loaded: true,
@@ -298,7 +298,7 @@ const storybookSecrets: CompanySecret[] = [
const adapterFixtures: AdapterInfo[] = [
{
type: "codex_local",
label: "Codex Local",
label: "Codex",
source: "builtin",
modelsCount: 3,
loaded: true,
@@ -313,7 +313,7 @@ const adapterFixtures: AdapterInfo[] = [
},
{
type: "claude_local",
label: "Claude Local",
label: "Claude Code",
source: "builtin",
modelsCount: 2,
loaded: true,
@@ -373,7 +373,7 @@ function hydrateDialogQueries(queryClient: ReturnType<typeof useQueryClient>) {
queryClient.setQueryData(queryKeys.adapters.all, [
{
type: "codex_local",
label: "Codex local",
label: "Codex",
source: "builtin",
modelsCount: 5,
loaded: true,
@@ -388,7 +388,7 @@ function hydrateDialogQueries(queryClient: ReturnType<typeof useQueryClient>) {
},
{
type: "claude_local",
label: "Claude local",
label: "Claude Code",
source: "builtin",
modelsCount: 4,
loaded: true,
@@ -721,7 +721,7 @@ function useCheapLaneAdapterOverrides(variant: CheapLaneVariant) {
queryClient.setQueryData(queryKeys.adapters.all, [
{
type: "codex_local",
label: "Codex local",
label: "Codex",
source: "builtin",
modelsCount: 5,
loaded: true,
@@ -736,7 +736,7 @@ function useCheapLaneAdapterOverrides(variant: CheapLaneVariant) {
},
{
type: "opencode_local",
label: "OpenCode local",
label: "OpenCode",
source: "builtin",
modelsCount: 2,
loaded: true,
@@ -121,7 +121,7 @@ const adapterSchema: JsonSchemaNode = {
title: "Adapter name",
description: "Human-readable name shown in the adapter manager.",
minLength: 3,
default: "Codex local",
default: "Codex",
},
mode: {
type: "string",
@@ -175,7 +175,7 @@ const adapterSchema: JsonSchemaNode = {
const validAdapterValues = {
...getDefaultValues(adapterSchema),
adapterName: "Codex local",
adapterName: "Codex",
mode: "implementation",
apiKey: "secret:openai-api-key",
concurrency: 2,