feat: add slash commands support to marketplace

- Add 'command' type to marketplace item schema with content and argumentHint
- Implement command detection in MarketplaceManager for project and global directories
- Add fetchCommands() to RemoteConfigLoader for API integration
- Add installCommand() and removeCommand() to SimpleInstaller
- Add 'Slash Commands' tab to marketplace UI with 3-tab navigation
- Update install modal and item card components for command type
- Add translations for all 18 locales
- Include comprehensive test coverage for new functionality
This commit is contained in:
Hannes Rudolph 2025-12-08 10:08:08 -07:00
parent 1370cb04f7
commit 252702326b
30 changed files with 612 additions and 89 deletions

View file

@ -27,7 +27,7 @@ export type McpInstallationMethod = z.infer<typeof mcpInstallationMethodSchema>
/**
* Component type validation
*/
export const marketplaceItemTypeSchema = z.enum(["mode", "mcp"] as const)
export const marketplaceItemTypeSchema = z.enum(["mode", "mcp", "command"] as const)
export type MarketplaceItemType = z.infer<typeof marketplaceItemTypeSchema>
@ -61,6 +61,13 @@ export const mcpMarketplaceItemSchema = baseMarketplaceItemSchema.extend({
export type McpMarketplaceItem = z.infer<typeof mcpMarketplaceItemSchema>
export const commandMarketplaceItemSchema = baseMarketplaceItemSchema.extend({
content: z.string().min(1), // Markdown content for slash command
argumentHint: z.string().optional(),
})
export type CommandMarketplaceItem = z.infer<typeof commandMarketplaceItemSchema>
/**
* Unified marketplace item schema using discriminated union
*/
@ -73,6 +80,10 @@ export const marketplaceItemSchema = z.discriminatedUnion("type", [
mcpMarketplaceItemSchema.extend({
type: z.literal("mcp"),
}),
// Command marketplace item
commandMarketplaceItemSchema.extend({
type: z.literal("command"),
}),
])
export type MarketplaceItem = z.infer<typeof marketplaceItemSchema>

View file

@ -284,6 +284,20 @@ export class MarketplaceManager {
} catch (error) {
// File doesn't exist or can't be read, skip
}
// Check commands in .roo/commands
const projectCommandsDir = path.join(workspaceFolder.uri.fsPath, ".roo", "commands")
try {
const entries = await fs.readdir(projectCommandsDir, { withFileTypes: true })
for (const entry of entries) {
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
const commandName = entry.name.slice(0, -3)
metadata[commandName] = { type: "command" }
}
}
} catch (error) {
// Directory doesn't exist or can't be read, skip
}
} catch (error) {
console.error("Error checking project installations:", error)
}
@ -329,6 +343,20 @@ export class MarketplaceManager {
} catch (error) {
// File doesn't exist or can't be read, skip
}
// Check global commands
const globalCommandsDir = path.join(globalSettingsPath, "commands")
try {
const entries = await fs.readdir(globalCommandsDir, { withFileTypes: true })
for (const entry of entries) {
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
const commandName = entry.name.slice(0, -3)
metadata[commandName] = { type: "command" }
}
}
} catch (error) {
// Directory doesn't exist or can't be read, skip
}
} catch (error) {
console.error("Error checking global installations:", error)
}

View file

@ -3,10 +3,11 @@ import * as yaml from "yaml"
import { z } from "zod"
import {
type MarketplaceItem,
type MarketplaceItemType,
commandMarketplaceItemSchema,
modeMarketplaceItemSchema,
mcpMarketplaceItemSchema,
type MarketplaceItem,
type MarketplaceItemType,
} from "@roo-code/types"
import { getRooCodeApiUrl } from "@roo-code/cloud"
@ -18,6 +19,10 @@ const mcpMarketplaceResponse = z.object({
items: z.array(mcpMarketplaceItemSchema),
})
const commandMarketplaceResponse = z.object({
items: z.array(commandMarketplaceItemSchema),
})
export class RemoteConfigLoader {
private apiBaseUrl: string
private cache: Map<string, { data: MarketplaceItem[]; timestamp: number }> = new Map()
@ -32,10 +37,11 @@ export class RemoteConfigLoader {
const modesPromise = this.fetchModes()
const mcpsPromise = hideMarketplaceMcps ? Promise.resolve([]) : this.fetchMcps()
const commandsPromise = this.fetchCommands()
const [modes, mcps] = await Promise.all([modesPromise, mcpsPromise])
const [modes, mcps, commands] = await Promise.all([modesPromise, mcpsPromise, commandsPromise])
items.push(...modes, ...mcps)
items.push(...modes, ...mcps, ...commands)
return items
}
@ -83,6 +89,30 @@ export class RemoteConfigLoader {
return items
}
private async fetchCommands(): Promise<MarketplaceItem[]> {
const cacheKey = "commands"
const cached = this.getFromCache(cacheKey)
if (cached) {
return cached
}
const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/commands`)
// The commands endpoint returns Markdown list (yaml frontmatter + markdown body per item)
// The test server sample is plain Markdown with YAML frontmatter; parse as plain text list.
const yamlData = yaml.parse(data)
const validated = commandMarketplaceResponse.parse(yamlData)
const items: MarketplaceItem[] = validated.items.map((item) => ({
type: "command" as const,
...item,
}))
this.setCache(cacheKey, items)
return items
}
private async fetchWithRetry<T>(url: string, maxRetries = 3): Promise<T> {
let lastError: Error

View file

@ -6,6 +6,7 @@ import type { MarketplaceItem, MarketplaceItemType, InstallMarketplaceItemOption
import { GlobalFileNames } from "../../shared/globalFileNames"
import { ensureSettingsDirectoryExists } from "../../utils/globalContext"
import type { CustomModesManager } from "../../core/config/CustomModesManager"
import { getGlobalRooDirectory, getProjectRooDirectoryForCwd } from "../roo-config"
export interface InstallOptions extends InstallMarketplaceItemOptions {
target: "project" | "global"
@ -26,11 +27,30 @@ export class SimpleInstaller {
return await this.installMode(item, target)
case "mcp":
return await this.installMcp(item, target, options)
case "command":
return await this.installCommand(item, target)
default:
throw new Error(`Unsupported item type: ${(item as any).type}`)
}
}
private async installCommand(
item: MarketplaceItem,
target: "project" | "global",
): Promise<{ filePath: string; line?: number }> {
if (!item.content || Array.isArray(item.content)) {
throw new Error("Command item missing content")
}
const dirPath = await this.getCommandDirPath(target)
await fs.mkdir(dirPath, { recursive: true })
const filePath = path.join(dirPath, `${item.id}.md`)
await fs.writeFile(filePath, item.content, "utf-8")
return { filePath }
}
private async installMode(
item: MarketplaceItem,
target: "project" | "global",
@ -288,11 +308,28 @@ export class SimpleInstaller {
case "mcp":
await this.removeMcp(item, target)
break
case "command":
await this.removeCommand(item, target)
break
default:
throw new Error(`Unsupported item type: ${(item as any).type}`)
}
}
private async removeCommand(item: MarketplaceItem, target: "project" | "global"): Promise<void> {
const dirPath = await this.getCommandDirPath(target)
const filePath = path.join(dirPath, `${item.id}.md`)
try {
await fs.unlink(filePath)
} catch (error: any) {
if (error.code === "ENOENT") {
return
}
throw error
}
}
private async removeMode(item: MarketplaceItem, target: "project" | "global"): Promise<void> {
if (!this.customModesManager) {
throw new Error("CustomModesManager is not available")
@ -381,4 +418,16 @@ export class SimpleInstaller {
return path.join(globalSettingsPath, GlobalFileNames.mcpSettings)
}
}
private async getCommandDirPath(target: "project" | "global"): Promise<string> {
if (target === "project") {
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]
if (!workspaceFolder) {
throw new Error("No workspace folder found")
}
return path.join(getProjectRooDirectoryForCwd(workspaceFolder.uri.fsPath), "commands")
}
return path.join(getGlobalRooDirectory(), "commands")
}
}

View file

@ -24,7 +24,7 @@ describe("RemoteConfigLoader", () => {
})
describe("loadAllItems", () => {
it("should fetch and combine modes and MCPs from API", async () => {
it("should fetch and combine modes, MCPs, and commands from API", async () => {
const mockModesYaml = `items:
- id: "test-mode"
name: "Test Mode"
@ -38,6 +38,13 @@ describe("RemoteConfigLoader", () => {
url: "https://github.com/test/test-mcp"
content: '{"command": "test"}'`
const mockCommandsYaml = `items:
- id: "explain"
name: "Explain"
description: "Get a detailed explanation"
author: "@roo"
content: "# Explain\\n\\nExplain the following: {args}"`
mockedAxios.get.mockImplementation((url: string) => {
if (url.includes("/modes")) {
return Promise.resolve({ data: mockModesYaml })
@ -45,12 +52,15 @@ describe("RemoteConfigLoader", () => {
if (url.includes("/mcps")) {
return Promise.resolve({ data: mockMcpsYaml })
}
if (url.includes("/commands")) {
return Promise.resolve({ data: mockCommandsYaml })
}
return Promise.reject(new Error("Unknown URL"))
})
const items = await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(2)
expect(mockedAxios.get).toHaveBeenCalledTimes(3)
expect(mockedAxios.get).toHaveBeenCalledWith(
"https://test.api.com/api/marketplace/modes",
expect.objectContaining({
@ -71,8 +81,18 @@ describe("RemoteConfigLoader", () => {
},
}),
)
expect(mockedAxios.get).toHaveBeenCalledWith(
"https://test.api.com/api/marketplace/commands",
expect.objectContaining({
timeout: 10000,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
}),
)
expect(items).toHaveLength(2)
expect(items).toHaveLength(3)
expect(items[0]).toEqual({
type: "mode",
id: "test-mode",
@ -88,6 +108,14 @@ describe("RemoteConfigLoader", () => {
url: "https://github.com/test/test-mcp",
content: '{"command": "test"}',
})
expect(items[2]).toEqual({
type: "command",
id: "explain",
name: "Explain",
description: "Get a detailed explanation",
author: "@roo",
content: "# Explain\n\nExplain the following: {args}",
})
})
it("should use cache on subsequent calls", async () => {
@ -104,6 +132,8 @@ describe("RemoteConfigLoader", () => {
url: "https://github.com/test/test-mcp"
content: "test content"`
const mockCommandsYaml = `items: []`
mockedAxios.get.mockImplementation((url: string) => {
if (url.includes("/modes")) {
return Promise.resolve({ data: mockModesYaml })
@ -111,16 +141,19 @@ describe("RemoteConfigLoader", () => {
if (url.includes("/mcps")) {
return Promise.resolve({ data: mockMcpsYaml })
}
if (url.includes("/commands")) {
return Promise.resolve({ data: mockCommandsYaml })
}
return Promise.reject(new Error("Unknown URL"))
})
// First call - should hit API
const items1 = await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(2)
expect(mockedAxios.get).toHaveBeenCalledTimes(3)
// Second call - should use cache
const items2 = await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(2) // Still 2, not 4
expect(mockedAxios.get).toHaveBeenCalledTimes(3) // Still 3, not 6
expect(items1).toEqual(items2)
})
@ -133,6 +166,7 @@ describe("RemoteConfigLoader", () => {
content: "test content"`
const mockMcpsYaml = `items: []`
const mockCommandsYaml = `items: []`
// Mock modes endpoint to fail twice then succeed
let modesCallCount = 0
@ -147,6 +181,9 @@ describe("RemoteConfigLoader", () => {
if (url.includes("/mcps")) {
return Promise.resolve({ data: mockMcpsYaml })
}
if (url.includes("/commands")) {
return Promise.resolve({ data: mockCommandsYaml })
}
return Promise.reject(new Error("Unknown URL"))
})
@ -185,6 +222,8 @@ describe("RemoteConfigLoader", () => {
url: "https://github.com/test/test-mcp"
content: "test content"`
const validCommandsYaml = `items: []`
mockedAxios.get.mockImplementation((url: string) => {
if (url.includes("/modes")) {
return Promise.resolve({ data: invalidModesYaml })
@ -192,6 +231,9 @@ describe("RemoteConfigLoader", () => {
if (url.includes("/mcps")) {
return Promise.resolve({ data: validMcpsYaml })
}
if (url.includes("/commands")) {
return Promise.resolve({ data: validCommandsYaml })
}
return Promise.reject(new Error("Unknown URL"))
})
@ -215,6 +257,13 @@ describe("RemoteConfigLoader", () => {
url: "https://github.com/test/test-mcp"
content: "test content"`
const mockCommandsYaml = `items:
- id: "target-command"
name: "Target Command"
description: "The command we want"
author: "@roo"
content: "# Command content"`
mockedAxios.get.mockImplementation((url: string) => {
if (url.includes("/modes")) {
return Promise.resolve({ data: mockModesYaml })
@ -222,11 +271,15 @@ describe("RemoteConfigLoader", () => {
if (url.includes("/mcps")) {
return Promise.resolve({ data: mockMcpsYaml })
}
if (url.includes("/commands")) {
return Promise.resolve({ data: mockCommandsYaml })
}
return Promise.reject(new Error("Unknown URL"))
})
const modeItem = await loader.getItem("target-mode", "mode" as MarketplaceItemType)
const mcpItem = await loader.getItem("target-mcp", "mcp" as MarketplaceItemType)
const commandItem = await loader.getItem("target-command", "command" as MarketplaceItemType)
const notFound = await loader.getItem("nonexistent", "mode" as MarketplaceItemType)
expect(modeItem).toEqual({
@ -246,6 +299,15 @@ describe("RemoteConfigLoader", () => {
content: "test content",
})
expect(commandItem).toEqual({
type: "command",
id: "target-command",
name: "Target Command",
description: "The command we want",
author: "@roo",
content: "# Command content",
})
expect(notFound).toBeNull()
})
})
@ -259,6 +321,7 @@ describe("RemoteConfigLoader", () => {
content: "test content"`
const mockMcpsYaml = `items: []`
const mockCommandsYaml = `items: []`
mockedAxios.get.mockImplementation((url: string) => {
if (url.includes("/modes")) {
@ -267,23 +330,26 @@ describe("RemoteConfigLoader", () => {
if (url.includes("/mcps")) {
return Promise.resolve({ data: mockMcpsYaml })
}
if (url.includes("/commands")) {
return Promise.resolve({ data: mockCommandsYaml })
}
return Promise.reject(new Error("Unknown URL"))
})
// First call
await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(2)
expect(mockedAxios.get).toHaveBeenCalledTimes(3)
// Second call - should use cache
await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(2)
expect(mockedAxios.get).toHaveBeenCalledTimes(3)
// Clear cache
loader.clearCache()
// Third call - should hit API again
await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(4)
expect(mockedAxios.get).toHaveBeenCalledTimes(6)
})
})
@ -296,6 +362,7 @@ describe("RemoteConfigLoader", () => {
content: "test content"`
const mockMcpsYaml = `items: []`
const mockCommandsYaml = `items: []`
mockedAxios.get.mockImplementation((url: string) => {
if (url.includes("/modes")) {
@ -304,6 +371,9 @@ describe("RemoteConfigLoader", () => {
if (url.includes("/mcps")) {
return Promise.resolve({ data: mockMcpsYaml })
}
if (url.includes("/commands")) {
return Promise.resolve({ data: mockCommandsYaml })
}
return Promise.reject(new Error("Unknown URL"))
})
@ -315,18 +385,18 @@ describe("RemoteConfigLoader", () => {
// First call
await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(2)
expect(mockedAxios.get).toHaveBeenCalledTimes(3)
// Second call immediately - should use cache
await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(2)
expect(mockedAxios.get).toHaveBeenCalledTimes(3)
// Advance time by 6 minutes (360,000 ms)
currentTime += 6 * 60 * 1000
// Third call - cache should be expired
await loader.loadAllItems()
expect(mockedAxios.get).toHaveBeenCalledTimes(4)
expect(mockedAxios.get).toHaveBeenCalledTimes(6)
// Restore original Date.now
Date.now = originalDateNow

View file

@ -15,6 +15,7 @@ vi.mock("fs/promises", () => ({
writeFile: vi.fn(),
mkdir: vi.fn(),
rm: vi.fn(),
unlink: vi.fn(),
}))
vi.mock("os")
vi.mock("vscode", () => ({
@ -346,4 +347,106 @@ describe("SimpleInstaller", () => {
)
})
})
describe("installCommand", () => {
const mockCommandItem: MarketplaceItem = {
id: "explain",
name: "Explain",
description: "Get a detailed explanation of code, concepts, or errors",
type: "command",
content: `---
description: "Get a detailed explanation of code, concepts, or errors"
argument-hint: "<code|concept|error>"
---
# Explain Request
Please provide a comprehensive explanation of the following:
**Input:** {args}`,
}
it("should install command to project .roo/commands/ directory", async () => {
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
const result = await installer.installItem(mockCommandItem, { target: "project" })
expect(result.filePath).toBe(path.join("/test/workspace", ".roo", "commands", "explain.md"))
expect(mockFs.mkdir).toHaveBeenCalledWith(expect.stringContaining(path.join(".roo", "commands")), {
recursive: true,
})
expect(mockFs.writeFile).toHaveBeenCalledWith(result.filePath, mockCommandItem.content, "utf-8")
})
it("should install command to global commands directory", async () => {
mockFs.writeFile.mockResolvedValueOnce(undefined as any)
const result = await installer.installItem(mockCommandItem, { target: "global" })
expect(result.filePath).toContain("commands")
expect(result.filePath).toContain("explain.md")
expect(mockFs.mkdir).toHaveBeenCalledWith(expect.any(String), { recursive: true })
expect(mockFs.writeFile).toHaveBeenCalledWith(result.filePath, mockCommandItem.content, "utf-8")
})
it("should throw error for missing content", async () => {
const noContentCommand: MarketplaceItem = {
...mockCommandItem,
content: undefined as any,
}
await expect(installer.installItem(noContentCommand, { target: "project" })).rejects.toThrow(
"Command item missing content",
)
})
it("should throw error for array content in command", async () => {
const arrayContentCommand: MarketplaceItem = {
...mockCommandItem,
content: ["content1", "content2"] as any,
}
await expect(installer.installItem(arrayContentCommand, { target: "project" })).rejects.toThrow(
"Command item missing content",
)
})
})
describe("removeCommand", () => {
const mockCommandItem: MarketplaceItem = {
id: "explain",
name: "Explain",
description: "Get a detailed explanation of code, concepts, or errors",
type: "command",
content: "# Explain\n\nContent here",
}
it("should remove command file from project directory", async () => {
mockFs.unlink.mockResolvedValueOnce(undefined as any)
await installer.removeItem(mockCommandItem, { target: "project" })
expect(mockFs.unlink).toHaveBeenCalledWith(
expect.stringContaining(path.join(".roo", "commands", "explain.md")),
)
})
it("should not throw error if command file does not exist", async () => {
const notFoundError = new Error("File not found") as any
notFoundError.code = "ENOENT"
mockFs.unlink.mockRejectedValueOnce(notFoundError)
await expect(installer.removeItem(mockCommandItem, { target: "project" })).resolves.not.toThrow()
})
it("should throw error for other file system errors", async () => {
const permissionError = new Error("Permission denied") as any
permissionError.code = "EPERM"
mockFs.unlink.mockRejectedValueOnce(permissionError)
await expect(installer.removeItem(mockCommandItem, { target: "project" })).rejects.toThrow(
"Permission denied",
)
})
})
})

View file

@ -16,7 +16,7 @@ export interface MarketplaceListViewProps {
stateManager: MarketplaceViewStateManager
allTags: string[]
filteredTags: string[]
filterByType?: "mcp" | "mode"
filterByType?: "mcp" | "mode" | "command"
}
export function MarketplaceListView({ stateManager, allTags, filteredTags, filterByType }: MarketplaceListViewProps) {
@ -48,7 +48,9 @@ export function MarketplaceListView({ stateManager, allTags, filteredTags, filte
? t("marketplace:filters.search.placeholderMcp")
: filterByType === "mode"
? t("marketplace:filters.search.placeholderMode")
: t("marketplace:filters.search.placeholder")
: filterByType === "command"
? t("marketplace:filters.search.placeholderCommand")
: t("marketplace:filters.search.placeholder")
}
value={state.filters.search}
onChange={(e) =>

View file

@ -13,7 +13,7 @@ import { ExtensionStateContext } from "@/context/ExtensionStateContext"
interface MarketplaceViewProps {
onDone?: () => void
stateManager: MarketplaceViewStateManager
targetTab?: "mcp" | "mode"
targetTab?: "mcp" | "mode" | "command"
}
export function MarketplaceView({ stateManager, onDone, targetTab }: MarketplaceViewProps) {
const { t } = useAppTranslation()
@ -43,7 +43,7 @@ export function MarketplaceView({ stateManager, onDone, targetTab }: Marketplace
}, [state.allItems, hasReceivedInitialState])
useEffect(() => {
if (targetTab && (targetTab === "mcp" || targetTab === "mode")) {
if (targetTab && (targetTab === "mcp" || targetTab === "mode" || targetTab === "command")) {
manager.transition({ type: "SET_ACTIVE_TAB", payload: { tab: targetTab } })
}
}, [targetTab, manager])
@ -117,10 +117,11 @@ export function MarketplaceView({ stateManager, onDone, targetTab }: Marketplace
<div className="absolute w-full h-[2px] -bottom-[2px] bg-vscode-input-border">
<div
className={cn(
"absolute w-1/2 h-[2px] bottom-0 bg-vscode-button-background transition-all duration-300 ease-in-out",
"absolute w-1/3 h-[2px] bottom-0 bg-vscode-button-background transition-all duration-300 ease-in-out",
{
"left-0": state.activeTab === "mcp",
"left-1/2": state.activeTab === "mode",
"left-1/3": state.activeTab === "command",
"left-2/3": state.activeTab === "mode",
},
)}
/>
@ -130,6 +131,13 @@ export function MarketplaceView({ stateManager, onDone, targetTab }: Marketplace
onClick={() => manager.transition({ type: "SET_ACTIVE_TAB", payload: { tab: "mcp" } })}>
MCP
</button>
<button
className="flex items-center justify-center gap-2 flex-1 text-sm font-medium rounded-sm transition-colors duration-300 relative z-10 text-vscode-foreground"
onClick={() =>
manager.transition({ type: "SET_ACTIVE_TAB", payload: { tab: "command" } })
}>
{t("marketplace:tabs.slashCommands")}
</button>
<button
className="flex items-center justify-center gap-2 flex-1 text-sm font-medium rounded-sm transition-colors duration-300 relative z-10 text-vscode-foreground"
onClick={() =>
@ -150,6 +158,14 @@ export function MarketplaceView({ stateManager, onDone, targetTab }: Marketplace
filterByType="mcp"
/>
)}
{state.activeTab === "command" && (
<MarketplaceListView
stateManager={stateManager}
allTags={allTags}
filteredTags={filteredTags}
filterByType="command"
/>
)}
{state.activeTab === "mode" && (
<MarketplaceListView
stateManager={stateManager}

View file

@ -22,7 +22,7 @@ export interface ViewState {
displayItems?: MarketplaceItem[] // Items currently being displayed (filtered or all)
displayOrganizationMcps?: MarketplaceItem[] // Organization MCPs currently being displayed (filtered or all)
isFetching: boolean
activeTab: "mcp" | "mode"
activeTab: "mcp" | "mode" | "command"
filters: {
type: string
search: string

View file

@ -39,6 +39,14 @@ describe("MarketplaceViewStateManager", () => {
content: "test content",
tags: ["test", "server"],
},
{
id: "test-command-1",
name: "Test Command 1",
description: "A test slash command",
type: "command",
content: "# Test Command\n\n{args}",
tags: ["test", "command"],
},
]
beforeEach(() => {
@ -307,6 +315,16 @@ describe("MarketplaceViewStateManager", () => {
expect(state.activeTab).toBe("mode")
})
it("should switch to command tab", async () => {
await stateManager.transition({
type: "SET_ACTIVE_TAB",
payload: { tab: "command" },
})
const state = stateManager.getState()
expect(state.activeTab).toBe("command")
})
it("should preserve items when switching tabs", async () => {
// Load items first
const message = {
@ -328,6 +346,29 @@ describe("MarketplaceViewStateManager", () => {
expect(state.allItems).toEqual(mockMarketplaceItems)
expect(state.displayItems).toEqual(mockMarketplaceItems)
})
it("should preserve items when switching to command tab", async () => {
// Load items first
const message = {
type: "state",
state: {
marketplaceItems: mockMarketplaceItems,
},
}
await stateManager.handleMessage(message)
// Switch to command tab
await stateManager.transition({
type: "SET_ACTIVE_TAB",
payload: { tab: "command" },
})
const state = stateManager.getState()
expect(state.activeTab).toBe("command")
expect(state.allItems).toEqual(mockMarketplaceItems)
// displayItems should include all items (filtering by type happens in the view)
expect(state.displayItems).toEqual(mockMarketplaceItems)
})
})
describe("error handling", () => {

View file

@ -218,7 +218,9 @@ export const MarketplaceInstallModal: React.FC<MarketplaceInstallModalProps> = (
? t("marketplace:install.successTitle", { name: item.name })
: item.type === "mcp"
? t("marketplace:install.titleMcp", { name: item.name })
: t("marketplace:install.titleMode", { name: item.name })}
: item.type === "command"
? t("marketplace:install.titleCommand", { name: item.name })
: t("marketplace:install.titleMode", { name: item.name })}
</DialogTitle>
<DialogDescription>
{installationComplete ? (
@ -243,7 +245,9 @@ export const MarketplaceInstallModal: React.FC<MarketplaceInstallModalProps> = (
<p className="text-sm text-muted-foreground">
{item.type === "mcp"
? t("marketplace:install.whatNextMcp")
: t("marketplace:install.whatNextMode")}
: item.type === "command"
? t("marketplace:install.whatNextCommand")
: t("marketplace:install.whatNextMode")}
</p>
</div>
</div>
@ -365,11 +369,13 @@ export const MarketplaceInstallModal: React.FC<MarketplaceInstallModalProps> = (
<Button variant="outline" onClick={onClose}>
{t("marketplace:install.done")}
</Button>
<Button onClick={() => handlePostInstallAction(item.type === "mcp" ? "mcp" : "modes")}>
{item.type === "mcp"
? t("marketplace:install.goToMcp")
: t("marketplace:install.goToModes")}
</Button>
{item.type !== "command" && (
<Button onClick={() => handlePostInstallAction(item.type === "mcp" ? "mcp" : "modes")}>
{item.type === "mcp"
? t("marketplace:install.goToMcp")
: t("marketplace:install.goToModes")}
</Button>
)}
</>
) : (
<>

View file

@ -68,6 +68,7 @@ export const MarketplaceItemCard: React.FC<MarketplaceItemCardProps> = ({ item,
const labels: Partial<Record<MarketplaceItem["type"], string>> = {
mode: t("marketplace:filters.type.mode"),
mcp: t("marketplace:filters.type.mcpServer"),
command: t("marketplace:filters.type.slashCommand"),
}
return labels[item.type] ?? "N/A"
}, [item.type, t])
@ -210,7 +211,9 @@ export const MarketplaceItemCard: React.FC<MarketplaceItemCardProps> = ({ item,
<AlertDialogTitle>
{item.type === "mode"
? t("marketplace:removeConfirm.mode.title")
: t("marketplace:removeConfirm.mcp.title")}
: item.type === "command"
? t("marketplace:removeConfirm.command.title")
: t("marketplace:removeConfirm.mcp.title")}
</AlertDialogTitle>
<AlertDialogDescription>
{item.type === "mode" ? (
@ -220,6 +223,8 @@ export const MarketplaceItemCard: React.FC<MarketplaceItemCardProps> = ({ item,
{t("marketplace:removeConfirm.mode.rulesWarning")}
</div>
</>
) : item.type === "command" ? (
t("marketplace:removeConfirm.command.message", { commandName: item.name })
) : (
t("marketplace:removeConfirm.mcp.message", { mcpName: item.name })
)}

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Instal·lat",
"settings": "Configuració",
"browse": "Navegar"
"browse": "Navegar",
"slashCommands": "Comandes Slash"
},
"done": "Fet",
"refresh": "Actualitzar",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Cercar elements del marketplace...",
"placeholderMcp": "Cercar MCPs...",
"placeholderMode": "Cercar modes..."
"placeholderMode": "Cercar modes...",
"placeholderCommand": "Cercar comandes Slash..."
},
"installed": {
"label": "Filtra per estat",
@ -23,7 +25,8 @@
"label": "Filtrar per tipus:",
"all": "Tots els tipus",
"mode": "Mode",
"mcpServer": "Servidor MCP"
"mcpServer": "Servidor MCP",
"slashCommand": "Comanda Slash"
},
"sort": {
"label": "Ordenar per:",
@ -91,6 +94,7 @@
"title": "Instal·lar {{name}}",
"titleMode": "Instal·lar mode {{name}}",
"titleMcp": "Instal·lar MCP {{name}}",
"titleCommand": "Instal·lar la comanda Slash {{name}}",
"scope": "Àmbit d'instal·lació",
"project": "Projecte (espai de treball actual)",
"global": "Global (tots els espais de treball)",
@ -103,6 +107,7 @@
"installed": "Instal·lat amb èxit!",
"whatNextMcp": "Ara pots configurar i utilitzar aquest servidor MCP. Feu clic a la icona MCP de la barra lateral per canviar de pestanya.",
"whatNextMode": "Ara pots utilitzar aquest mode. Feu clic a la icona Modes de la barra lateral per canviar de pestanya.",
"whatNextCommand": "Ara pots utilitzar aquesta comanda slash escrivint /{{name}} al xat.",
"done": "Fet",
"goToMcp": "Anar a la pestanya MCP",
"goToModes": "Anar a la configuració de Modes",
@ -149,6 +154,10 @@
"title": "Eliminar el servidor MCP",
"message": "Estàs segur que vols eliminar el servidor MCP \"{{mcpName}}\"?"
},
"command": {
"title": "Eliminar comanda Slash",
"message": "Estàs segur que vols eliminar la comanda slash \"{{commandName}}\"?"
},
"cancel": "Cancel·lar",
"confirm": "Eliminar"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Installiert",
"settings": "Einstellungen",
"browse": "Durchsuchen"
"browse": "Durchsuchen",
"slashCommands": "Slash Commands"
},
"done": "Fertig",
"refresh": "Aktualisieren",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Marketplace-Elemente durchsuchen...",
"placeholderMcp": "MCPs durchsuchen...",
"placeholderMode": "Modi durchsuchen..."
"placeholderMode": "Modi durchsuchen...",
"placeholderCommand": "Slash Commands durchsuchen..."
},
"installed": {
"label": "Nach Status filtern",
@ -23,7 +25,8 @@
"label": "Nach Typ filtern:",
"all": "Alle Typen",
"mode": "Modus",
"mcpServer": "MCP-Server"
"mcpServer": "MCP-Server",
"slashCommand": "Slash Command"
},
"sort": {
"label": "Sortieren nach:",
@ -91,6 +94,7 @@
"title": "{{name}} installieren",
"titleMode": "{{name}} Modus installieren",
"titleMcp": "{{name}} MCP installieren",
"titleCommand": "{{name}} Slash Command installieren",
"scope": "Installationsbereich",
"project": "Projekt (aktueller Arbeitsbereich)",
"global": "Global (alle Arbeitsbereiche)",
@ -103,6 +107,7 @@
"installed": "Erfolgreich installiert!",
"whatNextMcp": "Du kannst diesen MCP-Server jetzt konfigurieren und verwenden. Klicke auf das MCP-Symbol in der Seitenleiste, um die Tabs zu wechseln.",
"whatNextMode": "Du kannst diesen Modus jetzt verwenden. Klicke auf das Modi-Symbol in der Seitenleiste, um die Tabs zu wechseln.",
"whatNextCommand": "Du kannst diesen Slash Command jetzt verwenden, indem du /{{name}} im Chat eingibst.",
"done": "Fertig",
"goToMcp": "Zum MCP-Tab gehen",
"goToModes": "Zu den Modi-Einstellungen gehen",
@ -142,12 +147,16 @@
"removeConfirm": {
"mode": {
"title": "Modus entfernen",
"message": "Bist du sicher, dass du den Modus „{{modeName}}“ entfernen möchtest?",
"message": "Bist du sicher, dass du den Modus \"{{modeName}}\" entfernen möchtest?",
"rulesWarning": "Dadurch werden auch alle zugehörigen Regeldateien für diesen Modus entfernt."
},
"mcp": {
"title": "MCP-Server entfernen",
"message": "Bist du sicher, dass du den MCP-Server „{{mcpName}}“ entfernen möchtest?"
"message": "Bist du sicher, dass du den MCP-Server \"{{mcpName}}\" entfernen möchtest?"
},
"command": {
"title": "Slash Command entfernen",
"message": "Bist du sicher, dass du den Slash Command \"{{commandName}}\" entfernen möchtest?"
},
"cancel": "Abbrechen",
"confirm": "Entfernen"

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Installed",
"settings": "Settings",
"browse": "Browse"
"browse": "Browse",
"slashCommands": "Slash Commands"
},
"done": "Done",
"refresh": "Refresh",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Search marketplace items...",
"placeholderMcp": "Search MCPs...",
"placeholderMode": "Search Modes..."
"placeholderMode": "Search Modes...",
"placeholderCommand": "Search Slash Commands..."
},
"installed": {
"label": "Filter by status",
@ -23,7 +25,8 @@
"label": "Filter by type:",
"all": "All types",
"mode": "Mode",
"mcpServer": "MCP Server"
"mcpServer": "MCP Server",
"slashCommand": "Slash Command"
},
"sort": {
"label": "Sort by:",
@ -89,6 +92,7 @@
"title": "Install {{name}}",
"titleMode": "Install {{name}} Mode",
"titleMcp": "Install {{name}} MCP",
"titleCommand": "Install {{name}} Slash Command",
"scope": "Installation Scope",
"project": "Project (current workspace)",
"global": "Global (all workspaces)",
@ -102,6 +106,7 @@
"installed": "Successfully installed!",
"whatNextMcp": "You can now configure and use this MCP server. Click the MCP icon in the sidebar to switch tabs.",
"whatNextMode": "You can now use this mode. Click the Modes icon in the sidebar to switch tabs.",
"whatNextCommand": "You can now use this slash command by typing /{{name}} in the chat.",
"done": "Done",
"goToMcp": "Go to MCP Tab",
"goToModes": "Go to Modes Settings",
@ -147,6 +152,10 @@
"title": "Remove MCP Server",
"message": "Are you sure you want to remove the MCP server \"{{mcpName}}\"?"
},
"command": {
"title": "Remove Slash Command",
"message": "Are you sure you want to remove the slash command \"{{commandName}}\"?"
},
"cancel": "Cancel",
"confirm": "Remove"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Instalado",
"settings": "Configuración",
"browse": "Explorar"
"browse": "Explorar",
"slashCommands": "Comandos Slash"
},
"done": "Hecho",
"refresh": "Actualizar",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Buscar elementos del marketplace...",
"placeholderMcp": "Buscar MCPs...",
"placeholderMode": "Buscar modos..."
"placeholderMode": "Buscar modos...",
"placeholderCommand": "Buscar comandos Slash..."
},
"installed": {
"label": "Filtrar por estado",
@ -23,7 +25,8 @@
"label": "Filtrar por tipo:",
"all": "Todos los tipos",
"mode": "Modo",
"mcpServer": "Servidor MCP"
"mcpServer": "Servidor MCP",
"slashCommand": "Comando Slash"
},
"sort": {
"label": "Ordenar por:",
@ -91,6 +94,7 @@
"title": "Instalar {{name}}",
"titleMode": "Instalar modo {{name}}",
"titleMcp": "Instalar MCP {{name}}",
"titleCommand": "Instalar comando Slash {{name}}",
"scope": "Ámbito de instalación",
"project": "Proyecto (espacio de trabajo actual)",
"global": "Global (todos los espacios de trabajo)",
@ -103,6 +107,7 @@
"installed": "¡Instalado exitosamente!",
"whatNextMcp": "Ahora puedes configurar y usar este servidor MCP. Haz clic en el icono MCP en la barra lateral para cambiar de pestaña.",
"whatNextMode": "Ahora puedes usar este modo. Haz clic en el icono Modos en la barra lateral para cambiar de pestaña.",
"whatNextCommand": "Ahora puedes usar este comando slash escribiendo /{{name}} en el chat.",
"done": "Hecho",
"goToMcp": "Ir a la pestaña MCP",
"goToModes": "Ir a la configuración de Modos",
@ -149,6 +154,10 @@
"title": "Eliminar servidor MCP",
"message": "¿Estás seguro de que quieres eliminar el servidor MCP \"{{mcpName}}\"?"
},
"command": {
"title": "Eliminar comando Slash",
"message": "¿Estás seguro de que quieres eliminar el comando slash \"{{commandName}}\"?"
},
"cancel": "Cancelar",
"confirm": "Eliminar"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Installé",
"settings": "Paramètres",
"browse": "Parcourir"
"browse": "Parcourir",
"slashCommands": "Commandes Slash"
},
"done": "Terminé",
"refresh": "Actualiser",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Rechercher des éléments du marketplace...",
"placeholderMcp": "Rechercher des MCPs...",
"placeholderMode": "Rechercher des modes..."
"placeholderMode": "Rechercher des modes...",
"placeholderCommand": "Rechercher des commandes Slash..."
},
"installed": {
"label": "Filtrer par statut",
@ -23,7 +25,8 @@
"label": "Filtrer par type :",
"all": "Tous les types",
"mode": "Mode",
"mcpServer": "Serveur MCP"
"mcpServer": "Serveur MCP",
"slashCommand": "Commande Slash"
},
"sort": {
"label": "Trier par :",
@ -91,6 +94,7 @@
"title": "Installer {{name}}",
"titleMode": "Installer le mode {{name}}",
"titleMcp": "Installer le MCP {{name}}",
"titleCommand": "Installer la commande Slash {{name}}",
"scope": "Portée d'installation",
"project": "Projet (espace de travail actuel)",
"global": "Global (tous les espaces de travail)",
@ -103,6 +107,7 @@
"installed": "Installé avec succès !",
"whatNextMcp": "Vous pouvez maintenant configurer et utiliser ce serveur MCP. Cliquez sur l'icône MCP dans la barre latérale pour changer d'onglet.",
"whatNextMode": "Vous pouvez maintenant utiliser ce mode. Cliquez sur l'icône Modes dans la barre latérale pour changer d'onglet.",
"whatNextCommand": "Tu peux maintenant utiliser cette commande slash en tapant /{{name}} dans le chat.",
"done": "Terminé",
"goToMcp": "Aller à l'onglet MCP",
"goToModes": "Aller aux paramètres des Modes",
@ -149,6 +154,10 @@
"title": "Supprimer le serveur MCP",
"message": "Êtes-vous sûr de vouloir supprimer le serveur MCP « {{mcpName}} » ?"
},
"command": {
"title": "Supprimer la commande Slash",
"message": "Es-tu sûr de vouloir supprimer la commande slash \"{{commandName}}\" ?"
},
"cancel": "Annuler",
"confirm": "Supprimer"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "इंस्टॉल किया गया",
"settings": "सेटिंग्स",
"browse": "ब्राउज़ करें"
"browse": "ब्राउज़ करें",
"slashCommands": "Slash Commands"
},
"done": "पूर्ण",
"refresh": "रिफ्रेश करें",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Marketplace आइटम खोजें...",
"placeholderMcp": "MCP खोजें...",
"placeholderMode": "मोड खोजें..."
"placeholderMode": "मोड खोजें...",
"placeholderCommand": "Slash Commands खोजें..."
},
"installed": {
"label": "स्थिति के अनुसार फ़िल्टर करें",
@ -23,7 +25,8 @@
"label": "प्रकार के अनुसार फ़िल्टर करें:",
"all": "सभी प्रकार",
"mode": "मोड",
"mcpServer": "MCP सर्वर"
"mcpServer": "MCP सर्वर",
"slashCommand": "Slash Command"
},
"sort": {
"label": "इसके अनुसार क्रमबद्ध करें:",
@ -89,6 +92,7 @@
"title": "{{name}} इंस्टॉल करें",
"titleMode": "{{name}} मोड इंस्टॉल करें",
"titleMcp": "{{name}} MCP इंस्टॉल करें",
"titleCommand": "{{name}} Slash Command इंस्टॉल करें",
"scope": "इंस्टॉलेशन स्कोप",
"project": "प्रोजेक्ट (वर्तमान वर्कस्पेस)",
"global": "ग्लोबल (सभी वर्कस्पेस)",
@ -101,6 +105,7 @@
"installed": "सफलतापूर्वक इंस्टॉल किया गया!",
"whatNextMcp": "अब आप इस MCP सर्वर को कॉन्फ़िगर और उपयोग कर सकते हैं। टैब स्विच करने के लिए साइडबार में MCP आइकन पर क्लिक करें।",
"whatNextMode": "अब आप इस मोड का उपयोग कर सकते हैं। टैब स्विच करने के लिए साइडबार में मोड आइकन पर क्लिक करें।",
"whatNextCommand": "अब तुम चैट में /{{name}} टाइप करके इस slash command का उपयोग कर सकते हो।",
"done": "पूर्ण",
"goToMcp": "MCP टैब पर जाएं",
"goToModes": "मोड सेटिंग्स पर जाएं",
@ -147,6 +152,10 @@
"title": "MCP सर्वर हटाएं",
"message": "क्या आप वाकई MCP सर्वर \"{{mcpName}}\" को हटाना चाहते हैं?"
},
"command": {
"title": "Slash Command हटाएं",
"message": "क्या तुम निश्चित हो कि तुम \"{{commandName}}\" slash command को हटाना चाहते हो?"
},
"cancel": "रद्द करें",
"confirm": "हटाएं"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Terinstal",
"settings": "Pengaturan",
"browse": "Jelajahi"
"browse": "Jelajahi",
"slashCommands": "Perintah Slash"
},
"done": "Selesai",
"refresh": "Refresh",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Cari item marketplace...",
"placeholderMcp": "Cari MCP...",
"placeholderMode": "Cari Mode..."
"placeholderMode": "Cari Mode...",
"placeholderCommand": "Cari perintah Slash..."
},
"installed": {
"label": "Filter berdasarkan status",
@ -23,7 +25,8 @@
"label": "Filter berdasarkan tipe:",
"all": "Semua tipe",
"mode": "Mode",
"mcpServer": "Server MCP"
"mcpServer": "Server MCP",
"slashCommand": "Perintah Slash"
},
"sort": {
"label": "Urutkan berdasarkan:",
@ -89,6 +92,7 @@
"title": "Instal {{name}}",
"titleMode": "Instal Mode {{name}}",
"titleMcp": "Instal MCP {{name}}",
"titleCommand": "Pasang Perintah Slash {{name}}",
"scope": "Cakupan Instalasi",
"project": "Proyek (workspace saat ini)",
"global": "Global (semua workspace)",
@ -102,6 +106,7 @@
"installed": "Berhasil diinstal!",
"whatNextMcp": "Anda sekarang dapat mengkonfigurasi dan menggunakan server MCP ini. Klik ikon MCP di sidebar untuk beralih tab.",
"whatNextMode": "Anda sekarang dapat menggunakan mode ini. Klik ikon Mode di sidebar untuk beralih tab.",
"whatNextCommand": "Kamu sekarang bisa menggunakan perintah slash ini dengan mengetik /{{name}} di chat.",
"done": "Selesai",
"goToMcp": "Ke Tab MCP",
"goToModes": "Ke Pengaturan Mode",
@ -147,6 +152,10 @@
"title": "Hapus Server MCP",
"message": "Apakah Anda yakin ingin menghapus server MCP \"{{mcpName}}\"?"
},
"command": {
"title": "Hapus Perintah Slash",
"message": "Apakah Anda yakin ingin menghapus perintah slash \"{{commandName}}\"?"
},
"cancel": "Batal",
"confirm": "Hapus"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Installati",
"settings": "Impostazioni",
"browse": "Sfoglia"
"browse": "Sfoglia",
"slashCommands": "Comandi Slash"
},
"done": "Fatto",
"refresh": "Aggiorna",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Cerca elementi del Marketplace...",
"placeholderMcp": "Cerca MCP...",
"placeholderMode": "Cerca modalità..."
"placeholderMode": "Cerca modalità...",
"placeholderCommand": "Cerca comandi Slash..."
},
"installed": {
"label": "Filtra per stato",
@ -23,7 +25,8 @@
"label": "Filtra per tipo:",
"all": "Tutti i tipi",
"mode": "Modalità",
"mcpServer": "Server MCP"
"mcpServer": "Server MCP",
"slashCommand": "Comando Slash"
},
"sort": {
"label": "Ordina per:",
@ -89,6 +92,7 @@
"title": "Installa {{name}}",
"titleMode": "Installa modalità {{name}}",
"titleMcp": "Installa MCP {{name}}",
"titleCommand": "Installa comando Slash {{name}}",
"scope": "Ambito di installazione",
"project": "Progetto (area di lavoro corrente)",
"global": "Globale (tutte le aree di lavoro)",
@ -101,6 +105,7 @@
"installed": "Installato con successo!",
"whatNextMcp": "Ora puoi configurare e utilizzare questo server MCP. Clicca sull'icona MCP nella barra laterale per cambiare scheda.",
"whatNextMode": "Ora puoi utilizzare questa modalità. Clicca sull'icona delle modalità nella barra laterale per cambiare scheda.",
"whatNextCommand": "Ora puoi usare questo comando slash digitando /{{name}} nella chat.",
"done": "Fatto",
"goToMcp": "Vai alla scheda MCP",
"goToModes": "Vai alle impostazioni Modalità",
@ -147,6 +152,10 @@
"title": "Rimuovi server MCP",
"message": "Sei sicuro di voler rimuovere il server MCP \"{{mcpName}}\"?"
},
"command": {
"title": "Rimuovi comando Slash",
"message": "Sei sicuro di voler rimuovere il comando slash \"{{commandName}}\"?"
},
"cancel": "Annulla",
"confirm": "Rimuovi"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "インストール済み",
"settings": "設定",
"browse": "参照"
"browse": "参照",
"slashCommands": "スラッシュコマンド"
},
"done": "完了",
"refresh": "更新",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Marketplaceアイテムを検索...",
"placeholderMcp": "MCPを検索...",
"placeholderMode": "モードを検索..."
"placeholderMode": "モードを検索...",
"placeholderCommand": "スラッシュコマンドを検索..."
},
"installed": {
"label": "ステータスで絞り込む",
@ -23,7 +25,8 @@
"label": "タイプでフィルター:",
"all": "すべてのタイプ",
"mode": "モード",
"mcpServer": "MCPサーバー"
"mcpServer": "MCPサーバー",
"slashCommand": "スラッシュコマンド"
},
"sort": {
"label": "並び順:",
@ -89,6 +92,7 @@
"title": "{{name}}をインストール",
"titleMode": "{{name}}モードをインストール",
"titleMcp": "{{name}} MCPをインストール",
"titleCommand": "{{name}}スラッシュコマンドをインストール",
"scope": "インストール範囲",
"project": "プロジェクト(現在のワークスペース)",
"global": "グローバル(すべてのワークスペース)",
@ -101,6 +105,7 @@
"installed": "正常にインストールされました!",
"whatNextMcp": "このMCPサーバーを設定して使用できるようになりました。サイドバーのMCPアイコンをクリックしてタブを切り替えてください。",
"whatNextMode": "このモードを使用できるようになりました。サイドバーのモードアイコンをクリックしてタブを切り替えてください。",
"whatNextCommand": "チャットで /{{name}} と入力することで、このスラッシュコマンドを使用できるようになりました。",
"done": "完了",
"goToMcp": "MCPタブに移動",
"goToModes": "モード設定に移動",
@ -147,6 +152,10 @@
"title": "MCPサーバーを削除",
"message": "MCPサーバー「{{mcpName}}」を本当に削除しますか?"
},
"command": {
"title": "スラッシュコマンドを削除",
"message": "スラッシュコマンド「{{commandName}}」を削除してもよろしいですか?"
},
"cancel": "キャンセル",
"confirm": "削除"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "설치됨",
"settings": "설정",
"browse": "찾아보기"
"browse": "찾아보기",
"slashCommands": "슬래시 명령어"
},
"done": "완료",
"refresh": "새로고침",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Marketplace 아이템 검색...",
"placeholderMcp": "MCP 검색...",
"placeholderMode": "모드 검색..."
"placeholderMode": "모드 검색...",
"placeholderCommand": "슬래시 명령어 검색..."
},
"installed": {
"label": "상태별로 필터링",
@ -23,7 +25,8 @@
"label": "유형별 필터:",
"all": "모든 유형",
"mode": "모드",
"mcpServer": "MCP 서버"
"mcpServer": "MCP 서버",
"slashCommand": "슬래시 명령어"
},
"sort": {
"label": "정렬 기준:",
@ -89,6 +92,7 @@
"title": "{{name}} 설치",
"titleMode": "{{name}} 모드 설치",
"titleMcp": "{{name}} MCP 설치",
"titleCommand": "{{name}} 슬래시 명령어 설치",
"scope": "설치 범위",
"project": "프로젝트 (현재 워크스페이스)",
"global": "전역 (모든 워크스페이스)",
@ -101,6 +105,7 @@
"installed": "성공적으로 설치되었습니다!",
"whatNextMcp": "이제 이 MCP 서버를 설정하고 사용할 수 있습니다. 사이드바의 MCP 아이콘을 클릭하여 탭을 전환하세요.",
"whatNextMode": "이제 이 모드를 사용할 수 있습니다. 사이드바의 모드 아이콘을 클릭하여 탭을 전환하세요.",
"whatNextCommand": "이제 채팅에서 /{{name}}을 입력하여 이 슬래시 명령어를 사용할 수 있습니다.",
"done": "완료",
"goToMcp": "MCP 탭으로 이동",
"goToModes": "모드 설정으로 이동",
@ -147,6 +152,10 @@
"title": "MCP 서버 제거",
"message": "정말로 '{{mcpName}}' MCP 서버를 제거하시겠습니까?"
},
"command": {
"title": "슬래시 명령어 제거",
"message": "슬래시 명령어 \"{{commandName}}\"을 제거하시겠습니까?"
},
"cancel": "취소",
"confirm": "제거"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Geïnstalleerd",
"settings": "Instellingen",
"browse": "Bladeren"
"browse": "Bladeren",
"slashCommands": "Slash-commando's"
},
"done": "Gereed",
"refresh": "Vernieuwen",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Marketplace-items zoeken...",
"placeholderMcp": "MCP's zoeken...",
"placeholderMode": "Modi zoeken..."
"placeholderMode": "Modi zoeken...",
"placeholderCommand": "Zoek slash-commando's..."
},
"installed": {
"label": "Filteren op status",
@ -23,7 +25,8 @@
"label": "Filteren op type:",
"all": "Alle types",
"mode": "Modus",
"mcpServer": "MCP-server"
"mcpServer": "MCP-server",
"slashCommand": "Slash-commando"
},
"sort": {
"label": "Sorteren op:",
@ -89,6 +92,7 @@
"title": "{{name}} installeren",
"titleMode": "{{name}} modus installeren",
"titleMcp": "{{name}} MCP installeren",
"titleCommand": "Slash-commando {{name}} installeren",
"scope": "Installatiebereik",
"project": "Project (huidige werkruimte)",
"global": "Globaal (alle werkruimtes)",
@ -101,6 +105,7 @@
"installed": "Succesvol geïnstalleerd!",
"whatNextMcp": "Je kunt deze MCP-server nu configureren en gebruiken. Klik op het MCP-pictogram in de zijbalk om van tabblad te wisselen.",
"whatNextMode": "Je kunt deze modus nu gebruiken. Klik op het modi-pictogram in de zijbalk om van tabblad te wisselen.",
"whatNextCommand": "Je kunt dit slash-commando nu gebruiken door /{{name}} in de chat te typen.",
"done": "Gereed",
"goToMcp": "Ga naar MCP-tabblad",
"goToModes": "Ga naar Modi-instellingen",
@ -147,6 +152,10 @@
"title": "MCP-server verwijderen",
"message": "Weet je zeker dat je de MCP-server \"{{mcpName}}\" wilt verwijderen?"
},
"command": {
"title": "Slash-commando verwijderen",
"message": "Weet je zeker dat je het slash-commando \"{{commandName}}\" wilt verwijderen?"
},
"cancel": "Annuleren",
"confirm": "Verwijderen"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Zainstalowane",
"settings": "Ustawienia",
"browse": "Przeglądaj"
"browse": "Przeglądaj",
"slashCommands": "Polecenia Slash"
},
"done": "Gotowe",
"refresh": "Odśwież",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Szukaj elementów marketplace...",
"placeholderMcp": "Szukaj MCPs...",
"placeholderMode": "Szukaj trybów..."
"placeholderMode": "Szukaj trybów...",
"placeholderCommand": "Szukaj poleceń Slash..."
},
"installed": {
"label": "Filtruj według statusu",
@ -23,7 +25,8 @@
"label": "Filtruj według typu:",
"all": "Wszystkie typy",
"mode": "Tryb",
"mcpServer": "Serwer MCP"
"mcpServer": "Serwer MCP",
"slashCommand": "Polecenie Slash"
},
"sort": {
"label": "Sortuj według:",
@ -89,6 +92,7 @@
"title": "Zainstaluj {{name}}",
"titleMode": "Zainstaluj tryb {{name}}",
"titleMcp": "Zainstaluj MCP {{name}}",
"titleCommand": "Zainstaluj polecenie Slash {{name}}",
"scope": "Zakres instalacji",
"project": "Projekt (bieżący obszar roboczy)",
"global": "Globalnie (wszystkie obszary robocze)",
@ -101,6 +105,7 @@
"installed": "Zainstalowano pomyślnie!",
"whatNextMcp": "Możesz teraz skonfigurować i używać tego serwera MCP. Kliknij ikonę MCP na pasku bocznym, aby przełączyć zakładki.",
"whatNextMode": "Możesz teraz używać tego trybu. Kliknij ikonę Tryby na pasku bocznym, aby przełączyć zakładki.",
"whatNextCommand": "Możesz teraz używać tego polecenia slash, wpisując /{{name}} w czacie.",
"done": "Gotowe",
"goToMcp": "Przejdź do zakładki MCP",
"goToModes": "Przejdź do ustawień Trybów",
@ -140,12 +145,16 @@
"removeConfirm": {
"mode": {
"title": "Usuń tryb",
"message": "Czy na pewno chcesz usunąć tryb „{{modeName}}”?",
"message": "Czy na pewno chcesz usunąć tryb \"{{modeName}}\"?",
"rulesWarning": "Spowoduje to również usunięcie wszelkich powiązanych plików reguł dla tego trybu."
},
"mcp": {
"title": "Usuń serwer MCP",
"message": "Czy na pewno chcesz usunąć serwer MCP „{{mcpName}}”?"
"message": "Czy na pewno chcesz usunąć serwer MCP \"{{mcpName}}\"?"
},
"command": {
"title": "Usuń polecenie Slash",
"message": "Czy na pewno chcesz usunąć polecenie slash \"{{commandName}}\"?"
},
"cancel": "Anuluj",
"confirm": "Usuń"

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Instalado",
"settings": "Configurações",
"browse": "Navegar"
"browse": "Navegar",
"slashCommands": "Comandos Slash"
},
"done": "Concluído",
"refresh": "Atualizar",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Buscar itens do marketplace...",
"placeholderMcp": "Buscar MCPs...",
"placeholderMode": "Buscar modos..."
"placeholderMode": "Buscar modos...",
"placeholderCommand": "Buscar comandos Slash..."
},
"installed": {
"label": "Filtrar por status",
@ -23,7 +25,8 @@
"label": "Filtrar por tipo:",
"all": "Todos os tipos",
"mode": "Modo",
"mcpServer": "Servidor MCP"
"mcpServer": "Servidor MCP",
"slashCommand": "Comando Slash"
},
"sort": {
"label": "Ordenar por:",
@ -89,6 +92,7 @@
"title": "Instalar {{name}}",
"titleMode": "Instalar modo {{name}}",
"titleMcp": "Instalar MCP {{name}}",
"titleCommand": "Instalar comando Slash {{name}}",
"scope": "Escopo da instalação",
"project": "Projeto (workspace atual)",
"global": "Global (todos os workspaces)",
@ -101,6 +105,7 @@
"installed": "Instalado com sucesso!",
"whatNextMcp": "Agora você pode configurar e usar este servidor MCP. Clique no ícone MCP na barra lateral para trocar de aba.",
"whatNextMode": "Agora você pode usar este modo. Clique no ícone Modos na barra lateral para trocar de aba.",
"whatNextCommand": "Agora você pode usar este comando slash digitando /{{name}} no chat.",
"done": "Concluído",
"goToMcp": "Ir para aba MCP",
"goToModes": "Ir para configurações de Modos",
@ -147,6 +152,10 @@
"title": "Remover Servidor MCP",
"message": "Tem certeza de que deseja remover o servidor MCP \"{{mcpName}}\"?"
},
"command": {
"title": "Remover comando Slash",
"message": "Tem certeza de que deseja remover o comando slash \"{{commandName}}\"?"
},
"cancel": "Cancelar",
"confirm": "Remover"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Установлено",
"settings": "Настройки",
"browse": "Обзор"
"browse": "Обзор",
"slashCommands": "Slash-команды"
},
"done": "Готово",
"refresh": "Обновить",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Поиск элементов marketplace...",
"placeholderMcp": "Поиск MCPs...",
"placeholderMode": "Поиск режимов..."
"placeholderMode": "Поиск режимов...",
"placeholderCommand": "Искать Slash-команды..."
},
"installed": {
"label": "Фильтр по статусу",
@ -23,7 +25,8 @@
"label": "Фильтр по типу:",
"all": "Все типы",
"mode": "Режим",
"mcpServer": "MCP сервер"
"mcpServer": "MCP сервер",
"slashCommand": "Slash-команда"
},
"sort": {
"label": "Сортировать по:",
@ -89,6 +92,7 @@
"title": "Установить {{name}}",
"titleMode": "Установить режим {{name}}",
"titleMcp": "Установить MCP {{name}}",
"titleCommand": "Установить Slash-команду {{name}}",
"scope": "Область установки",
"project": "Проект (текущая рабочая область)",
"global": "Глобально (все рабочие области)",
@ -101,6 +105,7 @@
"installed": "Успешно установлено!",
"whatNextMcp": "Теперь вы можете настроить и использовать этот MCP сервер. Нажмите на иконку MCP в боковой панели для переключения вкладок.",
"whatNextMode": "Теперь вы можете использовать этот режим. Нажмите на иконку Режимы в боковой панели для переключения вкладок.",
"whatNextCommand": "Теперь ты можешь использовать эту slash-команду, набрав /{{name}} в чате.",
"done": "Готово",
"goToMcp": "Перейти во вкладку MCP",
"goToModes": "Перейти в настройки Режимов",
@ -147,6 +152,10 @@
"title": "Удалить сервер MCP",
"message": "Вы уверены, что хотите удалить сервер MCP «{{mcpName}}»?"
},
"command": {
"title": "Удалить Slash-команду",
"message": "Ты уверен, что хочешь удалить slash-команду \"{{commandName}}\"?"
},
"cancel": "Отмена",
"confirm": "Удалить"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Yüklü",
"settings": "Ayarlar",
"browse": "Gözat"
"browse": "Gözat",
"slashCommands": "Slash Komutları"
},
"done": "Tamamlandı",
"refresh": "Yenile",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Marketplace öğelerini ara...",
"placeholderMcp": "MCP'leri ara...",
"placeholderMode": "Modları ara..."
"placeholderMode": "Modları ara...",
"placeholderCommand": "Slash komutlarını ara..."
},
"installed": {
"label": "Duruma göre filtrele",
@ -23,7 +25,8 @@
"label": "Türe göre filtrele:",
"all": "Tüm türler",
"mode": "Mod",
"mcpServer": "MCP Sunucusu"
"mcpServer": "MCP Sunucusu",
"slashCommand": "Slash Komutu"
},
"sort": {
"label": "Sırala:",
@ -89,6 +92,7 @@
"title": "{{name}} Yükle",
"titleMode": "{{name}} Modunu Yükle",
"titleMcp": "{{name}} MCP'sini Yükle",
"titleCommand": "{{name}} Slash Komutunu Yükle",
"scope": "Yükleme Kapsamı",
"project": "Proje (mevcut çalışma alanı)",
"global": "Global (tüm çalışma alanları)",
@ -101,6 +105,7 @@
"installed": "Başarıyla yüklendi!",
"whatNextMcp": "Artık bu MCP sunucusunu yapılandırabilir ve kullanabilirsiniz. Sekmeleri değiştirmek için kenar çubuğundaki MCP simgesine tıklayın.",
"whatNextMode": "Artık bu modu kullanabilirsiniz. Sekmeleri değiştirmek için kenar çubuğundaki Modlar simgesine tıklayın.",
"whatNextCommand": "Artık sohbette /{{name}} yazarak bu slash komutunu kullanabilirsin.",
"done": "Tamamlandı",
"goToMcp": "MCP Sekmesine Git",
"goToModes": "Modlar Ayarlarına Git",
@ -147,6 +152,10 @@
"title": "MCP Sunucusunu Kaldır",
"message": "\"{{mcpName}}\" MCP sunucusunu kaldırmak istediğinizden emin misiniz?"
},
"command": {
"title": "Slash Komutunu Kaldır",
"message": "\"{{commandName}}\" slash komutunu kaldırmak istediğine emin misin?"
},
"cancel": "İptal",
"confirm": "Kaldır"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "Đã cài đặt",
"settings": "Cài đặt",
"browse": "Duyệt"
"browse": "Duyệt",
"slashCommands": "Lệnh Slash"
},
"done": "Hoàn thành",
"refresh": "Làm mới",
@ -11,7 +12,8 @@
"search": {
"placeholder": "Tìm kiếm các mục marketplace...",
"placeholderMcp": "Tìm kiếm MCP...",
"placeholderMode": "Tìm kiếm Chế độ..."
"placeholderMode": "Tìm kiếm Chế độ...",
"placeholderCommand": "Tìm kiếm lệnh Slash..."
},
"installed": {
"label": "Lọc theo trạng thái",
@ -23,7 +25,8 @@
"label": "Lọc theo loại:",
"all": "Tất cả loại",
"mode": "Chế độ",
"mcpServer": "Máy chủ MCP"
"mcpServer": "Máy chủ MCP",
"slashCommand": "Lệnh Slash"
},
"sort": {
"label": "Sắp xếp theo:",
@ -89,6 +92,7 @@
"title": "Cài đặt {{name}}",
"titleMode": "Cài đặt Chế độ {{name}}",
"titleMcp": "Cài đặt MCP {{name}}",
"titleCommand": "Cài đặt lệnh Slash {{name}}",
"scope": "Phạm vi cài đặt",
"project": "Dự án (không gian làm việc hiện tại)",
"global": "Toàn cục (tất cả không gian làm việc)",
@ -101,6 +105,7 @@
"installed": "Cài đặt thành công!",
"whatNextMcp": "Bây giờ bạn có thể cấu hình và sử dụng máy chủ MCP này. Nhấp vào biểu tượng MCP trong thanh bên để chuyển tab.",
"whatNextMode": "Bây giờ bạn có thể sử dụng chế độ này. Nhấp vào biểu tượng Chế độ trong thanh bên để chuyển tab.",
"whatNextCommand": "Bây giờ bạn có thể sử dụng lệnh slash này bằng cách gõ /{{name}} trong trò chuyện.",
"done": "Hoàn thành",
"goToMcp": "Đi đến Tab MCP",
"goToModes": "Đi đến Cài đặt Chế độ",
@ -147,6 +152,10 @@
"title": "Xóa máy chủ MCP",
"message": "Bạn có chắc chắn muốn xóa máy chủ MCP \"{{mcpName}}\" không?"
},
"command": {
"title": "Xóa lệnh Slash",
"message": "Bạn có chắc chắn muốn xóa lệnh slash \"{{commandName}}\" không?"
},
"cancel": "Hủy",
"confirm": "Xóa"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "已安装",
"settings": "设置",
"browse": "浏览"
"browse": "浏览",
"slashCommands": "Slash 命令"
},
"done": "完成",
"refresh": "刷新",
@ -11,7 +12,8 @@
"search": {
"placeholder": "搜索 Marketplace 项目...",
"placeholderMcp": "搜索 MCP...",
"placeholderMode": "搜索模式..."
"placeholderMode": "搜索模式...",
"placeholderCommand": "搜索 Slash 命令..."
},
"installed": {
"label": "按状态筛选",
@ -23,7 +25,8 @@
"label": "按类型筛选:",
"all": "所有类型",
"mode": "模式",
"mcpServer": "MCP 服务"
"mcpServer": "MCP 服务",
"slashCommand": "Slash 命令"
},
"sort": {
"label": "排序方式:",
@ -89,6 +92,7 @@
"title": "安装 {{name}}",
"titleMode": "安装 {{name}} 模式",
"titleMcp": "安装 {{name}} MCP",
"titleCommand": "安装 {{name}} Slash 命令",
"scope": "安装范围",
"project": "项目(当前工作区)",
"global": "全局(所有工作区)",
@ -101,6 +105,7 @@
"installed": "安装成功!",
"whatNextMcp": "现在您可以配置和使用此 MCP 服务。点击侧边栏中的 MCP 图标切换标签页。",
"whatNextMode": "现在您可以使用此模式。点击侧边栏中的模式图标切换标签页。",
"whatNextCommand": "你现在可以在聊天中输入 /{{name}} 来使用这个 slash 命令。",
"done": "完成",
"goToMcp": "转到 MCP 标签页",
"goToModes": "转到模式设置",
@ -147,6 +152,10 @@
"title": "删除 MCP 服务器",
"message": "您确定要删除 MCP 服务器\"{{mcpName}}\"吗?"
},
"command": {
"title": "移除 Slash 命令",
"message": "确定要移除 slash 命令\"{{commandName}}\"吗?"
},
"cancel": "取消",
"confirm": "删除"
},

View file

@ -3,7 +3,8 @@
"tabs": {
"installed": "已安裝",
"settings": "設定",
"browse": "瀏覽"
"browse": "瀏覽",
"slashCommands": "Slash 指令"
},
"done": "完成",
"refresh": "重新整理",
@ -11,7 +12,8 @@
"search": {
"placeholder": "搜尋市集項目...",
"placeholderMcp": "搜尋 MCP...",
"placeholderMode": "搜尋模式..."
"placeholderMode": "搜尋模式...",
"placeholderCommand": "搜尋 Slash 指令..."
},
"installed": {
"label": "按狀態篩選",
@ -23,7 +25,8 @@
"label": "依類型篩選:",
"all": "所有類型",
"mode": "模式",
"mcpServer": "MCP 伺服器"
"mcpServer": "MCP 伺服器",
"slashCommand": "Slash 指令"
},
"sort": {
"label": "排序方式:",
@ -89,6 +92,7 @@
"title": "安裝 {{name}}",
"titleMode": "安裝 {{name}} 模式",
"titleMcp": "安裝 {{name}} MCP",
"titleCommand": "安裝 {{name}} Slash 指令",
"scope": "安裝範圍",
"project": "專案(目前工作區)",
"global": "全域(所有工作區)",
@ -102,6 +106,7 @@
"installed": "安裝成功!",
"whatNextMcp": "您現在可以設定和使用此 MCP 伺服器。點選側邊欄的 MCP 圖示來切換分頁。",
"whatNextMode": "您現在可以使用此模式。點選側邊欄的模式圖示來切換分頁。",
"whatNextCommand": "你現在可以在聊天中輸入 /{{name}} 來使用這個 slash 指令。",
"done": "完成",
"goToMcp": "前往 MCP 分頁",
"goToModes": "前往模式設定",
@ -147,6 +152,10 @@
"title": "移除 MCP 伺服器",
"message": "您確定要移除 MCP 伺服器「{{mcpName}}」嗎?"
},
"command": {
"title": "移除 Slash 指令",
"message": "確定要移除 slash 指令「{{commandName}}」嗎?"
},
"cancel": "取消",
"confirm": "移除"
},