(
name: string,
options: {
skills?: boolean;
skillNames?: readonly string[];
version?: string;
sessionStartSkill?: string;
mcpServers?: Record<string, unknown>;
hooks?: readonly unknown[];
commands?: Record<string, string>;
} = {},
)
| 16 | } |
| 17 | |
| 18 | async function makePlugin( |
| 19 | name: string, |
| 20 | options: { |
| 21 | skills?: boolean; |
| 22 | skillNames?: readonly string[]; |
| 23 | version?: string; |
| 24 | sessionStartSkill?: string; |
| 25 | mcpServers?: Record<string, unknown>; |
| 26 | hooks?: readonly unknown[]; |
| 27 | commands?: Record<string, string>; |
| 28 | } = {}, |
| 29 | ): Promise<string> { |
| 30 | const root = await mkdtemp(path.join(tmpdir(), `plugin-${name}-`)); |
| 31 | const manifest: Record<string, unknown> = { name }; |
| 32 | if (options.version !== undefined) { |
| 33 | manifest['version'] = options.version; |
| 34 | } |
| 35 | const skillNames = options.skillNames ?? (options.skills === true ? ['demo-skill'] : []); |
| 36 | if (skillNames.length > 0) { |
| 37 | manifest['skills'] = './skills/'; |
| 38 | await mkdir(path.join(root, 'skills'), { recursive: true }); |
| 39 | for (const skillName of skillNames) { |
| 40 | await mkdir(path.join(root, 'skills', skillName), { recursive: true }); |
| 41 | await writeFile( |
| 42 | path.join(root, 'skills', skillName, 'SKILL.md'), |
| 43 | `---\nname: ${skillName}\ndescription: A demo\n---\nbody`, |
| 44 | 'utf8', |
| 45 | ); |
| 46 | } |
| 47 | } |
| 48 | if (options.sessionStartSkill !== undefined) { |
| 49 | manifest['sessionStart'] = { skill: options.sessionStartSkill }; |
| 50 | } |
| 51 | if (options.mcpServers !== undefined) { |
| 52 | manifest['mcpServers'] = options.mcpServers; |
| 53 | } |
| 54 | if (options.hooks !== undefined) { |
| 55 | manifest['hooks'] = options.hooks; |
| 56 | } |
| 57 | if (options.commands !== undefined) { |
| 58 | manifest['commands'] = ['./commands']; |
| 59 | await mkdir(path.join(root, 'commands'), { recursive: true }); |
| 60 | for (const [file, body] of Object.entries(options.commands)) { |
| 61 | const filePath = path.join(root, 'commands', file); |
| 62 | await mkdir(path.dirname(filePath), { recursive: true }); |
| 63 | await writeFile(filePath, body, 'utf8'); |
| 64 | } |
| 65 | } |
| 66 | await writeFile( |
| 67 | path.join(root, 'kimi.plugin.json'), |
| 68 | JSON.stringify(manifest), |
| 69 | 'utf8', |
| 70 | ); |
| 71 | return realpath(root); |
| 72 | } |
| 73 | |
| 74 | describe('PluginManager', () => { |
| 75 | it('install() adds a plugin and load() rehydrates it from disk', async () => { |
no test coverage detected