From 45a850afc02fcfd227234efcebe9ba46966d7f94 Mon Sep 17 00:00:00 2001 From: ismeth Date: Fri, 20 Feb 2026 21:53:05 +0100 Subject: [PATCH] fix: enforce directory param in skill resolution, replace legacy k2p5 model ID - Make directory required in SkillLoadOptions, getAllSkills, and async skill template resolvers to prevent unsafe process.cwd() fallback - Remove dead skill export and process.cwd() fallback in skill tool - Replace kimi-for-coding/k2p5 with kimi-for-coding/kimi-k2.5 in council-members-generator --- src/cli/council-members-generator.ts | 2 +- src/features/opencode-skill-loader/skill-content.test.ts | 8 ++++---- src/features/opencode-skill-loader/skill-discovery.ts | 4 ++-- .../opencode-skill-loader/skill-resolution-options.ts | 2 +- .../opencode-skill-loader/skill-template-resolver.ts | 4 ++-- src/tools/delegate-task/skill-resolver.ts | 2 +- src/tools/skill/index.ts | 2 +- src/tools/skill/tools.ts | 6 ++---- src/tools/skill/types.ts | 4 ++-- 9 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/cli/council-members-generator.ts b/src/cli/council-members-generator.ts index 66ad95720..2f6c25ef1 100644 --- a/src/cli/council-members-generator.ts +++ b/src/cli/council-members-generator.ts @@ -27,7 +27,7 @@ const COUNCIL_CANDIDATES: Array<{ }, { provider: (a) => a.kimiForCoding, - model: "kimi-for-coding/k2p5", + model: "kimi-for-coding/kimi-k2.5", name: "Kimi 2.5", } ] diff --git a/src/features/opencode-skill-loader/skill-content.test.ts b/src/features/opencode-skill-loader/skill-content.test.ts index 506de215b..78f1983af 100644 --- a/src/features/opencode-skill-loader/skill-content.test.ts +++ b/src/features/opencode-skill-loader/skill-content.test.ts @@ -185,7 +185,7 @@ describe("resolveMultipleSkillsAsync", () => { const skillNames = ["playwright", "git-master"] // when: resolving multiple skills async - const result = await resolveMultipleSkillsAsync(skillNames) + const result = await resolveMultipleSkillsAsync(skillNames, { directory: process.cwd() }) // then: all builtin skills resolved expect(result.resolved.size).toBe(2) @@ -199,7 +199,7 @@ describe("resolveMultipleSkillsAsync", () => { const skillNames = ["playwright", "nonexistent-skill-12345"] // when: resolving multiple skills async - const result = await resolveMultipleSkillsAsync(skillNames) + const result = await resolveMultipleSkillsAsync(skillNames, { directory: process.cwd() }) // then: existing skills resolved, non-existing in notFound expect(result.resolved.size).toBe(1) @@ -286,7 +286,7 @@ describe("resolveMultipleSkillsAsync", () => { const skillNames = ["git-master"] // when: resolving without any gitMasterConfig - const result = await resolveMultipleSkillsAsync(skillNames) + const result = await resolveMultipleSkillsAsync(skillNames, { directory: process.cwd() }) // then: watermark is injected (default is ON) expect(result.resolved.size).toBe(1) @@ -357,7 +357,7 @@ describe("resolveMultipleSkillsAsync", () => { const skillNames: string[] = [] // when: resolving multiple skills async - const result = await resolveMultipleSkillsAsync(skillNames) + const result = await resolveMultipleSkillsAsync(skillNames, { directory: process.cwd() }) // then: empty results expect(result.resolved.size).toBe(0) diff --git a/src/features/opencode-skill-loader/skill-discovery.ts b/src/features/opencode-skill-loader/skill-discovery.ts index 2ee2cbca3..2dab0c6c9 100644 --- a/src/features/opencode-skill-loader/skill-discovery.ts +++ b/src/features/opencode-skill-loader/skill-discovery.ts @@ -9,8 +9,8 @@ export function clearSkillCache(): void { skillCache.clear() } -export async function getAllSkills(options?: SkillResolutionOptions): Promise { - const directory = options?.directory ?? process.cwd() +export async function getAllSkills(options: SkillResolutionOptions & { directory: string }): Promise { + const directory = options.directory const cacheKey = `${options?.browserProvider ?? "playwright"}:${directory}` const hasDisabledSkills = options?.disabledSkills && options.disabledSkills.size > 0 diff --git a/src/features/opencode-skill-loader/skill-resolution-options.ts b/src/features/opencode-skill-loader/skill-resolution-options.ts index e2ba58ecd..53f93b78f 100644 --- a/src/features/opencode-skill-loader/skill-resolution-options.ts +++ b/src/features/opencode-skill-loader/skill-resolution-options.ts @@ -4,6 +4,6 @@ export interface SkillResolutionOptions { gitMasterConfig?: GitMasterConfig browserProvider?: BrowserAutomationProvider disabledSkills?: Set - /** Project directory to discover project-level skills from. Falls back to process.cwd() if not provided. */ + /** Project directory to discover project-level skills from. Required for async resolution — process.cwd() is unsafe in OpenCode. */ directory?: string } diff --git a/src/features/opencode-skill-loader/skill-template-resolver.ts b/src/features/opencode-skill-loader/skill-template-resolver.ts index 046256c37..7a5132737 100644 --- a/src/features/opencode-skill-loader/skill-template-resolver.ts +++ b/src/features/opencode-skill-loader/skill-template-resolver.ts @@ -51,7 +51,7 @@ export function resolveMultipleSkills( export async function resolveSkillContentAsync( skillName: string, - options?: SkillResolutionOptions + options: SkillResolutionOptions & { directory: string } ): Promise { const allSkills = await getAllSkills(options) const skill = allSkills.find((loadedSkill) => loadedSkill.name === skillName) @@ -68,7 +68,7 @@ export async function resolveSkillContentAsync( export async function resolveMultipleSkillsAsync( skillNames: string[], - options?: SkillResolutionOptions + options: SkillResolutionOptions & { directory: string } ): Promise<{ resolved: Map; notFound: string[] }> { const allSkills = await getAllSkills(options) const skillMap = new Map() diff --git a/src/tools/delegate-task/skill-resolver.ts b/src/tools/delegate-task/skill-resolver.ts index 217079a83..ff9d0165d 100644 --- a/src/tools/delegate-task/skill-resolver.ts +++ b/src/tools/delegate-task/skill-resolver.ts @@ -3,7 +3,7 @@ import { resolveMultipleSkillsAsync, getAllSkills } from "../../features/opencod export async function resolveSkillContent( skills: string[], - options: { gitMasterConfig?: GitMasterConfig; browserProvider?: BrowserAutomationProvider; disabledSkills?: Set; directory?: string } + options: { gitMasterConfig?: GitMasterConfig; browserProvider?: BrowserAutomationProvider; disabledSkills?: Set; directory: string } ): Promise<{ content: string | undefined; error: string | null }> { if (skills.length === 0) { return { content: undefined, error: null } diff --git a/src/tools/skill/index.ts b/src/tools/skill/index.ts index 3c32b1c2e..c5d035fcd 100644 --- a/src/tools/skill/index.ts +++ b/src/tools/skill/index.ts @@ -1,3 +1,3 @@ export * from "./constants" export * from "./types" -export { skill, createSkillTool } from "./tools" +export { createSkillTool } from "./tools" diff --git a/src/tools/skill/tools.ts b/src/tools/skill/tools.ts index f9e084815..5376af220 100644 --- a/src/tools/skill/tools.ts +++ b/src/tools/skill/tools.ts @@ -181,7 +181,7 @@ async function formatMcpCapabilities( return sections.join("\n") } -export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition { +export function createSkillTool(options: SkillLoadOptions): ToolDefinition { let cachedSkills: LoadedSkill[] | null = null let cachedCommands: CommandInfo[] | null = options.commands ?? null let cachedDescription: string | null = null @@ -250,7 +250,7 @@ export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition body = injectGitMasterConfig(body, options.gitMasterConfig) } - const dir = matchedSkill.path ? dirname(matchedSkill.path) : matchedSkill.resolvedPath || options.directory || process.cwd() + const dir = matchedSkill.path ? dirname(matchedSkill.path) : matchedSkill.resolvedPath || options.directory const output = [ `## Skill: ${matchedSkill.name}`, @@ -309,5 +309,3 @@ export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition }, }) } - -export const skill: ToolDefinition = createSkillTool() diff --git a/src/tools/skill/types.ts b/src/tools/skill/types.ts index 5fe6b087b..42d1dfacb 100644 --- a/src/tools/skill/types.ts +++ b/src/tools/skill/types.ts @@ -33,6 +33,6 @@ export interface SkillLoadOptions { /** Git master configuration for watermark/co-author settings */ gitMasterConfig?: GitMasterConfig disabledSkills?: Set - /** Project directory for skill discovery and base directory resolution. Should be ctx.directory from PluginContext. */ - directory?: string + /** Project directory for skill discovery and base directory resolution. Must be ctx.directory from PluginContext — process.cwd() is unsafe in OpenCode. */ + directory: string }