Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/types/src/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
export interface SkillMetadata {
name: string // Required: skill identifier
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
path: string // Absolute path to SKILL.md
source: "global" | "project" // Where the skill was discovered
/**
* @deprecated Use modeSlugs instead. Kept for backward compatibility.
* If set, skill is only available in this mode.
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ export interface WebviewMessage {
modeConfig?: ModeConfig
timeout?: number
payload?: WebViewMessagePayload
source?: "global" | "project" | "built-in"
source?: "global" | "project"
skillName?: string // For skill operations (createSkill, deleteSkill, moveSkill, openSkillFile)
/** @deprecated Use skillModeSlugs instead */
skillMode?: string // For skill operations (current mode restriction)
Expand Down
2 changes: 1 addition & 1 deletion src/core/auto-approval/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export async function checkAutoApproval({
return { decision: "approve" }
}

// The skill tool only loads pre-defined instructions from built-in, global, or project skills.
// The skill tool only loads pre-defined instructions from global or project skills.
// It does not read arbitrary files - skills must be explicitly installed/defined by the user.
// Auto-approval is intentional to provide a seamless experience when loading task instructions.
if (tool.tool === "skill") {
Expand Down
5 changes: 1 addition & 4 deletions src/core/prompts/sections/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ export async function getSkillsSection(
.map((skill) => {
const name = escapeXml(skill.name)
const description = escapeXml(skill.description)
// Only include location for file-based skills (not built-in)
// Built-in skills are loaded via the skill tool by name, not by path
const isFileBasedSkill = skill.source !== "built-in" && skill.path !== "built-in"
const locationLine = isFileBasedSkill ? `\n <location>${escapeXml(skill.path)}</location>` : ""
const locationLine = `\n <location>${escapeXml(skill.path)}</location>`
return ` <skill>\n <name>${name}</name>\n <description>${description}</description>${locationLine}\n </skill>`
})
.join("\n")
Expand Down
16 changes: 8 additions & 8 deletions src/core/tools/__tests__/skillTool.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe("skillTool", () => {
)
})

it("should successfully load built-in skill", async () => {
it("should successfully load a global skill", async () => {
const block: ToolUse<"skill"> = {
type: "tool_use" as const,
name: "skill" as const,
Expand All @@ -113,7 +113,7 @@ describe("skillTool", () => {
const mockSkillContent = {
name: "create-mcp-server",
description: "Instructions for creating MCP servers",
source: "built-in",
source: "global",
instructions: "Step 1: Create the server...",
}

Expand All @@ -127,15 +127,15 @@ describe("skillTool", () => {
tool: "skill",
skill: "create-mcp-server",
args: undefined,
source: "built-in",
source: "global",
description: "Instructions for creating MCP servers",
}),
)

expect(mockCallbacks.pushToolResult).toHaveBeenCalledWith(
`Skill: create-mcp-server
Description: Instructions for creating MCP servers
Source: built-in
Source: global

--- Skill Instructions ---

Expand All @@ -158,7 +158,7 @@ Step 1: Create the server...`,
const mockSkillContent = {
name: "create-mcp-server",
description: "Instructions for creating MCP servers",
source: "built-in",
source: "global",
instructions: "Step 1: Create the server...",
}

Expand All @@ -170,7 +170,7 @@ Step 1: Create the server...`,
`Skill: create-mcp-server
Description: Instructions for creating MCP servers
Provided arguments: weather API server
Source: built-in
Source: global

--- Skill Instructions ---

Expand All @@ -192,7 +192,7 @@ Step 1: Create the server...`,
mockSkillsManager.getSkillContent.mockResolvedValue({
name: "create-mcp-server",
description: "Test",
source: "built-in",
source: "global",
instructions: "Test instructions",
})

Expand Down Expand Up @@ -264,7 +264,7 @@ Step 1: Create the server...`,
const mockSkillContent = {
name: "create-mcp-server",
description: "Test",
source: "built-in",
source: "global",
instructions: "Test instructions",
}

Expand Down
20 changes: 0 additions & 20 deletions src/core/webview/__tests__/skillsMessageHandler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ vi.mock("../../../i18n", () => ({
"skills:errors.missing_delete_fields": "Missing required fields: skillName or source",
"skills:errors.missing_move_fields": "Missing required fields: skillName or source",
"skills:errors.skill_not_found": `Skill "${params?.name}" not found`,
"skills:errors.cannot_modify_builtin": "Built-in skills cannot be created, deleted, or moved",
}
return translations[key] || key
},
Expand Down Expand Up @@ -333,25 +332,6 @@ describe("skillsMessageHandler", () => {
"Failed to move skill: Skills manager not available",
)
})

it("returns undefined when trying to move a built-in skill", async () => {
const provider = createMockProvider(true)

const result = await handleMoveSkill(provider, {
type: "moveSkill",
skillName: "test-skill",
source: "built-in",
newSkillMode: "code",
} as WebviewMessage)

expect(result).toBeUndefined()
expect(mockLog).toHaveBeenCalledWith(
"Error moving skill: Built-in skills cannot be created, deleted, or moved",
)
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
"Failed to move skill: Built-in skills cannot be created, deleted, or moved",
)
})
})

describe("handleOpenSkillFile", () => {
Expand Down
37 changes: 7 additions & 30 deletions src/core/webview/skillsMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { ClineProvider } from "./ClineProvider"
import { openFile } from "../../integrations/misc/open-file"
import { t } from "../../i18n"

type SkillSource = SkillMetadata["source"]

/**
* Handles the requestSkills message - returns all skills metadata
*/
Expand Down Expand Up @@ -36,7 +38,7 @@ export async function handleCreateSkill(
): Promise<SkillMetadata[] | undefined> {
try {
const skillName = message.skillName
const source = message.source
const source = message.source as SkillSource
const skillDescription = message.skillDescription
// Support new modeSlugs array or fall back to legacy skillMode
const modeSlugs = message.skillModeSlugs ?? (message.skillMode ? [message.skillMode] : undefined)
Expand All @@ -45,11 +47,6 @@ export async function handleCreateSkill(
throw new Error(t("skills:errors.missing_create_fields"))
}

// Built-in skills cannot be created
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"))
Expand Down Expand Up @@ -81,19 +78,14 @@ export async function handleDeleteSkill(
): Promise<SkillMetadata[] | undefined> {
try {
const skillName = message.skillName
const source = message.source
const source = message.source as SkillSource
// 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"))
}

// Built-in skills cannot be deleted
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"))
Expand Down Expand Up @@ -122,19 +114,14 @@ export async function handleMoveSkill(
): Promise<SkillMetadata[] | undefined> {
try {
const skillName = message.skillName
const source = message.source
const source = message.source as SkillSource
const currentMode = message.skillMode
const newMode = message.newSkillMode

if (!skillName || !source) {
throw new Error(t("skills:errors.missing_move_fields"))
}

// Built-in skills cannot be moved
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"))
Expand Down Expand Up @@ -163,18 +150,13 @@ export async function handleUpdateSkillModes(
): Promise<SkillMetadata[] | undefined> {
try {
const skillName = message.skillName
const source = message.source
const source = message.source as SkillSource
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"))
Expand All @@ -200,17 +182,12 @@ export async function handleUpdateSkillModes(
export async function handleOpenSkillFile(provider: ClineProvider, message: WebviewMessage): Promise<void> {
try {
const skillName = message.skillName
const source = message.source
const source = message.source as SkillSource

if (!skillName || !source) {
throw new Error(t("skills:errors.missing_delete_fields"))
}

// Built-in skills cannot be opened as files (they have no file path)
if (source === "built-in") {
throw new Error(t("skills:errors.cannot_open_builtin"))
}

const skillsManager = provider.getSkillsManager()
if (!skillsManager) {
throw new Error(t("skills:errors.manager_unavailable"))
Expand Down
4 changes: 1 addition & 3 deletions src/i18n/locales/ca/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/de/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/en/skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
"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",
"cannot_modify_builtin": "Built-in skills cannot be created, deleted, or moved",
"cannot_open_builtin": "Built-in skills cannot be opened as files"
"skill_not_found": "Skill \"{{name}}\" not found"
}
}
4 changes: 1 addition & 3 deletions src/i18n/locales/es/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/fr/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/hi/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/id/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/it/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/ja/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/ko/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/nl/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/pl/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/pt-BR/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/i18n/locales/ru/skills.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading