Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion docs/supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ For each tool you select, OpenSpec installs:
| Gemini CLI | `.gemini/skills/` | `.gemini/commands/opsx/` |
| GitHub Copilot | `.github/skills/` | `.github/prompts/` |
| iFlow | `.iflow/skills/` | `.iflow/commands/` |
| Lingma | `.lingma/skills/` | `.lingma/commands/` |
| Kilo Code | `.kilocode/skills/` | `.kilocode/workflows/` |
| OpenCode | `.opencode/skills/` | `.opencode/command/` |
| Qoder | `.qoder/skills/` | `.qoder/commands/opsx/` |
Expand All @@ -53,7 +54,7 @@ openspec init --tools all
openspec init --tools none
```

**Available tool IDs:** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codebuddy`, `codex`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `kilocode`, `opencode`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
**Available tool IDs:** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codebuddy`, `codex`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `lingma`, `kilocode`, `opencode`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`

## What Gets Installed

Expand Down
1 change: 1 addition & 0 deletions src/core/command-generation/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { geminiAdapter } from './gemini.js';
export { githubCopilotAdapter } from './github-copilot.js';
export { iflowAdapter } from './iflow.js';
export { kilocodeAdapter } from './kilocode.js';
export { lingmaAdapter } from './lingma.js';
export { opencodeAdapter } from './opencode.js';
export { qoderAdapter } from './qoder.js';
export { qwenAdapter } from './qwen.js';
Expand Down
33 changes: 33 additions & 0 deletions src/core/command-generation/adapters/lingma.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Lingma Command Adapter
*
* Formats commands for Lingma following its skill specification.
*/

import path from 'path';
import type { CommandContent, ToolCommandAdapter } from '../types.js';

/**
* Lingma adapter for command generation.
* File path: .lingma/commands/opsx-<id>.md
* Structure: Command markdown files with YAML frontmatter
*/
export const lingmaAdapter: ToolCommandAdapter = {
toolId: 'lingma',

getFilePath(commandId: string): string {
return path.join('.lingma', 'commands', `opsx-${commandId}.md`);
},

formatFile(content: CommandContent): string {
return `---
name: /opsx-${content.id}
id: opsx-${content.id}
category: ${content.category}
description: ${content.description}
---

${content.body}
`;
},
};
2 changes: 2 additions & 0 deletions src/core/command-generation/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { geminiAdapter } from './adapters/gemini.js';
import { githubCopilotAdapter } from './adapters/github-copilot.js';
import { iflowAdapter } from './adapters/iflow.js';
import { kilocodeAdapter } from './adapters/kilocode.js';
import { lingmaAdapter } from './adapters/lingma.js';
import { opencodeAdapter } from './adapters/opencode.js';
import { qoderAdapter } from './adapters/qoder.js';
import { qwenAdapter } from './adapters/qwen.js';
Expand Down Expand Up @@ -52,6 +53,7 @@ export class CommandAdapterRegistry {
CommandAdapterRegistry.register(githubCopilotAdapter);
CommandAdapterRegistry.register(iflowAdapter);
CommandAdapterRegistry.register(kilocodeAdapter);
CommandAdapterRegistry.register(lingmaAdapter);
CommandAdapterRegistry.register(opencodeAdapter);
CommandAdapterRegistry.register(qoderAdapter);
CommandAdapterRegistry.register(qwenAdapter);
Expand Down
1 change: 1 addition & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const AI_TOOLS: AIToolOption[] = [
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code', skillsDir: '.qwen' },
{ name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode', skillsDir: '.roo' },
{ name: 'Trae', value: 'trae', available: true, successLabel: 'Trae', skillsDir: '.trae' },
{ name: 'Lingma', value: 'lingma', available: true, successLabel: 'Lingma', skillsDir: '.lingma' },
{ name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf', skillsDir: '.windsurf' },
{ name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
];
1 change: 1 addition & 0 deletions src/core/legacy-cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const LEGACY_SLASH_COMMAND_PATHS: Record<string, LegacySlashCommandPatter
'continue': { type: 'files', pattern: '.continue/prompts/openspec-*.prompt' },
'antigravity': { type: 'files', pattern: '.agent/workflows/openspec-*.md' },
'iflow': { type: 'files', pattern: '.iflow/commands/openspec-*.md' },
'lingma': { type: 'files', pattern: '.lingma/commands/openspec-*.md' },
'qwen': { type: 'files', pattern: '.qwen/commands/openspec-*.toml' },
'codex': { type: 'files', pattern: '.codex/prompts/openspec-*.md' },
};
Expand Down
30 changes: 29 additions & 1 deletion test/core/command-generation/adapters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { geminiAdapter } from '../../../src/core/command-generation/adapters/gem
import { githubCopilotAdapter } from '../../../src/core/command-generation/adapters/github-copilot.js';
import { iflowAdapter } from '../../../src/core/command-generation/adapters/iflow.js';
import { kilocodeAdapter } from '../../../src/core/command-generation/adapters/kilocode.js';
import { lingmaAdapter } from '../../../src/core/command-generation/adapters/lingma.js';
import { opencodeAdapter } from '../../../src/core/command-generation/adapters/opencode.js';
import { qoderAdapter } from '../../../src/core/command-generation/adapters/qoder.js';
import { qwenAdapter } from '../../../src/core/command-generation/adapters/qwen.js';
Expand Down Expand Up @@ -542,6 +543,33 @@ describe('command-generation/adapters', () => {
});
});

describe('lingmaAdapter', () => {
it('should have correct toolId', () => {
expect(lingmaAdapter.toolId).toBe('lingma');
});

it('should generate correct file path', () => {
const filePath = lingmaAdapter.getFilePath('explore');
expect(filePath).toBe(path.join('.lingma', 'commands', 'opsx-explore.md'));
});

it('should generate correct file paths for different commands', () => {
expect(lingmaAdapter.getFilePath('new')).toBe(path.join('.lingma', 'commands', 'opsx-new.md'));
expect(lingmaAdapter.getFilePath('apply')).toBe(path.join('.lingma', 'commands', 'opsx-apply.md'));
});

it('should format file with name, id, category, and description', () => {
const output = lingmaAdapter.formatFile(sampleContent);
expect(output).toContain('---\n');
expect(output).toContain('name: /opsx-explore');
expect(output).toContain('id: opsx-explore');
expect(output).toContain('category: Workflow');
expect(output).toContain('description: Enter explore mode for thinking');
expect(output).toContain('---\n\n');
expect(output).toContain('This is the command body.\n\nWith multiple lines.');
});
});

describe('cross-platform path handling', () => {
it('Claude adapter uses path.join for paths', () => {
// path.join handles platform-specific separators
Expand All @@ -566,7 +594,7 @@ describe('command-generation/adapters', () => {
amazonQAdapter, antigravityAdapter, auggieAdapter, clineAdapter,
codexAdapter, codebuddyAdapter, continueAdapter, costrictAdapter,
crushAdapter, factoryAdapter, geminiAdapter, githubCopilotAdapter,
iflowAdapter, kilocodeAdapter, opencodeAdapter, qoderAdapter,
iflowAdapter, kilocodeAdapter, lingmaAdapter, opencodeAdapter, qoderAdapter,
qwenAdapter, roocodeAdapter
];
for (const adapter of adapters) {
Expand Down