diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03e5cc5..0ab15ac 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) @@ -215,7 +215,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 @@ -226,7 +226,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 e3d06b6..213ffbf 100644 --- a/README-zh.md +++ b/README-zh.md @@ -219,13 +219,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 afa2bb7..f44ae6d 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,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 @@ -238,13 +238,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 db14527..b57f8ab 100644 --- a/src/core/platforms.ts +++ b/src/core/platforms.ts @@ -198,6 +198,13 @@ export const PLATFORMS: Platform[] = [ supportsHooks: true, hookFormat: '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 10ff8d8..9eb3dad 100644 --- a/src/core/superpowers.ts +++ b/src/core/superpowers.ts @@ -23,6 +23,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 e05172a..dca910b 100644 --- a/test/ts/ci-workflows.test.ts +++ b/test/ts/ci-workflows.test.ts @@ -35,8 +35,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 f12d254..96eba8d 100644 --- a/test/ts/init-e2e.test.ts +++ b/test/ts/init-e2e.test.ts @@ -191,7 +191,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 = [ @@ -209,6 +209,7 @@ describe('comet init E2E', () => { '.kilocode', '.augment', '.kiro', + '.kimi-code', '.lingma', '.junie', '.codebuddy', @@ -321,4 +322,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 2cd4a11..59d7234 100644 --- a/test/ts/openspec.test.ts +++ b/test/ts/openspec.test.ts @@ -39,6 +39,25 @@ describe('openspec', () => { }); describe('installOpenSpec', () => { + it('accepts the Kimi OpenSpec tool id from platform definitions', async () => { + mockedExecFileSync.mockReturnValueOnce(Buffer.from('/usr/bin/openspec')); + mockedExecFileSync.mockReturnValueOnce(Buffer.from('ok')); + + const { installOpenSpec } = await import('../../src/core/openspec.js'); + const result = await installOpenSpec('/tmp/test', ['kimi'], 'project'); + + expect(result).toBe('installed'); + 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 () => { mockedExecFileSync.mockReturnValueOnce(Buffer.from('/usr/bin/openspec')); mockedExecFileSync.mockReturnValueOnce(Buffer.from('ok')); diff --git a/test/ts/superpowers.test.ts b/test/ts/superpowers.test.ts index d3376fb..3243c5d 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); }); });