Parcourir la source

ux: improve Skills and Slash Commands settings UI with multi-mode support (#11157)

Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>
Co-authored-by: Roo Code <[email protected]>
Bruno Bergher il y a 1 semaine
Parent
commit
54ea34e2c1
59 fichiers modifiés avec 2240 ajouts et 1084 suppressions
  1. 11 1
      packages/types/src/skills.ts
  2. 7 0
      packages/types/src/vscode-extension-host.ts
  3. 8 6
      src/core/webview/__tests__/skillsMessageHandler.spec.ts
  4. 47 5
      src/core/webview/skillsMessageHandler.ts
  5. 5 0
      src/core/webview/webviewMessageHandler.ts
  6. 1 0
      src/i18n/locales/ca/skills.json
  7. 1 0
      src/i18n/locales/de/skills.json
  8. 1 0
      src/i18n/locales/en/skills.json
  9. 1 0
      src/i18n/locales/es/skills.json
  10. 1 0
      src/i18n/locales/fr/skills.json
  11. 1 0
      src/i18n/locales/hi/skills.json
  12. 1 0
      src/i18n/locales/id/skills.json
  13. 1 0
      src/i18n/locales/it/skills.json
  14. 1 0
      src/i18n/locales/ja/skills.json
  15. 1 0
      src/i18n/locales/ko/skills.json
  16. 1 0
      src/i18n/locales/nl/skills.json
  17. 1 0
      src/i18n/locales/pl/skills.json
  18. 1 0
      src/i18n/locales/pt-BR/skills.json
  19. 1 0
      src/i18n/locales/ru/skills.json
  20. 1 0
      src/i18n/locales/tr/skills.json
  21. 1 0
      src/i18n/locales/vi/skills.json
  22. 1 0
      src/i18n/locales/zh-CN/skills.json
  23. 1 0
      src/i18n/locales/zh-TW/skills.json
  24. 121 14
      src/services/skills/SkillsManager.ts
  25. 9 3
      src/services/skills/__tests__/SkillsManager.spec.ts
  26. 11 1
      src/shared/skills.ts
  27. 0 84
      webview-ui/src/components/chat/SlashCommandItem.tsx
  28. 83 48
      webview-ui/src/components/settings/CreateSkillDialog.tsx
  29. 156 0
      webview-ui/src/components/settings/CreateSlashCommandDialog.tsx
  30. 4 4
      webview-ui/src/components/settings/SettingsView.tsx
  31. 259 94
      webview-ui/src/components/settings/SkillsSettings.tsx
  32. 149 174
      webview-ui/src/components/settings/SlashCommandsSettings.tsx
  33. 121 9
      webview-ui/src/components/settings/__tests__/CreateSkillDialog.spec.tsx
  34. 31 0
      webview-ui/src/components/settings/__tests__/SettingsView.change-detection.spec.tsx
  35. 20 0
      webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx
  36. 31 0
      webview-ui/src/components/settings/__tests__/SettingsView.unsaved-changes.spec.tsx
  37. 106 92
      webview-ui/src/components/settings/__tests__/SkillsSettings.spec.tsx
  38. 169 276
      webview-ui/src/components/settings/__tests__/SlashCommandsSettings.spec.tsx
  39. 1 1
      webview-ui/src/components/ui/checkbox.tsx
  40. 1 1
      webview-ui/src/components/ui/input.tsx
  41. 1 1
      webview-ui/src/components/ui/textarea.tsx
  42. 49 13
      webview-ui/src/i18n/locales/ca/settings.json
  43. 58 22
      webview-ui/src/i18n/locales/de/settings.json
  44. 52 14
      webview-ui/src/i18n/locales/en/settings.json
  45. 50 14
      webview-ui/src/i18n/locales/es/settings.json
  46. 47 11
      webview-ui/src/i18n/locales/fr/settings.json
  47. 48 13
      webview-ui/src/i18n/locales/hi/settings.json
  48. 48 42
      webview-ui/src/i18n/locales/id/settings.json
  49. 47 12
      webview-ui/src/i18n/locales/it/settings.json
  50. 47 12
      webview-ui/src/i18n/locales/ja/settings.json
  51. 47 12
      webview-ui/src/i18n/locales/ko/settings.json
  52. 49 14
      webview-ui/src/i18n/locales/nl/settings.json
  53. 47 12
      webview-ui/src/i18n/locales/pl/settings.json
  54. 47 12
      webview-ui/src/i18n/locales/pt-BR/settings.json
  55. 47 12
      webview-ui/src/i18n/locales/ru/settings.json
  56. 47 12
      webview-ui/src/i18n/locales/tr/settings.json
  57. 47 12
      webview-ui/src/i18n/locales/vi/settings.json
  58. 47 12
      webview-ui/src/i18n/locales/zh-CN/settings.json
  59. 47 19
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 11 - 1
packages/types/src/skills.ts

@@ -7,7 +7,17 @@ export interface SkillMetadata {
 	description: string // Required: when to use this skill
 	path: string // Absolute path to SKILL.md (or "<built-in:name>" for built-in skills)
 	source: "global" | "project" | "built-in" // Where the skill was discovered
-	mode?: string // If set, skill is only available in this mode
+	/**
+	 * @deprecated Use modeSlugs instead. Kept for backward compatibility.
+	 * If set, skill is only available in this mode.
+	 */
+	mode?: string
+	/**
+	 * Mode slugs where this skill is available.
+	 * - undefined or empty array means the skill is available in all modes ("Any mode").
+	 * - An array with one or more mode slugs restricts the skill to those modes.
+	 */
+	modeSlugs?: string[]
 }
 
 /**

+ 7 - 0
packages/types/src/vscode-extension-host.ts

@@ -605,6 +605,7 @@ export interface WebviewMessage {
 		| "createSkill"
 		| "deleteSkill"
 		| "moveSkill"
+		| "updateSkillModes"
 		| "openSkillFile"
 	text?: string
 	editedMessageContent?: string
@@ -641,9 +642,15 @@ export interface WebviewMessage {
 	payload?: WebViewMessagePayload
 	source?: "global" | "project" | "built-in"
 	skillName?: string // For skill operations (createSkill, deleteSkill, moveSkill, openSkillFile)
+	/** @deprecated Use skillModeSlugs instead */
 	skillMode?: string // For skill operations (current mode restriction)
+	/** @deprecated Use newSkillModeSlugs instead */
 	newSkillMode?: string // For moveSkill (target mode)
 	skillDescription?: string // For createSkill (skill description)
+	/** Mode slugs for skill operations. undefined/empty = any mode */
+	skillModeSlugs?: string[] // For skill operations (mode restrictions)
+	/** Target mode slugs for updateSkillModes */
+	newSkillModeSlugs?: string[] // For updateSkillModes (new mode restrictions)
 	requestId?: string
 	ids?: string[]
 	hasSystemPromptOverride?: boolean

+ 8 - 6
src/core/webview/__tests__/skillsMessageHandler.spec.ts

@@ -52,6 +52,7 @@ describe("skillsMessageHandler", () => {
 	const mockDeleteSkill = vi.fn()
 	const mockMoveSkill = vi.fn()
 	const mockGetSkill = vi.fn()
+	const mockFindSkillByNameAndSource = vi.fn()
 
 	const createMockProvider = (hasSkillsManager: boolean = true): ClineProvider => {
 		const skillsManager = hasSkillsManager
@@ -61,6 +62,7 @@ describe("skillsMessageHandler", () => {
 					deleteSkill: mockDeleteSkill,
 					moveSkill: mockMoveSkill,
 					getSkill: mockGetSkill,
+					findSkillByNameAndSource: mockFindSkillByNameAndSource,
 				}
 			: undefined
 
@@ -158,7 +160,7 @@ describe("skillsMessageHandler", () => {
 			} as WebviewMessage)
 
 			expect(result).toEqual(mockSkills)
-			expect(mockCreateSkill).toHaveBeenCalledWith("new-skill", "project", "New skill description", "code")
+			expect(mockCreateSkill).toHaveBeenCalledWith("new-skill", "project", "New skill description", ["code"])
 		})
 
 		it("returns undefined when required fields are missing", async () => {
@@ -355,7 +357,7 @@ describe("skillsMessageHandler", () => {
 	describe("handleOpenSkillFile", () => {
 		it("opens a skill file successfully", async () => {
 			const provider = createMockProvider(true)
-			mockGetSkill.mockReturnValue(mockSkills[0])
+			mockFindSkillByNameAndSource.mockReturnValue(mockSkills[0])
 
 			await handleOpenSkillFile(provider, {
 				type: "openSkillFile",
@@ -363,13 +365,13 @@ describe("skillsMessageHandler", () => {
 				source: "global",
 			} as WebviewMessage)
 
-			expect(mockGetSkill).toHaveBeenCalledWith("test-skill", "global", undefined)
+			expect(mockFindSkillByNameAndSource).toHaveBeenCalledWith("test-skill", "global")
 			expect(openFile).toHaveBeenCalledWith("/path/to/test-skill/SKILL.md")
 		})
 
 		it("opens a skill file with mode restriction", async () => {
 			const provider = createMockProvider(true)
-			mockGetSkill.mockReturnValue(mockSkills[1])
+			mockFindSkillByNameAndSource.mockReturnValue(mockSkills[1])
 
 			await handleOpenSkillFile(provider, {
 				type: "openSkillFile",
@@ -378,7 +380,7 @@ describe("skillsMessageHandler", () => {
 				skillMode: "code",
 			} as WebviewMessage)
 
-			expect(mockGetSkill).toHaveBeenCalledWith("project-skill", "project", "code")
+			expect(mockFindSkillByNameAndSource).toHaveBeenCalledWith("project-skill", "project")
 			expect(openFile).toHaveBeenCalledWith("/project/.roo/skills/project-skill/SKILL.md")
 		})
 
@@ -416,7 +418,7 @@ describe("skillsMessageHandler", () => {
 
 		it("shows error when skill is not found", async () => {
 			const provider = createMockProvider(true)
-			mockGetSkill.mockReturnValue(undefined)
+			mockFindSkillByNameAndSource.mockReturnValue(undefined)
 
 			await handleOpenSkillFile(provider, {
 				type: "openSkillFile",

+ 47 - 5
src/core/webview/skillsMessageHandler.ts

@@ -38,7 +38,8 @@ export async function handleCreateSkill(
 		const skillName = message.skillName
 		const source = message.source
 		const skillDescription = message.skillDescription
-		const skillMode = message.skillMode
+		// Support new modeSlugs array or fall back to legacy skillMode
+		const modeSlugs = message.skillModeSlugs ?? (message.skillMode ? [message.skillMode] : undefined)
 
 		if (!skillName || !source || !skillDescription) {
 			throw new Error(t("skills:errors.missing_create_fields"))
@@ -54,7 +55,7 @@ export async function handleCreateSkill(
 			throw new Error(t("skills:errors.manager_unavailable"))
 		}
 
-		const createdPath = await skillsManager.createSkill(skillName, source, skillDescription, skillMode)
+		const createdPath = await skillsManager.createSkill(skillName, source, skillDescription, modeSlugs)
 
 		// Open the created file in the editor
 		openFile(createdPath)
@@ -81,7 +82,8 @@ export async function handleDeleteSkill(
 	try {
 		const skillName = message.skillName
 		const source = message.source
-		const skillMode = message.skillMode
+		// Support new skillModeSlugs array or fall back to legacy skillMode
+		const skillMode = message.skillModeSlugs?.[0] ?? message.skillMode
 
 		if (!skillName || !source) {
 			throw new Error(t("skills:errors.missing_delete_fields"))
@@ -152,6 +154,46 @@ export async function handleMoveSkill(
 	}
 }
 
+/**
+ * Handles the updateSkillModes message - updates the mode associations for a skill
+ */
+export async function handleUpdateSkillModes(
+	provider: ClineProvider,
+	message: WebviewMessage,
+): Promise<SkillMetadata[] | undefined> {
+	try {
+		const skillName = message.skillName
+		const source = message.source
+		const newModeSlugs = message.newSkillModeSlugs
+
+		if (!skillName || !source) {
+			throw new Error(t("skills:errors.missing_update_modes_fields"))
+		}
+
+		// Built-in skills cannot be modified
+		if (source === "built-in") {
+			throw new Error(t("skills:errors.cannot_modify_builtin"))
+		}
+
+		const skillsManager = provider.getSkillsManager()
+		if (!skillsManager) {
+			throw new Error(t("skills:errors.manager_unavailable"))
+		}
+
+		await skillsManager.updateSkillModes(skillName, source, newModeSlugs)
+
+		// Send updated skills list
+		const skills = skillsManager.getSkillsMetadata()
+		await provider.postMessageToWebview({ type: "skills", skills })
+		return skills
+	} catch (error) {
+		const errorMessage = error instanceof Error ? error.message : String(error)
+		provider.log(`Error updating skill modes: ${errorMessage}`)
+		vscode.window.showErrorMessage(`Failed to update skill modes: ${errorMessage}`)
+		return undefined
+	}
+}
+
 /**
  * Handles the openSkillFile message - opens a skill file in the editor
  */
@@ -159,7 +201,6 @@ export async function handleOpenSkillFile(provider: ClineProvider, message: Webv
 	try {
 		const skillName = message.skillName
 		const source = message.source
-		const skillMode = message.skillMode
 
 		if (!skillName || !source) {
 			throw new Error(t("skills:errors.missing_delete_fields"))
@@ -175,7 +216,8 @@ export async function handleOpenSkillFile(provider: ClineProvider, message: Webv
 			throw new Error(t("skills:errors.manager_unavailable"))
 		}
 
-		const skill = skillsManager.getSkill(skillName, source, skillMode)
+		// Find skill by name and source (skills may have modeSlugs arrays now)
+		const skill = skillsManager.findSkillByNameAndSource(skillName, source)
 		if (!skill) {
 			throw new Error(t("skills:errors.skill_not_found", { name: skillName }))
 		}

+ 5 - 0
src/core/webview/webviewMessageHandler.ts

@@ -37,6 +37,7 @@ import {
 	handleCreateSkill,
 	handleDeleteSkill,
 	handleMoveSkill,
+	handleUpdateSkillModes,
 	handleOpenSkillFile,
 } from "./skillsMessageHandler"
 import { changeLanguage, t } from "../../i18n"
@@ -2992,6 +2993,10 @@ export const webviewMessageHandler = async (
 			await handleMoveSkill(provider, message)
 			break
 		}
+		case "updateSkillModes": {
+			await handleUpdateSkillModes(provider, message)
+			break
+		}
 		case "openSkillFile": {
 			await handleOpenSkillFile(provider, message)
 			break

+ 1 - 0
src/i18n/locales/ca/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "No s'ha trobat l'habilitat \"{{name}}\" a {{source}}{{modeInfo}}",
 		"missing_create_fields": "Falten camps obligatoris: skillName, source o skillDescription",
 		"missing_move_fields": "Falten camps obligatoris: skillName o source",
+		"missing_update_modes_fields": "Falten camps obligatoris: skillName o source",
 		"manager_unavailable": "El gestor d'habilitats no està disponible",
 		"missing_delete_fields": "Falten camps obligatoris: skillName o source",
 		"skill_not_found": "No s'ha trobat l'habilitat \"{{name}}\"",

+ 1 - 0
src/i18n/locales/de/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Skill \"{{name}}\" nicht gefunden in {{source}}{{modeInfo}}",
 		"missing_create_fields": "Erforderliche Felder fehlen: skillName, source oder skillDescription",
 		"missing_move_fields": "Erforderliche Felder fehlen: skillName oder source",
+		"missing_update_modes_fields": "Erforderliche Felder fehlen: skillName oder source",
 		"manager_unavailable": "Skill-Manager nicht verfügbar",
 		"missing_delete_fields": "Erforderliche Felder fehlen: skillName oder source",
 		"skill_not_found": "Skill \"{{name}}\" nicht gefunden",

+ 1 - 0
src/i18n/locales/en/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Skill \"{{name}}\" not found in {{source}}{{modeInfo}}",
 		"missing_create_fields": "Missing required fields: skillName, source, or skillDescription",
 		"missing_move_fields": "Missing required fields: skillName or source",
+		"missing_update_modes_fields": "Missing required fields: skillName or source",
 		"manager_unavailable": "Skills manager not available",
 		"missing_delete_fields": "Missing required fields: skillName or source",
 		"skill_not_found": "Skill \"{{name}}\" not found",

+ 1 - 0
src/i18n/locales/es/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "No se encontró la habilidad \"{{name}}\" en {{source}}{{modeInfo}}",
 		"missing_create_fields": "Faltan campos obligatorios: skillName, source o skillDescription",
 		"missing_move_fields": "Faltan campos obligatorios: skillName o source",
+		"missing_update_modes_fields": "Faltan campos obligatorios: skillName o source",
 		"manager_unavailable": "El gestor de habilidades no está disponible",
 		"missing_delete_fields": "Faltan campos obligatorios: skillName o source",
 		"skill_not_found": "No se encontró la habilidad \"{{name}}\"",

+ 1 - 0
src/i18n/locales/fr/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Compétence \"{{name}}\" introuvable dans {{source}}{{modeInfo}}",
 		"missing_create_fields": "Champs obligatoires manquants : skillName, source ou skillDescription",
 		"missing_move_fields": "Champs obligatoires manquants : skillName ou source",
+		"missing_update_modes_fields": "Champs obligatoires manquants : skillName ou source",
 		"manager_unavailable": "Le gestionnaire de compétences n'est pas disponible",
 		"missing_delete_fields": "Champs obligatoires manquants : skillName ou source",
 		"skill_not_found": "Compétence \"{{name}}\" introuvable",

+ 1 - 0
src/i18n/locales/hi/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "स्किल \"{{name}}\" {{source}}{{modeInfo}} में नहीं मिला",
 		"missing_create_fields": "आवश्यक फ़ील्ड गायब हैं: skillName, source, या skillDescription",
 		"missing_move_fields": "आवश्यक फ़ील्ड गायब हैं: skillName या source",
+		"missing_update_modes_fields": "आवश्यक फ़ील्ड गायब हैं: skillName या source",
 		"manager_unavailable": "स्किल मैनेजर उपलब्ध नहीं है",
 		"missing_delete_fields": "आवश्यक फ़ील्ड गायब हैं: skillName या source",
 		"skill_not_found": "स्किल \"{{name}}\" नहीं मिला",

+ 1 - 0
src/i18n/locales/id/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Skill \"{{name}}\" tidak ditemukan di {{source}}{{modeInfo}}",
 		"missing_create_fields": "Bidang wajib tidak ada: skillName, source, atau skillDescription",
 		"missing_move_fields": "Bidang wajib tidak ada: skillName atau source",
+		"missing_update_modes_fields": "Bidang wajib tidak ada: skillName atau source",
 		"manager_unavailable": "Manajer skill tidak tersedia",
 		"missing_delete_fields": "Bidang wajib tidak ada: skillName atau source",
 		"skill_not_found": "Skill \"{{name}}\" tidak ditemukan",

+ 1 - 0
src/i18n/locales/it/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Skill \"{{name}}\" non trovata in {{source}}{{modeInfo}}",
 		"missing_create_fields": "Campi obbligatori mancanti: skillName, source o skillDescription",
 		"missing_move_fields": "Campi obbligatori mancanti: skillName o source",
+		"missing_update_modes_fields": "Campi obbligatori mancanti: skillName o source",
 		"manager_unavailable": "Il gestore delle skill non è disponibile",
 		"missing_delete_fields": "Campi obbligatori mancanti: skillName o source",
 		"skill_not_found": "Skill \"{{name}}\" non trovata",

+ 1 - 0
src/i18n/locales/ja/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "スキル「{{name}}」が{{source}}{{modeInfo}}に見つかりません",
 		"missing_create_fields": "必須フィールドが不足しています:skillName、source、またはskillDescription",
 		"missing_move_fields": "必須フィールドが不足しています:skillNameまたはsource",
+		"missing_update_modes_fields": "必須フィールドが不足しています:skillNameまたはsource",
 		"manager_unavailable": "スキルマネージャーが利用できません",
 		"missing_delete_fields": "必須フィールドが不足しています:skillNameまたはsource",
 		"skill_not_found": "スキル「{{name}}」が見つかりません",

+ 1 - 0
src/i18n/locales/ko/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "{{source}}{{modeInfo}}에서 스킬 \"{{name}}\"을(를) 찾을 수 없습니다",
 		"missing_create_fields": "필수 필드 누락: skillName, source 또는 skillDescription",
 		"missing_move_fields": "필수 필드 누락: skillName 또는 source",
+		"missing_update_modes_fields": "필수 필드 누락: skillName 또는 source",
 		"manager_unavailable": "스킬 관리자를 사용할 수 없습니다",
 		"missing_delete_fields": "필수 필드 누락: skillName 또는 source",
 		"skill_not_found": "스킬 \"{{name}}\"을(를) 찾을 수 없습니다",

+ 1 - 0
src/i18n/locales/nl/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Vaardigheid \"{{name}}\" niet gevonden in {{source}}{{modeInfo}}",
 		"missing_create_fields": "Vereiste velden ontbreken: skillName, source of skillDescription",
 		"missing_move_fields": "Vereiste velden ontbreken: skillName of source",
+		"missing_update_modes_fields": "Vereiste velden ontbreken: skillName of source",
 		"manager_unavailable": "Vaardigheidenbeheerder niet beschikbaar",
 		"missing_delete_fields": "Vereiste velden ontbreken: skillName of source",
 		"skill_not_found": "Vaardigheid \"{{name}}\" niet gevonden",

+ 1 - 0
src/i18n/locales/pl/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Nie znaleziono umiejętności \"{{name}}\" w {{source}}{{modeInfo}}",
 		"missing_create_fields": "Brakuje wymaganych pól: skillName, source lub skillDescription",
 		"missing_move_fields": "Brakuje wymaganych pól: skillName lub source",
+		"missing_update_modes_fields": "Brakuje wymaganych pól: skillName lub source",
 		"manager_unavailable": "Menedżer umiejętności niedostępny",
 		"missing_delete_fields": "Brakuje wymaganych pól: skillName lub source",
 		"skill_not_found": "Nie znaleziono umiejętności \"{{name}}\"",

+ 1 - 0
src/i18n/locales/pt-BR/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Habilidade \"{{name}}\" não encontrada em {{source}}{{modeInfo}}",
 		"missing_create_fields": "Campos obrigatórios ausentes: skillName, source ou skillDescription",
 		"missing_move_fields": "Campos obrigatórios ausentes: skillName ou source",
+		"missing_update_modes_fields": "Campos obrigatórios ausentes: skillName ou source",
 		"manager_unavailable": "Gerenciador de habilidades não disponível",
 		"missing_delete_fields": "Campos obrigatórios ausentes: skillName ou source",
 		"skill_not_found": "Habilidade \"{{name}}\" não encontrada",

+ 1 - 0
src/i18n/locales/ru/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Навык \"{{name}}\" не найден в {{source}}{{modeInfo}}",
 		"missing_create_fields": "Отсутствуют обязательные поля: skillName, source или skillDescription",
 		"missing_move_fields": "Отсутствуют обязательные поля: skillName или source",
+		"missing_update_modes_fields": "Отсутствуют обязательные поля: skillName или source",
 		"manager_unavailable": "Менеджер навыков недоступен",
 		"missing_delete_fields": "Отсутствуют обязательные поля: skillName или source",
 		"skill_not_found": "Навык \"{{name}}\" не найден",

+ 1 - 0
src/i18n/locales/tr/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "\"{{name}}\" becerisi {{source}}{{modeInfo}} içinde bulunamadı",
 		"missing_create_fields": "Gerekli alanlar eksik: skillName, source veya skillDescription",
 		"missing_move_fields": "Gerekli alanlar eksik: skillName veya source",
+		"missing_update_modes_fields": "Gerekli alanlar eksik: skillName veya source",
 		"manager_unavailable": "Beceri yöneticisi kullanılamıyor",
 		"missing_delete_fields": "Gerekli alanlar eksik: skillName veya source",
 		"skill_not_found": "\"{{name}}\" becerisi bulunamadı",

+ 1 - 0
src/i18n/locales/vi/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "Không tìm thấy kỹ năng \"{{name}}\" trong {{source}}{{modeInfo}}",
 		"missing_create_fields": "Thiếu các trường bắt buộc: skillName, source hoặc skillDescription",
 		"missing_move_fields": "Thiếu các trường bắt buộc: skillName hoặc source",
+		"missing_update_modes_fields": "Thiếu các trường bắt buộc: skillName hoặc source",
 		"manager_unavailable": "Trình quản lý kỹ năng không khả dụng",
 		"missing_delete_fields": "Thiếu các trường bắt buộc: skillName hoặc source",
 		"skill_not_found": "Không tìm thấy kỹ năng \"{{name}}\"",

+ 1 - 0
src/i18n/locales/zh-CN/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "在 {{source}}{{modeInfo}} 中未找到技能 \"{{name}}\"",
 		"missing_create_fields": "缺少必填字段:skillName、source 或 skillDescription",
 		"missing_move_fields": "缺少必填字段:skillName 或 source",
+		"missing_update_modes_fields": "缺少必填字段:skillName 或 source",
 		"manager_unavailable": "技能管理器不可用",
 		"missing_delete_fields": "缺少必填字段:skillName 或 source",
 		"skill_not_found": "未找到技能 \"{{name}}\"",

+ 1 - 0
src/i18n/locales/zh-TW/skills.json

@@ -8,6 +8,7 @@
 		"not_found": "在 {{source}}{{modeInfo}} 中找不到技能「{{name}}」",
 		"missing_create_fields": "缺少必填欄位:skillName、source 或 skillDescription",
 		"missing_move_fields": "缺少必填欄位:skillName 或 source",
+		"missing_update_modes_fields": "缺少必填欄位:skillName 或 source",
 		"manager_unavailable": "技能管理器無法使用",
 		"missing_delete_fields": "缺少必填欄位:skillName 或 source",
 		"skill_not_found": "找不到技能「{{name}}」",

+ 121 - 14
src/services/skills/SkillsManager.ts

@@ -143,15 +143,34 @@ export class SkillsManager {
 				return
 			}
 
-			// Create unique key combining name, source, and mode for override resolution
-			const skillKey = this.getSkillKey(effectiveSkillName, source, mode)
+			// Parse modeSlugs from frontmatter (new format) or fall back to directory-based mode
+			// Priority: frontmatter.modeSlugs > frontmatter.mode > directory mode
+			let modeSlugs: string[] | undefined
+			if (Array.isArray(frontmatter.modeSlugs)) {
+				modeSlugs = frontmatter.modeSlugs.filter((s: unknown) => typeof s === "string" && s.length > 0)
+				if (modeSlugs.length === 0) {
+					modeSlugs = undefined // Empty array means "any mode"
+				}
+			} else if (typeof frontmatter.mode === "string" && frontmatter.mode.length > 0) {
+				// Legacy single mode in frontmatter
+				modeSlugs = [frontmatter.mode]
+			} else if (mode) {
+				// Fall back to directory-based mode (skills-{mode}/)
+				modeSlugs = [mode]
+			}
+
+			// Create unique key combining name, source, and modeSlugs for override resolution
+			// For backward compatibility, use first mode slug or undefined for the key
+			const primaryMode = modeSlugs?.[0]
+			const skillKey = this.getSkillKey(effectiveSkillName, source, primaryMode)
 
 			this.skills.set(skillKey, {
 				name: effectiveSkillName,
 				description,
 				path: skillMdPath,
 				source,
-				mode, // undefined for generic skills, string for mode-specific
+				mode: primaryMode, // Deprecated: kept for backward compatibility
+				modeSlugs, // New: array of mode slugs, undefined = any mode
 			})
 		} catch (error) {
 			console.error(`Failed to load skill at ${skillDir}:`, error)
@@ -174,8 +193,11 @@ export class SkillsManager {
 
 		// Then, add discovered skills (will override built-in skills with same name)
 		for (const skill of this.skills.values()) {
-			// Skip mode-specific skills that don't match current mode
-			if (skill.mode && skill.mode !== currentMode) continue
+			// Check if skill is available in current mode:
+			// - modeSlugs undefined or empty = available in all modes ("Any mode")
+			// - modeSlugs array with values = available only if currentMode is in the array
+			const isAvailableInMode = this.isSkillAvailableInMode(skill, currentMode)
+			if (!isAvailableInMode) continue
 
 			const existingSkill = resolvedSkills.get(skill.name)
 
@@ -194,6 +216,20 @@ export class SkillsManager {
 		return Array.from(resolvedSkills.values())
 	}
 
+	/**
+	 * Check if a skill is available in the given mode.
+	 * - modeSlugs undefined or empty = available in all modes ("Any mode")
+	 * - modeSlugs with values = available only if mode is in the array
+	 */
+	private isSkillAvailableInMode(skill: SkillMetadata, currentMode: string): boolean {
+		// No mode restrictions = available in all modes
+		if (!skill.modeSlugs || skill.modeSlugs.length === 0) {
+			return true
+		}
+		// Check if current mode is in the allowed modes
+		return skill.modeSlugs.includes(currentMode)
+	}
+
 	/**
 	 * Determine if newSkill should override existingSkill based on priority rules.
 	 * Priority: project > global > built-in, mode-specific > generic
@@ -214,8 +250,11 @@ export class SkillsManager {
 		if (newPriority < existingPriority) return false
 
 		// Same source: mode-specific overrides generic
-		if (newSkill.mode && !existing.mode) return true
-		if (!newSkill.mode && existing.mode) return false
+		// A skill with modeSlugs (restricted) is more specific than one without (any mode)
+		const existingHasModes = existing.modeSlugs && existing.modeSlugs.length > 0
+		const newHasModes = newSkill.modeSlugs && newSkill.modeSlugs.length > 0
+		if (newHasModes && !existingHasModes) return true
+		if (!newHasModes && existingHasModes) return false
 
 		// Same source and same mode-specificity: keep existing (first wins)
 		return false
@@ -276,6 +315,19 @@ export class SkillsManager {
 		return this.skills.get(skillKey)
 	}
 
+	/**
+	 * Find a skill by name and source (regardless of mode).
+	 * Useful for opening/editing skills where the exact mode key may vary.
+	 */
+	findSkillByNameAndSource(name: string, source: "global" | "project"): SkillMetadata | undefined {
+		for (const skill of this.skills.values()) {
+			if (skill.name === name && skill.source === source) {
+				return skill
+			}
+		}
+		return undefined
+	}
+
 	/**
 	 * Validate skill name per agentskills.io spec using shared validation.
 	 * Converts error codes to user-friendly error messages.
@@ -307,10 +359,15 @@ export class SkillsManager {
 	 * @param name - Skill name (must be valid per agentskills.io spec)
 	 * @param source - "global" or "project"
 	 * @param description - Skill description
-	 * @param mode - Optional mode restriction (creates in skills-{mode}/ directory)
+	 * @param modeSlugs - Optional mode restrictions (undefined/empty = any mode)
 	 * @returns Path to created SKILL.md file
 	 */
-	async createSkill(name: string, source: "global" | "project", description: string, mode?: string): Promise<string> {
+	async createSkill(
+		name: string,
+		source: "global" | "project",
+		description: string,
+		modeSlugs?: string[],
+	): Promise<string> {
 		// Validate skill name
 		const validation = this.validateSkillName(name)
 		if (!validation.valid) {
@@ -335,9 +392,8 @@ export class SkillsManager {
 			baseDir = path.join(provider.cwd, ".roo")
 		}
 
-		// Determine skills directory (with optional mode suffix)
-		const skillsDirName = mode ? `skills-${mode}` : "skills"
-		const skillsDir = path.join(baseDir, skillsDirName)
+		// Always use the generic skills directory (mode info stored in frontmatter now)
+		const skillsDir = path.join(baseDir, "skills")
 		const skillDir = path.join(skillsDir, name)
 		const skillMdPath = path.join(skillDir, "SKILL.md")
 
@@ -355,9 +411,17 @@ export class SkillsManager {
 			.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
 			.join(" ")
 
+		// Build frontmatter with optional modeSlugs
+		const frontmatterLines = [`name: ${name}`, `description: ${trimmedDescription}`]
+		if (modeSlugs && modeSlugs.length > 0) {
+			frontmatterLines.push(`modeSlugs:`)
+			for (const slug of modeSlugs) {
+				frontmatterLines.push(`  - ${slug}`)
+			}
+		}
+
 		const skillContent = `---
-name: ${name}
-description: ${trimmedDescription}
+${frontmatterLines.join("\n")}
 ---
 
 # ${titleName}
@@ -471,6 +535,49 @@ Add your skill instructions here.
 		await this.discoverSkills()
 	}
 
+	/**
+	 * Update the mode associations for a skill by modifying its SKILL.md frontmatter.
+	 * @param name - Skill name
+	 * @param source - Where the skill is located ("global" or "project")
+	 * @param newModeSlugs - New mode slugs (undefined/empty = any mode)
+	 */
+	async updateSkillModes(name: string, source: "global" | "project", newModeSlugs?: string[]): Promise<void> {
+		// Find any skill with this name and source (regardless of current mode)
+		let skill: SkillMetadata | undefined
+		for (const s of this.skills.values()) {
+			if (s.name === name && s.source === source) {
+				skill = s
+				break
+			}
+		}
+
+		if (!skill) {
+			throw new Error(t("skills:errors.not_found", { name, source, modeInfo: "" }))
+		}
+
+		// Read the current SKILL.md file
+		const fileContent = await fs.readFile(skill.path, "utf-8")
+		const { data: frontmatter, content: body } = matter(fileContent)
+
+		// Update the frontmatter with new modeSlugs
+		if (newModeSlugs && newModeSlugs.length > 0) {
+			frontmatter.modeSlugs = newModeSlugs
+			// Remove legacy mode field if present
+			delete frontmatter.mode
+		} else {
+			// Empty/undefined = any mode, remove mode restrictions
+			delete frontmatter.modeSlugs
+			delete frontmatter.mode
+		}
+
+		// Serialize back to SKILL.md format
+		const newContent = matter.stringify(body, frontmatter)
+		await fs.writeFile(skill.path, newContent, "utf-8")
+
+		// Refresh skills list
+		await this.discoverSkills()
+	}
+
 	/**
 	 * Get all skills directories to scan, including mode-specific directories.
 	 */

+ 9 - 3
src/services/skills/__tests__/SkillsManager.spec.ts

@@ -1004,7 +1004,7 @@ Instructions`)
 			expect(writeCall[1]).toContain("description: A new skill description")
 		})
 
-		it("should create a mode-specific skill", async () => {
+		it("should create a mode-specific skill with modeSlugs array", async () => {
 			mockDirectoryExists.mockResolvedValue(false)
 			mockRealpath.mockImplementation(async (p: string) => p)
 			mockReaddir.mockResolvedValue([])
@@ -1012,9 +1012,15 @@ Instructions`)
 			mockMkdir.mockResolvedValue(undefined)
 			mockWriteFile.mockResolvedValue(undefined)
 
-			const createdPath = await skillsManager.createSkill("code-skill", "global", "A code skill", "code")
+			const createdPath = await skillsManager.createSkill("code-skill", "global", "A code skill", ["code"])
 
-			expect(createdPath).toBe(p(GLOBAL_ROO_DIR, "skills-code", "code-skill", "SKILL.md"))
+			// Skills are always created in the generic skills directory now; mode info is in frontmatter
+			expect(createdPath).toBe(p(GLOBAL_ROO_DIR, "skills", "code-skill", "SKILL.md"))
+
+			// Verify frontmatter contains modeSlugs
+			const writeCall = mockWriteFile.mock.calls[0]
+			expect(writeCall[1]).toContain("modeSlugs:")
+			expect(writeCall[1]).toContain("- code")
 		})
 
 		it("should create a project skill", async () => {

+ 11 - 1
src/shared/skills.ts

@@ -7,7 +7,17 @@ export interface SkillMetadata {
 	description: string // Required: when to use this skill
 	path: string // Absolute path to SKILL.md (or "<built-in:name>" for built-in skills)
 	source: "global" | "project" | "built-in" // Where the skill was discovered
-	mode?: string // If set, skill is only available in this mode
+	/**
+	 * @deprecated Use modeSlugs instead. Kept for backward compatibility.
+	 * If set, skill is only available in this mode.
+	 */
+	mode?: string
+	/**
+	 * Mode slugs where this skill is available.
+	 * - undefined or empty array means the skill is available in all modes ("Any mode").
+	 * - An array with one or more mode slugs restricts the skill to those modes.
+	 */
+	modeSlugs?: string[]
 }
 
 /**

+ 0 - 84
webview-ui/src/components/chat/SlashCommandItem.tsx

@@ -1,84 +0,0 @@
-import React from "react"
-import { Edit, Trash2 } from "lucide-react"
-
-import type { Command } from "@roo-code/types"
-
-import { useAppTranslation } from "@/i18n/TranslationContext"
-import { Button, StandardTooltip } from "@/components/ui"
-import { vscode } from "@/utils/vscode"
-
-interface SlashCommandItemProps {
-	command: Command
-	onDelete: (command: Command) => void
-	onClick?: (command: Command) => void
-}
-
-export const SlashCommandItem: React.FC<SlashCommandItemProps> = ({ command, onDelete, onClick }) => {
-	const { t } = useAppTranslation()
-
-	// Built-in commands cannot be edited or deleted
-	const isBuiltIn = command.source === "built-in"
-
-	const handleEdit = () => {
-		if (command.filePath) {
-			vscode.postMessage({
-				type: "openFile",
-				text: command.filePath,
-			})
-		} else {
-			// Fallback: request to open command file by name and source
-			vscode.postMessage({
-				type: "openCommandFile",
-				text: command.name,
-				values: { source: command.source },
-			})
-		}
-	}
-
-	const handleDelete = () => {
-		onDelete(command)
-	}
-
-	return (
-		<div className="px-4 py-2 text-sm flex items-center group hover:bg-vscode-list-hoverBackground">
-			{/* Command name - clickable */}
-			<div className="flex-1 min-w-0 cursor-pointer" onClick={() => onClick?.(command)}>
-				<div>
-					<span className="truncate text-vscode-foreground">{command.name}</span>
-					{command.description && (
-						<div className="text-xs text-vscode-descriptionForeground truncate mt-0.5">
-							{command.description}
-						</div>
-					)}
-				</div>
-			</div>
-
-			{/* Action buttons - only show for non-built-in commands */}
-			{!isBuiltIn && (
-				<div className="flex items-center gap-2 ml-2">
-					<StandardTooltip content={t("chat:slashCommands.editCommand")}>
-						<Button
-							variant="ghost"
-							size="icon"
-							tabIndex={-1}
-							onClick={handleEdit}
-							className="size-6 flex items-center justify-center opacity-60 hover:opacity-100">
-							<Edit className="w-4 h-4" />
-						</Button>
-					</StandardTooltip>
-
-					<StandardTooltip content={t("chat:slashCommands.deleteCommand")}>
-						<Button
-							variant="ghost"
-							size="icon"
-							tabIndex={-1}
-							onClick={handleDelete}
-							className="size-6 flex items-center justify-center opacity-60 hover:opacity-100 hover:text-red-400">
-							<Trash2 className="w-4 h-4" />
-						</Button>
-					</StandardTooltip>
-				</div>
-			)}
-		</div>
-	)
-}

+ 83 - 48
webview-ui/src/components/settings/CreateSkillDialog.tsx

@@ -7,17 +7,20 @@ import { useAppTranslation } from "@/i18n/TranslationContext"
 import { useExtensionState } from "@/context/ExtensionStateContext"
 import {
 	Button,
+	Checkbox,
 	Dialog,
 	DialogContent,
 	DialogDescription,
 	DialogFooter,
 	DialogHeader,
 	DialogTitle,
+	Input,
 	Select,
 	SelectContent,
 	SelectItem,
 	SelectTrigger,
 	SelectValue,
+	Textarea,
 } from "@/components/ui"
 import { vscode } from "@/utils/vscode"
 
@@ -65,9 +68,6 @@ const validateDescription = (description: string): string | null => {
 	return null
 }
 
-// Sentinel value for "Any mode" since Radix Select doesn't allow empty string values
-const MODE_ANY = "__any__"
-
 export const CreateSkillDialog: React.FC<CreateSkillDialogProps> = ({
 	open,
 	onOpenChange,
@@ -80,11 +80,14 @@ export const CreateSkillDialog: React.FC<CreateSkillDialogProps> = ({
 	const [name, setName] = useState("")
 	const [description, setDescription] = useState("")
 	const [source, setSource] = useState<"global" | "project">(hasWorkspace ? "project" : "global")
-	const [mode, setMode] = useState<string>(MODE_ANY)
 	const [nameError, setNameError] = useState<string | null>(null)
 	const [descriptionError, setDescriptionError] = useState<string | null>(null)
 
-	// Get available modes for the dropdown (built-in + custom modes)
+	// Multi-mode selection state (same pattern as SkillsSettings mode dialog)
+	const [selectedModes, setSelectedModes] = useState<string[]>([])
+	const [isAnyMode, setIsAnyMode] = useState(true)
+
+	// Get available modes for the checkboxes (built-in + custom modes)
 	const availableModes = useMemo(() => {
 		return getAllModes(customModes).map((m) => ({ slug: m.slug, name: m.name }))
 	}, [customModes])
@@ -93,7 +96,8 @@ export const CreateSkillDialog: React.FC<CreateSkillDialogProps> = ({
 		setName("")
 		setDescription("")
 		setSource(hasWorkspace ? "project" : "global")
-		setMode(MODE_ANY)
+		setSelectedModes([])
+		setIsAnyMode(true)
 		setNameError(null)
 		setDescriptionError(null)
 	}, [hasWorkspace])
@@ -114,6 +118,33 @@ export const CreateSkillDialog: React.FC<CreateSkillDialogProps> = ({
 		setDescriptionError(null)
 	}, [])
 
+	// Handle "Any mode" toggle - mutually exclusive with specific modes
+	const handleAnyModeToggle = useCallback((checked: boolean) => {
+		if (checked) {
+			setIsAnyMode(true)
+			setSelectedModes([]) // Clear specific modes when "Any mode" is selected
+		} else {
+			setIsAnyMode(false)
+		}
+	}, [])
+
+	// Handle specific mode toggle - unchecks "Any mode" when a specific mode is selected
+	const handleModeToggle = useCallback((modeSlug: string, checked: boolean) => {
+		if (checked) {
+			setIsAnyMode(false) // Uncheck "Any mode" when selecting a specific mode
+			setSelectedModes((prev) => [...prev, modeSlug])
+		} else {
+			setSelectedModes((prev) => {
+				const newModes = prev.filter((m) => m !== modeSlug)
+				// If no modes selected, default back to "Any mode"
+				if (newModes.length === 0) {
+					setIsAnyMode(true)
+				}
+				return newModes
+			})
+		}
+	}, [])
+
 	const handleCreate = useCallback(() => {
 		// Validate fields
 		const nameValidationError = validateSkillName(name)
@@ -130,73 +161,64 @@ export const CreateSkillDialog: React.FC<CreateSkillDialogProps> = ({
 		}
 
 		// Send message to create skill
-		// Convert MODE_ANY sentinel value to undefined for the backend
+		// Convert to modeSlugs: undefined for "Any mode", or array of selected modes
+		const modeSlugs = isAnyMode ? undefined : selectedModes.length > 0 ? selectedModes : undefined
 		vscode.postMessage({
 			type: "createSkill",
 			skillName: name,
 			source,
 			skillDescription: description,
-			skillMode: mode === MODE_ANY ? undefined : mode,
+			skillModeSlugs: modeSlugs,
 		})
 
 		// Close dialog and notify parent
 		handleClose()
 		onSkillCreated()
-	}, [name, description, source, mode, handleClose, onSkillCreated])
+	}, [name, description, source, isAnyMode, selectedModes, handleClose, onSkillCreated])
 
 	return (
 		<Dialog open={open} onOpenChange={onOpenChange}>
 			<DialogContent className="sm:max-w-md">
 				<DialogHeader>
 					<DialogTitle>{t("settings:skills.createDialog.title")}</DialogTitle>
-					<DialogDescription>{t("settings:skills.createDialog.description")}</DialogDescription>
+					<DialogDescription></DialogDescription>
 				</DialogHeader>
 
-				<div className="flex flex-col gap-4 py-4">
+				<div className="flex flex-col gap-4">
 					{/* Name Input */}
-					<div className="flex flex-col gap-1.5">
+					<div className="flex flex-col gap-1">
 						<label htmlFor="skill-name" className="text-sm font-medium text-vscode-foreground">
-							{t("settings:skills.createDialog.nameLabel")} *
+							{t("settings:skills.createDialog.nameLabel")}
 						</label>
-						<input
+						<Input
 							id="skill-name"
 							type="text"
 							value={name}
 							onChange={handleNameChange}
 							placeholder={t("settings:skills.createDialog.namePlaceholder")}
 							maxLength={64}
-							className="w-full bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded px-3 py-2 text-sm focus:outline-none focus:border-vscode-focusBorder"
+							className="w-full bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded-xl px-3 py-2 focus:outline-none focus:border-vscode-focusBorder"
 						/>
-						<span className="text-xs text-vscode-descriptionForeground">
-							{t("settings:skills.createDialog.nameHint")}
-						</span>
 						{nameError && <span className="text-xs text-vscode-errorForeground">{t(nameError)}</span>}
 					</div>
 
 					{/* Description Input */}
-					<div className="flex flex-col gap-1.5">
-						<label htmlFor="skill-description" className="text-sm font-medium text-vscode-foreground">
-							{t("settings:skills.createDialog.descriptionLabel")} *
-						</label>
-						<textarea
+					<div className="flex flex-col gap-1">
+						<Textarea
 							id="skill-description"
 							value={description}
 							onChange={handleDescriptionChange}
 							placeholder={t("settings:skills.createDialog.descriptionPlaceholder")}
 							maxLength={1024}
-							rows={3}
-							className="w-full bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded px-3 py-2 text-sm focus:outline-none focus:border-vscode-focusBorder resize-none"
+							rows={5}
 						/>
-						<span className="text-xs text-vscode-descriptionForeground">
-							{t("settings:skills.createDialog.descriptionHint")}
-						</span>
 						{descriptionError && (
 							<span className="text-xs text-vscode-errorForeground">{t(descriptionError)}</span>
 						)}
 					</div>
 
 					{/* Source Selection */}
-					<div className="flex flex-col gap-1.5">
+					<div className="flex flex-col gap-1">
 						<label className="text-sm font-medium text-vscode-foreground">
 							{t("settings:skills.createDialog.sourceLabel")}
 						</label>
@@ -211,32 +233,45 @@ export const CreateSkillDialog: React.FC<CreateSkillDialogProps> = ({
 								)}
 							</SelectContent>
 						</Select>
-						<span className="text-xs text-vscode-descriptionForeground">
-							{t("settings:skills.createDialog.sourceHint")}
-						</span>
 					</div>
 
 					{/* Mode Selection (Optional) */}
-					<div className="flex flex-col gap-1.5">
+					<div className="flex flex-col gap-1">
 						<label className="text-sm font-medium text-vscode-foreground">
 							{t("settings:skills.createDialog.modeLabel")}
 						</label>
-						<Select value={mode} onValueChange={setMode}>
-							<SelectTrigger className="w-full">
-								<SelectValue placeholder={t("settings:skills.createDialog.modePlaceholder")} />
-							</SelectTrigger>
-							<SelectContent>
-								<SelectItem value={MODE_ANY}>{t("settings:skills.createDialog.modeAny")}</SelectItem>
-								{availableModes.map((m) => (
-									<SelectItem key={m.slug} value={m.slug}>
-										{m.name}
-									</SelectItem>
-								))}
-							</SelectContent>
-						</Select>
-						<span className="text-xs text-vscode-descriptionForeground">
-							{t("settings:skills.createDialog.modeHint")}
+						<span className="text-xs text-vscode-descriptionForeground mb-1">
+							{t("settings:skills.modeDialog.intro")}
 						</span>
+
+						{/* Individual mode checkboxes */}
+						<div className="flex flex-col max-h-28 overflow-y-auto">
+							{/* Any mode option */}
+							<div className="flex items-center gap-3 p-1 rounded-lg hover:bg-vscode-list-hoverBackground">
+								<Checkbox
+									id="create-mode-any"
+									checked={isAnyMode}
+									onCheckedChange={(checked) => handleAnyModeToggle(checked === true)}
+								/>
+								<label htmlFor="create-mode-any" className="flex-1 cursor-pointer font-medium">
+									{t("settings:skills.modeDialog.anyMode")}
+								</label>
+							</div>
+							{availableModes.map((m) => (
+								<div
+									key={m.slug}
+									className="flex items-center gap-3 p-1 rounded-lg hover:bg-vscode-list-hoverBackground">
+									<Checkbox
+										id={`create-mode-${m.slug}`}
+										checked={selectedModes.includes(m.slug)}
+										onCheckedChange={(checked) => handleModeToggle(m.slug, checked === true)}
+									/>
+									<label htmlFor={`create-mode-${m.slug}`} className="flex-1 cursor-pointer">
+										{m.name}
+									</label>
+								</div>
+							))}
+						</div>
 					</div>
 				</div>
 

+ 156 - 0
webview-ui/src/components/settings/CreateSlashCommandDialog.tsx

@@ -0,0 +1,156 @@
+import React, { useState, useCallback } from "react"
+
+import { useAppTranslation } from "@/i18n/TranslationContext"
+import {
+	Button,
+	Dialog,
+	DialogContent,
+	DialogDescription,
+	DialogFooter,
+	DialogHeader,
+	DialogTitle,
+	Input,
+	Select,
+	SelectContent,
+	SelectItem,
+	SelectTrigger,
+	SelectValue,
+} from "@/components/ui"
+import { vscode } from "@/utils/vscode"
+
+interface CreateSlashCommandDialogProps {
+	open: boolean
+	onOpenChange: (open: boolean) => void
+	onCommandCreated: () => void
+	hasWorkspace: boolean
+}
+
+/**
+ * Validate command name:
+ * - Required
+ * - Must be alphanumeric with hyphens/underscores only
+ * - Max 64 characters
+ */
+const validateCommandName = (name: string): string | null => {
+	if (!name.trim()) return "settings:slashCommands.validation.nameRequired"
+	if (name.length > 64) return "settings:slashCommands.validation.nameTooLong"
+	// Allow alphanumeric, hyphens, underscores
+	if (!/^[a-zA-Z0-9_-]+$/.test(name)) return "settings:slashCommands.validation.nameInvalid"
+	return null
+}
+
+export const CreateSlashCommandDialog: React.FC<CreateSlashCommandDialogProps> = ({
+	open,
+	onOpenChange,
+	onCommandCreated,
+	hasWorkspace,
+}) => {
+	const { t } = useAppTranslation()
+
+	const [name, setName] = useState("")
+	const [source, setSource] = useState<"global" | "project">(hasWorkspace ? "project" : "global")
+	const [nameError, setNameError] = useState<string | null>(null)
+
+	const resetForm = useCallback(() => {
+		setName("")
+		setSource(hasWorkspace ? "project" : "global")
+		setNameError(null)
+	}, [hasWorkspace])
+
+	const handleClose = useCallback(() => {
+		resetForm()
+		onOpenChange(false)
+	}, [resetForm, onOpenChange])
+
+	const handleNameChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
+		// Allow alphanumeric, hyphens, underscores - convert to lowercase for consistency
+		const value = e.target.value.toLowerCase().replace(/[^a-z0-9_-]/g, "")
+		setName(value)
+		setNameError(null)
+	}, [])
+
+	const handleCreate = useCallback(() => {
+		// Validate name
+		const nameValidationError = validateCommandName(name)
+		if (nameValidationError) {
+			setNameError(nameValidationError)
+			return
+		}
+
+		// Append .md if not already present
+		const fileName = name.trim().endsWith(".md") ? name.trim() : `${name.trim()}.md`
+
+		// Send message to create command
+		vscode.postMessage({
+			type: "createCommand",
+			text: fileName,
+			values: { source },
+		})
+
+		// Close dialog and notify parent
+		handleClose()
+		onCommandCreated()
+	}, [name, source, handleClose, onCommandCreated])
+
+	return (
+		<Dialog open={open} onOpenChange={onOpenChange}>
+			<DialogContent className="sm:max-w-md">
+				<DialogHeader>
+					<DialogTitle>{t("settings:slashCommands.createDialog.title")}</DialogTitle>
+					<DialogDescription></DialogDescription>
+				</DialogHeader>
+
+				<div className="flex flex-col gap-4">
+					{/* Name Input */}
+					<div className="flex flex-col gap-1">
+						<label htmlFor="command-name" className="text-sm font-medium text-vscode-foreground">
+							{t("settings:slashCommands.createDialog.nameLabel")}
+						</label>
+						<Input
+							id="command-name"
+							type="text"
+							value={name}
+							onChange={handleNameChange}
+							placeholder={t("settings:slashCommands.createDialog.namePlaceholder")}
+							maxLength={64}
+							className="w-full bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded-xl px-3 py-2 text-sm focus:outline-none focus:border-vscode-focusBorder"
+						/>
+						<span className="text-xs text-vscode-descriptionForeground">
+							{t("settings:slashCommands.createDialog.nameHint")}
+						</span>
+						{nameError && <span className="text-xs text-vscode-errorForeground">{t(nameError)}</span>}
+					</div>
+
+					{/* Source Selection */}
+					<div className="flex flex-col gap-1">
+						<label className="text-sm font-medium text-vscode-foreground">
+							{t("settings:slashCommands.createDialog.sourceLabel")}
+						</label>
+						<Select value={source} onValueChange={(value) => setSource(value as "global" | "project")}>
+							<SelectTrigger className="w-full">
+								<SelectValue />
+							</SelectTrigger>
+							<SelectContent>
+								<SelectItem value="global">{t("settings:slashCommands.source.global")}</SelectItem>
+								{hasWorkspace && (
+									<SelectItem value="project">
+										{t("settings:slashCommands.source.project")}
+									</SelectItem>
+								)}
+							</SelectContent>
+						</Select>
+					</div>
+				</div>
+
+				<DialogFooter>
+					<Button variant="secondary" onClick={handleClose}>
+						{t("settings:slashCommands.createDialog.cancel")}
+					</Button>
+					<Button variant="primary" onClick={handleCreate} disabled={!name}>
+						{t("settings:slashCommands.createDialog.create")}
+					</Button>
+				</DialogFooter>
+			</DialogContent>
+		</Dialog>
+	)
+}

+ 4 - 4
webview-ui/src/components/settings/SettingsView.tsx

@@ -29,7 +29,7 @@ import {
 	Users2,
 	ArrowLeft,
 	GitCommitVertical,
-	Zap,
+	GraduationCap,
 } from "lucide-react"
 
 import {
@@ -512,10 +512,10 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		() => [
 			{ id: "providers", icon: Plug },
 			{ id: "modes", icon: Users2 },
-			{ id: "mcp", icon: Server },
-			{ id: "autoApprove", icon: CheckCheck },
+			{ id: "skills", icon: GraduationCap },
 			{ id: "slashCommands", icon: SquareSlash },
-			{ id: "skills", icon: Zap },
+			{ id: "autoApprove", icon: CheckCheck },
+			{ id: "mcp", icon: Server },
 			{ id: "browser", icon: SquareMousePointer },
 			{ id: "checkpoints", icon: GitCommitVertical },
 			{ id: "notifications", icon: Bell },

+ 259 - 94
webview-ui/src/components/settings/SkillsSettings.tsx

@@ -1,9 +1,11 @@
 import React, { useState, useEffect, useMemo, useCallback } from "react"
-import { Plus, Globe, Folder } from "lucide-react"
+import { Plus, Globe, Folder, Edit, Trash2, Settings } from "lucide-react"
 import { Trans } from "react-i18next"
 
 import type { SkillMetadata } from "@roo-code/types"
 
+import { getAllModes } from "@roo/modes"
+
 import { useAppTranslation } from "@/i18n/TranslationContext"
 import { useExtensionState } from "@/context/ExtensionStateContext"
 import {
@@ -16,29 +18,44 @@ import {
 	AlertDialogHeader,
 	AlertDialogTitle,
 	Button,
+	Checkbox,
+	Dialog,
+	DialogContent,
+	DialogDescription,
+	DialogFooter,
+	DialogHeader,
+	DialogTitle,
+	StandardTooltip,
 } from "@/components/ui"
 import { vscode } from "@/utils/vscode"
 import { buildDocLink } from "@/utils/docLinks"
 
 import { SectionHeader } from "./SectionHeader"
-import { Section } from "./Section"
-import { SearchableSetting } from "./SearchableSetting"
-import { SkillItem } from "./SkillItem"
 import { CreateSkillDialog } from "./CreateSkillDialog"
-import type { SectionName } from "./SettingsView"
 
 export const SkillsSettings: React.FC = () => {
 	const { t } = useAppTranslation()
-	const { cwd, skills: rawSkills } = useExtensionState()
+	const { cwd, skills: rawSkills, customModes } = useExtensionState()
 	const skills = useMemo(() => rawSkills ?? [], [rawSkills])
 
 	const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
 	const [skillToDelete, setSkillToDelete] = useState<SkillMetadata | null>(null)
 	const [createDialogOpen, setCreateDialogOpen] = useState(false)
 
+	// Mode selection modal state
+	const [modeDialogOpen, setModeDialogOpen] = useState(false)
+	const [skillToEditModes, setSkillToEditModes] = useState<SkillMetadata | null>(null)
+	const [selectedModes, setSelectedModes] = useState<string[]>([])
+	const [isAnyMode, setIsAnyMode] = useState(true)
+
 	// Check if we're in a workspace/project
 	const hasWorkspace = Boolean(cwd)
 
+	// Get available modes for the checkboxes (built-in + custom modes)
+	const availableModes = useMemo(() => {
+		return getAllModes(customModes).map((m) => ({ slug: m.slug, name: m.name }))
+	}, [customModes])
+
 	const handleRefresh = useCallback(() => {
 		vscode.postMessage({ type: "requestSkills" })
 	}, [])
@@ -59,7 +76,7 @@ export const SkillsSettings: React.FC = () => {
 				type: "deleteSkill",
 				skillName: skillToDelete.name,
 				source: skillToDelete.source,
-				skillMode: skillToDelete.mode,
+				skillModeSlugs: skillToDelete.modeSlugs,
 			})
 			setDeleteDialogOpen(false)
 			setSkillToDelete(null)
@@ -76,30 +93,136 @@ export const SkillsSettings: React.FC = () => {
 			type: "openSkillFile",
 			skillName: skill.name,
 			source: skill.source,
-			skillMode: skill.mode,
+			skillModeSlugs: skill.modeSlugs,
 		})
 	}, [])
 
+	// Open mode selection modal
+	const handleOpenModeDialog = useCallback((skill: SkillMetadata) => {
+		setSkillToEditModes(skill)
+		// Initialize state from skill's current modeSlugs
+		const hasModeSlugs = skill.modeSlugs && skill.modeSlugs.length > 0
+		setIsAnyMode(!hasModeSlugs)
+		setSelectedModes(hasModeSlugs ? [...skill.modeSlugs!] : [])
+		setModeDialogOpen(true)
+	}, [])
+
+	// Handle "Any mode" toggle - mutually exclusive with specific modes
+	const handleAnyModeToggle = useCallback((checked: boolean) => {
+		if (checked) {
+			setIsAnyMode(true)
+			setSelectedModes([]) // Clear specific modes when "Any mode" is selected
+		} else {
+			setIsAnyMode(false)
+		}
+	}, [])
+
+	// Handle specific mode toggle - unchecks "Any mode" when a specific mode is selected
+	const handleModeToggle = useCallback((modeSlug: string, checked: boolean) => {
+		if (checked) {
+			setIsAnyMode(false) // Uncheck "Any mode" when selecting a specific mode
+			setSelectedModes((prev) => [...prev, modeSlug])
+		} else {
+			setSelectedModes((prev) => {
+				const newModes = prev.filter((m) => m !== modeSlug)
+				// If no modes selected, default back to "Any mode"
+				if (newModes.length === 0) {
+					setIsAnyMode(true)
+				}
+				return newModes
+			})
+		}
+	}, [])
+
+	// Save mode changes
+	const handleSaveModes = useCallback(() => {
+		if (skillToEditModes) {
+			const newModeSlugs = isAnyMode ? undefined : selectedModes.length > 0 ? selectedModes : undefined
+			vscode.postMessage({
+				type: "updateSkillModes",
+				skillName: skillToEditModes.name,
+				source: skillToEditModes.source,
+				newSkillModeSlugs: newModeSlugs,
+			})
+			setModeDialogOpen(false)
+			setSkillToEditModes(null)
+		}
+	}, [skillToEditModes, isAnyMode, selectedModes])
+
+	const handleCloseModeDialog = useCallback(() => {
+		setModeDialogOpen(false)
+		setSkillToEditModes(null)
+	}, [])
+
 	// No-op callback - the backend sends updated skills list via ExtensionStateContext
 	const handleSkillCreated = useCallback(() => {}, [])
 
 	// Group skills by source
 	const projectSkills = useMemo(() => skills.filter((skill) => skill.source === "project"), [skills])
-
 	const globalSkills = useMemo(() => skills.filter((skill) => skill.source === "global"), [skills])
 
+	// Render a single skill item
+	const renderSkillItem = useCallback(
+		(skill: SkillMetadata) => {
+			const isBuiltIn = skill.source === "built-in"
+
+			return (
+				<div
+					key={`${skill.source}-${skill.name}-${skill.modeSlugs?.join(",") || "any"}`}
+					className="p-2.5 px-2 rounded-xl border border-transparent">
+					<div className="flex items-start justify-between gap-2 flex-col min-[400px]:flex-row overflow-hidden">
+						<div className="flex-1 min-w-0">
+							{/* Skill name */}
+							<div className="flex items-center gap-2 overflow-hidden">
+								<span className="font-medium truncate">{skill.name}</span>
+							</div>
+							{/* Skill description */}
+							{skill.description && (
+								<div className="text-xs text-vscode-descriptionForeground mt-1 line-clamp-3">
+									{skill.description}
+								</div>
+							)}
+						</div>
+
+						{/* Actions */}
+						<div className="flex items-center gap-1 px-0 ml-0 min-[400px]:ml-0 min-[400px]:mt-4 flex-shrink-0">
+							{/* Mode settings button (gear icon) - only for non-built-in skills */}
+							{!isBuiltIn && (
+								<StandardTooltip content={t("settings:skills.configureModes")}>
+									<Button variant="ghost" size="icon" onClick={() => handleOpenModeDialog(skill)}>
+										<Settings className="size-4" />
+									</Button>
+								</StandardTooltip>
+							)}
+
+							<StandardTooltip content={t("settings:skills.editSkill")}>
+								<Button variant="ghost" size="icon" onClick={() => handleEditClick(skill)}>
+									<Edit />
+								</Button>
+							</StandardTooltip>
+
+							{!isBuiltIn && (
+								<StandardTooltip content={t("settings:skills.deleteSkill")}>
+									<Button variant="ghost" size="icon" onClick={() => handleDeleteClick(skill)}>
+										<Trash2 className="text-destructive" />
+									</Button>
+								</StandardTooltip>
+							)}
+						</div>
+					</div>
+				</div>
+			)
+		},
+		[t, handleOpenModeDialog, handleEditClick, handleDeleteClick],
+	)
+
 	return (
-		<div>
-			<SectionHeader>{t("settings:sections.skills")}</SectionHeader>
-
-			<Section>
-				{/* Description section */}
-				<SearchableSetting
-					settingId="skills-description"
-					section={"skills" as SectionName}
-					label={t("settings:sections.skills")}
-					className="mb-4">
-					<p className="text-sm text-vscode-descriptionForeground mb-2">
+		<div className="flex flex-col h-full overflow-hidden">
+			{/* Fixed Header */}
+			<div className="flex-shrink-0">
+				<SectionHeader>{t("settings:sections.skills")}</SectionHeader>
+				<div className="flex flex-col gap-2 px-5 py-2">
+					<p className="text-vscode-descriptionForeground text-sm m-0">
 						<Trans
 							i18nKey="settings:skills.description"
 							components={{
@@ -115,86 +238,73 @@ export const SkillsSettings: React.FC = () => {
 							}}
 						/>
 					</p>
-				</SearchableSetting>
-
-				{/* Project Skills Section - Only show if in a workspace */}
-				{hasWorkspace && (
-					<SearchableSetting
-						settingId="skills-project"
-						section={"skills" as SectionName}
-						label={t("settings:skills.projectSkills")}
-						className="mb-6">
-						<div className="flex items-center justify-between mb-2">
-							<div className="flex items-center gap-1.5">
-								<Folder className="w-3 h-3" />
-								<h4 className="text-sm font-medium m-0">{t("settings:skills.projectSkills")}</h4>
+
+					{/* Add Skill button */}
+					<Button variant="secondary" className="py-1" onClick={() => setCreateDialogOpen(true)}>
+						<Plus />
+						{t("settings:skills.addSkill")}
+					</Button>
+				</div>
+			</div>
+
+			{/* Scrollable List Area */}
+			<div className="flex-1 overflow-y-auto px-4 py-2 min-h-0">
+				<div className="flex flex-col gap-1">
+					{/* Project Skills Section - Only show if in a workspace */}
+					{hasWorkspace && (
+						<>
+							<div className="flex items-center gap-2 px-2 py-2 mt-2 cursor-default">
+								<Folder className="size-4 shrink-0" />
+								<span className="font-medium text-lg">{t("settings:skills.workspaceSkills")}</span>
 							</div>
-							<Button
-								variant="ghost"
-								size="sm"
-								onClick={() => setCreateDialogOpen(true)}
-								className="h-6 px-2 text-xs opacity-60 hover:opacity-100">
-								<Plus className="w-3 h-3 mr-1" />
-								{t("settings:skills.addSkill")}
-							</Button>
-						</div>
-						<div className="border border-vscode-panel-border rounded-md">
 							{projectSkills.length > 0 ? (
-								projectSkills.map((skill) => (
-									<SkillItem
-										key={`project-${skill.name}-${skill.mode || "any"}`}
-										skill={skill}
-										onEdit={() => handleEditClick(skill)}
-										onDelete={() => handleDeleteClick(skill)}
-									/>
-								))
+								projectSkills.map(renderSkillItem)
 							) : (
-								<div className="px-4 py-6 text-sm text-vscode-descriptionForeground text-center">
-									{t("settings:skills.noProjectSkills")}
+								<div className="px-2 pb-4 text-sm text-vscode-descriptionForeground cursor-default">
+									{t("settings:skills.noWorkspaceSkills")}
 								</div>
 							)}
-						</div>
-					</SearchableSetting>
-				)}
-
-				{/* Global Skills Section */}
-				<SearchableSetting
-					settingId="skills-global"
-					section={"skills" as SectionName}
-					label={t("settings:skills.globalSkills")}
-					className="mb-6">
-					<div className="flex items-center justify-between mb-2">
-						<div className="flex items-center gap-1.5">
-							<Globe className="w-3 h-3" />
-							<h4 className="text-sm font-medium m-0">{t("settings:skills.globalSkills")}</h4>
-						</div>
-						<Button
-							variant="ghost"
-							size="sm"
-							onClick={() => setCreateDialogOpen(true)}
-							className="h-6 px-2 text-xs opacity-60 hover:opacity-100">
-							<Plus className="w-3 h-3 mr-1" />
-							{t("settings:skills.addSkill")}
-						</Button>
-					</div>
-					<div className="border border-vscode-panel-border rounded-md">
-						{globalSkills.length > 0 ? (
-							globalSkills.map((skill) => (
-								<SkillItem
-									key={`global-${skill.name}-${skill.mode || "any"}`}
-									skill={skill}
-									onEdit={() => handleEditClick(skill)}
-									onDelete={() => handleDeleteClick(skill)}
-								/>
-							))
-						) : (
-							<div className="px-4 py-6 text-sm text-vscode-descriptionForeground text-center">
-								{t("settings:skills.noGlobalSkills")}
-							</div>
-						)}
+						</>
+					)}
+
+					{/* Global Skills Section */}
+					<div className="flex items-center gap-2 px-2 py-2 mt-2 cursor-default">
+						<Globe className="size-4 shrink-0" />
+						<span className="font-medium text-lg">{t("settings:skills.globalSkills")}</span>
 					</div>
-				</SearchableSetting>
-			</Section>
+					{globalSkills.length > 0 ? (
+						globalSkills.map(renderSkillItem)
+					) : (
+						<div className="px-2 pb-4 text-sm text-vscode-descriptionForeground cursor-default">
+							{t("settings:skills.noGlobalSkills")}
+						</div>
+					)}
+				</div>
+			</div>
+
+			{/* Fixed Footer */}
+			<div className="px-6 py-1 text-sm border-t border-vscode-panel-border text-muted-foreground">
+				<Trans
+					i18nKey="settings:skills.footer"
+					components={{
+						MarketplaceLink: (
+							<span
+								onClick={() => {
+									window.postMessage(
+										{
+											type: "action",
+											action: "marketplaceButtonClicked",
+											values: { marketplaceTab: "mode" },
+										},
+										"*",
+									)
+								}}
+								className="text-vscode-textLink-foreground hover:underline cursor-pointer"
+							/>
+						),
+					}}
+				/>
+			</div>
 
 			{/* Delete Confirmation Dialog */}
 			<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
@@ -223,6 +333,61 @@ export const SkillsSettings: React.FC = () => {
 				onSkillCreated={handleSkillCreated}
 				hasWorkspace={hasWorkspace}
 			/>
+
+			{/* Mode Selection Dialog */}
+			<Dialog open={modeDialogOpen} onOpenChange={setModeDialogOpen}>
+				<DialogContent className="max-w-md">
+					<DialogHeader>
+						<DialogTitle>{t("settings:skills.modeDialog.title")}</DialogTitle>
+						<DialogDescription></DialogDescription>
+					</DialogHeader>
+
+					<div className="flex flex-col gap-1">
+						{/* Intro text */}
+						<p className="text-vscode-descriptionForeground">{t("settings:skills.modeDialog.intro")}</p>
+
+						{/* Any mode option */}
+						<div className="flex items-center gap-3 px-1 rounded-lg hover:bg-vscode-list-hoverBackground">
+							<Checkbox
+								id="mode-any"
+								checked={isAnyMode}
+								onCheckedChange={(checked) => handleAnyModeToggle(checked === true)}
+							/>
+							<label htmlFor="mode-any" className="flex-1 cursor-pointer font-medium">
+								{t("settings:skills.modeDialog.anyMode")}
+							</label>
+						</div>
+
+						{/* Separator */}
+						<div className="h-px bg-vscode-widget-border" />
+
+						{/* Individual mode checkboxes */}
+						<div className="flex flex-col max-h-60 overflow-y-auto">
+							{availableModes.map((mode) => (
+								<div
+									key={mode.slug}
+									className="flex items-center gap-3 p-1 rounded-lg hover:bg-vscode-list-hoverBackground">
+									<Checkbox
+										id={`mode-${mode.slug}`}
+										checked={selectedModes.includes(mode.slug)}
+										onCheckedChange={(checked) => handleModeToggle(mode.slug, checked === true)}
+									/>
+									<label htmlFor={`mode-${mode.slug}`} className="flex-1 cursor-pointer">
+										{mode.name}
+									</label>
+								</div>
+							))}
+						</div>
+					</div>
+
+					<DialogFooter>
+						<Button variant="secondary" onClick={handleCloseModeDialog}>
+							{t("settings:skills.modeDialog.cancel")}
+						</Button>
+						<Button onClick={handleSaveModes}>{t("settings:skills.modeDialog.save")}</Button>
+					</DialogFooter>
+				</DialogContent>
+			</Dialog>
 		</div>
 	)
 }

+ 149 - 174
webview-ui/src/components/settings/SlashCommandsSettings.tsx

@@ -1,5 +1,5 @@
-import React, { useState, useEffect } from "react"
-import { Plus, Globe, Folder, Settings } from "lucide-react"
+import React, { useState, useEffect, useMemo, useCallback } from "react"
+import { Plus, Globe, Folder, Edit, Trash2 } from "lucide-react"
 import { Trans } from "react-i18next"
 
 import type { Command } from "@roo-code/types"
@@ -16,41 +16,41 @@ import {
 	AlertDialogHeader,
 	AlertDialogTitle,
 	Button,
+	StandardTooltip,
 } from "@/components/ui"
 import { vscode } from "@/utils/vscode"
 import { buildDocLink } from "@/utils/docLinks"
 
 import { SectionHeader } from "./SectionHeader"
-import { Section } from "./Section"
-import { SearchableSetting } from "./SearchableSetting"
-import { SlashCommandItem } from "../chat/SlashCommandItem"
+import { CreateSlashCommandDialog } from "./CreateSlashCommandDialog"
 
 export const SlashCommandsSettings: React.FC = () => {
 	const { t } = useAppTranslation()
-	const { commands, cwd } = useExtensionState()
+	const { commands: rawCommands, cwd } = useExtensionState()
+	const commands = useMemo(() => rawCommands ?? [], [rawCommands])
+
 	const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
 	const [commandToDelete, setCommandToDelete] = useState<Command | null>(null)
-	const [globalNewName, setGlobalNewName] = useState("")
-	const [workspaceNewName, setWorkspaceNewName] = useState("")
+	const [createDialogOpen, setCreateDialogOpen] = useState(false)
 
 	// Check if we're in a workspace/project
 	const hasWorkspace = Boolean(cwd)
 
+	const handleRefresh = useCallback(() => {
+		vscode.postMessage({ type: "requestCommands" })
+	}, [])
+
 	// Request commands when component mounts
 	useEffect(() => {
 		handleRefresh()
-	}, [])
+	}, [handleRefresh])
 
-	const handleRefresh = () => {
-		vscode.postMessage({ type: "requestCommands" })
-	}
-
-	const handleDeleteClick = (command: Command) => {
+	const handleDeleteClick = useCallback((command: Command) => {
 		setCommandToDelete(command)
 		setDeleteDialogOpen(true)
-	}
+	}, [])
 
-	const handleDeleteConfirm = () => {
+	const handleDeleteConfirm = useCallback(() => {
 		if (commandToDelete) {
 			vscode.postMessage({
 				type: "deleteCommand",
@@ -62,57 +62,91 @@ export const SlashCommandsSettings: React.FC = () => {
 			// Refresh the commands list after deletion
 			setTimeout(handleRefresh, 100)
 		}
-	}
+	}, [commandToDelete, handleRefresh])
 
-	const handleDeleteCancel = () => {
+	const handleDeleteCancel = useCallback(() => {
 		setDeleteDialogOpen(false)
 		setCommandToDelete(null)
-	}
-
-	const handleCreateCommand = (source: "global" | "project", name: string) => {
-		if (!name.trim()) return
-
-		// Append .md if not already present
-		const fileName = name.trim().endsWith(".md") ? name.trim() : `${name.trim()}.md`
-
-		vscode.postMessage({
-			type: "createCommand",
-			text: fileName,
-			values: { source },
-		})
+	}, [])
 
-		// Clear the input and refresh
-		if (source === "global") {
-			setGlobalNewName("")
+	const handleEditClick = useCallback((command: Command) => {
+		if (command.filePath) {
+			vscode.postMessage({
+				type: "openFile",
+				text: command.filePath,
+			})
 		} else {
-			setWorkspaceNewName("")
+			// Fallback: request to open command file by name and source
+			vscode.postMessage({
+				type: "openCommandFile",
+				text: command.name,
+				values: { source: command.source },
+			})
 		}
-		setTimeout(handleRefresh, 500)
-	}
+	}, [])
 
-	const handleCommandClick = (command: Command) => {
-		// For now, we'll just show the command name - editing functionality can be added later
-		// This could be enhanced to open the command file in the editor
-		console.log(`Command clicked: ${command.name} (${command.source})`)
-	}
+	// No-op callback - the backend sends updated commands list via ExtensionStateContext
+	const handleCommandCreated = useCallback(() => {
+		setTimeout(handleRefresh, 500)
+	}, [handleRefresh])
 
 	// Group commands by source
-	const builtInCommands = commands?.filter((cmd) => cmd.source === "built-in") || []
-	const globalCommands = commands?.filter((cmd) => cmd.source === "global") || []
-	const projectCommands = commands?.filter((cmd) => cmd.source === "project") || []
+	const projectCommands = useMemo(() => commands.filter((cmd) => cmd.source === "project"), [commands])
+	const globalCommands = useMemo(() => commands.filter((cmd) => cmd.source === "global"), [commands])
+
+	// Render a single command item
+	const renderCommandItem = useCallback(
+		(command: Command) => {
+			const isBuiltIn = command.source === "built-in"
+
+			return (
+				<div
+					key={`${command.source}-${command.name}`}
+					className="p-2.5 px-2 rounded-xl border border-transparent">
+					<div className="flex items-start justify-between gap-2 flex-col min-[400px]:flex-row overflow-hidden">
+						<div className="flex-1 min-w-0">
+							{/* Command name */}
+							<div className="flex items-center gap-2 overflow-hidden">
+								<span className="font-medium truncate">{command.name}</span>
+							</div>
+							{/* Command description */}
+							{command.description && (
+								<div className="text-xs text-vscode-descriptionForeground mt-1 line-clamp-3">
+									{command.description}
+								</div>
+							)}
+						</div>
 
-	return (
-		<div>
-			<SectionHeader>{t("settings:sections.slashCommands")}</SectionHeader>
+						{/* Actions */}
+						<div className="flex items-center gap-1 px-0 ml-0 min-[400px]:ml-0 min-[400px]:mt-2 flex-shrink-0">
+							<StandardTooltip content={t("settings:slashCommands.editCommand")}>
+								<Button variant="ghost" size="icon" onClick={() => handleEditClick(command)}>
+									<Edit />
+								</Button>
+							</StandardTooltip>
+
+							{!isBuiltIn && (
+								<StandardTooltip content={t("settings:slashCommands.deleteCommand")}>
+									<Button variant="ghost" size="icon" onClick={() => handleDeleteClick(command)}>
+										<Trash2 className="text-destructive" />
+									</Button>
+								</StandardTooltip>
+							)}
+						</div>
+					</div>
+				</div>
+			)
+		},
+		[t, handleEditClick, handleDeleteClick],
+	)
 
-			<Section>
-				{/* Description section */}
-				<SearchableSetting
-					settingId="slash-commands-description"
-					section="slashCommands"
-					label={t("settings:sections.slashCommands")}
-					className="mb-4">
-					<p className="text-sm text-vscode-descriptionForeground">
+	return (
+		<div className="flex flex-col h-full overflow-hidden">
+			{/* Fixed Header */}
+			<div className="flex-shrink-0">
+				<SectionHeader>{t("settings:sections.slashCommands")}</SectionHeader>
+				<div className="flex flex-col gap-2 px-5 py-2">
+					<p className="text-vscode-descriptionForeground text-sm m-0">
 						<Trans
 							i18nKey="settings:slashCommands.description"
 							components={{
@@ -128,143 +162,84 @@ export const SlashCommandsSettings: React.FC = () => {
 							}}
 						/>
 					</p>
-				</SearchableSetting>
-
-				{/* Global Commands Section */}
-				<SearchableSetting
-					settingId="slash-commands-global"
-					section="slashCommands"
-					label={t("chat:slashCommands.globalCommands")}
-					className="mb-6">
-					<div className="flex items-center gap-1.5 mb-2">
-						<Globe className="w-3 h-3" />
-						<h4 className="text-sm font-medium m-0">{t("chat:slashCommands.globalCommands")}</h4>
-					</div>
-					<div className="border border-vscode-panel-border rounded-md">
-						{globalCommands.map((command) => (
-							<SlashCommandItem
-								key={`global-${command.name}`}
-								command={command}
-								onDelete={handleDeleteClick}
-								onClick={handleCommandClick}
-							/>
-						))}
-						{/* New global command input */}
-						<div className="px-4 py-2 flex items-center gap-2 hover:bg-vscode-list-hoverBackground border-t border-vscode-panel-border">
-							<input
-								type="text"
-								value={globalNewName}
-								onChange={(e) => setGlobalNewName(e.target.value)}
-								placeholder={t("chat:slashCommands.newGlobalCommandPlaceholder")}
-								className="flex-1 bg-vscode-input-background text-vscode-input-foreground placeholder-vscode-input-placeholderForeground border border-vscode-input-border rounded px-2 py-1 text-sm focus:outline-none focus:border-vscode-focusBorder"
-								onKeyDown={(e) => {
-									if (e.key === "Enter") {
-										handleCreateCommand("global", globalNewName)
-									}
-								}}
-							/>
-							<Button
-								variant="ghost"
-								size="icon"
-								onClick={() => handleCreateCommand("global", globalNewName)}
-								disabled={!globalNewName.trim()}
-								className="size-6 flex items-center justify-center opacity-60 hover:opacity-100">
-								<Plus className="w-4 h-4" />
-							</Button>
-						</div>
-					</div>
-				</SearchableSetting>
 
-				{/* Workspace Commands Section - Only show if in a workspace */}
-				{hasWorkspace && (
-					<SearchableSetting
-						settingId="slash-commands-workspace"
-						section="slashCommands"
-						label={t("chat:slashCommands.workspaceCommands")}
-						className="mb-6">
-						<div className="flex items-center gap-1.5 mb-2">
-							<Folder className="w-3 h-3" />
-							<h4 className="text-sm font-medium m-0">{t("chat:slashCommands.workspaceCommands")}</h4>
-						</div>
-						<div className="border border-vscode-panel-border rounded-md">
-							{projectCommands.map((command) => (
-								<SlashCommandItem
-									key={`project-${command.name}`}
-									command={command}
-									onDelete={handleDeleteClick}
-									onClick={handleCommandClick}
-								/>
-							))}
-							{/* New workspace command input */}
-							<div className="px-4 py-2 flex items-center gap-2 hover:bg-vscode-list-hoverBackground border-t border-vscode-panel-border">
-								<input
-									type="text"
-									value={workspaceNewName}
-									onChange={(e) => setWorkspaceNewName(e.target.value)}
-									placeholder={t("chat:slashCommands.newWorkspaceCommandPlaceholder")}
-									className="flex-1 bg-vscode-input-background text-vscode-input-foreground placeholder-vscode-input-placeholderForeground border border-vscode-input-border rounded px-2 py-1 text-sm focus:outline-none focus:border-vscode-focusBorder"
-									onKeyDown={(e) => {
-										if (e.key === "Enter") {
-											handleCreateCommand("project", workspaceNewName)
-										}
-									}}
-								/>
-								<Button
-									variant="ghost"
-									size="icon"
-									onClick={() => handleCreateCommand("project", workspaceNewName)}
-									disabled={!workspaceNewName.trim()}
-									className="size-6 flex items-center justify-center opacity-60 hover:opacity-100">
-									<Plus className="w-4 h-4" />
-								</Button>
+					{/* Add Command button */}
+					<Button variant="secondary" className="py-1" onClick={() => setCreateDialogOpen(true)}>
+						<Plus />
+						{t("settings:slashCommands.addCommand")}
+					</Button>
+				</div>
+			</div>
+
+			{/* Scrollable List Area */}
+			<div className="flex-1 overflow-y-auto px-4 py-2 min-h-0">
+				<div className="flex flex-col gap-1">
+					{/* Project Commands Section - Only show if in a workspace */}
+					{hasWorkspace && (
+						<>
+							<div className="flex items-center gap-2 px-2 py-2 mt-2 cursor-default">
+								<Folder className="size-4 shrink-0" />
+								<span className="font-medium text-lg">
+									{t("settings:slashCommands.workspaceCommands")}
+								</span>
 							</div>
+							{projectCommands.length > 0 ? (
+								projectCommands.map(renderCommandItem)
+							) : (
+								<div className="px-2 pb-4 text-sm text-vscode-descriptionForeground cursor-default">
+									{t("settings:slashCommands.noWorkspaceCommands")}
+								</div>
+							)}
+						</>
+					)}
+
+					{/* Global Commands Section */}
+					<div className="flex items-center gap-2 px-2 py-2 mt-2 cursor-default">
+						<Globe className="size-4 shrink-0" />
+						<span className="font-medium text-lg">{t("settings:slashCommands.globalCommands")}</span>
+					</div>
+					{globalCommands.length > 0 ? (
+						globalCommands.map(renderCommandItem)
+					) : (
+						<div className="px-2 pb-4 text-sm text-vscode-descriptionForeground cursor-default">
+							{t("settings:slashCommands.noGlobalCommands")}
 						</div>
-					</SearchableSetting>
-				)}
+					)}
+				</div>
+			</div>
 
-				{/* Built-in Commands Section */}
-				{builtInCommands.length > 0 && (
-					<SearchableSetting
-						settingId="slash-commands-built-in"
-						section="slashCommands"
-						label={t("chat:slashCommands.builtInCommands")}
-						className="mb-6">
-						<div className="flex items-center gap-1.5 mb-2">
-							<Settings className="w-3 h-3" />
-							<h4 className="text-sm font-medium m-0">{t("chat:slashCommands.builtInCommands")}</h4>
-						</div>
-						<div className="border border-vscode-panel-border rounded-md">
-							{builtInCommands.map((command) => (
-								<SlashCommandItem
-									key={`built-in-${command.name}`}
-									command={command}
-									onDelete={handleDeleteClick}
-									onClick={handleCommandClick}
-								/>
-							))}
-						</div>
-					</SearchableSetting>
-				)}
-			</Section>
+			{/* Fixed Footer */}
+			<div className="px-6 py-1 text-sm border-t border-vscode-panel-border text-muted-foreground">
+				{t("settings:slashCommands.footer")}
+			</div>
 
+			{/* Delete Confirmation Dialog */}
 			<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
 				<AlertDialogContent>
 					<AlertDialogHeader>
-						<AlertDialogTitle>{t("chat:slashCommands.deleteDialog.title")}</AlertDialogTitle>
+						<AlertDialogTitle>{t("settings:slashCommands.deleteDialog.title")}</AlertDialogTitle>
 						<AlertDialogDescription>
-							{t("chat:slashCommands.deleteDialog.description", { name: commandToDelete?.name })}
+							{t("settings:slashCommands.deleteDialog.description", { name: commandToDelete?.name })}
 						</AlertDialogDescription>
 					</AlertDialogHeader>
 					<AlertDialogFooter>
 						<AlertDialogCancel onClick={handleDeleteCancel}>
-							{t("chat:slashCommands.deleteDialog.cancel")}
+							{t("settings:slashCommands.deleteDialog.cancel")}
 						</AlertDialogCancel>
 						<AlertDialogAction onClick={handleDeleteConfirm}>
-							{t("chat:slashCommands.deleteDialog.confirm")}
+							{t("settings:slashCommands.deleteDialog.confirm")}
 						</AlertDialogAction>
 					</AlertDialogFooter>
 				</AlertDialogContent>
 			</AlertDialog>
+
+			{/* Create Command Dialog */}
+			<CreateSlashCommandDialog
+				open={createDialogOpen}
+				onOpenChange={setCreateDialogOpen}
+				onCommandCreated={handleCommandCreated}
+				hasWorkspace={hasWorkspace}
+			/>
 		</div>
 	)
 }

+ 121 - 9
webview-ui/src/components/settings/__tests__/CreateSkillDialog.spec.tsx

@@ -34,6 +34,36 @@ vi.mock("@/components/ui", () => ({
 			{children}
 		</button>
 	),
+	Checkbox: ({ id, checked, onCheckedChange }: any) => (
+		<input
+			type="checkbox"
+			id={id}
+			checked={checked}
+			data-testid={`checkbox-${id}`}
+			onChange={(e) => onCheckedChange(e.target.checked)}
+		/>
+	),
+	Input: ({ value, onChange, placeholder, id, type, className, ...props }: any) => (
+		<input
+			type={type || "text"}
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
+	Textarea: ({ value, onChange, placeholder, id, className, ...props }: any) => (
+		<textarea
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
 	Dialog: ({ children, open }: any) => (
 		<div data-testid="dialog" data-open={open}>
 			{open && children}
@@ -235,7 +265,7 @@ describe("CreateSkillDialog", () => {
 				skillName: "my-skill",
 				source: "project",
 				skillDescription: "My skill description",
-				skillMode: undefined,
+				skillModeSlugs: undefined,
 			})
 		})
 	})
@@ -352,7 +382,72 @@ describe("CreateSkillDialog", () => {
 		expect(screen.getByTestId("select-item-project")).toBeInTheDocument()
 	})
 
-	it("renders mode selection dropdown", () => {
+	it("renders mode selection checkboxes", () => {
+		render(
+			<CreateSkillDialog
+				open={true}
+				onOpenChange={mockOnOpenChange}
+				onSkillCreated={mockOnSkillCreated}
+				hasWorkspace={true}
+			/>,
+		)
+
+		// Should have "Any mode" checkbox (checked by default)
+		expect(screen.getByTestId("checkbox-create-mode-any")).toBeInTheDocument()
+		expect(screen.getByTestId("checkbox-create-mode-any")).toBeChecked()
+		// Should have built-in mode checkboxes
+		expect(screen.getByTestId("checkbox-create-mode-code")).toBeInTheDocument()
+		expect(screen.getByTestId("checkbox-create-mode-architect")).toBeInTheDocument()
+		// Should have custom mode checkbox from state
+		expect(screen.getByTestId("checkbox-create-mode-custom-mode")).toBeInTheDocument()
+	})
+
+	it("unchecks 'Any mode' when a specific mode is selected", () => {
+		render(
+			<CreateSkillDialog
+				open={true}
+				onOpenChange={mockOnOpenChange}
+				onSkillCreated={mockOnSkillCreated}
+				hasWorkspace={true}
+			/>,
+		)
+
+		// Initially "Any mode" is checked
+		expect(screen.getByTestId("checkbox-create-mode-any")).toBeChecked()
+		expect(screen.getByTestId("checkbox-create-mode-code")).not.toBeChecked()
+
+		// Click on "Code" mode checkbox
+		fireEvent.click(screen.getByTestId("checkbox-create-mode-code"))
+
+		// "Any mode" should be unchecked and "Code" should be checked
+		expect(screen.getByTestId("checkbox-create-mode-any")).not.toBeChecked()
+		expect(screen.getByTestId("checkbox-create-mode-code")).toBeChecked()
+	})
+
+	it("reverts to 'Any mode' when all specific modes are unchecked", () => {
+		render(
+			<CreateSkillDialog
+				open={true}
+				onOpenChange={mockOnOpenChange}
+				onSkillCreated={mockOnSkillCreated}
+				hasWorkspace={true}
+			/>,
+		)
+
+		// Select a specific mode
+		fireEvent.click(screen.getByTestId("checkbox-create-mode-code"))
+		expect(screen.getByTestId("checkbox-create-mode-any")).not.toBeChecked()
+		expect(screen.getByTestId("checkbox-create-mode-code")).toBeChecked()
+
+		// Uncheck it
+		fireEvent.click(screen.getByTestId("checkbox-create-mode-code"))
+
+		// Should revert to "Any mode"
+		expect(screen.getByTestId("checkbox-create-mode-any")).toBeChecked()
+		expect(screen.getByTestId("checkbox-create-mode-code")).not.toBeChecked()
+	})
+
+	it("sends selected modes when creating skill with specific modes", async () => {
 		render(
 			<CreateSkillDialog
 				open={true}
@@ -362,13 +457,30 @@ describe("CreateSkillDialog", () => {
 			/>,
 		)
 
-		// Should have "Any mode" option (uses __any__ sentinel value)
-		expect(screen.getByTestId("select-item-__any__")).toBeInTheDocument()
-		// Should have built-in modes
-		expect(screen.getByTestId("select-item-code")).toBeInTheDocument()
-		expect(screen.getByTestId("select-item-architect")).toBeInTheDocument()
-		// Should have custom modes from state
-		expect(screen.getByTestId("select-item-custom-mode")).toBeInTheDocument()
+		const nameInput = screen.getByPlaceholderText("settings:skills.createDialog.namePlaceholder")
+		const descInput = screen.getByPlaceholderText("settings:skills.createDialog.descriptionPlaceholder")
+
+		fireEvent.change(nameInput, { target: { value: "my-skill" } })
+		fireEvent.change(descInput, { target: { value: "My skill description" } })
+
+		// Select "Code" and "Architect" modes
+		fireEvent.click(screen.getByTestId("checkbox-create-mode-code"))
+		fireEvent.click(screen.getByTestId("checkbox-create-mode-architect"))
+
+		const buttons = screen.getAllByTestId("button")
+		const createButton = buttons.find((btn) => btn.getAttribute("data-variant") === "primary")
+
+		fireEvent.click(createButton!)
+
+		await waitFor(() => {
+			expect(vscode.postMessage).toHaveBeenCalledWith({
+				type: "createSkill",
+				skillName: "my-skill",
+				source: "project",
+				skillDescription: "My skill description",
+				skillModeSlugs: ["code", "architect"],
+			})
+		})
 	})
 
 	it("clears form after successful skill creation", async () => {

+ 31 - 0
webview-ui/src/components/settings/__tests__/SettingsView.change-detection.spec.tsx

@@ -33,6 +33,37 @@ vi.mock("@src/components/ui", () => ({
 			Toggle
 		</button>
 	),
+	Input: ({ value, onChange, placeholder, id, type, className, ...props }: any) => (
+		<input
+			type={type || "text"}
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
+	Textarea: ({ value, onChange, placeholder, id, className, ...props }: any) => (
+		<textarea
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
+	Checkbox: ({ checked, onCheckedChange, id, className, ...props }: any) => (
+		<input
+			type="checkbox"
+			checked={checked}
+			onChange={(e) => onCheckedChange?.(e.target.checked)}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
 	AlertDialog: ({ open, children }: any) => (open ? <div data-testid="alert-dialog">{children}</div> : null),
 	AlertDialogContent: ({ children }: any) => <div>{children}</div>,
 	AlertDialogTitle: ({ children }: any) => <div data-testid="alert-title">{children}</div>,

+ 20 - 0
webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx

@@ -121,6 +121,26 @@ vi.mock("@/components/ui", () => ({
 			Toggle
 		</button>
 	),
+	Checkbox: ({ checked, onCheckedChange, id, className, ...props }: any) => (
+		<input
+			type="checkbox"
+			checked={checked}
+			onChange={(e) => onCheckedChange?.(e.target.checked)}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
+	Textarea: ({ value, onChange, placeholder, id, className, ...props }: any) => (
+		<textarea
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
 	Popover: ({ children }: any) => <div data-testid="popover">{children}</div>,
 	PopoverTrigger: ({ children }: any) => <div data-testid="popover-trigger">{children}</div>,
 	PopoverContent: ({ children }: any) => <div data-testid="popover-content">{children}</div>,

+ 31 - 0
webview-ui/src/components/settings/__tests__/SettingsView.unsaved-changes.spec.tsx

@@ -34,6 +34,37 @@ vi.mock("@src/components/ui", () => ({
 			Toggle
 		</button>
 	),
+	Input: ({ value, onChange, placeholder, id, type, className, ...props }: any) => (
+		<input
+			type={type || "text"}
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
+	Textarea: ({ value, onChange, placeholder, id, className, ...props }: any) => (
+		<textarea
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
+	Checkbox: ({ checked, onCheckedChange, id, className, ...props }: any) => (
+		<input
+			type="checkbox"
+			checked={checked}
+			onChange={(e) => onCheckedChange?.(e.target.checked)}
+			id={id}
+			className={className}
+			{...props}
+		/>
+	),
 	AlertDialog: ({ children }: any) => <div>{children}</div>,
 	AlertDialogContent: ({ children }: any) => <div>{children}</div>,
 	AlertDialogTitle: ({ children }: any) => <div>{children}</div>,

+ 106 - 92
webview-ui/src/components/settings/__tests__/SkillsSettings.spec.tsx

@@ -32,6 +32,14 @@ vi.mock("@/utils/docLinks", () => ({
 	buildDocLink: (path: string, anchor?: string) => `https://docs.example.com/${path}${anchor ? `#${anchor}` : ""}`,
 }))
 
+// Mock modes
+vi.mock("@roo/modes", () => ({
+	getAllModes: () => [
+		{ slug: "code", name: "Code" },
+		{ slug: "architect", name: "Architect" },
+	],
+}))
+
 // Mock UI components
 vi.mock("@/components/ui", () => ({
 	AlertDialog: ({ children, open }: any) => (
@@ -65,23 +73,43 @@ vi.mock("@/components/ui", () => ({
 			{children}
 		</button>
 	),
-}))
-
-// Mock SkillItem component
-vi.mock("../SkillItem", () => ({
-	SkillItem: ({ skill, onEdit, onDelete }: any) => (
-		<div data-testid={`skill-item-${skill.name}`}>
-			<span>{skill.name}</span>
-			{skill.description && <span>{skill.description}</span>}
-			{skill.mode && <span data-testid={`skill-mode-${skill.name}`}>{skill.mode}</span>}
-			<button onClick={onEdit} data-testid={`edit-${skill.name}`}>
-				Edit
-			</button>
-			<button onClick={onDelete} data-testid={`delete-${skill.name}`}>
-				Delete
-			</button>
+	StandardTooltip: ({ children }: any) => <>{children}</>,
+	Dialog: ({ children, open, _onOpenChange }: any) => (
+		<div data-testid="mode-dialog" data-open={open}>
+			{open && children}
+		</div>
+	),
+	DialogContent: ({ children }: any) => <div data-testid="dialog-content">{children}</div>,
+	DialogHeader: ({ children }: any) => <div data-testid="dialog-header">{children}</div>,
+	DialogTitle: ({ children }: any) => <div data-testid="dialog-title">{children}</div>,
+	DialogDescription: ({ children }: any) => <div data-testid="dialog-description">{children}</div>,
+	DialogFooter: ({ children }: any) => <div data-testid="dialog-footer">{children}</div>,
+	Checkbox: ({ id, checked, onCheckedChange }: any) => (
+		<input
+			type="checkbox"
+			id={id}
+			checked={checked}
+			onChange={(e) => onCheckedChange?.(e.target.checked)}
+			data-testid={`checkbox-${id}`}
+		/>
+	),
+	Select: ({ children, value, _onValueChange }: any) => (
+		<div data-testid="select" data-value={value}>
+			{children}
+		</div>
+	),
+	SelectTrigger: ({ children, className }: any) => (
+		<button data-testid="select-trigger" className={className}>
+			{children}
+		</button>
+	),
+	SelectContent: ({ children }: any) => <div data-testid="select-content">{children}</div>,
+	SelectItem: ({ children, value }: any) => (
+		<div data-testid={`select-item-${value}`} data-value={value}>
+			{children}
 		</div>
 	),
+	SelectValue: () => <span data-testid="select-value" />,
 }))
 
 // Mock CreateSkillDialog component
@@ -102,20 +130,11 @@ vi.mock("../CreateSkillDialog", () => ({
 	),
 }))
 
-// Mock SectionHeader and Section components
+// Mock SectionHeader component
 vi.mock("../SectionHeader", () => ({
 	SectionHeader: ({ children }: any) => <div data-testid="section-header">{children}</div>,
 }))
 
-vi.mock("../Section", () => ({
-	Section: ({ children }: any) => <div data-testid="section">{children}</div>,
-}))
-
-// Mock SearchableSetting
-vi.mock("../SearchableSetting", () => ({
-	SearchableSetting: ({ children }: any) => <div data-testid="searchable-setting">{children}</div>,
-}))
-
 const mockSkills: SkillMetadata[] = [
 	{
 		name: "project-skill",
@@ -126,9 +145,9 @@ const mockSkills: SkillMetadata[] = [
 	{
 		name: "project-mode-skill",
 		description: "A project mode-specific skill",
-		path: "/workspace/.roo/skills-architect/project-mode-skill/SKILL.md",
+		path: "/workspace/.roo/skills/project-mode-skill/SKILL.md",
 		source: "project",
-		mode: "architect",
+		modeSlugs: ["architect"],
 	},
 	{
 		name: "global-skill",
@@ -192,29 +211,29 @@ describe("SkillsSettings", () => {
 	it("displays project skills section when in a workspace", () => {
 		renderSkillsSettings()
 
-		expect(screen.getByText("settings:skills.projectSkills")).toBeInTheDocument()
-		expect(screen.getByTestId("skill-item-project-skill")).toBeInTheDocument()
+		expect(screen.getByText("settings:skills.workspaceSkills")).toBeInTheDocument()
+		expect(screen.getByText("project-skill")).toBeInTheDocument()
 	})
 
 	it("displays global skills section", () => {
 		renderSkillsSettings()
 
 		expect(screen.getByText("settings:skills.globalSkills")).toBeInTheDocument()
-		expect(screen.getByTestId("skill-item-global-skill")).toBeInTheDocument()
+		expect(screen.getByText("global-skill")).toBeInTheDocument()
 	})
 
 	it("does not display project skills section when not in a workspace", () => {
 		const globalOnlySkills = mockSkills.filter((s) => s.source === "global")
 		renderSkillsSettings(globalOnlySkills, "")
 
-		expect(screen.queryByText("settings:skills.projectSkills")).not.toBeInTheDocument()
+		expect(screen.queryByText("settings:skills.workspaceSkills")).not.toBeInTheDocument()
 	})
 
 	it("shows empty state for project skills when none exist", () => {
 		const globalOnlySkills = mockSkills.filter((s) => s.source === "global")
 		renderSkillsSettings(globalOnlySkills)
 
-		expect(screen.getByText("settings:skills.noProjectSkills")).toBeInTheDocument()
+		expect(screen.getByText("settings:skills.noWorkspaceSkills")).toBeInTheDocument()
 	})
 
 	it("shows empty state for global skills when none exist", () => {
@@ -228,25 +247,29 @@ describe("SkillsSettings", () => {
 		renderSkillsSettings()
 
 		// Project skills
-		expect(screen.getByTestId("skill-item-project-skill")).toBeInTheDocument()
-		expect(screen.getByTestId("skill-item-project-mode-skill")).toBeInTheDocument()
+		expect(screen.getByText("project-skill")).toBeInTheDocument()
+		expect(screen.getByText("project-mode-skill")).toBeInTheDocument()
 
 		// Global skills
-		expect(screen.getByTestId("skill-item-global-skill")).toBeInTheDocument()
+		expect(screen.getByText("global-skill")).toBeInTheDocument()
 	})
 
-	it("displays mode badge for mode-specific skills", () => {
+	it("displays skill descriptions", () => {
 		renderSkillsSettings()
 
-		expect(screen.getByTestId("skill-mode-project-mode-skill")).toBeInTheDocument()
-		expect(screen.getByText("architect")).toBeInTheDocument()
+		expect(screen.getByText("A project skill")).toBeInTheDocument()
+		expect(screen.getByText("A global skill")).toBeInTheDocument()
 	})
 
 	it("opens create skill dialog when add button is clicked", () => {
 		renderSkillsSettings()
 
-		const addButtons = screen.getAllByTestId("button")
-		fireEvent.click(addButtons[0])
+		// There's now a single "Add Skill" button at the top
+		const addButton = screen
+			.getAllByTestId("button")
+			.find((btn) => btn.textContent?.includes("settings:skills.addSkill"))
+		expect(addButton).toBeInTheDocument()
+		fireEvent.click(addButton!)
 
 		expect(screen.getByTestId("create-skill-dialog")).toHaveAttribute("data-open", "true")
 	})
@@ -254,8 +277,13 @@ describe("SkillsSettings", () => {
 	it("opens delete confirmation dialog when delete button is clicked", () => {
 		renderSkillsSettings()
 
-		const deleteButton = screen.getByTestId("delete-project-skill")
-		fireEvent.click(deleteButton)
+		// Find all delete buttons (buttons with Trash icon)
+		const buttons = screen.getAllByTestId("button")
+		// The delete button should be after the edit button for each skill
+		// Find the first delete button (for project-skill)
+		const deleteButtons = buttons.filter((btn) => btn.querySelector('[class*="text-destructive"]'))
+		expect(deleteButtons.length).toBeGreaterThan(0)
+		fireEvent.click(deleteButtons[0])
 
 		expect(screen.getByTestId("alert-dialog")).toHaveAttribute("data-open", "true")
 		expect(screen.getByText("settings:skills.deleteDialog.title")).toBeInTheDocument()
@@ -264,8 +292,10 @@ describe("SkillsSettings", () => {
 	it("deletes skill when confirmation is clicked", async () => {
 		renderSkillsSettings()
 
-		const deleteButton = screen.getByTestId("delete-project-skill")
-		fireEvent.click(deleteButton)
+		// Find and click delete button
+		const buttons = screen.getAllByTestId("button")
+		const deleteButtons = buttons.filter((btn) => btn.querySelector('[class*="text-destructive"]'))
+		fireEvent.click(deleteButtons[0])
 
 		const confirmButton = screen.getByTestId("alert-dialog-action")
 		fireEvent.click(confirmButton)
@@ -283,8 +313,10 @@ describe("SkillsSettings", () => {
 	it("cancels deletion when cancel is clicked", () => {
 		renderSkillsSettings()
 
-		const deleteButton = screen.getByTestId("delete-project-skill")
-		fireEvent.click(deleteButton)
+		// Find and click delete button
+		const buttons = screen.getAllByTestId("button")
+		const deleteButtons = buttons.filter((btn) => btn.querySelector('[class*="text-destructive"]'))
+		fireEvent.click(deleteButtons[0])
 
 		const cancelButton = screen.getByTestId("alert-dialog-cancel")
 		fireEvent.click(cancelButton)
@@ -295,8 +327,19 @@ describe("SkillsSettings", () => {
 	it("opens skill file when edit button is clicked", () => {
 		renderSkillsSettings()
 
-		const editButton = screen.getByTestId("edit-project-skill")
-		fireEvent.click(editButton)
+		// Find edit buttons (buttons without destructive class that are icon size)
+		const buttons = screen.getAllByTestId("button")
+		// Filter to find edit buttons (the ones with Edit icon, not Add, Delete, or Settings/Gear)
+		// Edit button uses lucide-square-pen icon, Settings uses lucide-settings
+		const editButtons = buttons.filter(
+			(btn) =>
+				btn.getAttribute("data-size") === "icon" &&
+				!btn.querySelector('[class*="text-destructive"]') &&
+				!btn.querySelector('[class*="lucide-settings"]') &&
+				btn.querySelector('[class*="lucide-square-pen"]'),
+		)
+		// Click the first edit button (for project-skill)
+		fireEvent.click(editButtons[0])
 
 		expect(vscode.postMessage).toHaveBeenCalledWith({
 			type: "openSkillFile",
@@ -306,47 +349,16 @@ describe("SkillsSettings", () => {
 		})
 	})
 
-	it("sends mode when editing mode-specific skill", () => {
-		renderSkillsSettings()
-
-		const editButton = screen.getByTestId("edit-project-mode-skill")
-		fireEvent.click(editButton)
-
-		expect(vscode.postMessage).toHaveBeenCalledWith({
-			type: "openSkillFile",
-			skillName: "project-mode-skill",
-			source: "project",
-			skillMode: "architect",
-		})
-	})
-
-	it("sends mode when deleting mode-specific skill", async () => {
-		renderSkillsSettings()
-
-		const deleteButton = screen.getByTestId("delete-project-mode-skill")
-		fireEvent.click(deleteButton)
-
-		const confirmButton = screen.getByTestId("alert-dialog-action")
-		fireEvent.click(confirmButton)
-
-		await waitFor(() => {
-			expect(vscode.postMessage).toHaveBeenCalledWith({
-				type: "deleteSkill",
-				skillName: "project-mode-skill",
-				source: "project",
-				skillMode: "architect",
-			})
-		})
-	})
-
 	it("does not manually refresh after deletion (backend sends updated skills via context)", async () => {
 		renderSkillsSettings()
 
 		// Clear mock calls after initial mount
 		;(vscode.postMessage as any).mockClear()
 
-		const deleteButton = screen.getByTestId("delete-project-skill")
-		fireEvent.click(deleteButton)
+		// Find and click delete button
+		const buttons = screen.getAllByTestId("button")
+		const deleteButtons = buttons.filter((btn) => btn.querySelector('[class*="text-destructive"]'))
+		fireEvent.click(deleteButtons[0])
 
 		const confirmButton = screen.getByTestId("alert-dialog-action")
 		fireEvent.click(confirmButton)
@@ -375,8 +387,10 @@ describe("SkillsSettings", () => {
 		;(vscode.postMessage as any).mockClear()
 
 		// Open create dialog
-		const addButtons = screen.getAllByTestId("button")
-		fireEvent.click(addButtons[0])
+		const addButton = screen
+			.getAllByTestId("button")
+			.find((btn) => btn.textContent?.includes("settings:skills.addSkill"))
+		fireEvent.click(addButton!)
 
 		// Simulate skill creation
 		const createButton = screen.getByTestId("create-skill-button")
@@ -392,7 +406,7 @@ describe("SkillsSettings", () => {
 	it("renders empty state when no skills exist", () => {
 		renderSkillsSettings([])
 
-		expect(screen.getByText("settings:skills.noProjectSkills")).toBeInTheDocument()
+		expect(screen.getByText("settings:skills.noWorkspaceSkills")).toBeInTheDocument()
 		expect(screen.getByText("settings:skills.noGlobalSkills")).toBeInTheDocument()
 	})
 
@@ -420,17 +434,17 @@ describe("SkillsSettings", () => {
 
 		renderSkillsSettings(multipleSkills)
 
-		expect(screen.getByTestId("skill-item-skill-1")).toBeInTheDocument()
-		expect(screen.getByTestId("skill-item-skill-2")).toBeInTheDocument()
-		expect(screen.getByTestId("skill-item-skill-3")).toBeInTheDocument()
+		expect(screen.getByText("skill-1")).toBeInTheDocument()
+		expect(screen.getByText("skill-2")).toBeInTheDocument()
+		expect(screen.getByText("skill-3")).toBeInTheDocument()
 	})
 
-	it("renders add skill button in each section", () => {
+	it("renders a single add skill button", () => {
 		renderSkillsSettings()
 
-		// Should have two "Add Skill" buttons - one for project, one for global
+		// Should have one "Add Skill" button at the top
 		const buttons = screen.getAllByTestId("button")
 		const addButtons = buttons.filter((btn) => btn.textContent?.includes("settings:skills.addSkill"))
-		expect(addButtons.length).toBe(2)
+		expect(addButtons.length).toBe(1)
 	})
 })

+ 169 - 276
webview-ui/src/components/settings/__tests__/SlashCommandsSettings.spec.tsx

@@ -54,58 +54,82 @@ vi.mock("@/components/ui", () => ({
 			{children}
 		</button>
 	),
-	Button: ({ children, onClick, disabled, className, variant, size, tabIndex }: any) => (
+	Button: ({ children, onClick, disabled, className, variant, size }: any) => (
 		<button
 			onClick={onClick}
 			disabled={disabled}
 			className={className}
 			data-variant={variant}
 			data-size={size}
-			tabIndex={tabIndex}
 			data-testid="button">
 			{children}
 		</button>
 	),
-	StandardTooltip: ({ children, content }: any) => (
-		<div title={content} data-testid="tooltip">
+	StandardTooltip: ({ children }: any) => <>{children}</>,
+	Dialog: ({ children, open, _onOpenChange }: any) => (
+		<div data-testid="dialog" data-open={open}>
+			{open && children}
+		</div>
+	),
+	DialogContent: ({ children }: any) => <div data-testid="dialog-content">{children}</div>,
+	DialogHeader: ({ children }: any) => <div data-testid="dialog-header">{children}</div>,
+	DialogTitle: ({ children }: any) => <div data-testid="dialog-title">{children}</div>,
+	DialogDescription: ({ children }: any) => <div data-testid="dialog-description">{children}</div>,
+	DialogFooter: ({ children }: any) => <div data-testid="dialog-footer">{children}</div>,
+	Input: ({ id, value, onChange, placeholder, maxLength, className }: any) => (
+		<input
+			id={id}
+			value={value}
+			onChange={onChange}
+			placeholder={placeholder}
+			maxLength={maxLength}
+			className={className}
+			data-testid={`input-${id}`}
+		/>
+	),
+	Select: ({ children, value, onValueChange }: any) => (
+		<div data-testid="select" data-value={value} onClick={() => onValueChange?.("global")}>
 			{children}
 		</div>
 	),
+	SelectTrigger: ({ children, className }: any) => (
+		<button data-testid="select-trigger" className={className}>
+			{children}
+		</button>
+	),
+	SelectContent: ({ children }: any) => <div data-testid="select-content">{children}</div>,
+	SelectItem: ({ children, value }: any) => (
+		<div data-testid={`select-item-${value}`} data-value={value}>
+			{children}
+		</div>
+	),
+	SelectValue: () => <span data-testid="select-value" />,
 }))
 
-// Mock SlashCommandItem component - we need to handle the built-in check
-vi.mock("../../chat/SlashCommandItem", () => ({
-	SlashCommandItem: ({ command, onDelete, onClick }: any) => (
-		<div data-testid={`command-item-${command.name}`}>
-			<span>{command.name}</span>
-			{command.description && <span>{command.description}</span>}
-			{command.source !== "built-in" && (
-				<button onClick={() => onDelete(command)} data-testid={`delete-${command.name}`}>
-					Delete
-				</button>
+// Mock CreateSlashCommandDialog component
+vi.mock("../CreateSlashCommandDialog", () => ({
+	CreateSlashCommandDialog: ({ open, onOpenChange, onCommandCreated }: any) => (
+		<div data-testid="create-command-dialog" data-open={open}>
+			{open && (
+				<>
+					<button onClick={() => onOpenChange(false)} data-testid="close-dialog">
+						Close
+					</button>
+					<button onClick={onCommandCreated} data-testid="create-command-button">
+						Create
+					</button>
+				</>
 			)}
-			<button onClick={() => onClick?.(command)} data-testid={`click-${command.name}`}>
-				Click
-			</button>
 		</div>
 	),
 }))
 
-// Mock SectionHeader and Section components
+// Mock SectionHeader component
 vi.mock("../SectionHeader", () => ({
 	SectionHeader: ({ children }: any) => <div data-testid="section-header">{children}</div>,
 }))
 
-vi.mock("../Section", () => ({
-	Section: ({ children }: any) => <div data-testid="section">{children}</div>,
-}))
-
 const mockCommands: Command[] = [
-	{
-		name: "built-in-command",
-		description: "A built-in command",
-		source: "built-in",
-	},
 	{
 		name: "global-command",
 		description: "A global command",
@@ -140,7 +164,7 @@ const renderSlashCommandsSettings = (commands: Command[] = mockCommands, cwd?: s
 	// Update the mock state before rendering
 	mockExtensionState = {
 		commands,
-		cwd: cwd || "/workspace",
+		cwd: cwd !== undefined ? cwd : "/workspace",
 	}
 
 	return render(
@@ -157,242 +181,127 @@ describe("SlashCommandsSettings", () => {
 		vi.clearAllMocks()
 	})
 
-	it("renders section header with icon and title", () => {
+	it("renders section header", () => {
 		renderSlashCommandsSettings()
 
 		expect(screen.getByTestId("section-header")).toBeInTheDocument()
 		expect(screen.getByText("settings:sections.slashCommands")).toBeInTheDocument()
 	})
 
-	it("renders description with documentation link", () => {
-		renderSlashCommandsSettings()
-
-		// The Trans component doesn't render the link in our mock, so we just check for the description
-		const description = screen.getByText((_content, element) => {
-			return element?.className === "text-sm text-vscode-descriptionForeground"
-		})
-		expect(description).toBeInTheDocument()
-	})
-
 	it("requests commands on mount", () => {
 		renderSlashCommandsSettings()
 
 		expect(vscode.postMessage).toHaveBeenCalledWith({ type: "requestCommands" })
 	})
 
-	it("displays built-in commands in their own section", () => {
+	it("displays project commands section when in a workspace", () => {
 		renderSlashCommandsSettings()
 
-		expect(screen.getByText("chat:slashCommands.builtInCommands")).toBeInTheDocument()
-		expect(screen.getByTestId("command-item-built-in-command")).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.workspaceCommands")).toBeInTheDocument()
 	})
 
-	it("displays global commands in their own section", () => {
+	it("displays global commands section", () => {
 		renderSlashCommandsSettings()
 
-		expect(screen.getByText("chat:slashCommands.globalCommands")).toBeInTheDocument()
-		expect(screen.getByTestId("command-item-global-command")).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.globalCommands")).toBeInTheDocument()
 	})
 
-	it("displays project commands when in a workspace", () => {
-		renderSlashCommandsSettings()
+	it("hides project commands section when not in workspace", () => {
+		const globalOnlyCommands = mockCommands.filter((c) => c.source === "global")
+		renderSlashCommandsSettings(globalOnlyCommands, "")
 
-		expect(screen.getByText("chat:slashCommands.workspaceCommands")).toBeInTheDocument()
-		expect(screen.getByTestId("command-item-project-command")).toBeInTheDocument()
+		expect(screen.queryByText("settings:slashCommands.workspaceCommands")).not.toBeInTheDocument()
 	})
 
-	it("does not display project commands when not in a workspace", () => {
-		// Pass empty string for cwd to simulate no workspace
-		// The component checks Boolean(cwd) which is false for empty string
-		// However, it seems the component still renders the section but without commands
-		const commandsWithoutProject = mockCommands.filter((cmd) => cmd.source !== "project")
-		renderSlashCommandsSettings(commandsWithoutProject, "")
-
-		// Project commands should not be shown
-		expect(screen.queryByTestId("command-item-project-command")).not.toBeInTheDocument()
-
-		// The section might still be rendered but should be empty of project commands
-		// This is acceptable behavior as it allows users to add project commands even without a workspace
-	})
-
-	it("shows input field for creating new global command", () => {
-		renderSlashCommandsSettings()
-
-		const input = screen.getAllByPlaceholderText("chat:slashCommands.newGlobalCommandPlaceholder")[0]
-		expect(input).toBeInTheDocument()
-	})
-
-	it("shows input field for creating new workspace command when in workspace", () => {
-		renderSlashCommandsSettings()
+	it("shows empty state message for global commands when none exist", () => {
+		const projectOnlyCommands = mockCommands.filter((c) => c.source === "project")
+		renderSlashCommandsSettings(projectOnlyCommands)
 
-		const input = screen.getByPlaceholderText("chat:slashCommands.newWorkspaceCommandPlaceholder")
-		expect(input).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.noGlobalCommands")).toBeInTheDocument()
 	})
 
-	it("creates new global command when entering name and clicking add button", async () => {
-		renderSlashCommandsSettings()
-
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-		const addButton = screen.getAllByTestId("button")[0]
-
-		fireEvent.change(input, { target: { value: "new-command" } })
-		fireEvent.click(addButton)
-
-		await waitFor(() => {
-			expect(vscode.postMessage).toHaveBeenCalledWith({
-				type: "createCommand",
-				text: "new-command.md",
-				values: { source: "global" },
-			})
-		})
+	it("shows empty state message for workspace commands when none exist", () => {
+		const globalOnlyCommands = mockCommands.filter((c) => c.source === "global")
+		renderSlashCommandsSettings(globalOnlyCommands)
 
-		expect(input.value).toBe("")
+		expect(screen.getByText("settings:slashCommands.noWorkspaceCommands")).toBeInTheDocument()
 	})
 
-	it("creates new workspace command when entering name and clicking add button", async () => {
+	it("groups commands by source correctly", () => {
 		renderSlashCommandsSettings()
 
-		const input = screen.getByPlaceholderText(
-			"chat:slashCommands.newWorkspaceCommandPlaceholder",
-		) as HTMLInputElement
-		const addButtons = screen.getAllByTestId("button")
-		const workspaceAddButton = addButtons[1] // Second add button is for workspace
+		// Should show both sections
+		expect(screen.getByText("settings:slashCommands.workspaceCommands")).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.globalCommands")).toBeInTheDocument()
 
-		fireEvent.change(input, { target: { value: "workspace-command" } })
-		fireEvent.click(workspaceAddButton)
-
-		await waitFor(() => {
-			expect(vscode.postMessage).toHaveBeenCalledWith({
-				type: "createCommand",
-				text: "workspace-command.md",
-				values: { source: "project" },
-			})
-		})
-
-		expect(input.value).toBe("")
+		// Should show command names
+		expect(screen.getByText("global-command")).toBeInTheDocument()
+		expect(screen.getByText("project-command")).toBeInTheDocument()
 	})
 
-	it("appends .md extension if not present when creating command", async () => {
+	it("displays command descriptions", () => {
 		renderSlashCommandsSettings()
 
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-		const addButton = screen.getAllByTestId("button")[0]
-
-		fireEvent.change(input, { target: { value: "command-without-extension" } })
-		fireEvent.click(addButton)
-
-		await waitFor(() => {
-			expect(vscode.postMessage).toHaveBeenCalledWith({
-				type: "createCommand",
-				text: "command-without-extension.md",
-				values: { source: "global" },
-			})
-		})
+		expect(screen.getByText("A global command")).toBeInTheDocument()
+		expect(screen.getByText("A project command")).toBeInTheDocument()
 	})
 
-	it("does not double-append .md extension if already present", async () => {
+	it("opens create command dialog when add button is clicked", () => {
 		renderSlashCommandsSettings()
 
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-		const addButton = screen.getAllByTestId("button")[0]
-
-		fireEvent.change(input, { target: { value: "command-with-extension.md" } })
-		fireEvent.click(addButton)
+		// Find the "Add Slash Command" button and click it
+		const addButton = screen.getByText("settings:slashCommands.addCommand").closest("button")
+		expect(addButton).toBeInTheDocument()
+		fireEvent.click(addButton!)
 
-		await waitFor(() => {
-			expect(vscode.postMessage).toHaveBeenCalledWith({
-				type: "createCommand",
-				text: "command-with-extension.md",
-				values: { source: "global" },
-			})
-		})
-	})
-
-	it("creates command on Enter key press", async () => {
-		renderSlashCommandsSettings()
-
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-
-		fireEvent.change(input, { target: { value: "enter-command" } })
-		fireEvent.keyDown(input, { key: "Enter" })
-
-		await waitFor(() => {
-			expect(vscode.postMessage).toHaveBeenCalledWith({
-				type: "createCommand",
-				text: "enter-command.md",
-				values: { source: "global" },
-			})
-		})
-	})
-
-	it("disables add button when input is empty", () => {
-		renderSlashCommandsSettings()
-
-		const addButton = screen.getAllByTestId("button")[0]
-		expect(addButton).toBeDisabled()
-	})
-
-	it("enables add button when input has value", () => {
-		renderSlashCommandsSettings()
-
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-		const addButton = screen.getAllByTestId("button")[0]
-
-		fireEvent.change(input, { target: { value: "test" } })
-		expect(addButton).not.toBeDisabled()
+		// Dialog should now be open
+		expect(screen.getByTestId("create-command-dialog")).toHaveAttribute("data-open", "true")
 	})
 
 	it("opens delete confirmation dialog when delete button is clicked", () => {
 		renderSlashCommandsSettings()
 
-		const deleteButton = screen.getByTestId("delete-global-command")
-		fireEvent.click(deleteButton)
+		// Find the delete button for the global command (using the button with Trash2 icon)
+		const deleteButtons = screen.getAllByTestId("button").filter((btn) => btn.querySelector(".text-destructive"))
+		fireEvent.click(deleteButtons[0])
 
+		// Alert dialog should be open with delete confirmation
 		expect(screen.getByTestId("alert-dialog")).toHaveAttribute("data-open", "true")
-		expect(screen.getByText("chat:slashCommands.deleteDialog.title")).toBeInTheDocument()
-		expect(screen.getByText("chat:slashCommands.deleteDialog.description global-command")).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.deleteDialog.title")).toBeInTheDocument()
 	})
 
 	it("deletes command when confirmation is clicked", async () => {
 		renderSlashCommandsSettings()
 
-		const deleteButton = screen.getByTestId("delete-global-command")
-		fireEvent.click(deleteButton)
+		// Click delete button for global command
+		const deleteButtons = screen.getAllByTestId("button").filter((btn) => btn.querySelector(".text-destructive"))
+		fireEvent.click(deleteButtons[0])
 
+		// Click confirm delete
 		const confirmButton = screen.getByTestId("alert-dialog-action")
 		fireEvent.click(confirmButton)
 
 		await waitFor(() => {
 			expect(vscode.postMessage).toHaveBeenCalledWith({
 				type: "deleteCommand",
-				text: "global-command",
-				values: { source: "global" },
+				text: expect.any(String),
+				values: { source: expect.any(String) },
 			})
 		})
-
-		expect(screen.getByTestId("alert-dialog")).toHaveAttribute("data-open", "false")
 	})
 
 	it("cancels deletion when cancel is clicked", () => {
 		renderSlashCommandsSettings()
 
-		const deleteButton = screen.getByTestId("delete-global-command")
-		fireEvent.click(deleteButton)
+		// Click delete button
+		const deleteButtons = screen.getAllByTestId("button").filter((btn) => btn.querySelector(".text-destructive"))
+		fireEvent.click(deleteButtons[0])
 
+		// Click cancel
 		const cancelButton = screen.getByTestId("alert-dialog-cancel")
 		fireEvent.click(cancelButton)
 
-		expect(screen.getByTestId("alert-dialog")).toHaveAttribute("data-open", "false")
+		// Dialog should be closed and no delete message sent
 		expect(vscode.postMessage).not.toHaveBeenCalledWith(
 			expect.objectContaining({
 				type: "deleteCommand",
@@ -400,19 +309,49 @@ describe("SlashCommandsSettings", () => {
 		)
 	})
 
+	it("opens command file when edit button is clicked", () => {
+		renderSlashCommandsSettings()
+
+		// Clear mocks after initial mount
+		vi.clearAllMocks()
+
+		// Find edit buttons (icon size buttons without text-destructive, with lucide-square-pen icon)
+		const allButtons = screen.getAllByTestId("button")
+		const editButtons = allButtons.filter(
+			(btn) =>
+				btn.getAttribute("data-size") === "icon" &&
+				!btn.querySelector('[class*="text-destructive"]') &&
+				btn.querySelector('[class*="lucide-square-pen"]'),
+		)
+
+		// Click the first edit button
+		fireEvent.click(editButtons[0])
+
+		expect(vscode.postMessage).toHaveBeenCalledWith({
+			type: "openFile",
+			text: expect.any(String),
+		})
+	})
+
 	it("refreshes commands after deletion", async () => {
 		renderSlashCommandsSettings()
 
-		const deleteButton = screen.getByTestId("delete-global-command")
-		fireEvent.click(deleteButton)
+		// Click delete button
+		const deleteButtons = screen.getAllByTestId("button").filter((btn) => btn.querySelector(".text-destructive"))
+		fireEvent.click(deleteButtons[0])
 
+		// Click confirm delete
 		const confirmButton = screen.getByTestId("alert-dialog-action")
 		fireEvent.click(confirmButton)
 
-		// Wait for the setTimeout to execute
+		// Wait for refresh to be called after timeout
 		await waitFor(
 			() => {
-				expect(vscode.postMessage).toHaveBeenCalledWith({ type: "requestCommands" })
+				// Initial mount call + refresh after deletion
+				const requestCommandsCalls = (vscode.postMessage as any).mock.calls.filter(
+					(call: any) => call[0].type === "requestCommands",
+				)
+				expect(requestCommandsCalls.length).toBeGreaterThanOrEqual(2)
 			},
 			{ timeout: 200 },
 		)
@@ -421,105 +360,59 @@ describe("SlashCommandsSettings", () => {
 	it("refreshes commands after creating new command", async () => {
 		renderSlashCommandsSettings()
 
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-		const addButton = screen.getAllByTestId("button")[0]
+		// Open create dialog
+		const addButton = screen.getByText("settings:slashCommands.addCommand").closest("button")
+		fireEvent.click(addButton!)
 
-		fireEvent.change(input, { target: { value: "new-command" } })
-		fireEvent.click(addButton)
+		// Click create button in dialog
+		const createButton = screen.getByTestId("create-command-button")
+		fireEvent.click(createButton)
 
-		// Wait for the setTimeout to execute
+		// Wait for refresh to be called after timeout
 		await waitFor(
 			() => {
-				expect(vscode.postMessage).toHaveBeenCalledWith({ type: "requestCommands" })
+				// Initial mount call + refresh after creation
+				const requestCommandsCalls = (vscode.postMessage as any).mock.calls.filter(
+					(call: any) => call[0].type === "requestCommands",
+				)
+				expect(requestCommandsCalls.length).toBeGreaterThanOrEqual(2)
 			},
 			{ timeout: 600 },
 		)
 	})
 
-	it("handles command click event", () => {
-		renderSlashCommandsSettings()
-
-		const commandButton = screen.getByTestId("click-global-command")
-		fireEvent.click(commandButton)
-
-		// The current implementation just logs to console
-		// In a real scenario, this might open the command file for editing
-		expect(commandButton).toBeInTheDocument()
-	})
-
-	it("does not show delete button for built-in commands", () => {
-		renderSlashCommandsSettings()
+	it("renders empty state when no commands exist", () => {
+		renderSlashCommandsSettings([])
 
-		// The SlashCommandItem component handles this internally
-		// We're just verifying the command is rendered
-		expect(screen.getByTestId("command-item-built-in-command")).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.noGlobalCommands")).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.noWorkspaceCommands")).toBeInTheDocument()
 	})
 
-	it("trims whitespace from command names", async () => {
-		renderSlashCommandsSettings()
-
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-		const addButton = screen.getAllByTestId("button")[0]
+	it("handles multiple commands of the same type", () => {
+		const multipleCommands: Command[] = [
+			{ name: "global-1", description: "First global", source: "global" },
+			{ name: "global-2", description: "Second global", source: "global" },
+			{ name: "project-1", description: "First project", source: "project" },
+		]
 
-		fireEvent.change(input, { target: { value: "  trimmed-command  " } })
-		fireEvent.click(addButton)
+		renderSlashCommandsSettings(multipleCommands)
 
-		await waitFor(() => {
-			expect(vscode.postMessage).toHaveBeenCalledWith({
-				type: "createCommand",
-				text: "trimmed-command.md",
-				values: { source: "global" },
-			})
-		})
+		expect(screen.getByText("global-1")).toBeInTheDocument()
+		expect(screen.getByText("global-2")).toBeInTheDocument()
+		expect(screen.getByText("project-1")).toBeInTheDocument()
 	})
 
-	it("does not create command with empty name after trimming", () => {
+	it("renders a single add command button", () => {
 		renderSlashCommandsSettings()
 
-		const input = screen.getAllByPlaceholderText(
-			"chat:slashCommands.newGlobalCommandPlaceholder",
-		)[0] as HTMLInputElement
-		const addButton = screen.getAllByTestId("button")[0]
-
-		fireEvent.change(input, { target: { value: "   " } })
-
-		expect(addButton).toBeDisabled()
+		// Should only have one "Add Slash Command" button
+		const addButtons = screen.getAllByText("settings:slashCommands.addCommand")
+		expect(addButtons.length).toBe(1)
 	})
 
-	it("renders empty state when no commands exist", () => {
-		renderSlashCommandsSettings([])
-
-		// Should still show the input fields for creating new commands
-		expect(screen.getAllByPlaceholderText("chat:slashCommands.newGlobalCommandPlaceholder")[0]).toBeInTheDocument()
-	})
-
-	it("handles multiple commands of the same type", () => {
-		const multipleCommands: Command[] = [
-			{
-				name: "global-1",
-				description: "First global",
-				source: "global",
-			},
-			{
-				name: "global-2",
-				description: "Second global",
-				source: "global",
-			},
-			{
-				name: "global-3",
-				description: "Third global",
-				source: "global",
-			},
-		]
-
-		renderSlashCommandsSettings(multipleCommands)
+	it("renders footer text", () => {
+		renderSlashCommandsSettings()
 
-		expect(screen.getByTestId("command-item-global-1")).toBeInTheDocument()
-		expect(screen.getByTestId("command-item-global-2")).toBeInTheDocument()
-		expect(screen.getByTestId("command-item-global-3")).toBeInTheDocument()
+		expect(screen.getByText("settings:slashCommands.footer")).toBeInTheDocument()
 	})
 })

+ 1 - 1
webview-ui/src/components/ui/checkbox.tsx

@@ -13,7 +13,7 @@ const checkboxVariants = cva(
 		variants: {
 			variant: {
 				default:
-					"border-vscode-foreground data-[state=checked]:bg-vscode-foreground data-[state=checked]:text-primary-foreground",
+					"border-vscode-foreground data-[state=checked]:bg-primary data-[state=checked]:text-foreground",
 				description:
 					"border-vscode-descriptionForeground data-[state=checked]:bg-vscode-descriptionForeground data-[state=checked]:text-white",
 			},

+ 1 - 1
webview-ui/src/components/ui/input.tsx

@@ -8,7 +8,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
 			<input
 				type={type}
 				className={cn(
-					"flex w-full text-vscode-input-foreground border border-vscode-dropdown-border  bg-vscode-input-background rounded-xs px-3 py-1 text-base transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-vscode-focusBorder disabled:cursor-not-allowed disabled:opacity-50",
+					"flex w-full text-vscode-input-foreground border border-vscode-dropdown-border bg-vscode-input-background rounded-xl px-3 py-1 text-base transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-vscode-focusBorder disabled:cursor-not-allowed disabled:opacity-50",
 					className,
 				)}
 				ref={ref}

+ 1 - 1
webview-ui/src/components/ui/textarea.tsx

@@ -7,7 +7,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<"tex
 		return (
 			<textarea
 				className={cn(
-					"flex min-h-[60px] w-full rounded-xs px-3 py-2 text-base placeholder:text-muted-foreground focus:outline-0 focus-visible:outline-none focus-visible:border-vscode-focusBorder disabled:cursor-not-allowed disabled:opacity-50",
+					"flex min-h-[60px] w-full rounded-xl px-3 py-2 text-base placeholder:text-muted-foreground focus:outline-0 focus-visible:outline-none focus-visible:border-vscode-focusBorder disabled:cursor-not-allowed disabled:opacity-50",
 					"border border-[var(--vscode-input-border,var(--vscode-input-background))] focus-visible:border-vscode-focusBorder",
 					"bg-vscode-input-background",
 					"text-vscode-input-foreground",

+ 49 - 13
webview-ui/src/i18n/locales/ca/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Gestiona les teves comandes de barra per executar ràpidament fluxos de treball i accions personalitzades. <DocsLink>Aprèn-ne més</DocsLink>"
+		"description": "Gestiona les teves comandes de barra per executar ràpidament fluxos de treball i accions personalitzades. <DocsLink>Aprèn-ne més</DocsLink>",
+		"workspaceCommands": "Comandes de l'espai de treball",
+		"globalCommands": "Comandes globals",
+		"noWorkspaceCommands": "Sense comandes en aquest projecte encara.",
+		"noGlobalCommands": "Sense comandes globals encara.",
+		"addCommand": "Afegir comanda de barra",
+		"editCommand": "Editar comanda",
+		"deleteCommand": "Suprimir comanda",
+		"deleteDialog": {
+			"title": "Suprimir comanda",
+			"description": "Estàs segur que vols suprimir la comanda \"{{name}}\"? Aquesta acció no es pot desfer.",
+			"confirm": "Suprimir",
+			"cancel": "Cancel·lar"
+		},
+		"createDialog": {
+			"title": "Crear una nova comanda de barra",
+			"nameLabel": "Nom",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Només lletres minúscules, números, guions i subratllats",
+			"sourceLabel": "Ubicació",
+			"create": "Crear",
+			"cancel": "Cancel·lar"
+		},
+		"source": {
+			"global": "Global (disponible en tots els espais de treball)",
+			"project": "Espai de treball"
+		},
+		"validation": {
+			"nameRequired": "El nom és obligatori",
+			"nameTooLong": "El nom ha de tenir 64 caràcters o menys",
+			"nameInvalid": "El nom ha de contenir només lletres, números, guions i subratllats"
+		},
+		"footer": "Utilitza comandes de barra per accedir ràpidament a indicacions i fluxos de treball utilitzats freqüentment."
 	},
 	"prompts": {
 		"description": "Configura les indicacions de suport utilitzades per a accions ràpides com millorar indicacions, explicar codi i solucionar problemes. Aquestes indicacions ajuden Roo a proporcionar millor assistència per a tasques comunes de desenvolupament."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Màximes Sol·licituds",
-			"description": "Fes aquesta quantitat de sol·licituds API automàticament abans de demanar aprovació per continuar amb la tasca.",
 			"unlimited": "Il·limitat"
 		},
 		"selectOptionsFirst": "Seleccioneu almenys una opció a continuació per activar l'aprovació automàtica",
@@ -899,7 +930,6 @@
 		"simplifiedExplanation": "Pots ajustar la configuració detallada del model més tard."
 	},
 	"footer": {
-		"feedback": "Si teniu qualsevol pregunta o comentari, no dubteu a obrir un issue a <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> o unir-vos a <redditLink>reddit.com/r/RooCode</redditLink> o <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Permetre informes anònims d'errors i ús",
 			"description": "Ajuda a millorar Roo Code enviant dades d'ús anònimes i informes d'error. Aquesta telemetria no recull codi, prompts o informació personal. Consulta la nostra <privacyLink>política de privacitat</privacyLink> per a més detalls. Pots desactivar-ho en qualsevol moment."
@@ -996,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Gestiona les skills que proporcionen instruccions contextuals a l'agent. Les skills s'apliquen automàticament quan són rellevants per a les teves tasques. <DocsLink>Més informació</DocsLink>",
-		"projectSkills": "Skills del Projecte",
-		"globalSkills": "Skills Globals",
-		"noProjectSkills": "No hi ha skills de projecte configurades. Crea'n una per afegir capacitats específiques del projecte a l'agent.",
-		"noGlobalSkills": "No hi ha skills globals configurades. Crea'n una per afegir capacitats a l'agent disponibles en tots els projectes.",
+		"workspaceSkills": "Skills de l'espai de treball",
+		"globalSkills": "Skills globals",
+		"noWorkspaceSkills": "Sense skills en aquest projecte encara.",
+		"noGlobalSkills": "Sense skills globals encara.",
 		"addSkill": "Afegir Skill",
 		"editSkill": "Editar skill",
 		"deleteSkill": "Eliminar skill",
-		"changeMode": "Canvia mode",
+		"configureModes": "Disponibilitat del mode",
 		"modeAny": "Qualsevol mode",
+		"modeCount": "{{count}} modes",
 		"deleteDialog": {
 			"title": "Eliminar Skill",
 			"description": "Estàs segur que vols eliminar la skill \"{{name}}\"? Aquesta acció no es pot desfer.",
 			"confirm": "Eliminar",
 			"cancel": "Cancel·lar"
 		},
+		"modeDialog": {
+			"title": "Configurar els modes de Skill",
+			"description": "Tria quins modes poden usar aquesta skill",
+			"intro": "Per mantenir el teu context lleuger, et recomanem que només facis disponibles skills per als modes que les necessiten.",
+			"anyMode": "Qualsevol mode (disponible a tot arreu)",
+			"save": "Desar",
+			"cancel": "Cancel·lar"
+		},
 		"createDialog": {
 			"title": "Crear Nova Skill",
-			"description": "Defineix una nova plantilla de skill que proporcioni instruccions contextuals a l'agent.",
 			"nameLabel": "Nom",
 			"namePlaceholder": "el-meu-nom-de-skill",
-			"nameHint": "Només lletres minúscules, números i guions (1-64 caràcters)",
 			"descriptionLabel": "Descripció",
 			"descriptionPlaceholder": "Descriu quan s'hauria d'utilitzar aquesta skill...",
-			"descriptionHint": "Explica què fa aquesta skill i quan l'agent hauria d'aplicar-la (1-1024 caràcters)",
 			"sourceLabel": "Ubicació",
-			"sourceHint": "Tria si aquesta skill està disponible globalment o només en aquest projecte",
 			"modeLabel": "Mode (opcional)",
 			"modePlaceholder": "Qualsevol mode",
 			"modeHint": "Restringeix aquesta skill a un mode específic",
@@ -1039,6 +1074,7 @@
 			"nameInvalid": "El nom ha de tenir entre 1 i 64 caràcters, només lletres minúscules, números o guions",
 			"descriptionRequired": "La descripció és obligatòria",
 			"descriptionTooLong": "La descripció ha de tenir com a màxim 1024 caràcters"
-		}
+		},
+		"footer": "Crea les teves pròpies skills amb el mode Skill Writer, disponible al <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 58 - 22
webview-ui/src/i18n/locales/de/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Verwalte deine Slash-Befehle, um benutzerdefinierte Workflows und Aktionen schnell auszuführen. <DocsLink>Mehr erfahren</DocsLink>"
+		"description": "Verwalte deine Slash-Befehle, um benutzerdefinierte Workflows und Aktionen schnell auszuführen. <DocsLink>Mehr erfahren</DocsLink>",
+		"workspaceCommands": "Workspace-Befehle",
+		"globalCommands": "Globale Befehle",
+		"noWorkspaceCommands": "Noch keine Befehle in diesem Projekt.",
+		"noGlobalCommands": "Noch keine globalen Befehle.",
+		"addCommand": "Slash-Befehl hinzufügen",
+		"editCommand": "Befehl bearbeiten",
+		"deleteCommand": "Befehl löschen",
+		"deleteDialog": {
+			"title": "Befehl löschen",
+			"description": "Bist du sicher, dass du den Befehl \"{{name}}\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.",
+			"confirm": "Löschen",
+			"cancel": "Abbrechen"
+		},
+		"createDialog": {
+			"title": "Neuen Slash-Befehl erstellen",
+			"nameLabel": "Name",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Nur Kleinbuchstaben, Zahlen, Bindestriche und Unterstriche",
+			"sourceLabel": "Speicherort",
+			"create": "Erstellen",
+			"cancel": "Abbrechen"
+		},
+		"source": {
+			"global": "Global (in allen Workspaces verfügbar)",
+			"project": "Workspace"
+		},
+		"validation": {
+			"nameRequired": "Name ist erforderlich",
+			"nameTooLong": "Name muss 64 Zeichen oder weniger sein",
+			"nameInvalid": "Name darf nur Buchstaben, Zahlen, Bindestriche und Unterstriche enthalten"
+		},
+		"footer": "Nutze Slash-Befehle, um schnell auf häufig verwendete Prompts und Workflows zuzugreifen."
 	},
 	"prompts": {
 		"description": "Konfiguriere Support-Prompts, die für schnelle Aktionen wie das Verbessern von Prompts, das Erklären von Code und das Beheben von Problemen verwendet werden. Diese Prompts helfen Roo dabei, bessere Unterstützung für häufige Entwicklungsaufgaben zu bieten."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Maximale Anfragen",
-			"description": "Automatisch so viele API-Anfragen stellen, bevor du um die Erlaubnis gebeten wirst, mit der Aufgabe fortzufahren.",
 			"unlimited": "Unbegrenzt"
 		},
 		"selectOptionsFirst": "Wähle mindestens eine Option unten aus, um die automatische Genehmigung zu aktivieren",
@@ -899,7 +930,6 @@
 		"simplifiedExplanation": "Du kannst detaillierte Modelleinstellungen später anpassen."
 	},
 	"footer": {
-		"feedback": "Wenn du Fragen oder Feedback hast, kannst du gerne ein Issue auf <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> eröffnen oder <redditLink>reddit.com/r/RooCode</redditLink> oder <discordLink>discord.gg/roocode</discordLink> beitreten",
 		"telemetry": {
 			"label": "Anonyme Fehler- und Nutzungsberichte zulassen",
 			"description": "Hilf mit, Roo Code zu verbessern, indem du anonyme Nutzungsdaten und Fehlerberichte sendest. Diese Telemetrie sammelt keine Code-, Prompt- oder persönliche Informationen. Weitere Einzelheiten findest du in unserer <privacyLink>Datenschutzrichtlinie</privacyLink>."
@@ -996,49 +1026,55 @@
 	},
 	"skills": {
 		"description": "Verwalten Sie Skills, die dem Agenten kontextbezogene Anweisungen bereitstellen. Skills werden automatisch angewendet, wenn sie für Ihre Aufgaben relevant sind. <DocsLink>Mehr erfahren</DocsLink>",
-		"projectSkills": "Projekt-Skills",
+		"workspaceSkills": "Workspace-Skills",
 		"globalSkills": "Globale Skills",
-		"noProjectSkills": "Keine Projekt-Skills konfiguriert. Erstellen Sie eine, um projektspezifische Agentenfähigkeiten hinzuzufügen.",
-		"noGlobalSkills": "Keine globalen Skills konfiguriert. Erstellen Sie eine, um Agentenfähigkeiten hinzuzufügen, die in allen Projekten verfügbar sind.",
+		"noWorkspaceSkills": "Noch keine Skills in diesem Projekt.",
+		"noGlobalSkills": "Noch keine globalen Skills.",
 		"addSkill": "Skill hinzufügen",
 		"editSkill": "Skill bearbeiten",
 		"deleteSkill": "Skill löschen",
-		"changeMode": "Modus ändern",
+		"configureModes": "Modverfügbarkeit",
 		"modeAny": "Beliebiger Modus",
+		"modeCount": "{{count}} Modi",
 		"deleteDialog": {
 			"title": "Skill löschen",
-			"description": "Sind Sie sicher, dass Sie die Skill \"{{name}}\" löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
+			"description": "Bist du sicher, dass du den Skill \"{{name}}\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.",
 			"confirm": "Löschen",
 			"cancel": "Abbrechen"
 		},
+		"modeDialog": {
+			"title": "Skill-Modi konfigurieren",
+			"description": "Wähle aus, welche Modi diesen Skill nutzen können",
+			"intro": "Um deinen Kontext leicht zu halten, empfehlen wir, Skills nur für die Modi verfügbar zu machen, die sie benötigen.",
+			"anyMode": "Beliebiger Modus (überall verfügbar)",
+			"save": "Speichern",
+			"cancel": "Abbrechen"
+		},
 		"createDialog": {
-			"title": "Neue Skill erstellen",
-			"description": "Definieren Sie eine neue Skill-Vorlage, die dem Agenten kontextbezogene Anweisungen bereitstellt.",
+			"title": "Neuen Skill erstellen",
 			"nameLabel": "Name",
-			"namePlaceholder": "mein-skill-name",
-			"nameHint": "Nur Kleinbuchstaben, Zahlen und Bindestriche (1-64 Zeichen)",
+			"namePlaceholder": "my-skill-name",
 			"descriptionLabel": "Beschreibung",
-			"descriptionPlaceholder": "Beschreiben Sie, wann diese Skill verwendet werden sollte...",
-			"descriptionHint": "Erklären Sie, was diese Skill tut und wann der Agent sie anwenden sollte (1-1024 Zeichen)",
-			"sourceLabel": "Standort",
-			"sourceHint": "Wählen Sie, ob diese Skill global oder nur in diesem Projekt verfügbar ist",
+			"descriptionPlaceholder": "Beschreibe, wann dieser Skill verwendet werden sollte...",
+			"sourceLabel": "Speicherort",
 			"modeLabel": "Modus (optional)",
 			"modePlaceholder": "Beliebiger Modus",
-			"modeHint": "Beschränken Sie diese Skill auf einen bestimmten Modus",
 			"modeAny": "Beliebiger Modus",
+			"modeHint": "Beschränke diesen Skill auf einen bestimmten Modus",
 			"create": "Erstellen",
 			"cancel": "Abbrechen"
 		},
 		"source": {
 			"global": "Global (in allen Projekten verfügbar)",
-			"project": "Projekt (nur dieser Arbeitsbereich)"
+			"project": "Projekt (nur dieser Workspace)"
 		},
 		"validation": {
 			"nameRequired": "Name ist erforderlich",
-			"nameTooLong": "Name darf höchstens 64 Zeichen lang sein",
-			"nameInvalid": "Name muss 1-64 Kleinbuchstaben, Zahlen oder Bindestriche enthalten",
+			"nameTooLong": "Name muss 64 Zeichen oder weniger sein",
+			"nameInvalid": "Name muss 1-64 Kleinbuchstaben, Zahlen oder Bindestriche sein",
 			"descriptionRequired": "Beschreibung ist erforderlich",
-			"descriptionTooLong": "Beschreibung darf höchstens 1024 Zeichen lang sein"
-		}
+			"descriptionTooLong": "Beschreibung muss 1024 Zeichen oder weniger sein"
+		},
+		"footer": "Erstelle deine eigenen Skills mit dem Skill Writer Modus, verfügbar im <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 52 - 14
webview-ui/src/i18n/locales/en/settings.json

@@ -69,40 +69,77 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Manage your slash commands to quickly execute custom workflows and actions. <DocsLink>Learn more</DocsLink>"
+		"description": "Slash commands are shortcuts for reusable workflows and actions to be used on demand (like creating a PR, running a set of tasks, etc). <DocsLink>Learn more</DocsLink>",
+		"workspaceCommands": "Workspace Commands",
+		"globalCommands": "Global Commands",
+		"noWorkspaceCommands": "No commands in this project yet.",
+		"noGlobalCommands": "No global commands yet.",
+		"addCommand": "Add Slash Command",
+		"editCommand": "Edit command",
+		"deleteCommand": "Delete command",
+		"deleteDialog": {
+			"title": "Delete Command",
+			"description": "Are you sure you want to delete the command \"{{name}}\"? This action cannot be undone.",
+			"confirm": "Delete",
+			"cancel": "Cancel"
+		},
+		"createDialog": {
+			"title": "Create New Slash Command",
+			"nameLabel": "Name",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Lowercase letters, numbers, hyphens and underscores only",
+			"sourceLabel": "Location",
+			"create": "Create",
+			"cancel": "Cancel"
+		},
+		"source": {
+			"global": "Global (available in all workspaces)",
+			"project": "Workspace"
+		},
+		"validation": {
+			"nameRequired": "Name is required",
+			"nameTooLong": "Name must be 64 characters or less",
+			"nameInvalid": "Name must contain only letters, numbers, hyphens, and underscores"
+		},
+		"footer": "Use slash commands for quick access to frequently used prompts and workflows."
 	},
 	"skills": {
-		"description": "Manage skills that provide contextual instructions to the agent. Skills are automatically applied when relevant to your tasks. <DocsLink>Learn more</DocsLink>",
-		"projectSkills": "Project Skills",
+		"description": "Skills encapsulate contextual instructions and workflows, which the agent can load on-demand. They can include general instructions, specific workflows and even scripts to be run as needed. <DocsLink>Learn more</DocsLink>",
+		"workspaceSkills": "Workspace Skills",
 		"globalSkills": "Global Skills",
-		"noProjectSkills": "No project skills configured. Create one to add project-specific agent capabilities.",
-		"noGlobalSkills": "No global skills configured. Create one to add agent capabilities available across all projects.",
+		"noWorkspaceSkills": "No skills in this project yet.",
+		"noGlobalSkills": "No global skills yet.",
 		"addSkill": "Add Skill",
 		"editSkill": "Edit skill",
 		"deleteSkill": "Delete skill",
-		"changeMode": "Change mode",
+		"configureModes": "Mode availability",
 		"modeAny": "Any mode",
+		"modeCount": "{{count}} modes",
 		"deleteDialog": {
 			"title": "Delete Skill",
 			"description": "Are you sure you want to delete the skill \"{{name}}\"? This action cannot be undone.",
 			"confirm": "Delete",
 			"cancel": "Cancel"
 		},
+		"modeDialog": {
+			"title": "Configure Skill Modes",
+			"description": "Choose which modes can use this skill",
+			"intro": "To keep your context light, we recommend only making skills available for the modes that need them.",
+			"anyMode": "Any mode (available everywhere)",
+			"save": "Save",
+			"cancel": "Cancel"
+		},
 		"createDialog": {
 			"title": "Create New Skill",
-			"description": "Define a new skill template that provides contextual instructions to the agent.",
 			"nameLabel": "Name",
-			"namePlaceholder": "my-skill-name",
-			"nameHint": "Lowercase letters, numbers, and hyphens only (1-64 characters)",
+			"namePlaceholder": "my-skill-name (Lowercase letters, numbers, and hyphens only)",
 			"descriptionLabel": "Description",
-			"descriptionPlaceholder": "Describe when this skill should be used...",
-			"descriptionHint": "Explain what this skill does and when the agent should apply it (1-1024 characters)",
+			"descriptionPlaceholder": "Describe when this skill should be used and its contents...",
 			"sourceLabel": "Location",
-			"sourceHint": "Choose whether this skill is available globally or only in this project",
 			"modeLabel": "Mode (optional)",
 			"modePlaceholder": "Any mode",
-			"modeHint": "Restrict this skill to a specific mode",
 			"modeAny": "Any mode",
+			"modeHint": "Restrict this skill to a specific mode",
 			"create": "Create",
 			"cancel": "Cancel"
 		},
@@ -116,7 +153,8 @@
 			"nameInvalid": "Name must be 1-64 lowercase letters, numbers, or hyphens",
 			"descriptionRequired": "Description is required",
 			"descriptionTooLong": "Description must be 1024 characters or less"
-		}
+		},
+		"footer": "Create your own skills with the Skill Writer mode, available in the <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	},
 	"ui": {
 		"collapseThinking": {

+ 50 - 14
webview-ui/src/i18n/locales/es/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Gestiona tus comandos de barra para ejecutar rápidamente flujos de trabajo y acciones personalizadas. <DocsLink>Saber más</DocsLink>"
+		"description": "Gestiona tus comandos de barra para ejecutar rápidamente flujos de trabajo y acciones personalizadas. <DocsLink>Saber más</DocsLink>",
+		"workspaceCommands": "Comandos del espacio de trabajo",
+		"globalCommands": "Comandos globales",
+		"noWorkspaceCommands": "Sin comandos en este proyecto aún.",
+		"noGlobalCommands": "Sin comandos globales aún.",
+		"addCommand": "Agregar comando de barra",
+		"editCommand": "Editar comando",
+		"deleteCommand": "Eliminar comando",
+		"deleteDialog": {
+			"title": "Eliminar comando",
+			"description": "¿Estás seguro de que quieres eliminar el comando \"{{name}}\"? Esta acción no se puede deshacer.",
+			"confirm": "Eliminar",
+			"cancel": "Cancelar"
+		},
+		"createDialog": {
+			"title": "Crear nuevo comando de barra",
+			"nameLabel": "Nombre",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Solo letras minúsculas, números, guiones y guiones bajos",
+			"sourceLabel": "Ubicación",
+			"create": "Crear",
+			"cancel": "Cancelar"
+		},
+		"source": {
+			"global": "Global (disponible en todos los espacios de trabajo)",
+			"project": "Espacio de trabajo"
+		},
+		"validation": {
+			"nameRequired": "El nombre es obligatorio",
+			"nameTooLong": "El nombre debe tener 64 caracteres o menos",
+			"nameInvalid": "El nombre solo puede contener letras, números, guiones y guiones bajos"
+		},
+		"footer": "Usa comandos de barra para acceder rápidamente a indicaciones y flujos de trabajo frecuentemente utilizados."
 	},
 	"prompts": {
 		"description": "Configura indicaciones de soporte que se utilizan para acciones rápidas como mejorar indicaciones, explicar código y solucionar problemas. Estas indicaciones ayudan a Roo a brindar mejor asistencia para tareas comunes de desarrollo."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Solicitudes máximas",
-			"description": "Realizar automáticamente esta cantidad de solicitudes a la API antes de pedir aprobación para continuar con la tarea.",
 			"unlimited": "Ilimitado"
 		},
 		"selectOptionsFirst": "Selecciona al menos una opción a continuación para habilitar la aprobación automática",
@@ -899,7 +930,6 @@
 		"simplifiedExplanation": "Puedes ajustar la configuración detallada del modelo más tarde."
 	},
 	"footer": {
-		"feedback": "Si tiene alguna pregunta o comentario, no dude en abrir un issue en <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> o unirse a <redditLink>reddit.com/r/RooCode</redditLink> o <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Permitir informes anónimos de errores y uso",
 			"description": "Ayuda a mejorar Roo Code enviando datos de uso anónimos e informes de errores. Esta telemetría no recopila código, prompts o información personal. Consulta nuestra <privacyLink>política de privacidad</privacyLink> para más detalles."
@@ -996,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Gestiona skills que proporcionan instrucciones contextuales al agente. Las skills se aplican automáticamente cuando son relevantes para tus tareas. <DocsLink>Más información</DocsLink>",
-		"projectSkills": "Skills del Proyecto",
-		"globalSkills": "Skills Globales",
-		"noProjectSkills": "No hay skills de proyecto configuradas. Crea una para añadir capacidades específicas del proyecto al agente.",
-		"noGlobalSkills": "No hay skills globales configuradas. Crea una para añadir capacidades al agente disponibles en todos los proyectos.",
-		"addSkill": "Añadir Skill",
+		"workspaceSkills": "Skills del espacio de trabajo",
+		"globalSkills": "Skills globales",
+		"noWorkspaceSkills": "Sin skills en este proyecto aún.",
+		"noGlobalSkills": "Sin skills globales aún.",
+		"addSkill": "Agregar Skill",
 		"editSkill": "Editar skill",
 		"deleteSkill": "Eliminar skill",
-		"changeMode": "Cambiar modo",
+		"configureModes": "Disponibilidad de modo",
 		"modeAny": "Cualquier modo",
+		"modeCount": "{{count}} modos",
 		"deleteDialog": {
 			"title": "Eliminar Skill",
 			"description": "¿Estás seguro de que quieres eliminar la skill \"{{name}}\"? Esta acción no se puede deshacer.",
 			"confirm": "Eliminar",
 			"cancel": "Cancelar"
 		},
+		"modeDialog": {
+			"title": "Configurar modos de Skill",
+			"description": "Elige qué modos pueden usar esta skill",
+			"intro": "Para mantener tu contexto ligero, recomendamos que solo hagas disponibles las skills para los modos que las necesitan.",
+			"anyMode": "Cualquier modo (disponible en todas partes)",
+			"save": "Guardar",
+			"cancel": "Cancelar"
+		},
 		"createDialog": {
 			"title": "Crear Nueva Skill",
-			"description": "Define una nueva plantilla de skill que proporcione instrucciones contextuales al agente.",
 			"nameLabel": "Nombre",
 			"namePlaceholder": "mi-nombre-de-skill",
-			"nameHint": "Solo letras minúsculas, números y guiones (1-64 caracteres)",
 			"descriptionLabel": "Descripción",
 			"descriptionPlaceholder": "Describe cuándo debería usarse esta skill...",
-			"descriptionHint": "Explica qué hace esta skill y cuándo el agente debería aplicarla (1-1024 caracteres)",
 			"sourceLabel": "Ubicación",
-			"sourceHint": "Elige si esta skill está disponible globalmente o solo en este proyecto",
 			"modeLabel": "Modo (opcional)",
 			"modePlaceholder": "Cualquier modo",
 			"modeHint": "Restringe esta skill a un modo específico",
@@ -1039,6 +1074,7 @@
 			"nameInvalid": "El nombre debe tener entre 1 y 64 caracteres, solo letras minúsculas, números o guiones",
 			"descriptionRequired": "La descripción es obligatoria",
 			"descriptionTooLong": "La descripción debe tener como máximo 1024 caracteres"
-		}
+		},
+		"footer": "Crea tus propias skills con el modo Skill Writer, disponible en el <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 11
webview-ui/src/i18n/locales/fr/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Gérez vos commandes slash pour exécuter rapidement des flux de travail et des actions personnalisées. <DocsLink>En savoir plus</DocsLink>"
+		"description": "Gérez vos commandes slash pour exécuter rapidement des flux de travail et des actions personnalisées. <DocsLink>En savoir plus</DocsLink>",
+		"workspaceCommands": "Commandes de l'espace de travail",
+		"globalCommands": "Commandes globales",
+		"noWorkspaceCommands": "Aucune commande dans ce projet pour le moment.",
+		"noGlobalCommands": "Aucune commande globale pour le moment.",
+		"addCommand": "Ajouter une commande slash",
+		"editCommand": "Modifier la commande",
+		"deleteCommand": "Supprimer la commande",
+		"deleteDialog": {
+			"title": "Supprimer la commande",
+			"description": "Êtes-vous sûr de vouloir supprimer la commande \"{{name}}\" ? Cette action ne peut pas être annulée.",
+			"confirm": "Supprimer",
+			"cancel": "Annuler"
+		},
+		"createDialog": {
+			"title": "Créer une nouvelle commande slash",
+			"nameLabel": "Nom",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Lettres minuscules, chiffres, tirets et traits de soulignement uniquement",
+			"sourceLabel": "Emplacement",
+			"create": "Créer",
+			"cancel": "Annuler"
+		},
+		"source": {
+			"global": "Global (disponible dans tous les espaces de travail)",
+			"project": "Espace de travail"
+		},
+		"validation": {
+			"nameRequired": "Le nom est obligatoire",
+			"nameTooLong": "Le nom doit contenir 64 caractères ou moins",
+			"nameInvalid": "Le nom ne peut contenir que des lettres, des chiffres, des tirets et des traits de soulignement"
+		},
+		"footer": "Utilisez les commandes slash pour accéder rapidement aux invites et flux de travail fréquemment utilisés."
 	},
 	"prompts": {
 		"description": "Configurez les invites de support utilisées pour les actions rapides comme l'amélioration des invites, l'explication du code et la résolution des problèmes. Ces invites aident Roo à fournir une meilleure assistance pour les tâches de développement courantes."
@@ -245,7 +277,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Requêtes maximales",
-			"description": "Effectuer automatiquement ce nombre de requêtes API avant de demander l'approbation pour continuer la tâche.",
 			"unlimited": "Illimité"
 		},
 		"apiCostLimit": {
@@ -899,7 +930,6 @@
 		"simplifiedExplanation": "Tu peux ajuster les paramètres détaillés du modèle ultérieurement."
 	},
 	"footer": {
-		"feedback": "Si vous avez des questions ou des commentaires, n'hésitez pas à ouvrir un problème sur <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> ou à rejoindre <redditLink>reddit.com/r/RooCode</redditLink> ou <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Autoriser les rapports anonymes d'erreurs et d'utilisation",
 			"description": "Aidez à améliorer Roo Code en envoyant des données d'utilisation anonymes et des rapports d'erreurs. Cette télémétrie ne collecte pas de code, de prompts ou d'informations personnelles. Consultez notre <privacyLink>politique de confidentialité</privacyLink> pour plus de détails."
@@ -996,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Gérez les skills qui fournissent des instructions contextuelles à l'agent. Les skills sont automatiquement appliquées lorsqu'elles sont pertinentes pour vos tâches. <DocsLink>En savoir plus</DocsLink>",
-		"projectSkills": "Skills du Projet",
+		"workspaceSkills": "Skills de l'espace de travail",
 		"globalSkills": "Skills Globales",
-		"noProjectSkills": "Aucune skill de projet configurée. Créez-en une pour ajouter des capacités spécifiques au projet à l'agent.",
+		"noWorkspaceSkills": "Aucune skill dans ce projet pour le moment.",
 		"noGlobalSkills": "Aucune skill globale configurée. Créez-en une pour ajouter des capacités à l'agent disponibles dans tous les projets.",
 		"addSkill": "Ajouter une Skill",
 		"editSkill": "Modifier la skill",
 		"deleteSkill": "Supprimer la skill",
-		"changeMode": "Changer de mode",
+		"configureModes": "Disponibilité du mode",
 		"modeAny": "N'importe quel mode",
+		"modeCount": "{{count}} modes",
 		"deleteDialog": {
 			"title": "Supprimer la Skill",
 			"description": "Êtes-vous sûr de vouloir supprimer la skill \"{{name}}\" ? Cette action ne peut pas être annulée.",
 			"confirm": "Supprimer",
 			"cancel": "Annuler"
 		},
+		"modeDialog": {
+			"title": "Configurer les modes Skill",
+			"description": "Choisissez les modes qui peuvent utiliser cette skill",
+			"intro": "Pour garder votre contexte léger, nous vous recommandons de ne mettre les skills à disposition que pour les modes qui en ont besoin.",
+			"anyMode": "N'importe quel mode (disponible partout)",
+			"save": "Enregistrer",
+			"cancel": "Annuler"
+		},
 		"createDialog": {
 			"title": "Créer une Nouvelle Skill",
-			"description": "Définissez un nouveau modèle de skill qui fournit des instructions contextuelles à l'agent.",
 			"nameLabel": "Nom",
 			"namePlaceholder": "mon-nom-de-skill",
-			"nameHint": "Lettres minuscules, chiffres et tirets uniquement (1-64 caractères)",
 			"descriptionLabel": "Description",
 			"descriptionPlaceholder": "Décrivez quand cette skill devrait être utilisée...",
-			"descriptionHint": "Expliquez ce que fait cette skill et quand l'agent devrait l'appliquer (1-1024 caractères)",
 			"sourceLabel": "Emplacement",
-			"sourceHint": "Choisissez si cette skill est disponible globalement ou uniquement dans ce projet",
 			"modeLabel": "Mode (optionnel)",
 			"modePlaceholder": "N'importe quel mode",
 			"modeHint": "Restreindre cette skill à un mode spécifique",
@@ -1039,6 +1074,7 @@
 			"nameInvalid": "Le nom doit contenir entre 1 et 64 lettres minuscules, chiffres ou tirets",
 			"descriptionRequired": "La description est obligatoire",
 			"descriptionTooLong": "La description doit contenir au maximum 1024 caractères"
-		}
+		},
+		"footer": "Créez vos propres skills avec le mode Skill Writer, disponible dans le <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 48 - 13
webview-ui/src/i18n/locales/hi/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "कस्टम वर्कफ़्लो और क्रियाओं को तेज़ी से निष्पादित करने के लिए अपने स्लैश कमांड प्रबंधित करें। <DocsLink>और जानें</DocsLink>"
+		"description": "कस्टम वर्कफ़्लो और क्रियाओं को तेज़ी से निष्पादित करने के लिए अपने स्लैश कमांड प्रबंधित करें। <DocsLink>और जानें</DocsLink>",
+		"workspaceCommands": "वर्कस्पेस कमांड",
+		"globalCommands": "ग्लोबल कमांड",
+		"noWorkspaceCommands": "इस प्रोजेक्ट में अभी तक कोई कमांड नहीं।",
+		"noGlobalCommands": "अभी तक कोई ग्लोबल कमांड नहीं।",
+		"addCommand": "स्लैश कमांड जोड़ें",
+		"editCommand": "कमांड संपादित करें",
+		"deleteCommand": "कमांड हटाएं",
+		"deleteDialog": {
+			"title": "कमांड हटाएं",
+			"description": "क्या आप वाकई कमांड \"{{name}}\" को हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।",
+			"confirm": "हटाएं",
+			"cancel": "रद्द करें"
+		},
+		"createDialog": {
+			"title": "नया स्लैश कमांड बनाएं",
+			"nameLabel": "नाम",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "केवल लोअरकेस अक्षर, संख्याएं, हाइफन और अंडरस्कोर",
+			"sourceLabel": "स्थान",
+			"create": "बनाएं",
+			"cancel": "रद्द करें"
+		},
+		"source": {
+			"global": "ग्लोबल (सभी वर्कस्पेस में उपलब्ध)",
+			"project": "वर्कस्पेस"
+		},
+		"validation": {
+			"nameRequired": "नाम आवश्यक है",
+			"nameTooLong": "नाम 64 वर्ण या उससे कम होना चाहिए",
+			"nameInvalid": "नाम में केवल अक्षर, संख्याएं, हाइफन और अंडरस्कोर हो सकते हैं"
+		},
+		"footer": "अक्सर उपयोग किए जाने वाले संकेतों और वर्कफ़्लो तक तेज़ी से पहुंचने के लिए स्लैश कमांड का उपयोग करें।"
 	},
 	"prompts": {
 		"description": "प्रॉम्प्ट्स को बेहतर बनाना, कोड की व्याख्या करना और समस्याओं को ठीक करना जैसी त्वरित कार्रवाइयों के लिए उपयोग किए जाने वाले सहायक प्रॉम्प्ट्स को कॉन्फ़िगर करें। ये प्रॉम्प्ट्स Roo को सामान्य विकास कार्यों के लिए बेहतर सहायता प्रदान करने में मदद करते हैं।"
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "अधिकतम अनुरोध",
-			"description": "कार्य जारी रखने के लिए अनुमति मांगने से पहले स्वचालित रूप से इतने API अनुरोध करें।",
 			"unlimited": "असीमित"
 		},
 		"selectOptionsFirst": "स्वतः-अनुमोदन सक्षम करने के लिए नीचे से कम से कम एक विकल्प चुनें",
@@ -665,7 +696,6 @@
 				"label": "अधिकतम डायग्नोस्टिक संदेश",
 				"description": "प्रति फ़ाइल शामिल किए जाने वाले डायग्नोस्टिक संदेशों की अधिकतम संख्या। यह सीमा स्वचालित समावेशन (जब चेकबॉक्स सक्षम है) और मैन्युअल @problems उल्लेख दोनों पर लागू होती है। उच्च मान अधिक संदर्भ प्रदान करते हैं लेकिन टोकन उपयोग बढ़ाते हैं।",
 				"resetTooltip": "डिफ़ॉल्ट मान पर रीसेट करें (50)",
-				"unlimited": "असीमित डायग्नोस्टिक संदेश",
 				"unlimitedLabel": "असीमित"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "आप बाद में विस्तृत मॉडल सेटिंग्स समायोजित कर सकते हैं।"
 	},
 	"footer": {
-		"feedback": "यदि आपके कोई प्रश्न या प्रतिक्रिया है, तो <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> पर एक मुद्दा खोलने या <redditLink>reddit.com/r/RooCode</redditLink> या <discordLink>discord.gg/roocode</discordLink> में शामिल होने में संकोच न करें",
 		"telemetry": {
 			"label": "गुमनाम त्रुटि और उपयोग रिपोर्टिंग की अनुमति दें",
 			"description": "गुमनाम उपयोग डेटा और त्रुटि रिपोर्ट भेजकर Roo Code को बेहतर बनाने में मदद करें। यह टेलीमेट्री कोड, प्रॉम्प्ट या व्यक्तिगत जानकारी एकत्र नहीं करती। अधिक विवरण के लिए हमारी <privacyLink>गोपनीयता नीति</privacyLink> देखें। आप इसे कभी भी बंद कर सकते हैं।"
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Skills का प्रबंधन करें जो एजेंट को संदर्भात्मक निर्देश प्रदान करते हैं। जब आपके कार्यों के लिए प्रासंगिक हों तो Skills स्वचालित रूप से लागू होते हैं। <DocsLink>और जानें</DocsLink>",
-		"projectSkills": "प्रोजेक्ट Skills",
+		"workspaceSkills": "वर्कस्पेस Skills",
 		"globalSkills": "ग्लोबल Skills",
-		"noProjectSkills": "कोई प्रोजेक्ट skills कॉन्फ़िगर नहीं किया गया। प्रोजेक्ट-विशिष्ट एजेंट क्षमताएं जोड़ने के लिए एक बनाएं।",
-		"noGlobalSkills": "कोई ग्लोबल skills कॉन्फ़िगर नहीं किया गया। सभी प्रोजेक्ट्स में उपलब्ध एजेंट क्षमताएं जोड़ने के लिए एक बनाएं।",
+		"noWorkspaceSkills": "इस प्रोजेक्ट में अभी तक कोई skills नहीं।",
+		"noGlobalSkills": "अभी तक कोई ग्लोबल skills नहीं।",
 		"addSkill": "Skill जोड़ें",
 		"editSkill": "Skill संपादित करें",
 		"deleteSkill": "Skill हटाएं",
-		"changeMode": "मोड बदलें",
+		"configureModes": "मोड उपलब्धता",
 		"modeAny": "कोई भी मोड",
+		"modeCount": "{{count}} मोड",
 		"deleteDialog": {
 			"title": "Skill हटाएं",
 			"description": "क्या आप वाकई skill \"{{name}}\" को हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।",
 			"confirm": "हटाएं",
 			"cancel": "रद्द करें"
 		},
+		"modeDialog": {
+			"title": "Skill मोड कॉन्फ़िगर करें",
+			"description": "चुनें कि कौन से मोड इस Skill का उपयोग कर सकते हैं",
+			"intro": "अपना संदर्भ हल्का रखने के लिए, हम अनुशंसा करते हैं कि Skill को केवल उन मोड के लिए उपलब्ध बनाएं जिन्हें इसकी आवश्यकता है।",
+			"anyMode": "कोई भी मोड (हर जगह उपलब्ध)",
+			"save": "सहेजें",
+			"cancel": "रद्द करें"
+		},
 		"createDialog": {
 			"title": "नया Skill बनाएं",
-			"description": "एक नया skill टेम्पलेट परिभाषित करें जो एजेंट को संदर्भात्मक निर्देश प्रदान करता है।",
 			"nameLabel": "नाम",
 			"namePlaceholder": "my-skill-name",
-			"nameHint": "केवल छोटे अक्षर, संख्याएं और हाइफ़न (1-64 वर्ण)",
 			"descriptionLabel": "विवरण",
 			"descriptionPlaceholder": "वर्णन करें कि इस skill का उपयोग कब किया जाना चाहिए...",
-			"descriptionHint": "समझाएं कि यह skill क्या करता है और एजेंट को इसे कब लागू करना चाहिए (1-1024 वर्ण)",
 			"sourceLabel": "स्थान",
-			"sourceHint": "चुनें कि यह skill ग्लोबल रूप से उपलब्ध है या केवल इस प्रोजेक्ट में",
 			"modeLabel": "मोड (वैकल्पिक)",
 			"modePlaceholder": "कोई भी मोड",
 			"modeHint": "इस skill को किसी विशिष्ट मोड तक सीमित करें",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "नाम 1-64 छोटे अक्षर, संख्याएं या हाइफ़न होना चाहिए",
 			"descriptionRequired": "विवरण आवश्यक है",
 			"descriptionTooLong": "विवरण 1024 वर्णों से अधिक नहीं होना चाहिए"
-		}
+		},
+		"footer": "Skill Writer मोड के साथ अपने स्वयं के Skills बनाएं, <MarketplaceLink>Modes Marketplace</MarketplaceLink> में उपलब्ध।"
 	}
 }

+ 48 - 42
webview-ui/src/i18n/locales/id/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Kelola perintah slash kamu untuk mengeksekusi alur kerja dan tindakan kustom dengan cepat. <DocsLink>Pelajari lebih lanjut</DocsLink>"
+		"description": "Kelola perintah slash kamu untuk mengeksekusi alur kerja dan tindakan kustom dengan cepat. <DocsLink>Pelajari lebih lanjut</DocsLink>",
+		"workspaceCommands": "Perintah Ruang Kerja",
+		"globalCommands": "Perintah Global",
+		"noWorkspaceCommands": "Tidak ada perintah di proyek ini belum.",
+		"noGlobalCommands": "Tidak ada perintah global belum.",
+		"addCommand": "Tambahkan Perintah Slash",
+		"editCommand": "Edit perintah",
+		"deleteCommand": "Hapus perintah",
+		"deleteDialog": {
+			"title": "Hapus Perintah",
+			"description": "Apakah Anda yakin ingin menghapus perintah \"{{name}}\"? Tindakan ini tidak dapat dibatalkan.",
+			"confirm": "Hapus",
+			"cancel": "Batal"
+		},
+		"createDialog": {
+			"title": "Buat Perintah Slash Baru",
+			"nameLabel": "Nama",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Hanya huruf kecil, angka, tanda hubung dan garis bawah",
+			"sourceLabel": "Lokasi",
+			"create": "Buat",
+			"cancel": "Batal"
+		},
+		"source": {
+			"global": "Global (tersedia di semua ruang kerja)",
+			"project": "Ruang Kerja"
+		},
+		"validation": {
+			"nameRequired": "Nama diperlukan",
+			"nameTooLong": "Nama harus 64 karakter atau kurang",
+			"nameInvalid": "Nama hanya dapat berisi huruf, angka, tanda hubung, dan garis bawah"
+		},
+		"footer": "Gunakan perintah slash untuk akses cepat ke prompt dan alur kerja yang sering digunakan."
 	},
 	"prompts": {
 		"description": "Konfigurasi support prompt yang digunakan untuk aksi cepat seperti meningkatkan prompt, menjelaskan kode, dan memperbaiki masalah. Prompt ini membantu Roo memberikan bantuan yang lebih baik untuk tugas pengembangan umum."
@@ -242,13 +274,8 @@
 			"addButton": "Tambah",
 			"autoDenied": "Perintah dengan awalan `{{prefix}}` telah dilarang oleh pengguna. Jangan menghindari pembatasan ini dengan menjalankan perintah lain."
 		},
-		"showMenu": {
-			"label": "Tampilkan menu auto-approve di tampilan chat",
-			"description": "Ketika diaktifkan, menu auto-approve akan ditampilkan di bagian bawah tampilan chat, memungkinkan akses cepat ke pengaturan auto-approve"
-		},
 		"apiRequestLimit": {
 			"title": "Permintaan Maks",
-			"description": "Secara otomatis membuat sejumlah permintaan API ini sebelum meminta persetujuan untuk melanjutkan tugas.",
 			"unlimited": "Tidak terbatas"
 		},
 		"selectOptionsFirst": "Pilih setidaknya satu opsi di bawah ini untuk mengaktifkan persetujuan otomatis",
@@ -647,7 +674,6 @@
 				"label": "Pesan diagnostik maksimum",
 				"description": "Jumlah maksimum pesan diagnostik yang akan disertakan per file. Batas ini berlaku untuk penyertaan otomatis (ketika checkbox diaktifkan) dan penyebutan manual @problems. Nilai yang lebih tinggi memberikan lebih banyak konteks tetapi meningkatkan penggunaan token.",
 				"resetTooltip": "Reset ke nilai default (50)",
-				"unlimited": "Pesan diagnostik tidak terbatas",
 				"unlimitedLabel": "Tak terbatas"
 			},
 			"delayAfterWrite": {
@@ -803,27 +829,6 @@
 		}
 	},
 	"experimental": {
-		"warning": "⚠️",
-		"autoCondenseContextPercent": {
-			"label": "Ambang batas untuk memicu kondensasi konteks cerdas",
-			"description": "Ketika context window mencapai ambang batas ini, Roo akan secara otomatis mengondensasikannya."
-		},
-		"condensingApiConfiguration": {
-			"label": "Konfigurasi API untuk Kondensasi Konteks",
-			"description": "Pilih konfigurasi API mana yang akan digunakan untuk operasi kondensasi konteks. Biarkan tidak dipilih untuk menggunakan konfigurasi aktif saat ini.",
-			"useCurrentConfig": "Default"
-		},
-		"customCondensingPrompt": {
-			"label": "Prompt Kondensasi Konteks Kustom",
-			"description": "Kustomisasi system prompt yang digunakan untuk kondensasi konteks. Biarkan kosong untuk menggunakan prompt default.",
-			"placeholder": "Masukkan prompt kondensasi kustom kamu di sini...\n\nKamu dapat menggunakan struktur yang sama dengan prompt default:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps",
-			"reset": "Reset ke Default",
-			"hint": "Kosong = gunakan prompt default"
-		},
-		"AUTO_CONDENSE_CONTEXT": {
-			"name": "Secara cerdas mengondensasi context window",
-			"description": "Kondensasi konteks cerdas menggunakan panggilan LLM untuk merangkum percakapan masa lalu ketika context window tugas mencapai ambang batas yang telah ditetapkan, daripada menghapus pesan lama ketika konteks penuh."
-		},
 		"DIFF_STRATEGY_UNIFIED": {
 			"name": "Gunakan strategi unified diff eksperimental",
 			"description": "Aktifkan strategi unified diff eksperimental. Strategi ini mungkin mengurangi jumlah retry yang disebabkan oleh error model tetapi dapat menyebabkan perilaku yang tidak terduga atau edit yang salah. Hanya aktifkan jika kamu memahami risikonya dan bersedia meninjau semua perubahan dengan hati-hati."
@@ -832,10 +837,6 @@
 			"name": "Gunakan tool insert content eksperimental",
 			"description": "Aktifkan tool insert content eksperimental, memungkinkan Roo menyisipkan konten pada nomor baris spesifik tanpa perlu membuat diff."
 		},
-		"AUTOCOMPLETE": {
-			"name": "Gunakan fitur \"autocomplete\" eksperimental",
-			"description": "Ketika diaktifkan, Roo akan memberikan saran kode inline saat kamu mengetik. Memerlukan Roo Code API Provider."
-		},
 		"CONCURRENT_FILE_READS": {
 			"name": "Aktifkan pembacaan file bersamaan",
 			"description": "Ketika diaktifkan, Roo dapat membaca beberapa file dalam satu permintaan. Ketika dinonaktifkan, Roo harus membaca file satu per satu. Menonaktifkan ini dapat membantu saat bekerja dengan model yang kurang mampu atau ketika kamu ingin kontrol lebih terhadap akses file."
@@ -929,7 +930,6 @@
 		"simplifiedExplanation": "Anda dapat menyesuaikan pengaturan model terperinci nanti."
 	},
 	"footer": {
-		"feedback": "Jika kamu punya pertanyaan atau feedback, jangan ragu untuk membuka issue di <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> atau bergabung <redditLink>reddit.com/r/RooCode</redditLink> atau <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Izinkan pelaporan error dan penggunaan anonim",
 			"description": "Bantu tingkatkan Roo Code dengan mengirimkan data penggunaan anonim dan laporan error. Telemetri ini tidak mengumpulkan kode, prompt, atau informasi pribadi. Lihat <privacyLink>kebijakan privasi</privacyLink> kami untuk detail lebih lanjut. Anda dapat menonaktifkannya kapan saja."
@@ -1026,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Kelola skills yang memberikan instruksi kontekstual kepada agen. Skills diterapkan secara otomatis saat relevan dengan tugas Anda. <DocsLink>Pelajari lebih lanjut</DocsLink>",
-		"projectSkills": "Skills Proyek",
+		"workspaceSkills": "Skills Ruang Kerja",
 		"globalSkills": "Skills Global",
-		"noProjectSkills": "Tidak ada skills proyek yang dikonfigurasi. Buat satu untuk menambahkan kemampuan agen khusus proyek.",
-		"noGlobalSkills": "Tidak ada skills global yang dikonfigurasi. Buat satu untuk menambahkan kemampuan agen yang tersedia di semua proyek.",
+		"noWorkspaceSkills": "Tidak ada skills di proyek ini belum.",
+		"noGlobalSkills": "Tidak ada skills global belum.",
 		"addSkill": "Tambahkan Skill",
 		"editSkill": "Edit skill",
 		"deleteSkill": "Hapus skill",
-		"changeMode": "Ubah mode",
+		"configureModes": "Ketersediaan mode",
 		"modeAny": "Mode apa saja",
+		"modeCount": "{{count}} mode",
 		"deleteDialog": {
 			"title": "Hapus Skill",
 			"description": "Apakah Anda yakin ingin menghapus skill \"{{name}}\"? Tindakan ini tidak dapat dibatalkan.",
 			"confirm": "Hapus",
 			"cancel": "Batal"
 		},
+		"modeDialog": {
+			"title": "Konfigurasi Mode Skill",
+			"description": "Pilih mode mana yang dapat menggunakan skill ini",
+			"intro": "Untuk menjaga konteks Anda tetap ringan, kami merekomendasikan hanya membuat skills tersedia untuk mode yang membutuhkannya.",
+			"anyMode": "Mode apa pun (tersedia di mana-mana)",
+			"save": "Simpan",
+			"cancel": "Batal"
+		},
 		"createDialog": {
 			"title": "Buat Skill Baru",
-			"description": "Tentukan template skill baru yang memberikan instruksi kontekstual kepada agen.",
 			"nameLabel": "Nama",
 			"namePlaceholder": "nama-skill-saya",
-			"nameHint": "Hanya huruf kecil, angka, dan tanda hubung (1-64 karakter)",
 			"descriptionLabel": "Deskripsi",
 			"descriptionPlaceholder": "Jelaskan kapan skill ini harus digunakan...",
-			"descriptionHint": "Jelaskan apa yang dilakukan skill ini dan kapan agen harus menerapkannya (1-1024 karakter)",
 			"sourceLabel": "Lokasi",
-			"sourceHint": "Pilih apakah skill ini tersedia secara global atau hanya di proyek ini",
 			"modeLabel": "Mode (opsional)",
 			"modePlaceholder": "Mode apa saja",
 			"modeHint": "Batasi skill ini ke mode tertentu",
@@ -1069,6 +1074,7 @@
 			"nameInvalid": "Nama harus 1-64 huruf kecil, angka, atau tanda hubung",
 			"descriptionRequired": "Deskripsi diperlukan",
 			"descriptionTooLong": "Deskripsi harus 1024 karakter atau kurang"
-		}
+		},
+		"footer": "Buat skills Anda sendiri dengan mode Skill Writer, tersedia di <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/it/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Gestisci i tuoi comandi slash per eseguire rapidamente flussi di lavoro e azioni personalizzate. <DocsLink>Scopri di più</DocsLink>"
+		"description": "Gestisci i tuoi comandi slash per eseguire rapidamente flussi di lavoro e azioni personalizzate. <DocsLink>Scopri di più</DocsLink>",
+		"workspaceCommands": "Comandi dell'area di lavoro",
+		"globalCommands": "Comandi globali",
+		"noWorkspaceCommands": "Nessun comando in questo progetto ancora.",
+		"noGlobalCommands": "Nessun comando globale ancora.",
+		"addCommand": "Aggiungi comando Slash",
+		"editCommand": "Modifica comando",
+		"deleteCommand": "Elimina comando",
+		"deleteDialog": {
+			"title": "Elimina comando",
+			"description": "Sei sicuro di voler eliminare il comando \"{{name}}\"? Questa azione non può essere annullata.",
+			"confirm": "Elimina",
+			"cancel": "Annulla"
+		},
+		"createDialog": {
+			"title": "Crea nuovo comando Slash",
+			"nameLabel": "Nome",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Solo lettere minuscole, numeri, trattini e sottolineature",
+			"sourceLabel": "Posizione",
+			"create": "Crea",
+			"cancel": "Annulla"
+		},
+		"source": {
+			"global": "Global (disponibile in tutte le aree di lavoro)",
+			"project": "Area di lavoro"
+		},
+		"validation": {
+			"nameRequired": "Il nome è obbligatorio",
+			"nameTooLong": "Il nome deve avere 64 caratteri o meno",
+			"nameInvalid": "Il nome può contenere solo lettere, numeri, trattini e sottolineature"
+		},
+		"footer": "Usa i comandi slash per accedere rapidamente ai prompt e ai flussi di lavoro utilizzati di frequente."
 	},
 	"prompts": {
 		"description": "Configura i prompt di supporto utilizzati per azioni rapide come il miglioramento dei prompt, la spiegazione del codice e la risoluzione dei problemi. Questi prompt aiutano Roo a fornire una migliore assistenza per le attività di sviluppo comuni."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Richieste massime",
-			"description": "Esegui automaticamente questo numero di richieste API prima di chiedere l'approvazione per continuare con l'attività.",
 			"unlimited": "Illimitato"
 		},
 		"selectOptionsFirst": "Seleziona almeno un'opzione qui sotto per abilitare l'approvazione automatica",
@@ -665,7 +696,6 @@
 				"label": "Numero massimo di messaggi diagnostici",
 				"description": "Numero massimo di messaggi diagnostici da includere per file. Questo limite si applica sia all'inclusione automatica (quando la casella è abilitata) che alle menzioni manuali di @problems. Valori più alti forniscono più contesto ma aumentano l'utilizzo dei token.",
 				"resetTooltip": "Ripristina al valore predefinito (50)",
-				"unlimited": "Messaggi diagnostici illimitati",
 				"unlimitedLabel": "Illimitato"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "Puoi modificare le impostazioni dettagliate del modello in seguito."
 	},
 	"footer": {
-		"feedback": "Se hai domande o feedback, sentiti libero di aprire un issue su <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> o unirti a <redditLink>reddit.com/r/RooCode</redditLink> o <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Consenti segnalazioni anonime di errori e utilizzo",
 			"description": "Aiuta a migliorare Roo Code inviando dati di utilizzo anonimi e segnalazioni di errori. Questa telemetria non raccoglie codice, prompt o informazioni personali. Consulta la nostra <privacyLink>informativa sulla privacy</privacyLink> per maggiori dettagli."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Gestisci le skills che forniscono istruzioni contestuali all'agente. Le skills vengono applicate automaticamente quando rilevanti per le tue attività. <DocsLink>Scopri di più</DocsLink>",
-		"projectSkills": "Skills del Progetto",
+		"workspaceSkills": "Skills dell'area di lavoro",
 		"globalSkills": "Skills Globali",
-		"noProjectSkills": "Nessuna skill di progetto configurata. Creane una per aggiungere capacità specifiche del progetto all'agente.",
+		"noWorkspaceSkills": "Nessuna skill in questo progetto ancora.",
 		"noGlobalSkills": "Nessuna skill globale configurata. Creane una per aggiungere capacità all'agente disponibili in tutti i progetti.",
 		"addSkill": "Aggiungi Skill",
 		"editSkill": "Modifica skill",
 		"deleteSkill": "Elimina skill",
-		"changeMode": "Cambia modalità",
+		"configureModes": "Disponibilità modalità",
 		"modeAny": "Qualsiasi modalità",
+		"modeCount": "{{count}} modalità",
 		"deleteDialog": {
 			"title": "Elimina Skill",
 			"description": "Sei sicuro di voler eliminare la skill \"{{name}}\"? Questa azione non può essere annullata.",
 			"confirm": "Elimina",
 			"cancel": "Annulla"
 		},
+		"modeDialog": {
+			"title": "Configura modalità Skill",
+			"description": "Scegli quali modalità possono utilizzare questa skill",
+			"intro": "Per mantenere il tuo contesto leggero, consigliamo di rendere le skills disponibili solo per le modalità che le richiedono.",
+			"anyMode": "Qualsiasi modalità (disponibile ovunque)",
+			"save": "Salva",
+			"cancel": "Annulla"
+		},
 		"createDialog": {
 			"title": "Crea Nuova Skill",
-			"description": "Definisci un nuovo modello di skill che fornisce istruzioni contestuali all'agente.",
 			"nameLabel": "Nome",
 			"namePlaceholder": "il-mio-nome-skill",
-			"nameHint": "Solo lettere minuscole, numeri e trattini (1-64 caratteri)",
 			"descriptionLabel": "Descrizione",
 			"descriptionPlaceholder": "Descrivi quando questa skill dovrebbe essere utilizzata...",
-			"descriptionHint": "Spiega cosa fa questa skill e quando l'agente dovrebbe applicarla (1-1024 caratteri)",
 			"sourceLabel": "Posizione",
-			"sourceHint": "Scegli se questa skill è disponibile globalmente o solo in questo progetto",
 			"modeLabel": "Modalità (opzionale)",
 			"modePlaceholder": "Qualsiasi modalità",
 			"modeHint": "Limita questa skill a una modalità specifica",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "Il nome deve contenere 1-64 lettere minuscole, numeri o trattini",
 			"descriptionRequired": "La descrizione è obbligatoria",
 			"descriptionTooLong": "La descrizione deve essere di massimo 1024 caratteri"
-		}
+		},
+		"footer": "Crea le tue skill con la modalità Skill Writer, disponibile in <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/ja/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "スラッシュコマンドを管理して、カスタムワークフローやアクションを素早く実行します。<DocsLink>詳細はこちら</DocsLink>"
+		"description": "スラッシュコマンドを管理して、カスタムワークフローやアクションを素早く実行します。<DocsLink>詳細はこちら</DocsLink>",
+		"workspaceCommands": "ワークスペースコマンド",
+		"globalCommands": "グローバルコマンド",
+		"noWorkspaceCommands": "このプロジェクトにはまだコマンドがありません。",
+		"noGlobalCommands": "グローバルコマンドはまだありません。",
+		"addCommand": "スラッシュコマンドを追加",
+		"editCommand": "コマンドを編集",
+		"deleteCommand": "コマンドを削除",
+		"deleteDialog": {
+			"title": "コマンドを削除",
+			"description": "コマンド \"{{name}}\" を削除してもよろしいですか?このアクションは取り消せません。",
+			"confirm": "削除",
+			"cancel": "キャンセル"
+		},
+		"createDialog": {
+			"title": "新しいスラッシュコマンドを作成",
+			"nameLabel": "名前",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "小文字、数字、ハイフン、アンダースコアのみ",
+			"sourceLabel": "場所",
+			"create": "作成",
+			"cancel": "キャンセル"
+		},
+		"source": {
+			"global": "グローバル(すべてのワークスペースで利用可能)",
+			"project": "ワークスペース"
+		},
+		"validation": {
+			"nameRequired": "名前は必須です",
+			"nameTooLong": "名前は64文字以下である必要があります",
+			"nameInvalid": "名前は文字、数字、ハイフン、アンダースコアのみを含める必要があります"
+		},
+		"footer": "スラッシュコマンドを使用して、頻繁に使用するプロンプトとワークフローにすばやくアクセスします。"
 	},
 	"prompts": {
 		"description": "プロンプトの強化、コードの説明、問題の修正などの迅速なアクションに使用されるサポートプロンプトを設定します。これらのプロンプトは、Rooが一般的な開発タスクでより良いサポートを提供するのに役立ちます。"
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "最大リクエスト数",
-			"description": "タスクを続行するための承認を求める前に、自動的にこの数のAPIリクエストを行います。",
 			"unlimited": "無制限"
 		},
 		"selectOptionsFirst": "自動承認を有効にするには、以下のオプションを少なくとも1つ選択してください",
@@ -665,7 +696,6 @@
 				"label": "最大診断メッセージ数",
 				"description": "ファイルごとに含める診断メッセージの最大数。この制限は、自動インクルード(チェックボックスが有効な場合)と手動の@problemsメンションの両方に適用されます。値を高くするとより多くのコンテキストが提供されますが、トークンの使用量が増加します。",
 				"resetTooltip": "デフォルト値(50)にリセット",
-				"unlimited": "無制限の診断メッセージ",
 				"unlimitedLabel": "無制限"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "詳細なモデル設定は後で調整できます。"
 	},
 	"footer": {
-		"feedback": "質問やフィードバックがある場合は、<githubLink>github.com/RooCodeInc/Roo-Code</githubLink>で問題を開くか、<redditLink>reddit.com/r/RooCode</redditLink>や<discordLink>discord.gg/roocode</discordLink>に参加してください",
 		"telemetry": {
 			"label": "匿名のエラーと使用状況レポートを許可",
 			"description": "匿名の使用データとエラーレポートを送信してRoo Codeの改善にご協力ください。このテレメトリはコード、プロンプト、個人情報を収集しません。詳細については<privacyLink>プライバシーポリシー</privacyLink>をご覧ください。"
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "エージェントにコンテキスト指示を提供するスキルを管理します。スキルはタスクに関連する場合に自動的に適用されます。<DocsLink>詳細を見る</DocsLink>",
-		"projectSkills": "プロジェクトスキル",
+		"workspaceSkills": "ワークスペーススキル",
 		"globalSkills": "グローバルスキル",
-		"noProjectSkills": "プロジェクトスキルが設定されていません。プロジェクト固有のエージェント機能を追加するには、作成してください。",
+		"noWorkspaceSkills": "このプロジェクトにはまだスキルがありません。",
 		"noGlobalSkills": "グローバルスキルが設定されていません。すべてのプロジェクトで利用可能なエージェント機能を追加するには、作成してください。",
 		"addSkill": "スキルを追加",
 		"editSkill": "スキルを編集",
 		"deleteSkill": "スキルを削除",
-		"changeMode": "モードを変更",
+		"configureModes": "モード可用性",
 		"modeAny": "任意のモード",
+		"modeCount": "{{count}} モード",
 		"deleteDialog": {
 			"title": "スキルを削除",
 			"description": "スキル「{{name}}」を削除してもよろしいですか?この操作は元に戻せません。",
 			"confirm": "削除",
 			"cancel": "キャンセル"
 		},
+		"modeDialog": {
+			"title": "スキルモードを設定",
+			"description": "このスキルを使用できるモードを選択します",
+			"intro": "コンテキストを軽く保つために、必要なモードのみでスキルを利用可能にすることをお勧めします。",
+			"anyMode": "任意のモード(どこでも利用可能)",
+			"save": "保存",
+			"cancel": "キャンセル"
+		},
 		"createDialog": {
 			"title": "新しいスキルを作成",
-			"description": "エージェントにコンテキスト指示を提供する新しいスキルテンプレートを定義します。",
 			"nameLabel": "名前",
 			"namePlaceholder": "my-skill-name",
-			"nameHint": "小文字、数字、ハイフンのみ(1〜64文字)",
 			"descriptionLabel": "説明",
 			"descriptionPlaceholder": "このスキルをいつ使用するか説明してください...",
-			"descriptionHint": "このスキルが何をするか、エージェントがいつ適用すべきかを説明してください(1〜1024文字)",
 			"sourceLabel": "場所",
-			"sourceHint": "このスキルがグローバルに利用可能か、このプロジェクトのみかを選択してください",
 			"modeLabel": "モード(オプション)",
 			"modePlaceholder": "全てのモード",
 			"modeHint": "このスキルを特定のモードに制限する",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "名前は1〜64文字の小文字、数字、またはハイフンである必要があります",
 			"descriptionRequired": "説明は必須です",
 			"descriptionTooLong": "説明は1024文字以内である必要があります"
-		}
+		},
+		"footer": "スキルライターモードで独自のスキルを作成します。<MarketplaceLink>モードマーケットプレイス</MarketplaceLink>で入手可能です。"
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/ko/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "사용자 지정 워크플로와 작업을 신속하게 실행하기 위해 슬래시 명령을 관리합니다. <DocsLink>더 알아보기</DocsLink>"
+		"description": "사용자 지정 워크플로와 작업을 신속하게 실행하기 위해 슬래시 명령을 관리합니다. <DocsLink>더 알아보기</DocsLink>",
+		"workspaceCommands": "워크스페이스 명령",
+		"globalCommands": "글로벌 명령",
+		"noWorkspaceCommands": "이 프로젝트에 명령이 아직 없습니다.",
+		"noGlobalCommands": "글로벌 명령이 아직 없습니다.",
+		"addCommand": "슬래시 명령 추가",
+		"editCommand": "명령 편집",
+		"deleteCommand": "명령 삭제",
+		"deleteDialog": {
+			"title": "명령 삭제",
+			"description": "명령 \"{{name}}\"을(를) 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
+			"confirm": "삭제",
+			"cancel": "취소"
+		},
+		"createDialog": {
+			"title": "새 슬래시 명령 만들기",
+			"nameLabel": "이름",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "소문자, 숫자, 하이픈 및 밑줄만",
+			"sourceLabel": "위치",
+			"create": "만들기",
+			"cancel": "취소"
+		},
+		"source": {
+			"global": "글로벌 (모든 워크스페이스에서 사용 가능)",
+			"project": "워크스페이스"
+		},
+		"validation": {
+			"nameRequired": "이름은 필수입니다",
+			"nameTooLong": "이름은 64자 이하여야 합니다",
+			"nameInvalid": "이름은 문자, 숫자, 하이픈 및 밑줄만 포함할 수 있습니다"
+		},
+		"footer": "슬래시 명령을 사용하여 자주 사용하는 프롬프트 및 워크플로에 빠르게 액세스합니다."
 	},
 	"prompts": {
 		"description": "프롬프트 향상, 코드 설명, 문제 해결과 같은 빠른 작업에 사용되는 지원 프롬프트를 구성합니다. 이러한 프롬프트는 Roo가 일반적인 개발 작업에 대해 더 나은 지원을 제공하는 데 도움이 됩니다."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "최대 요청 수",
-			"description": "작업을 계속하기 위한 승인을 요청하기 전에 자동으로 이 수의 API 요청을 수행합니다.",
 			"unlimited": "무제한"
 		},
 		"selectOptionsFirst": "자동 승인을 활성화하려면 아래에서 하나 이상의 옵션을 선택하세요",
@@ -665,7 +696,6 @@
 				"label": "최대 진단 메시지",
 				"description": "파일당 포함할 최대 진단 메시지 수입니다. 이 제한은 자동 포함(체크박스가 활성화된 경우)과 수동 @problems 언급 모두에 적용됩니다. 값이 높을수록 더 많은 컨텍스트를 제공하지만 토큰 사용량이 증가합니다.",
 				"resetTooltip": "기본값(50)으로 재설정",
-				"unlimited": "무제한 진단 메시지",
 				"unlimitedLabel": "무제한"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "나중에 자세한 모델 설정을 조정할 수 있습니다."
 	},
 	"footer": {
-		"feedback": "질문이나 피드백이 있으시면 <githubLink>github.com/RooCodeInc/Roo-Code</githubLink>에서 이슈를 열거나 <redditLink>reddit.com/r/RooCode</redditLink> 또는 <discordLink>discord.gg/roocode</discordLink>에 가입하세요",
 		"telemetry": {
 			"label": "익명 오류 및 사용 보고 허용",
 			"description": "익명 사용 데이터 및 오류 보고서를 전송하여 Roo Code 개선에 도움을 주세요. 이 텔레메트리는 코드, 프롬프트 또는 개인 정보를 수집하지 않습니다. 자세한 내용은 <privacyLink>개인정보 보호정책</privacyLink>을 참조하세요."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "에이전트에 컨텍스트 지침을 제공하는 스킬을 관리합니다. 스킬은 작업과 관련이 있을 때 자동으로 적용됩니다. <DocsLink>자세히 알아보기</DocsLink>",
-		"projectSkills": "프로젝트 스킬",
+		"workspaceSkills": "워크스페이스 스킬",
 		"globalSkills": "전역 스킬",
-		"noProjectSkills": "구성된 프로젝트 스킬이 없습니다. 프로젝트별 에이전트 기능을 추가하려면 하나를 만드세요.",
+		"noWorkspaceSkills": "이 프로젝트에 스킬이 아직 없습니다.",
 		"noGlobalSkills": "구성된 전역 스킬이 없습니다. 모든 프로젝트에서 사용할 수 있는 에이전트 기능을 추가하려면 하나를 만드세요.",
 		"addSkill": "스킬 추가",
 		"editSkill": "스킬 편집",
 		"deleteSkill": "스킬 삭제",
-		"changeMode": "모드 변경",
+		"configureModes": "모드 가용성",
 		"modeAny": "모든 모드",
+		"modeCount": "{{count}} 모드",
 		"deleteDialog": {
 			"title": "스킬 삭제",
 			"description": "스킬 \"{{name}}\"을(를) 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
 			"confirm": "삭제",
 			"cancel": "취소"
 		},
+		"modeDialog": {
+			"title": "스킬 모드 구성",
+			"description": "이 스킬을 사용할 수 있는 모드를 선택합니다",
+			"intro": "컨텍스트를 가볍게 유지하기 위해 필요한 모드에서만 스킬을 사용 가능하게 하는 것을 권장합니다.",
+			"anyMode": "모든 모드 (모든 곳에서 사용 가능)",
+			"save": "저장",
+			"cancel": "취소"
+		},
 		"createDialog": {
 			"title": "새 스킬 만들기",
-			"description": "에이전트에 컨텍스트 지침을 제공하는 새 스킬 템플릿을 정의합니다.",
 			"nameLabel": "이름",
 			"namePlaceholder": "my-skill-name",
-			"nameHint": "소문자, 숫자 및 하이픈만 사용(1-64자)",
 			"descriptionLabel": "설명",
 			"descriptionPlaceholder": "이 스킬을 언제 사용해야 하는지 설명하세요...",
-			"descriptionHint": "이 스킬이 무엇을 하는지, 에이전트가 언제 적용해야 하는지 설명하세요(1-1024자)",
 			"sourceLabel": "위치",
-			"sourceHint": "이 스킬을 전역으로 사용할지 이 프로젝트에만 사용할지 선택하세요",
 			"modeLabel": "모드 (선택사항)",
 			"modePlaceholder": "모든 모드",
 			"modeHint": "이 스킬을 특정 모드로 제한",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "이름은 1-64자의 소문자, 숫자 또는 하이픈이어야 합니다",
 			"descriptionRequired": "설명은 필수입니다",
 			"descriptionTooLong": "설명은 1024자 이하여야 합니다"
-		}
+		},
+		"footer": "스킬 작성자 모드로 자신만의 스킬을 만들 수 있습니다. <MarketplaceLink>모드 마켓플레이스</MarketplaceLink>에서 사용 가능합니다."
 	}
 }

+ 49 - 14
webview-ui/src/i18n/locales/nl/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Beheer je slash-commando's om snel aangepaste workflows en acties uit te voeren. <DocsLink>Meer informatie</DocsLink>"
+		"description": "Beheer je slash-commando's om snel aangepaste workflows en acties uit te voeren. <DocsLink>Meer informatie</DocsLink>",
+		"workspaceCommands": "Werkruimteopdrachten",
+		"globalCommands": "Globale opdrachten",
+		"noWorkspaceCommands": "Nog geen opdrachten in dit project.",
+		"noGlobalCommands": "Nog geen globale opdrachten.",
+		"addCommand": "Slash-opdracht toevoegen",
+		"editCommand": "Opdracht bewerken",
+		"deleteCommand": "Opdracht verwijderen",
+		"deleteDialog": {
+			"title": "Opdracht verwijderen",
+			"description": "Weet je zeker dat je de opdracht \"{{name}}\" wilt verwijderen? Deze actie kan niet ongedaan gemaakt worden.",
+			"confirm": "Verwijderen",
+			"cancel": "Annuleren"
+		},
+		"createDialog": {
+			"title": "Nieuw slash-commando maken",
+			"nameLabel": "Naam",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Alleen kleine letters, cijfers, streepjes en onderstrepen",
+			"sourceLabel": "Locatie",
+			"create": "Maken",
+			"cancel": "Annuleren"
+		},
+		"source": {
+			"global": "Globaal (beschikbaar in alle werkruimten)",
+			"project": "Werkruimte"
+		},
+		"validation": {
+			"nameRequired": "Naam is vereist",
+			"nameTooLong": "Naam mag max. 64 tekens zijn",
+			"nameInvalid": "Naam mag alleen letters, cijfers, streepjes en onderstrepen bevatten"
+		},
+		"footer": "Gebruik slash-commando's voor snelle toegang tot veelgebruikte prompts en workflows."
 	},
 	"prompts": {
 		"description": "Configureer ondersteuningsprompts die worden gebruikt voor snelle acties zoals het verbeteren van prompts, het uitleggen van code en het oplossen van problemen. Deze prompts helpen Roo om betere ondersteuning te bieden voor veelvoorkomende ontwikkelingstaken."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Maximale verzoeken",
-			"description": "Voer automatisch dit aantal API-verzoeken uit voordat om goedkeuring wordt gevraagd om door te gaan met de taak.",
 			"unlimited": "Onbeperkt"
 		},
 		"selectOptionsFirst": "Selecteer ten minste één optie hieronder om automatische goedkeuring in te schakelen",
@@ -675,7 +706,6 @@
 				"label": "Maximale diagnostische berichten",
 				"description": "Maximaal aantal diagnostische berichten dat per bestand moet worden opgenomen. Deze limiet geldt voor zowel automatische opname (wanneer checkbox is ingeschakeld) als handmatige @problems vermeldingen. Hogere waarden bieden meer context maar verhogen het tokengebruik.",
 				"resetTooltip": "Reset naar standaardwaarde (50)",
-				"unlimited": "Onbeperkte diagnostische berichten",
 				"unlimitedLabel": "Onbeperkt"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "Je kunt later gedetailleerde modelinstellingen aanpassen."
 	},
 	"footer": {
-		"feedback": "Heb je vragen of feedback? Open gerust een issue op <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> of sluit je aan bij <redditLink>reddit.com/r/RooCode</redditLink> of <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Anonieme fout- en gebruiksrapportage toestaan",
 			"description": "Help Roo Code te verbeteren door anonieme gebruiksgegevens en foutmeldingen te versturen. Deze telemetrie verzamelt geen code, prompts of persoonlijke informatie. Zie ons <privacyLink>privacybeleid</privacyLink> voor meer details."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Beheer skills die contextuele instructies aan de agent verstrekken. Skills worden automatisch toegepast wanneer ze relevant zijn voor uw taken. <DocsLink>Meer informatie</DocsLink>",
-		"projectSkills": "Projectskills",
-		"globalSkills": "Globale Skills",
-		"noProjectSkills": "Geen projectskills geconfigureerd. Maak er een om projectspecifieke agentmogelijkheden toe te voegen.",
-		"noGlobalSkills": "Geen globale skills geconfigureerd. Maak er een om agentmogelijkheden toe te voegen die beschikbaar zijn in alle projecten.",
+		"workspaceSkills": "Werkruimteskills",
+		"globalSkills": "Globale skills",
+		"noWorkspaceSkills": "Nog geen skills in dit project.",
+		"noGlobalSkills": "Nog geen globale skills.",
 		"addSkill": "Skill toevoegen",
 		"editSkill": "Skill bewerken",
 		"deleteSkill": "Skill verwijderen",
-		"changeMode": "Modus wijzigen",
+		"configureModes": "Modebeschikbaarheid",
 		"modeAny": "Elke modus",
+		"modeCount": "{{count}} modus",
 		"deleteDialog": {
 			"title": "Skill verwijderen",
 			"description": "Weet u zeker dat u de skill \"{{name}}\" wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
 			"confirm": "Verwijderen",
 			"cancel": "Annuleren"
 		},
+		"modeDialog": {
+			"title": "Skillmodi configureren",
+			"description": "Kies welke modi deze skill kunnen gebruiken",
+			"intro": "Om je context licht te houden, raden we aan skills alleen beschikbaar te maken voor de modi die ze nodig hebben.",
+			"anyMode": "Elke modus (overal beschikbaar)",
+			"save": "Opslaan",
+			"cancel": "Annuleren"
+		},
 		"createDialog": {
 			"title": "Nieuwe Skill maken",
-			"description": "Definieer een nieuwe skillsjabloon die contextuele instructies aan de agent verstrekt.",
 			"nameLabel": "Naam",
 			"namePlaceholder": "mijn-skill-naam",
-			"nameHint": "Alleen kleine letters, cijfers en streepjes (1-64 tekens)",
 			"descriptionLabel": "Beschrijving",
 			"descriptionPlaceholder": "Beschrijf wanneer deze skill moet worden gebruikt...",
-			"descriptionHint": "Leg uit wat deze skill doet en wanneer de agent deze moet toepassen (1-1024 tekens)",
 			"sourceLabel": "Locatie",
-			"sourceHint": "Kies of deze skill globaal beschikbaar is of alleen in dit project",
 			"modeLabel": "Modus (optioneel)",
 			"modePlaceholder": "Elke modus",
 			"modeHint": "Beperk deze skill tot een specifieke modus",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "Naam moet 1-64 kleine letters, cijfers of streepjes zijn",
 			"descriptionRequired": "Beschrijving is verplicht",
 			"descriptionTooLong": "Beschrijving moet maximaal 1024 tekens zijn"
-		}
+		},
+		"footer": "Maak je eigen skills met de Skill Writer modus, beschikbaar in de <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/pl/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Zarządzaj poleceniami slash, aby szybko wykonywać niestandardowe przepływy pracy i akcje. <DocsLink>Dowiedz się więcej</DocsLink>"
+		"description": "Zarządzaj poleceniami slash, aby szybko wykonywać niestandardowe przepływy pracy i akcje. <DocsLink>Dowiedz się więcej</DocsLink>",
+		"workspaceCommands": "Polecenia obszaru roboczego",
+		"globalCommands": "Polecenia globalne",
+		"noWorkspaceCommands": "Brak poleceń w tym projekcie.",
+		"noGlobalCommands": "Brak poleceń globalnych.",
+		"addCommand": "Dodaj polecenie Slash",
+		"editCommand": "Edytuj polecenie",
+		"deleteCommand": "Usuń polecenie",
+		"deleteDialog": {
+			"title": "Usuń polecenie",
+			"description": "Czy na pewno chcesz usunąć polecenie \"{{name}}\"? Tej czynności nie można cofnąć.",
+			"confirm": "Usuń",
+			"cancel": "Anuluj"
+		},
+		"createDialog": {
+			"title": "Utwórz nowe polecenie Slash",
+			"nameLabel": "Nazwa",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Tylko małe litery, cyfry, łączniki i podkreślenia",
+			"sourceLabel": "Lokalizacja",
+			"create": "Utwórz",
+			"cancel": "Anuluj"
+		},
+		"source": {
+			"global": "Globalne (dostępne we wszystkich obszarach roboczych)",
+			"project": "Obszar roboczy"
+		},
+		"validation": {
+			"nameRequired": "Nazwa jest wymagana",
+			"nameTooLong": "Nazwa musi mieć 64 znaki lub mniej",
+			"nameInvalid": "Nazwa może zawierać tylko litery, cyfry, łączniki i podkreślenia"
+		},
+		"footer": "Używaj poleceń slash do szybkiego dostępu do często używanych podpowiedzi i przepływów pracy."
 	},
 	"prompts": {
 		"description": "Skonfiguruj podpowiedzi wsparcia używane do szybkich działań, takich jak ulepszanie podpowiedzi, wyjaśnianie kodu i rozwiązywanie problemów. Te podpowiedzi pomagają Roo zapewnić lepsze wsparcie dla typowych zadań programistycznych."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Maksymalna liczba żądań",
-			"description": "Automatycznie wykonaj tyle żądań API przed poproszeniem o zgodę na kontynuowanie zadania.",
 			"unlimited": "Bez limitu"
 		},
 		"selectOptionsFirst": "Wybierz co najmniej jedną opcję poniżej, aby włączyć automatyczne zatwierdzanie",
@@ -665,7 +696,6 @@
 				"label": "Maksymalna liczba komunikatów diagnostycznych",
 				"description": "Maksymalna liczba komunikatów diagnostycznych dołączanych na plik. Ten limit dotyczy zarówno automatycznego dołączania (gdy pole wyboru jest włączone) jak i ręcznych wzmianek @problems. Wyższe wartości dostarczają więcej kontekstu, ale zwiększają zużycie tokenów.",
 				"resetTooltip": "Resetuj do wartości domyślnej (50)",
-				"unlimited": "Nieograniczone komunikaty diagnostyczne",
 				"unlimitedLabel": "Nieograniczone"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "Można dostosować szczegółowe ustawienia modelu później."
 	},
 	"footer": {
-		"feedback": "Jeśli masz jakiekolwiek pytania lub opinie, śmiało otwórz zgłoszenie na <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> lub dołącz do <redditLink>reddit.com/r/RooCode</redditLink> lub <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Zezwól na anonimowe raportowanie błędów i użycia",
 			"description": "Pomóż ulepszyć Roo Code, wysyłając anonimowe dane użytkowania i raporty błędów. Ta telemetria nie zbiera kodu, promptów ani danych osobowych. Zobacz naszą <privacyLink>politykę prywatności</privacyLink>, aby uzyskać więcej szczegółów. Możesz to wyłączyć w dowolnym momencie."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Zarządzaj umiejętnościami, które dostarczają kontekstowe instrukcje dla agenta. Umiejętności są automatycznie stosowane, gdy są istotne dla Twoich zadań. <DocsLink>Dowiedz się więcej</DocsLink>",
-		"projectSkills": "Umiejętności Projektu",
+		"workspaceSkills": "Umiejętności obszaru roboczego",
 		"globalSkills": "Umiejętności Globalne",
-		"noProjectSkills": "Brak skonfigurowanych umiejętności projektu. Utwórz jedną, aby dodać możliwości agenta specyficzne dla projektu.",
+		"noWorkspaceSkills": "Brak umiejętności w tym projekcie.",
 		"noGlobalSkills": "Brak skonfigurowanych umiejętności globalnych. Utwórz jedną, aby dodać możliwości agenta dostępne we wszystkich projektach.",
 		"addSkill": "Dodaj Umiejętność",
 		"editSkill": "Edytuj umiejętność",
 		"deleteSkill": "Usuń umiejętność",
-		"changeMode": "Zmień tryb",
+		"configureModes": "Dostępność trybu",
 		"modeAny": "Dowolny tryb",
+		"modeCount": "{{count}} tryby",
 		"deleteDialog": {
 			"title": "Usuń Umiejętność",
 			"description": "Czy na pewno chcesz usunąć umiejętność \"{{name}}\"? Tej akcji nie można cofnąć.",
 			"confirm": "Usuń",
 			"cancel": "Anuluj"
 		},
+		"modeDialog": {
+			"title": "Konfiguruj tryby umiejętności",
+			"description": "Wybierz, które tryby mogą korzystać z tej umiejętności",
+			"intro": "Aby utrzymać lekki kontekst, zalecamy udostępnienie umiejętności tylko trybom, które ich potrzebują.",
+			"anyMode": "Dowolny tryb (dostępny wszędzie)",
+			"save": "Zapisz",
+			"cancel": "Anuluj"
+		},
 		"createDialog": {
 			"title": "Utwórz Nową Umiejętność",
-			"description": "Zdefiniuj nowy szablon umiejętności, który dostarcza kontekstowe instrukcje dla agenta.",
 			"nameLabel": "Nazwa",
 			"namePlaceholder": "moja-nazwa-umiejetnosci",
-			"nameHint": "Tylko małe litery, cyfry i myślniki (1-64 znaki)",
 			"descriptionLabel": "Opis",
 			"descriptionPlaceholder": "Opisz, kiedy ta umiejętność powinna być użyta...",
-			"descriptionHint": "Wyjaśnij, co robi ta umiejętność i kiedy agent powinien ją zastosować (1-1024 znaki)",
 			"sourceLabel": "Lokalizacja",
-			"sourceHint": "Wybierz, czy ta umiejętność jest dostępna globalnie, czy tylko w tym projekcie",
 			"modeLabel": "Tryb (opcjonalnie)",
 			"modePlaceholder": "Dowolny tryb",
 			"modeHint": "Ogranicz tę umiejętność do określonego trybu",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "Nazwa musi zawierać 1-64 małe litery, cyfry lub myślniki",
 			"descriptionRequired": "Opis jest wymagany",
 			"descriptionTooLong": "Opis musi mieć maksymalnie 1024 znaki"
-		}
+		},
+		"footer": "Twórz własne umiejętności za pomocą trybu Skill Writer, dostępnego w <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Gerencie seus comandos de barra para executar rapidamente fluxos de trabalho e ações personalizadas. <DocsLink>Saiba mais</DocsLink>"
+		"description": "Gerencie seus comandos de barra para executar rapidamente fluxos de trabalho e ações personalizadas. <DocsLink>Saiba mais</DocsLink>",
+		"workspaceCommands": "Comandos do espaço de trabalho",
+		"globalCommands": "Comandos globais",
+		"noWorkspaceCommands": "Sem comandos neste projeto ainda.",
+		"noGlobalCommands": "Sem comandos globais ainda.",
+		"addCommand": "Adicionar comando de barra",
+		"editCommand": "Editar comando",
+		"deleteCommand": "Excluir comando",
+		"deleteDialog": {
+			"title": "Excluir comando",
+			"description": "Tem certeza de que deseja excluir o comando \"{{name}}\"? Esta ação não pode ser desfeita.",
+			"confirm": "Excluir",
+			"cancel": "Cancelar"
+		},
+		"createDialog": {
+			"title": "Criar novo comando de barra",
+			"nameLabel": "Nome",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Apenas letras minúsculas, números, hífens e sublinhados",
+			"sourceLabel": "Localização",
+			"create": "Criar",
+			"cancel": "Cancelar"
+		},
+		"source": {
+			"global": "Global (disponível em todos os espaços de trabalho)",
+			"project": "Espaço de trabalho"
+		},
+		"validation": {
+			"nameRequired": "Nome é obrigatório",
+			"nameTooLong": "O nome deve ter 64 caracteres ou menos",
+			"nameInvalid": "O nome pode conter apenas letras, números, hífens e sublinhados"
+		},
+		"footer": "Use comandos de barra para acesso rápido a prompts e fluxos de trabalho usados com frequência."
 	},
 	"prompts": {
 		"description": "Configure prompts de suporte usados para ações rápidas como melhorar prompts, explicar código e corrigir problemas. Esses prompts ajudam o Roo a fornecer melhor assistência para tarefas comuns de desenvolvimento."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Máximo de Solicitações",
-			"description": "Fazer automaticamente este número de requisições à API antes de pedir aprovação para continuar com a tarefa.",
 			"unlimited": "Ilimitado"
 		},
 		"selectOptionsFirst": "Selecione pelo menos uma opção abaixo para habilitar a aprovação automática",
@@ -665,7 +696,6 @@
 				"label": "Máximo de mensagens de diagnóstico",
 				"description": "Número máximo de mensagens de diagnóstico a serem incluídas por arquivo. Este limite se aplica tanto à inclusão automática (quando a caixa de seleção está ativada) quanto às menções manuais de @problems. Valores mais altos fornecem mais contexto, mas aumentam o uso de tokens.",
 				"resetTooltip": "Redefinir para o valor padrão (50)",
-				"unlimited": "Mensagens de diagnóstico ilimitadas",
 				"unlimitedLabel": "Ilimitado"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "Você pode ajustar as configurações detalhadas do modelo mais tarde."
 	},
 	"footer": {
-		"feedback": "Se tiver alguma dúvida ou feedback, sinta-se à vontade para abrir um problema em <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> ou juntar-se a <redditLink>reddit.com/r/RooCode</redditLink> ou <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Permitir relatórios anônimos de erros e uso",
 			"description": "Ajude a melhorar o Roo Code enviando dados de uso anônimos e relatórios de erros. Esta telemetria não coleta código, prompts ou informações pessoais. Consulte nossa <privacyLink>política de privacidade</privacyLink> para mais detalhes."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Gerencie skills que fornecem instruções contextuais ao agente. As skills são aplicadas automaticamente quando relevantes para suas tarefas. <DocsLink>Saiba mais</DocsLink>",
-		"projectSkills": "Skills do Projeto",
+		"workspaceSkills": "Skills do espaço de trabalho",
 		"globalSkills": "Skills Globais",
-		"noProjectSkills": "Nenhuma skill de projeto configurada. Crie uma para adicionar capacidades específicas do projeto ao agente.",
+		"noWorkspaceSkills": "Sem skills neste projeto ainda.",
 		"noGlobalSkills": "Nenhuma skill global configurada. Crie uma para adicionar capacidades ao agente disponíveis em todos os projetos.",
 		"addSkill": "Adicionar Skill",
 		"editSkill": "Editar skill",
 		"deleteSkill": "Excluir skill",
-		"changeMode": "Alterar modo",
+		"configureModes": "Disponibilidade de modo",
 		"modeAny": "Qualquer modo",
+		"modeCount": "{{count}} modos",
 		"deleteDialog": {
 			"title": "Excluir Skill",
 			"description": "Tem certeza de que deseja excluir a skill \"{{name}}\"? Esta ação não pode ser desfeita.",
 			"confirm": "Excluir",
 			"cancel": "Cancelar"
 		},
+		"modeDialog": {
+			"title": "Configurar modos de Skill",
+			"description": "Escolha quais modos podem usar esta skill",
+			"intro": "Para manter seu contexto leve, recomendamos tornar skills disponíveis apenas para os modos que os precisam.",
+			"anyMode": "Qualquer modo (disponível em qualquer lugar)",
+			"save": "Salvar",
+			"cancel": "Cancelar"
+		},
 		"createDialog": {
 			"title": "Criar Nova Skill",
-			"description": "Defina um novo modelo de skill que fornece instruções contextuais ao agente.",
 			"nameLabel": "Nome",
 			"namePlaceholder": "meu-nome-de-skill",
-			"nameHint": "Apenas letras minúsculas, números e hífens (1-64 caracteres)",
 			"descriptionLabel": "Descrição",
 			"descriptionPlaceholder": "Descreva quando esta skill deve ser usada...",
-			"descriptionHint": "Explique o que esta skill faz e quando o agente deve aplicá-la (1-1024 caracteres)",
 			"sourceLabel": "Localização",
-			"sourceHint": "Escolha se esta skill está disponível globalmente ou apenas neste projeto",
 			"modeLabel": "Modo (opcional)",
 			"modePlaceholder": "Qualquer modo",
 			"modeHint": "Restrinja esta skill a um modo específico",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "O nome deve ter 1-64 letras minúsculas, números ou hífens",
 			"descriptionRequired": "A descrição é obrigatória",
 			"descriptionTooLong": "A descrição deve ter no máximo 1024 caracteres"
-		}
+		},
+		"footer": "Crie suas próprias skills com o modo Skill Writer, disponível em <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/ru/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Управляйте своими слэш-командами для быстрого выполнения пользовательских рабочих процессов и действий. <DocsLink>Узнать больше</DocsLink>"
+		"description": "Управляйте своими слэш-командами для быстрого выполнения пользовательских рабочих процессов и действий. <DocsLink>Узнать больше</DocsLink>",
+		"workspaceCommands": "Команды рабочего пространства",
+		"globalCommands": "Глобальные команды",
+		"noWorkspaceCommands": "Команды в этом проекте еще не добавлены.",
+		"noGlobalCommands": "Глобальные команды еще не добавлены.",
+		"addCommand": "Добавить слэш-команду",
+		"editCommand": "Редактировать команду",
+		"deleteCommand": "Удалить команду",
+		"deleteDialog": {
+			"title": "Удалить команду",
+			"description": "Вы уверены, что хотите удалить команду \"{{name}}\"? Это действие невозможно отменить.",
+			"confirm": "Удалить",
+			"cancel": "Отмена"
+		},
+		"createDialog": {
+			"title": "Создать новую слэш-команду",
+			"nameLabel": "Имя",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Только строчные буквы, цифры, дефисы и подчеркивания",
+			"sourceLabel": "Место",
+			"create": "Создать",
+			"cancel": "Отмена"
+		},
+		"source": {
+			"global": "Глобальное (доступно во всех рабочих пространствах)",
+			"project": "Рабочее пространство"
+		},
+		"validation": {
+			"nameRequired": "Имя обязательно",
+			"nameTooLong": "Имя должно быть не более 64 символов",
+			"nameInvalid": "Имя может содержать только буквы, цифры, дефисы и подчеркивания"
+		},
+		"footer": "Используйте слэш-команды для быстрого доступа к часто используемым подсказкам и рабочим процессам."
 	},
 	"prompts": {
 		"description": "Настройте промпты поддержки, используемые для быстрых действий, таких как улучшение промптов, объяснение кода и исправление проблем. Эти промпты помогают Roo обеспечить лучшую поддержку для общих задач разработки."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Максимум запросов",
-			"description": "Автоматически выполнять это количество API-запросов перед запросом разрешения на продолжение задачи.",
 			"unlimited": "Без ограничений"
 		},
 		"selectOptionsFirst": "Выберите хотя бы один вариант ниже, чтобы включить автоодобрение",
@@ -665,7 +696,6 @@
 				"label": "Максимальное количество диагностических сообщений",
 				"description": "Максимальное количество диагностических сообщений для включения в файл. Этот лимит применяется как к автоматическому включению (когда флажок включен), так и к ручным упоминаниям @problems. Более высокие значения предоставляют больше контекста, но увеличивают использование токенов.",
 				"resetTooltip": "Сбросить к значению по умолчанию (50)",
-				"unlimited": "Неограниченные диагностические сообщения",
 				"unlimitedLabel": "Неограниченно"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "Ты сможешь настроить подробные параметры модели позже."
 	},
 	"footer": {
-		"feedback": "Если у вас есть вопросы или предложения, откройте issue на <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> или присоединяйтесь к <redditLink>reddit.com/r/RooCode</redditLink> или <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Разрешить анонимную отправку ошибок и статистики использования",
 			"description": "Помогите улучшить Roo Code, отправляя анонимные данные об использовании и отчеты об ошибках. Эта телеметрия не собирает код, промпты или личную информацию. Смотрите нашу <privacyLink>политику конфиденциальности</privacyLink> для получения подробной информации."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Управляйте навыками, которые предоставляют контекстные инструкции агенту. Навыки автоматически применяются, когда они релевантны вашим задачам. <DocsLink>Узнать больше</DocsLink>",
-		"projectSkills": "Навыки Проекта",
+		"workspaceSkills": "Навыки рабочего пространства",
 		"globalSkills": "Глобальные Навыки",
-		"noProjectSkills": "Навыки проекта не настроены. Создайте навык, чтобы добавить возможности агента для конкретного проекта.",
+		"noWorkspaceSkills": "Навыки в этом проекте еще не добавлены.",
 		"noGlobalSkills": "Глобальные навыки не настроены. Создайте навык, чтобы добавить возможности агента, доступные во всех проектах.",
 		"addSkill": "Добавить Навык",
 		"editSkill": "Редактировать навык",
 		"deleteSkill": "Удалить навык",
-		"changeMode": "Изменить режим",
+		"configureModes": "Доступность режима",
 		"modeAny": "Любой режим",
+		"modeCount": "{{count}} режимов",
 		"deleteDialog": {
 			"title": "Удалить Навык",
 			"description": "Вы уверены, что хотите удалить навык \"{{name}}\"? Это действие нельзя отменить.",
 			"confirm": "Удалить",
 			"cancel": "Отмена"
 		},
+		"modeDialog": {
+			"title": "Настроить режимы навыка",
+			"description": "Выберите, какие режимы могут использовать этот навык",
+			"intro": "Чтобы сохранить контекст легким, мы рекомендуем делать навыки доступными только для режимов, которые их требуют.",
+			"anyMode": "Любой режим (доступно везде)",
+			"save": "Сохранить",
+			"cancel": "Отмена"
+		},
 		"createDialog": {
 			"title": "Создать Новый Навык",
-			"description": "Определите новый шаблон навыка, который предоставляет контекстные инструкции агенту.",
 			"nameLabel": "Имя",
 			"namePlaceholder": "my-skill-name",
-			"nameHint": "Только строчные буквы, цифры и дефисы (1-64 символа)",
 			"descriptionLabel": "Описание",
 			"descriptionPlaceholder": "Опишите, когда следует использовать этот навык...",
-			"descriptionHint": "Объясните, что делает этот навык и когда агент должен его применять (1-1024 символа)",
 			"sourceLabel": "Расположение",
-			"sourceHint": "Выберите, доступен ли этот навык глобально или только в этом проекте",
 			"modeLabel": "Режим (необязательно)",
 			"modePlaceholder": "Любой режим",
 			"modeHint": "Ограничьте этот навык определенным режимом",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "Имя должно содержать 1-64 строчные буквы, цифры или дефисы",
 			"descriptionRequired": "Описание обязательно",
 			"descriptionTooLong": "Описание должно быть не более 1024 символов"
-		}
+		},
+		"footer": "Создавайте собственные навыки с режимом Skill Writer, доступным в <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/tr/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Özel iş akışlarını ve eylemleri hızlı bir şekilde yürütmek için eğik çizgi komutlarınızı yönetin. <DocsLink>Daha fazla bilgi edinin</DocsLink>"
+		"description": "Özel iş akışlarını ve eylemleri hızlı bir şekilde yürütmek için eğik çizgi komutlarınızı yönetin. <DocsLink>Daha fazla bilgi edinin</DocsLink>",
+		"workspaceCommands": "Çalışma Alanı Komutları",
+		"globalCommands": "Genel Komutlar",
+		"noWorkspaceCommands": "Bu projede henüz komut yok.",
+		"noGlobalCommands": "Henüz genel komut yok.",
+		"addCommand": "Eğik Çizgi Komutu Ekle",
+		"editCommand": "Komutu düzenle",
+		"deleteCommand": "Komutu sil",
+		"deleteDialog": {
+			"title": "Komutu sil",
+			"description": "\"{{name}}\" komutunu silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
+			"confirm": "Sil",
+			"cancel": "İptal"
+		},
+		"createDialog": {
+			"title": "Yeni Eğik Çizgi Komutu Oluştur",
+			"nameLabel": "Ad",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Yalnızca küçük harfler, sayılar, tireleme ve alt çizgi",
+			"sourceLabel": "Konum",
+			"create": "Oluştur",
+			"cancel": "İptal"
+		},
+		"source": {
+			"global": "Genel (tüm çalışma alanlarında mevcut)",
+			"project": "Çalışma Alanı"
+		},
+		"validation": {
+			"nameRequired": "Ad gereklidir",
+			"nameTooLong": "Ad en fazla 64 karakter olmalıdır",
+			"nameInvalid": "Ad yalnızca harfleri, sayıları, tirelemeyi ve alt çizgiyi içerebilir"
+		},
+		"footer": "Sık kullanılan komutlara ve iş akışlarına hızlı erişim için eğik çizgi komutlarını kullanın."
 	},
 	"prompts": {
 		"description": "Prompt geliştirme, kod açıklama ve sorun çözme gibi hızlı eylemler için kullanılan destek promptlarını yapılandırın. Bu promptlar, Roo'nun yaygın geliştirme görevleri için daha iyi destek sağlamasına yardımcı olur."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Maksimum İstek",
-			"description": "Göreve devam etmek için onay istemeden önce bu sayıda API isteği otomatik olarak yap.",
 			"unlimited": "Sınırsız"
 		},
 		"selectOptionsFirst": "Otomatik onayı etkinleştirmek için aşağıdan en az bir seçenek seçin",
@@ -665,7 +696,6 @@
 				"label": "Maksimum tanı mesajı",
 				"description": "Dosya başına dahil edilecek maksimum tanı mesajı sayısı. Bu sınır hem otomatik dahil etme (onay kutusu etkinleştirildiğinde) hem de manuel @problems bahisleri için geçerlidir. Daha yüksek değerler daha fazla bağlam sağlar ancak jeton kullanımını artırır.",
 				"resetTooltip": "Varsayılan değere sıfırla (50)",
-				"unlimited": "Sınırsız tanı mesajları",
 				"unlimitedLabel": "Sınırsız"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "Ayrıntılı model ayarlarını daha sonra ayarlayabilirsiniz."
 	},
 	"footer": {
-		"feedback": "Herhangi bir sorunuz veya geri bildiriminiz varsa, <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> adresinde bir konu açmaktan veya <redditLink>reddit.com/r/RooCode</redditLink> ya da <discordLink>discord.gg/roocode</discordLink>'a katılmaktan çekinmeyin",
 		"telemetry": {
 			"label": "Anonim hata ve kullanım raporlamaya izin ver",
 			"description": "Anonim kullanım verileri ve hata raporları göndererek Roo Code'u geliştirmeye yardım edin. Bu telemetri kod, prompt veya kişisel bilgi toplamaz. Daha fazla ayrıntı için <privacyLink>gizlilik politikamıza</privacyLink> bakın. Bunu istediğiniz zaman kapatabilirsiniz."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Ajana bağlamsal talimatlar sağlayan becerileri yönetin. Beceriler, görevlerinizle ilgili olduklarında otomatik olarak uygulanır. <DocsLink>Daha fazla bilgi</DocsLink>",
-		"projectSkills": "Proje Becerileri",
+		"workspaceSkills": "Çalışma Alanı Becerileri",
 		"globalSkills": "Genel Beceriler",
-		"noProjectSkills": "Yapılandırılmış proje becerisi yok. Projeye özgü ajan yetenekleri eklemek için bir tane oluşturun.",
+		"noWorkspaceSkills": "Bu projede henüz beceri yok.",
 		"noGlobalSkills": "Yapılandırılmış genel beceri yok. Tüm projelerde kullanılabilir ajan yetenekleri eklemek için bir tane oluşturun.",
 		"addSkill": "Beceri Ekle",
 		"editSkill": "Beceriyi düzenle",
 		"deleteSkill": "Beceriyi sil",
-		"changeMode": "Modu değiştir",
+		"configureModes": "Mod Kullanılabilirliği",
 		"modeAny": "Herhangi bir mod",
+		"modeCount": "{{count}} mod",
 		"deleteDialog": {
 			"title": "Beceriyi Sil",
 			"description": "\"{{name}}\" becerisini silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
 			"confirm": "Sil",
 			"cancel": "İptal"
 		},
+		"modeDialog": {
+			"title": "Beceri Modlarını Yapılandır",
+			"description": "Bu beceriyi kullanabilecek modları seçin",
+			"intro": "Bağlamınızı hafif tutmak için, becerileri yalnızca onlara ihtiyaç duyan modlar için kullanılabilir hale getirilmesini öneririz.",
+			"anyMode": "Herhangi bir mod (her yerde mevcut)",
+			"save": "Kaydet",
+			"cancel": "İptal"
+		},
 		"createDialog": {
 			"title": "Yeni Beceri Oluştur",
-			"description": "Ajana bağlamsal talimatlar sağlayan yeni bir beceri şablonu tanımlayın.",
 			"nameLabel": "Ad",
 			"namePlaceholder": "benim-beceri-adim",
-			"nameHint": "Yalnızca küçük harfler, rakamlar ve kısa çizgiler (1-64 karakter)",
 			"descriptionLabel": "Açıklama",
 			"descriptionPlaceholder": "Bu becerinin ne zaman kullanılması gerektiğini açıklayın...",
-			"descriptionHint": "Bu becerinin ne yaptığını ve ajanın ne zaman uygulaması gerektiğini açıklayın (1-1024 karakter)",
 			"sourceLabel": "Konum",
-			"sourceHint": "Bu becerinin genel olarak mı yoksa yalnızca bu projede mi kullanılabilir olduğunu seçin",
 			"modeLabel": "Mod (isteğe bağlı)",
 			"modePlaceholder": "Herhangi bir mod",
 			"modeHint": "Bu beceriyi belirli bir modla sınırlayın",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "Ad 1-64 küçük harf, rakam veya kısa çizgi olmalıdır",
 			"descriptionRequired": "Açıklama gereklidir",
 			"descriptionTooLong": "Açıklama en fazla 1024 karakter olmalıdır"
-		}
+		},
+		"footer": "Skill Writer modu ile kendi becerilerinizi oluşturun. <MarketplaceLink>Modes Marketplace</MarketplaceLink>'de mevcut."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/vi/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "Quản lý các lệnh slash của bạn để thực thi nhanh các quy trình công việc và hành động tùy chỉnh. <DocsLink>Tìm hiểu thêm</DocsLink>"
+		"description": "Quản lý các lệnh slash của bạn để thực thi nhanh các quy trình công việc và hành động tùy chỉnh. <DocsLink>Tìm hiểu thêm</DocsLink>",
+		"workspaceCommands": "Lệnh không gian làm việc",
+		"globalCommands": "Lệnh toàn cầu",
+		"noWorkspaceCommands": "Chưa có lệnh trong dự án này.",
+		"noGlobalCommands": "Chưa có lệnh toàn cầu.",
+		"addCommand": "Thêm lệnh Slash",
+		"editCommand": "Chỉnh sửa lệnh",
+		"deleteCommand": "Xóa lệnh",
+		"deleteDialog": {
+			"title": "Xóa lệnh",
+			"description": "Bạn có chắc chắn muốn xóa lệnh \"{{name}}\" không? Không thể hoàn tác hành động này.",
+			"confirm": "Xóa",
+			"cancel": "Hủy"
+		},
+		"createDialog": {
+			"title": "Tạo lệnh Slash mới",
+			"nameLabel": "Tên",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "Chỉ chữ cái thường, số, dấu gạch ngang và dấu gạch dưới",
+			"sourceLabel": "Vị trí",
+			"create": "Tạo",
+			"cancel": "Hủy"
+		},
+		"source": {
+			"global": "Toàn cầu (có sẵn ở tất cả các không gian làm việc)",
+			"project": "Không gian làm việc"
+		},
+		"validation": {
+			"nameRequired": "Tên là bắt buộc",
+			"nameTooLong": "Tên phải có 64 ký tự hoặc ít hơn",
+			"nameInvalid": "Tên chỉ có thể chứa chữ cái, số, dấu gạch ngang và dấu gạch dưới"
+		},
+		"footer": "Sử dụng lệnh slash để truy cập nhanh các prompt và quy trình công việc được sử dụng thường xuyên."
 	},
 	"prompts": {
 		"description": "Cấu hình các lời nhắc hỗ trợ được sử dụng cho các hành động nhanh như cải thiện lời nhắc, giải thích mã và khắc phục sự cố. Những lời nhắc này giúp Roo cung cấp hỗ trợ tốt hơn cho các tác vụ phát triển phổ biến."
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "Số lượng yêu cầu tối đa",
-			"description": "Tự động thực hiện số lượng API request này trước khi yêu cầu phê duyệt để tiếp tục với nhiệm vụ.",
 			"unlimited": "Không giới hạn"
 		},
 		"selectOptionsFirst": "Chọn ít nhất một tùy chọn bên dưới để bật tự động phê duyệt",
@@ -665,7 +696,6 @@
 				"label": "Thông báo chẩn đoán tối đa",
 				"description": "Số lượng thông báo chẩn đoán tối đa được bao gồm cho mỗi tệp. Giới hạn này áp dụng cho cả việc bao gồm tự động (khi hộp kiểm được bật) và đề cập thủ công @problems. Giá trị cao hơn cung cấp nhiều ngữ cảnh hơn nhưng tăng mức sử dụng token.",
 				"resetTooltip": "Đặt lại về giá trị mặc định (50)",
-				"unlimited": "Thông báo chẩn đoán không giới hạn",
 				"unlimitedLabel": "Không giới hạn"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "Bạn có thể điều chỉnh cài đặt mô hình chi tiết sau."
 	},
 	"footer": {
-		"feedback": "Nếu bạn có bất kỳ câu hỏi hoặc phản hồi nào, vui lòng mở một vấn đề tại <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> hoặc tham gia <redditLink>reddit.com/r/RooCode</redditLink> hoặc <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "Cho phép báo cáo lỗi và sử dụng ẩn danh",
 			"description": "Giúp cải thiện Roo Code bằng cách gửi dữ liệu sử dụng ẩn danh và báo cáo lỗi. Telemetry này không thu thập mã, prompt hoặc thông tin cá nhân. Xem <privacyLink>chính sách bảo mật</privacyLink> của chúng tôi để biết thêm chi tiết. Bạn có thể tắt tính năng này bất cứ lúc nào."
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "Quản lý các skill cung cấp hướng dẫn theo ngữ cảnh cho agent. Các skill được áp dụng tự động khi chúng liên quan đến nhiệm vụ của bạn. <DocsLink>Tìm hiểu thêm</DocsLink>",
-		"projectSkills": "Skills Dự Án",
+		"workspaceSkills": "Kỹ năng không gian làm việc",
 		"globalSkills": "Skills Toàn Cục",
-		"noProjectSkills": "Không có skill dự án nào được cấu hình. Tạo một skill để thêm khả năng agent cụ thể cho dự án.",
+		"noWorkspaceSkills": "Chưa có kỹ năng trong dự án này.",
 		"noGlobalSkills": "Không có skill toàn cục nào được cấu hình. Tạo một skill để thêm khả năng agent có sẵn trong tất cả các dự án.",
 		"addSkill": "Thêm Skill",
 		"editSkill": "Chỉnh sửa skill",
 		"deleteSkill": "Xóa skill",
-		"changeMode": "Thay đổi chế độ",
+		"configureModes": "Tính khả dụng của chế độ",
 		"modeAny": "Bất kỳ chế độ nào",
+		"modeCount": "{{count}} chế độ",
 		"deleteDialog": {
 			"title": "Xóa Skill",
 			"description": "Bạn có chắc chắn muốn xóa skill \"{{name}}\" không? Hành động này không thể hoàn tác.",
 			"confirm": "Xóa",
 			"cancel": "Hủy"
 		},
+		"modeDialog": {
+			"title": "Cấu hình chế độ Skill",
+			"description": "Chọn chế độ nào có thể sử dụng skill này",
+			"intro": "Để giữ bối cảnh của bạn nhẹ, chúng tôi khuyên bạn nên chỉ cung cấp các kỹ năng cho các chế độ cần chúng.",
+			"anyMode": "Bất kỳ chế độ nào (có sẵn ở mọi nơi)",
+			"save": "Lưu",
+			"cancel": "Hủy"
+		},
 		"createDialog": {
 			"title": "Tạo Skill Mới",
-			"description": "Xác định một mẫu skill mới cung cấp hướng dẫn theo ngữ cảnh cho agent.",
 			"nameLabel": "Tên",
 			"namePlaceholder": "ten-skill-cua-toi",
-			"nameHint": "Chỉ chữ thường, số và dấu gạch ngang (1-64 ký tự)",
 			"descriptionLabel": "Mô tả",
 			"descriptionPlaceholder": "Mô tả khi nào nên sử dụng skill này...",
-			"descriptionHint": "Giải thích skill này làm gì và khi nào agent nên áp dụng nó (1-1024 ký tự)",
 			"sourceLabel": "Vị trí",
-			"sourceHint": "Chọn xem skill này có sẵn toàn cục hay chỉ trong dự án này",
 			"modeLabel": "Chế độ (tùy chọn)",
 			"modePlaceholder": "Bất kỳ chế độ nào",
 			"modeHint": "Hạn chế skill này cho một chế độ cụ thể",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "Tên phải là 1-64 chữ thường, số hoặc dấu gạch ngang",
 			"descriptionRequired": "Mô tả là bắt buộc",
 			"descriptionTooLong": "Mô tả phải có tối đa 1024 ký tự"
-		}
+		},
+		"footer": "Tạo các skill của riêng bạn với chế độ Skill Writer, có sẵn tại <MarketplaceLink>Modes Marketplace</MarketplaceLink>."
 	}
 }

+ 47 - 12
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "管理您的斜杠命令,以快速执行自定义工作流和操作。 <DocsLink>了解更多</DocsLink>"
+		"description": "管理您的斜杠命令,以快速执行自定义工作流和操作。 <DocsLink>了解更多</DocsLink>",
+		"workspaceCommands": "工作区命令",
+		"globalCommands": "全局命令",
+		"noWorkspaceCommands": "此项目中还没有命令。",
+		"noGlobalCommands": "还没有全局命令。",
+		"addCommand": "添加斜杠命令",
+		"editCommand": "编辑命令",
+		"deleteCommand": "删除命令",
+		"deleteDialog": {
+			"title": "删除命令",
+			"description": "确定要删除命令 \"{{name}}\" 吗?此操作无法撤销。",
+			"confirm": "删除",
+			"cancel": "取消"
+		},
+		"createDialog": {
+			"title": "创建新斜杠命令",
+			"nameLabel": "名称",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "仅限小写字母、数字、连字符和下划线",
+			"sourceLabel": "位置",
+			"create": "创建",
+			"cancel": "取消"
+		},
+		"source": {
+			"global": "全局(在所有工作区中可用)",
+			"project": "工作区"
+		},
+		"validation": {
+			"nameRequired": "名称为必填项",
+			"nameTooLong": "名称必须为 64 个字符或更少",
+			"nameInvalid": "名称只能包含字母、数字、连字符和下划线"
+		},
+		"footer": "使用斜杠命令快速访问经常使用的提示词和工作流。"
 	},
 	"prompts": {
 		"description": "配置用于快速操作的支持提示词,如增强提示词、解释代码和修复问题。这些提示词帮助 Roo 为常见开发任务提供更好的支持。"
@@ -244,7 +276,6 @@
 		},
 		"apiRequestLimit": {
 			"title": "最大请求数",
-			"description": "在请求批准以继续执行任务之前,自动发出此数量的 API 请求。",
 			"unlimited": "无限制"
 		},
 		"selectOptionsFirst": "请至少选择以下一个选项以启用自动批准",
@@ -665,7 +696,6 @@
 				"label": "最大诊断消息数",
 				"description": "每个文件包含的最大诊断消息数。此限制适用于自动包含(启用复选框时)和手动 @problems 提及。较高的值提供更多上下文,但会增加令牌使用量。",
 				"resetTooltip": "重置为默认值 (50)",
-				"unlimited": "无限制诊断消息",
 				"unlimitedLabel": "无限制"
 			},
 			"delayAfterWrite": {
@@ -900,7 +930,6 @@
 		"simplifiedExplanation": "你可以稍后调整详细的模型设置。"
 	},
 	"footer": {
-		"feedback": "如果您有任何问题或反馈,请随时在 <githubLink>github.com/RooCodeInc/Roo-Code</githubLink> 上提出问题或加入 <redditLink>reddit.com/r/RooCode</redditLink> 或 <discordLink>discord.gg/roocode</discordLink>",
 		"telemetry": {
 			"label": "允许匿名数据收集",
 			"description": "通过发送匿名使用数据和错误报告来帮助改进 Roo Code。此遥测不会收集代码、提示 或个人信息。详细信息请参阅我们的<privacyLink>隐私政策</privacyLink>。您可以随时关闭此功能。"
@@ -997,32 +1026,37 @@
 	},
 	"skills": {
 		"description": "管理为代理提供上下文指令的技能。技能会在与您的任务相关时自动应用。<DocsLink>了解更多</DocsLink>",
-		"projectSkills": "项目技能",
+		"workspaceSkills": "工作区技能",
 		"globalSkills": "全局技能",
-		"noProjectSkills": "未配置项目技能。创建一个以添加特定于项目的代理功能。",
+		"noWorkspaceSkills": "此项目中还没有技能。",
 		"noGlobalSkills": "未配置全局技能。创建一个以添加在所有项目中可用的代理功能。",
 		"addSkill": "添加技能",
 		"editSkill": "编辑技能",
 		"deleteSkill": "删除技能",
-		"changeMode": "更改模式",
+		"configureModes": "模式可用性",
 		"modeAny": "任意模式",
+		"modeCount": "{{count}} 种模式",
 		"deleteDialog": {
 			"title": "删除技能",
 			"description": "您确定要删除技能\"{{name}}\"吗?此操作无法撤销。",
 			"confirm": "删除",
 			"cancel": "取消"
 		},
+		"modeDialog": {
+			"title": "配置技能模式",
+			"description": "选择哪些模式可以使用此技能",
+			"intro": "为了保持上下文简洁,我们建议只在需要的模式中提供技能。",
+			"anyMode": "任何模式(随处可用)",
+			"save": "保存",
+			"cancel": "取消"
+		},
 		"createDialog": {
 			"title": "创建新技能",
-			"description": "定义一个新的技能模板,为代理提供上下文指令。",
 			"nameLabel": "名称",
 			"namePlaceholder": "my-skill-name",
-			"nameHint": "仅小写字母、数字和连字符(1-64个字符)",
 			"descriptionLabel": "描述",
 			"descriptionPlaceholder": "描述何时应使用此技能...",
-			"descriptionHint": "解释此技能的作用以及代理应何时应用它(1-1024个字符)",
 			"sourceLabel": "位置",
-			"sourceHint": "选择此技能是全局可用还是仅在此项目中可用",
 			"modeLabel": "模式(可选)",
 			"modePlaceholder": "任何模式",
 			"modeHint": "将此技能限制为特定模式",
@@ -1040,6 +1074,7 @@
 			"nameInvalid": "名称必须为1-64个小写字母、数字或连字符",
 			"descriptionRequired": "描述为必填项",
 			"descriptionTooLong": "描述不得超过1024个字符"
-		}
+		},
+		"footer": "使用技能编写器模式创建您自己的技能,可在 <MarketplaceLink>模式市集</MarketplaceLink> 中获得。"
 	}
 }

+ 47 - 19
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -69,7 +69,39 @@
 		}
 	},
 	"slashCommands": {
-		"description": "管理您的斜線命令,以便快速執行自訂工作流程和動作。 <DocsLink>了解更多</DocsLink>"
+		"description": "管理您的斜線命令,以便快速執行自訂工作流程和動作。 <DocsLink>了解更多</DocsLink>",
+		"workspaceCommands": "工作區命令",
+		"globalCommands": "全域命令",
+		"noWorkspaceCommands": "此專案中還沒有命令。",
+		"noGlobalCommands": "還沒有全域命令。",
+		"addCommand": "新增斜線命令",
+		"editCommand": "編輯命令",
+		"deleteCommand": "刪除命令",
+		"deleteDialog": {
+			"title": "刪除命令",
+			"description": "您確定要刪除命令 \"{{name}}\" 嗎?此動作無法復原。",
+			"confirm": "刪除",
+			"cancel": "取消"
+		},
+		"createDialog": {
+			"title": "建立新斜線命令",
+			"nameLabel": "名稱",
+			"namePlaceholder": "my-command-name",
+			"nameHint": "僅限小寫字母、數字、連字號和底線",
+			"sourceLabel": "位置",
+			"create": "建立",
+			"cancel": "取消"
+		},
+		"source": {
+			"global": "全域(可在所有工作區中使用)",
+			"project": "工作區"
+		},
+		"validation": {
+			"nameRequired": "名稱為必填項",
+			"nameTooLong": "名稱必須為 64 個字元或更少",
+			"nameInvalid": "名稱只能包含字母、數字、連字號和底線"
+		},
+		"footer": "使用斜線命令快速存取經常使用的提示詞和工作流程。"
 	},
 	"ui": {
 		"collapseThinking": {
@@ -992,44 +1024,39 @@
 			"cacheReads": "快取讀取"
 		}
 	},
-	"ui": {
-		"collapseThinking": {
-			"label": "預設折疊「思考」訊息",
-			"description": "啟用後,「思考」塊將預設折疊,直到您與其互動"
-		},
-		"requireCtrlEnterToSend": {
-			"label": "需要 {{primaryMod}}+Enter 傳送訊息",
-			"description": "啟用後,必須按 {{primaryMod}}+Enter 傳送訊息,而不只是 Enter"
-		}
-	},
 	"skills": {
 		"description": "管理為代理提供上下文指令的技能。技能會在與您的任務相關時自動套用。<DocsLink>深入了解</DocsLink>",
-		"projectSkills": "專案技能",
+		"workspaceSkills": "工作區技能",
 		"globalSkills": "全域技能",
-		"noProjectSkills": "未設定專案技能。建立一個以新增專案特定的代理功能。",
+		"noWorkspaceSkills": "此專案中還沒有技能。",
 		"noGlobalSkills": "未設定全域技能。建立一個以新增在所有專案中可用的代理功能。",
 		"addSkill": "新增技能",
 		"editSkill": "編輯技能",
 		"deleteSkill": "刪除技能",
-		"changeMode": "變更模式",
+		"configureModes": "模式可用性",
 		"modeAny": "任意模式",
+		"modeCount": "{{count}} 種模式",
 		"deleteDialog": {
 			"title": "刪除技能",
 			"description": "您確定要刪除技能「{{name}}」嗎?此動作無法復原。",
 			"confirm": "刪除",
 			"cancel": "取消"
 		},
+		"modeDialog": {
+			"title": "設定技能模式",
+			"description": "選擇哪些模式可以使用此技能",
+			"intro": "為了保持您的上下文簡潔,我們建議只在需要的模式中提供技能。",
+			"anyMode": "任何模式(隨處可用)",
+			"save": "儲存",
+			"cancel": "取消"
+		},
 		"createDialog": {
 			"title": "建立新技能",
-			"description": "定義新的技能範本,為代理提供上下文指令。",
 			"nameLabel": "名稱",
 			"namePlaceholder": "my-skill-name",
-			"nameHint": "僅限小寫字母、數字和連字號(1-64個字元)",
 			"descriptionLabel": "說明",
 			"descriptionPlaceholder": "描述何時應使用此技能...",
-			"descriptionHint": "說明此技能的作用以及代理應何時套用它(1-1024個字元)",
 			"sourceLabel": "位置",
-			"sourceHint": "選擇此技能是全域可用還是僅在此專案中可用",
 			"modeLabel": "模式(選填)",
 			"modePlaceholder": "任何模式",
 			"modeHint": "將此技能限制為特定模式",
@@ -1047,6 +1074,7 @@
 			"nameInvalid": "名稱必須為1-64個小寫字母、數字或連字號",
 			"descriptionRequired": "說明為必填",
 			"descriptionTooLong": "說明不得超過1024個字元"
-		}
+		},
+		"footer": "使用技能編寫器模式建立您自己的技能,可在 <MarketplaceLink>模式市集</MarketplaceLink> 中取得。"
 	}
 }