Skip to content

Commit af0b341

Browse files
preigileTabishB
andauthored
Add support for Junie from JetBrains tool and command generation (#853)
* Add support for Junie from JetBrains tool and command generation * Expand Junie support to include `opsx` file patterns and update documentation accordingly --------- Co-authored-by: TabishB <[email protected]>
1 parent 7fd5417 commit af0b341

File tree

7 files changed

+44
-1
lines changed

7 files changed

+44
-1
lines changed

docs/supported-tools.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ You can enable expanded workflows (`new`, `continue`, `ff`, `verify`, `sync`, `b
3737
| Gemini CLI (`gemini`) | `.gemini/skills/openspec-*/SKILL.md` | `.gemini/commands/opsx/<id>.toml` |
3838
| GitHub Copilot (`github-copilot`) | `.github/skills/openspec-*/SKILL.md` | `.github/prompts/opsx-<id>.prompt.md`\*\* |
3939
| iFlow (`iflow`) | `.iflow/skills/openspec-*/SKILL.md` | `.iflow/commands/opsx-<id>.md` |
40+
| Junie (`junie`) | `.junie/skills/openspec-*/SKILL.md` | `.junie/commands/opsx-<id>.md` |
4041
| Kilo Code (`kilocode`) | `.kilocode/skills/openspec-*/SKILL.md` | `.kilocode/workflows/opsx-<id>.md` |
4142
| Kiro (`kiro`) | `.kiro/skills/openspec-*/SKILL.md` | `.kiro/prompts/opsx-<id>.prompt.md` |
4243
| OpenCode (`opencode`) | `.opencode/skills/openspec-*/SKILL.md` | `.opencode/commands/opsx-<id>.md` |
@@ -69,7 +70,7 @@ openspec init --tools none
6970
openspec init --profile core
7071
```
7172

72-
**Available tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codex`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `forgecode`, `gemini`, `github-copilot`, `iflow`, `kilocode`, `kiro`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
73+
**Available tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codex`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `forgecode`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kiro`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
7374

7475
## Workflow-Dependent Installation
7576

src/core/command-generation/adapters/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export { factoryAdapter } from './factory.js';
1919
export { geminiAdapter } from './gemini.js';
2020
export { githubCopilotAdapter } from './github-copilot.js';
2121
export { iflowAdapter } from './iflow.js';
22+
export { junieAdapter } from './junie.js';
2223
export { kilocodeAdapter } from './kilocode.js';
2324
export { kiroAdapter } from './kiro.js';
2425
export { opencodeAdapter } from './opencode.js';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Junie Command Adapter
3+
*
4+
* Formats commands for Junie following its frontmatter specification.
5+
*/
6+
7+
import path from 'path';
8+
import type { CommandContent, ToolCommandAdapter } from '../types.js';
9+
10+
/**
11+
* Junie adapter for command generation.
12+
* File path: .junie/commands/opsx-<id>.md
13+
* Frontmatter: description
14+
*/
15+
export const junieAdapter: ToolCommandAdapter = {
16+
toolId: 'junie',
17+
18+
getFilePath(commandId: string): string {
19+
return path.join('.junie', 'commands', `opsx-${commandId}.md`);
20+
},
21+
22+
formatFile(content: CommandContent): string {
23+
return `---
24+
description: ${content.description}
25+
---
26+
27+
${content.body}
28+
`;
29+
},
30+
};

src/core/command-generation/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { factoryAdapter } from './adapters/factory.js';
2121
import { geminiAdapter } from './adapters/gemini.js';
2222
import { githubCopilotAdapter } from './adapters/github-copilot.js';
2323
import { iflowAdapter } from './adapters/iflow.js';
24+
import { junieAdapter } from './adapters/junie.js';
2425
import { kilocodeAdapter } from './adapters/kilocode.js';
2526
import { kiroAdapter } from './adapters/kiro.js';
2627
import { opencodeAdapter } from './adapters/opencode.js';
@@ -54,6 +55,7 @@ export class CommandAdapterRegistry {
5455
CommandAdapterRegistry.register(geminiAdapter);
5556
CommandAdapterRegistry.register(githubCopilotAdapter);
5657
CommandAdapterRegistry.register(iflowAdapter);
58+
CommandAdapterRegistry.register(junieAdapter);
5759
CommandAdapterRegistry.register(kilocodeAdapter);
5860
CommandAdapterRegistry.register(kiroAdapter);
5961
CommandAdapterRegistry.register(opencodeAdapter);

src/core/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const AI_TOOLS: AIToolOption[] = [
3535
{ name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI', skillsDir: '.gemini' },
3636
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot', skillsDir: '.github', detectionPaths: ['.github/copilot-instructions.md', '.github/instructions', '.github/workflows/copilot-setup-steps.yml', '.github/prompts', '.github/agents', '.github/skills', '.github/.mcp.json'] },
3737
{ name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow', skillsDir: '.iflow' },
38+
{ name: 'Junie', value: 'junie', available: true, successLabel: 'Junie', skillsDir: '.junie' },
3839
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code', skillsDir: '.kilocode' },
3940
{ name: 'Kiro', value: 'kiro', available: true, successLabel: 'Kiro', skillsDir: '.kiro' },
4041
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode', skillsDir: '.opencode' },

src/core/legacy-cleanup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const LEGACY_SLASH_COMMAND_PATHS: Record<string, LegacySlashCommandPatter
5454
'continue': { type: 'files', pattern: '.continue/prompts/openspec-*.prompt' },
5555
'antigravity': { type: 'files', pattern: '.agent/workflows/openspec-*.md' },
5656
'iflow': { type: 'files', pattern: '.iflow/commands/openspec-*.md' },
57+
'junie': { type: 'files', pattern: ['.junie/commands/opsx-*.md', '.junie/commands/openspec-*.md'] },
5758
'qwen': { type: 'files', pattern: '.qwen/commands/openspec-*.toml' },
5859
'codex': { type: 'files', pattern: '.codex/prompts/openspec-*.md' },
5960
};

test/core/command-generation/registry.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ describe('command-generation/registry', () => {
2121
expect(adapter?.toolId).toBe('windsurf');
2222
});
2323

24+
it('should return Junie adapter for "junie"', () => {
25+
const adapter = CommandAdapterRegistry.get('junie');
26+
expect(adapter).toBeDefined();
27+
expect(adapter?.toolId).toBe('junie');
28+
});
29+
2430
it('should return undefined for unregistered tool', () => {
2531
const adapter = CommandAdapterRegistry.get('unknown-tool');
2632
expect(adapter).toBeUndefined();
@@ -54,6 +60,7 @@ describe('command-generation/registry', () => {
5460
expect(CommandAdapterRegistry.has('claude')).toBe(true);
5561
expect(CommandAdapterRegistry.has('cursor')).toBe(true);
5662
expect(CommandAdapterRegistry.has('windsurf')).toBe(true);
63+
expect(CommandAdapterRegistry.has('junie')).toBe(true);
5764
});
5865

5966
it('should return false for unregistered tools', () => {

0 commit comments

Comments
 (0)