Skip to content

Commit ece61a6

Browse files
ningchenvzningchen
andauthored
feat: add Cline support (#213)
Co-authored-by: ningchen <[email protected]>
1 parent ecddffc commit ece61a6

File tree

13 files changed

+286
-0
lines changed

13 files changed

+286
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ These tools have built-in OpenSpec commands. Select the OpenSpec integration whe
9292
|------|----------|
9393
| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` |
9494
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
95+
| **Cline** | Rules in `.clinerules/` directory (`.clinerules/openspec-*.md`) |
9596
| **Factory Droid** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.factory/commands/`) |
9697
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
9798
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/workflows/`) |
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## Why
2+
Add support for Cline (VS Code extension) in OpenSpec to enable developers to use Cline's AI-powered coding capabilities for spec-driven development workflows.
3+
4+
## What Changes
5+
- Add Cline slash command configurator for proposal, apply, and archive operations
6+
- Add Cline root CLINE.md configurator for project-level instructions
7+
- Add Cline template exports
8+
- Update tool and slash command registries to include Cline
9+
- Add comprehensive test coverage
10+
- **BREAKING**: None - this is additive functionality
11+
12+
## Impact
13+
- Affected specs: cli-init (new tool option)
14+
- Affected code: src/core/configurators/slash/cline.ts, src/core/configurators/cline.ts, registry files
15+
- New files: .clinerules/openspec-*.md, CLINE.md
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## ADDED Requirements
2+
### Requirement: Cline Tool Support
3+
The system SHALL provide Cline (VS Code extension) as a supported tool option during OpenSpec initialization.
4+
5+
#### Scenario: Initialize project with Cline support
6+
- **WHEN** user runs `openspec init --tools cline`
7+
- **THEN** Cline-specific rule files are configured in `.clinerules/`
8+
- **AND** CLINE.md root file includes OpenSpec workflow instructions
9+
- **AND** Cline is registered as available configurator
10+
11+
#### Scenario: Cline proposal rule generation
12+
- **WHEN** Cline rules are configured
13+
- **THEN** `.clinerules/openspec-proposal.md` contains proposal workflow with guardrails
14+
- **AND** Includes Cline-specific Markdown heading frontmatter
15+
- **AND** Follows established slash command template pattern
16+
17+
#### Scenario: Cline apply and archive rules
18+
- **WHEN** Cline rules are configured
19+
- **THEN** `.clinerules/openspec-apply.md` contains implementation workflow
20+
- **AND** `.clinerules/openspec-archive.md` contains archiving workflow
21+
- **AND** Both commands include appropriate headers and references
22+
23+
#### Scenario: Cline root instructions
24+
- **WHEN** Cline is selected during initialization
25+
- **THEN** CLINE.md is created at project root
26+
- **AND** Contains OpenSpec markers for managed content
27+
- **AND** References `@/openspec/AGENTS.md` for workflow instructions
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## 1. Implementation
2+
- [x] 1.1 Create ClineSlashCommandConfigurator class in src/core/configurators/slash/cline.ts
3+
- [x] 1.2 Create ClineConfigurator class in src/core/configurators/cline.ts
4+
- [x] 1.3 Create cline-template.ts for template exports
5+
- [x] 1.4 Define file paths for Cline rules (.clinerules/)
6+
- [x] 1.5 Create Cline-specific frontmatter (Markdown heading format)
7+
- [x] 1.6 Register Cline in slash/registry.ts
8+
- [x] 1.7 Register Cline in configurators/registry.ts
9+
- [x] 1.8 Add Cline to AI_TOOLS in config.ts
10+
- [x] 1.9 Add getClineTemplate() to templates/index.ts
11+
- [x] 1.10 Update README with Cline documentation
12+
13+
## 2. Testing
14+
- [x] 2.1 Add init tests for CLINE.md creation and updates
15+
- [x] 2.2 Add init tests for .clinerules/ file creation
16+
- [x] 2.3 Add update tests for CLINE.md updates
17+
- [x] 2.4 Add update tests for .clinerules/ file refreshes
18+
- [x] 2.5 Test integration with openspec init --tools cline
19+
- [x] 2.6 Verify all 225 tests pass

src/core/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface AIToolOption {
1919
export const AI_TOOLS: AIToolOption[] = [
2020
{ name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie' },
2121
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code' },
22+
{ name: 'Cline', value: 'cline', available: true, successLabel: 'Cline' },
2223
{ name: 'Crush', value: 'crush', available: true, successLabel: 'Crush' },
2324
{ name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor' },
2425
{ name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid' },

src/core/configurators/cline.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import path from 'path';
2+
import { ToolConfigurator } from './base.js';
3+
import { FileSystemUtils } from '../../utils/file-system.js';
4+
import { TemplateManager } from '../templates/index.js';
5+
import { OPENSPEC_MARKERS } from '../config.js';
6+
7+
export class ClineConfigurator implements ToolConfigurator {
8+
name = 'Cline';
9+
configFileName = 'CLINE.md';
10+
isAvailable = true;
11+
12+
async configure(projectPath: string, openspecDir: string): Promise<void> {
13+
const filePath = path.join(projectPath, this.configFileName);
14+
const content = TemplateManager.getClineTemplate();
15+
16+
await FileSystemUtils.updateFileWithMarkers(
17+
filePath,
18+
content,
19+
OPENSPEC_MARKERS.start,
20+
OPENSPEC_MARKERS.end
21+
);
22+
}
23+
}

src/core/configurators/registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import { ToolConfigurator } from './base.js';
22
import { ClaudeConfigurator } from './claude.js';
3+
import { ClineConfigurator } from './cline.js';
34
import { AgentsStandardConfigurator } from './agents.js';
45

56
export class ToolRegistry {
67
private static tools: Map<string, ToolConfigurator> = new Map();
78

89
static {
910
const claudeConfigurator = new ClaudeConfigurator();
11+
const clineConfigurator = new ClineConfigurator();
1012
const agentsConfigurator = new AgentsStandardConfigurator();
1113
// Register with the ID that matches the checkbox value
1214
this.tools.set('claude', claudeConfigurator);
15+
this.tools.set('cline', clineConfigurator);
1316
this.tools.set('agents', agentsConfigurator);
1417
}
1518

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { SlashCommandConfigurator } from './base.js';
2+
import { SlashCommandId } from '../../templates/index.js';
3+
4+
const FILE_PATHS: Record<SlashCommandId, string> = {
5+
proposal: '.clinerules/openspec-proposal.md',
6+
apply: '.clinerules/openspec-apply.md',
7+
archive: '.clinerules/openspec-archive.md'
8+
};
9+
10+
export class ClineSlashCommandConfigurator extends SlashCommandConfigurator {
11+
readonly toolId = 'cline';
12+
readonly isAvailable = true;
13+
14+
protected getRelativePath(id: SlashCommandId): string {
15+
return FILE_PATHS[id];
16+
}
17+
18+
protected getFrontmatter(id: SlashCommandId): string | undefined {
19+
const descriptions: Record<SlashCommandId, string> = {
20+
proposal: 'Scaffold a new OpenSpec change and validate strictly.',
21+
apply: 'Implement an approved OpenSpec change and keep tasks in sync.',
22+
archive: 'Archive a deployed OpenSpec change and update specs.'
23+
};
24+
const description = descriptions[id];
25+
return `# OpenSpec: ${id.charAt(0).toUpperCase() + id.slice(1)}\n\n${description}`;
26+
}
27+
}

src/core/configurators/slash/registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { GitHubCopilotSlashCommandConfigurator } from './github-copilot.js';
99
import { AmazonQSlashCommandConfigurator } from './amazon-q.js';
1010
import { FactorySlashCommandConfigurator } from './factory.js';
1111
import { AuggieSlashCommandConfigurator } from './auggie.js';
12+
import { ClineSlashCommandConfigurator } from './cline.js';
1213
import { CrushSlashCommandConfigurator } from './crush.js';
1314

1415
export class SlashCommandRegistry {
@@ -25,6 +26,7 @@ export class SlashCommandRegistry {
2526
const amazonQ = new AmazonQSlashCommandConfigurator();
2627
const factory = new FactorySlashCommandConfigurator();
2728
const auggie = new AuggieSlashCommandConfigurator();
29+
const cline = new ClineSlashCommandConfigurator();
2830
const crush = new CrushSlashCommandConfigurator();
2931

3032
this.configurators.set(claude.toolId, claude);
@@ -37,6 +39,7 @@ export class SlashCommandRegistry {
3739
this.configurators.set(amazonQ.toolId, amazonQ);
3840
this.configurators.set(factory.toolId, factory);
3941
this.configurators.set(auggie.toolId, auggie);
42+
this.configurators.set(cline.toolId, cline);
4043
this.configurators.set(crush.toolId, crush);
4144
}
4245

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { agentsRootStubTemplate as clineTemplate } from './agents-root-stub.js';

0 commit comments

Comments
 (0)