From f9fd8e352165995550d60aabd9c5b9ddaae520f0 Mon Sep 17 00:00:00 2001 From: Joechan11 <512619302@qq.com> Date: Wed, 10 Jun 2026 23:01:53 +0800 Subject: [PATCH 1/2] feat(core): add Kimi Code CLI platform support --- .github/workflows/ci.yml | 8 ++++---- README-zh.md | 17 +++++++++-------- README.md | 17 +++++++++-------- src/core/platforms.ts | 7 +++++++ src/core/superpowers.ts | 1 + test/ts/ci-workflows.test.ts | 4 ++-- test/ts/detect.test.ts | 13 ++++++++++++- test/ts/init-e2e.test.ts | 30 +++++++++++++++++++++++++++++- test/ts/openspec.test.ts | 11 +++++++++++ test/ts/superpowers.test.ts | 6 ++++-- 10 files changed, 88 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 22aa33a..a557637 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,7 +127,7 @@ jobs: for sd in .claude/skills .cursor/skills .codex/skills .opencode/skills \ .windsurf/skills .cline/skills .roo/skills .continue/skills \ .gemini/skills .amazonq/skills .qwen/skills .kilocode/skills \ - .augment/skills .kiro/skills .lingma/skills .junie/skills \ + .augment/skills .kiro/skills .kimi-code/skills .lingma/skills .junie/skills \ .codebuddy/skills .cospec/skills .crush/skills .factory/skills \ .iflow/skills .pi/skills .qoder/skills .agents/skills \ .bob/skills .forge/skills .trae/skills .github/skills; do @@ -138,7 +138,7 @@ jobs: check_file "$PROJ/$sd/comet/scripts/comet-yaml-validate.sh" check_file "$PROJ/$sd/comet/scripts/comet-archive.sh" done - echo "All 28 platforms project Comet skills: OK" + echo "All 29 platforms project Comet skills: OK" shell: bash - name: Verify external installer status (project) @@ -195,7 +195,7 @@ jobs: for sd in .claude/skills .cursor/skills .codex/skills .config/opencode/skills \ .windsurf/skills .cline/skills .roo/skills .continue/skills \ .gemini/skills .amazonq/skills .qwen/skills .kilocode/skills \ - .augment/skills .kiro/skills .lingma/skills .junie/skills \ + .augment/skills .kiro/skills .kimi-code/skills .lingma/skills .junie/skills \ .codebuddy/skills .cospec/skills .crush/skills .factory/skills \ .iflow/skills .pi/skills .qoder/skills .gemini/antigravity/skills \ .bob/skills .forge/skills .trae/skills .github/skills; do @@ -206,7 +206,7 @@ jobs: check_file "$HOME_DIR/$sd/comet/scripts/comet-yaml-validate.sh" check_file "$HOME_DIR/$sd/comet/scripts/comet-archive.sh" done - echo "All 28 platforms global Comet skills: OK" + echo "All 29 platforms global Comet skills: OK" shell: bash - name: Verify external installer status (global) diff --git a/README-zh.md b/README-zh.md index 39a0d7e..95cbcae 100644 --- a/README-zh.md +++ b/README-zh.md @@ -174,7 +174,7 @@ npx skills add rpamis/comet ## 支持平台 -`comet init` 支持 28 个 AI 编码平台: +`comet init` 支持 29 个 AI 编码平台:
查看完整平台列表 @@ -188,13 +188,14 @@ npx skills add rpamis/comet | GitHub Copilot | `.github/` | Gemini CLI | `.gemini/` | | Amazon Q Developer | `.amazonq/` | Qwen Code | `.qwen/` | | Kilo Code | `.kilocode/` | Auggie | `.augment/` | -| Kiro | `.kiro/` | Lingma | `.lingma/` | -| Junie | `.junie/` | CodeBuddy | `.codebuddy/` | -| CoStrict | `.cospec/` | Crush | `.crush/` | -| Factory Droid | `.factory/` | iFlow | `.iflow/` | -| Pi | `.pi/` | Qoder | `.qoder/` | -| Antigravity | `.agents/` | Bob Shell | `.bob/` | -| ForgeCode | `.forge/` | Trae | `.trae/` | +| Kimi Code | `.kimi-code/` | Kiro | `.kiro/` | +| Lingma | `.lingma/` | Junie | `.junie/` | +| CodeBuddy | `.codebuddy/` | CoStrict | `.cospec/` | +| Crush | `.crush/` | Factory Droid | `.factory/` | +| iFlow | `.iflow/` | Pi | `.pi/` | +| Qoder | `.qoder/` | Antigravity | `.agents/` | +| Bob Shell | `.bob/` | ForgeCode | `.forge/` | +| Trae | `.trae/` | | |
diff --git a/README.md b/README.md index ed75012..f001ba7 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ Updates the npm package and refreshes installed Comet skills in detected project ## Supported Platforms -`comet init` supports 28 AI coding platforms: +`comet init` supports 29 AI coding platforms:
View full platform list @@ -189,13 +189,14 @@ Updates the npm package and refreshes installed Comet skills in detected project | GitHub Copilot | `.github/` | Gemini CLI | `.gemini/` | | Amazon Q Developer | `.amazonq/` | Qwen Code | `.qwen/` | | Kilo Code | `.kilocode/` | Auggie | `.augment/` | -| Kiro | `.kiro/` | Lingma | `.lingma/` | -| Junie | `.junie/` | CodeBuddy | `.codebuddy/` | -| CoStrict | `.cospec/` | Crush | `.crush/` | -| Factory Droid | `.factory/` | iFlow | `.iflow/` | -| Pi | `.pi/` | Qoder | `.qoder/` | -| Antigravity | `.agents/` | Bob Shell | `.bob/` | -| ForgeCode | `.forge/` | Trae | `.trae/` | +| Kimi Code | `.kimi-code/` | Kiro | `.kiro/` | +| Lingma | `.lingma/` | Junie | `.junie/` | +| CodeBuddy | `.codebuddy/` | CoStrict | `.cospec/` | +| Crush | `.crush/` | Factory Droid | `.factory/` | +| iFlow | `.iflow/` | Pi | `.pi/` | +| Qoder | `.qoder/` | Antigravity | `.agents/` | +| Bob Shell | `.bob/` | ForgeCode | `.forge/` | +| Trae | `.trae/` | | |
diff --git a/src/core/platforms.ts b/src/core/platforms.ts index 821e4ae..3c20d7e 100644 --- a/src/core/platforms.ts +++ b/src/core/platforms.ts @@ -60,6 +60,13 @@ export const PLATFORMS: Platform[] = [ { id: 'kilocode', name: 'Kilo Code', skillsDir: '.kilocode', openspecToolId: 'kilocode' }, { id: 'auggie', name: 'Auggie (Augment CLI)', skillsDir: '.augment', openspecToolId: 'auggie' }, { id: 'kiro', name: 'Kiro', skillsDir: '.kiro', openspecToolId: 'kiro' }, + { + id: 'kimicode', + name: 'Kimi Code', + skillsDir: '.kimi-code', + globalSkillsDir: '.kimi-code', + openspecToolId: 'kimi', + }, { id: 'lingma', name: 'Lingma', diff --git a/src/core/superpowers.ts b/src/core/superpowers.ts index cf138d6..bd78a74 100644 --- a/src/core/superpowers.ts +++ b/src/core/superpowers.ts @@ -24,6 +24,7 @@ const SKILLS_AGENT_MAP: Record = { kilocode: 'kilo', auggie: 'augment', kiro: 'kiro-cli', + kimicode: 'kimi-code-cli', lingma: null, junie: 'junie', codebuddy: 'codebuddy', diff --git a/test/ts/ci-workflows.test.ts b/test/ts/ci-workflows.test.ts index f4ac63c..33e02f8 100644 --- a/test/ts/ci-workflows.test.ts +++ b/test/ts/ci-workflows.test.ts @@ -30,8 +30,8 @@ describe('CI workflows', () => { expect(workflow).not.toContain('check_glob "$HOME_DIR/$sd/openspec-*"'); expect(workflow).not.toContain('check_dir "$HOME_DIR/$sd/brainstorming"'); expect(workflow).not.toContain('check_dir "$HOME_DIR/$sd/using-superpowers"'); - expect(workflow).toContain('All 28 platforms project Comet skills: OK'); - expect(workflow).toContain('All 28 platforms global Comet skills: OK'); + expect(workflow).toContain('All 29 platforms project Comet skills: OK'); + expect(workflow).toContain('All 29 platforms global Comet skills: OK'); }); it('defines PR title linting with Comet-specific semantic scopes', async () => { diff --git a/test/ts/detect.test.ts b/test/ts/detect.test.ts index 09a56bc..4fa5c42 100644 --- a/test/ts/detect.test.ts +++ b/test/ts/detect.test.ts @@ -43,6 +43,15 @@ describe('detect', () => { }); describe('platform global skills directories', () => { + it('declares Kimi Code global skills under the user .kimi-code directory', () => { + const kimicode = PLATFORMS.find((platform) => platform.id === 'kimicode'); + + expect(kimicode).toBeDefined(); + expect(kimicode?.skillsDir).toBe('.kimi-code'); + expect(kimicode?.globalSkillsDir).toBe('.kimi-code'); + expect(kimicode?.openspecToolId).toBe('kimi'); + }); + it('declares Lingma global skills under the user .lingma directory', () => { const lingma = PLATFORMS.find((platform) => platform.id === 'lingma'); @@ -75,10 +84,12 @@ describe('detect', () => { it('detects multiple platforms', async () => { await fs.mkdir(path.join(tmpDir, '.claude')); await fs.mkdir(path.join(tmpDir, '.cursor')); + await fs.mkdir(path.join(tmpDir, '.kimi-code')); const detected = await detectPlatforms(tmpDir); expect(detected.has('claude')).toBe(true); expect(detected.has('cursor')).toBe(true); - expect(detected.size).toBeGreaterThanOrEqual(2); + expect(detected.has('kimicode')).toBe(true); + expect(detected.size).toBeGreaterThanOrEqual(3); }); it('returns empty set when no platforms detected', async () => { diff --git a/test/ts/init-e2e.test.ts b/test/ts/init-e2e.test.ts index 4c0c1ef..7861416 100644 --- a/test/ts/init-e2e.test.ts +++ b/test/ts/init-e2e.test.ts @@ -164,7 +164,7 @@ describe('comet init E2E', () => { const { initCommand } = await import('../../src/commands/init.js'); const result = await captureJsonOutput(() => initCommand(tmpDir, { yes: true, json: true })); - expect((result.results as unknown[]).length).toBeGreaterThanOrEqual(28); + expect((result.results as unknown[]).length).toBeGreaterThanOrEqual(29); const manifest = await readManifest(); const platformDirs = [ @@ -182,6 +182,7 @@ describe('comet init E2E', () => { '.kilocode', '.augment', '.kiro', + '.kimi-code', '.lingma', '.junie', '.codebuddy', @@ -291,4 +292,31 @@ describe('comet init E2E', () => { fs.access(path.join(tmpDir, '.lingma', 'skills', 'comet', 'SKILL.md')), ).rejects.toThrow(); }, 20_000); + + it('installs Kimi Code global Comet skills to the user Kimi Code skills directory', async () => { + mockExternalSuccess(); + + await fs.mkdir(path.join(tmpDir, '.kimi-code'), { recursive: true }); + const fakeHome = path.join(tmpDir, 'fake-home'); + await fs.mkdir(fakeHome, { recursive: true }); + + vi.spyOn(os, 'homedir').mockReturnValue(fakeHome); + + const { initCommand } = await import('../../src/commands/init.js'); + const result = await captureJsonOutput(() => + initCommand(tmpDir, { yes: true, scope: 'global', json: true }), + ); + + expect(result.selectedPlatforms).toEqual(['kimicode']); + + const manifest = await readManifest(); + for (const skillPath of manifest.skills) { + const dest = path.join(fakeHome, '.kimi-code', 'skills', skillPath); + await expect(fs.access(dest)).resolves.toBeUndefined(); + } + + await expect( + fs.access(path.join(tmpDir, '.kimi-code', 'skills', 'comet', 'SKILL.md')), + ).rejects.toThrow(); + }, 20_000); }); diff --git a/test/ts/openspec.test.ts b/test/ts/openspec.test.ts index 4ca0c37..7d5d363 100644 --- a/test/ts/openspec.test.ts +++ b/test/ts/openspec.test.ts @@ -47,6 +47,17 @@ describe('openspec', () => { }); describe('installOpenSpec', () => { + it('accepts the Kimi OpenSpec tool id from platform definitions', async () => { + mockedExecSync.mockReturnValueOnce(Buffer.from('/usr/bin/openspec')); + mockedExecSync.mockReturnValueOnce(Buffer.from('ok')); + + const { installOpenSpec, quoteShellArg } = await import('../../src/core/openspec.js'); + const result = await installOpenSpec('/tmp/test', ['kimi'], 'project'); + + expect(result).toBe('installed'); + expect(mockedExecSync.mock.calls[1][0]).toContain(`--tools ${quoteShellArg('kimi')}`); + }); + it('installs openspec when CLI is available', async () => { mockedExecSync.mockReturnValueOnce(Buffer.from('/usr/bin/openspec')); mockedExecSync.mockReturnValueOnce(Buffer.from('ok')); diff --git a/test/ts/superpowers.test.ts b/test/ts/superpowers.test.ts index 678c841..e588d95 100644 --- a/test/ts/superpowers.test.ts +++ b/test/ts/superpowers.test.ts @@ -53,6 +53,7 @@ describe('superpowers', () => { expect(SKILLS_AGENT_MAP['gemini']).toBe('gemini-cli'); expect(SKILLS_AGENT_MAP['qwen']).toBe('qwen-code'); expect(SKILLS_AGENT_MAP['kiro']).toBe('kiro-cli'); + expect(SKILLS_AGENT_MAP['kimicode']).toBe('kimi-code-cli'); expect(SKILLS_AGENT_MAP['iflow']).toBe('iflow-cli'); expect(SKILLS_AGENT_MAP['factory']).toBe('droid'); expect(SKILLS_AGENT_MAP['amazon-q']).toBe('universal'); @@ -60,7 +61,7 @@ describe('superpowers', () => { expect(SKILLS_AGENT_MAP['lingma']).toBeNull(); }); - it('has entries for all 28 platforms', async () => { + it('has entries for all 29 platforms', async () => { const { SKILLS_AGENT_MAP } = await import('../../src/core/superpowers.js'); const platformIds = [ 'claude', @@ -78,6 +79,7 @@ describe('superpowers', () => { 'kilocode', 'auggie', 'kiro', + 'kimicode', 'lingma', 'junie', 'codebuddy', @@ -95,7 +97,7 @@ describe('superpowers', () => { for (const id of platformIds) { expect(SKILLS_AGENT_MAP).toHaveProperty(id); } - expect(Object.keys(SKILLS_AGENT_MAP)).toHaveLength(28); + expect(Object.keys(SKILLS_AGENT_MAP)).toHaveLength(29); }); }); From bdf1306bae177c5708dc40dc652f526dd76a6cb5 Mon Sep 17 00:00:00 2001 From: Joechan11 <512619302@qq.com> Date: Thu, 11 Jun 2026 22:47:00 +0800 Subject: [PATCH 2/2] test(openspec): fix kimi tool id test mock and assertion --- test/ts/openspec.test.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/ts/openspec.test.ts b/test/ts/openspec.test.ts index ebdf7de..59d7234 100644 --- a/test/ts/openspec.test.ts +++ b/test/ts/openspec.test.ts @@ -40,14 +40,22 @@ describe('openspec', () => { describe('installOpenSpec', () => { it('accepts the Kimi OpenSpec tool id from platform definitions', async () => { - mockedExecSync.mockReturnValueOnce(Buffer.from('/usr/bin/openspec')); - mockedExecSync.mockReturnValueOnce(Buffer.from('ok')); + mockedExecFileSync.mockReturnValueOnce(Buffer.from('/usr/bin/openspec')); + mockedExecFileSync.mockReturnValueOnce(Buffer.from('ok')); - const { installOpenSpec, quoteShellArg } = await import('../../src/core/openspec.js'); + const { installOpenSpec } = await import('../../src/core/openspec.js'); const result = await installOpenSpec('/tmp/test', ['kimi'], 'project'); expect(result).toBe('installed'); - expect(mockedExecSync.mock.calls[1][0]).toContain(`--tools ${quoteShellArg('kimi')}`); + expect(mockedExecFileSync.mock.calls[1][0]).toBe('openspec'); + expect(mockedExecFileSync.mock.calls[1][1]).toEqual([ + 'init', + '/tmp/test', + '--tools', + 'kimi', + '--profile', + 'custom', + ]); }); it('installs openspec when CLI is available', async () => {