Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
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 @@ -30,6 +30,7 @@ You can enable expanded workflows (`new`, `continue`, `ff`, `verify`, `bulk-arch
| Cline (`cline`) | `.cline/skills/openspec-*/SKILL.md` | `.clinerules/workflows/opsx-<id>.md` |
| CodeBuddy (`codebuddy`) | `.codebuddy/skills/openspec-*/SKILL.md` | `.codebuddy/commands/opsx/<id>.md` |
| Codex (`codex`) | `.codex/skills/openspec-*/SKILL.md` | `$CODEX_HOME/prompts/opsx-<id>.md`\* |
| Devin Desktop (`devin`) | `.devin/skills/openspec-*/SKILL.md` | `.devin/workflows/opsx-<id>.md` |
Comment thread
mehdishahdoost marked this conversation as resolved.
| ForgeCode (`forgecode`) | `.forge/skills/openspec-*/SKILL.md` | Not generated (no command adapter; use skill-based `/openspec-*` invocations) |
| Continue (`continue`) | `.continue/skills/openspec-*/SKILL.md` | `.continue/prompts/opsx-<id>.prompt` |
| CoStrict (`costrict`) | `.cospec/skills/openspec-*/SKILL.md` | `.cospec/openspec/commands/opsx-<id>.md` |
Expand Down Expand Up @@ -75,7 +76,7 @@ openspec init --tools none
openspec init --profile core
```

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

## Workflow-Dependent Installation

Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/add-devin-desktop-support/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-04
33 changes: 33 additions & 0 deletions openspec/changes/add-devin-desktop-support/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## Why

- Windsurf has been rebranded to **Devin Desktop**, the new flagship AI coding assistant from Cognition. Users who previously used Windsurf are now transitioning to Devin Desktop.
- Devin Desktop uses the same workflow system as Windsurf (Cascade workflows stored in `.devin/workflows/`), making it a natural migration path for existing OpenSpec users.
- OpenSpec currently supports Windsurf but not Devin Desktop. Adding Devin Desktop support ensures users can continue using OpenSpec with their new tool without manual migration.
- The adapter pattern is already established and proven with Windsurf; extending it to Devin Desktop is straightforward and maintains consistency across the tool ecosystem.

## What Changes

- Add **Devin Desktop** (`devin`) to the CLI tool picker (`openspec init`) so users can select it during setup.
- Create a new **Devin adapter** (`src/core/command-generation/adapters/devin.ts`) that generates commands in `.devin/workflows/opsx-<id>.md` with the same frontmatter structure as Windsurf.
- Register the Devin adapter in the command adapter registry (`src/core/command-generation/registry.ts`) and export it from the adapters index.
- Update `docs/supported-tools.md` to include Devin Desktop in the tool reference table.
- Ensure `openspec update` refreshes existing Devin workflows in-place, mirroring current behavior for other editors.
- Extend unit tests for init/update to cover Devin Desktop generation and updates.
- Update CLI prompts and documentation to advertise Devin Desktop support.

## Impact

- **Specs:** `cli-init`, `cli-update`, `command-generation`
- **Code:**
- `src/core/command-generation/adapters/devin.ts` (new adapter)
- `src/core/command-generation/registry.ts` (register adapter)
- `src/core/command-generation/adapters/index.ts` (export adapter)
- CLI tool selection logic
- **Docs:** `docs/supported-tools.md`
- **Tests:** init/update integration coverage for Devin Desktop workflows

## Notes

- This is a **migration enabler** for existing Windsurf users transitioning to Devin Desktop.
- Windsurf support can remain in place for backward compatibility with users still on Windsurf.
- The implementation closely mirrors the existing Windsurf adapter, reducing complexity and risk.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## MODIFIED Requirements

### Requirement: AI Tool Configuration
The command SHALL configure AI coding assistants with OpenSpec instructions using a marker system.

#### Scenario: Prompting for AI tool selection
- **WHEN** run interactively
- **THEN** prompt the user with "Which AI tools do you use?" using a multi-select menu
- **AND** list every available tool with a checkbox:
- Claude Code (creates or refreshes CLAUDE.md and slash commands)
- Cursor (creates or refreshes `.cursor/commands/*` slash commands)
- OpenCode (creates or refreshes `.opencode/command/openspec-*.md` slash commands)
- Devin Desktop (creates or refreshes `.devin/workflows/opsx-*.md` workflows)
- Windsurf (creates or refreshes `.windsurf/workflows/opsx-*.md` workflows)
- AGENTS.md standard (creates or refreshes AGENTS.md with OpenSpec markers)
- **AND** show "(already configured)" beside tools whose managed files exist so users understand selections will refresh content
- **AND** treat disabled tools as "coming soon" and keep them unselectable
- **AND** allow confirming with Enter after selecting one or more tools

### Requirement: Slash Command Configuration
The init command SHALL generate slash command files for supported editors using shared templates.

#### Scenario: Generating workflows for Devin Desktop
- **WHEN** the user selects Devin Desktop during initialization
- **THEN** create `.devin/workflows/opsx-propose.md`, `.devin/workflows/opsx-apply.md`, and `.devin/workflows/opsx-archive.md`
- **AND** populate each file from shared templates (wrapped in OpenSpec markers) so workflow text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
- **AND** use the same frontmatter structure as Windsurf (name, description, category, tags)

#### Scenario: Generating workflows for Windsurf
- **WHEN** the user selects Windsurf during initialization
- **THEN** create `.windsurf/workflows/opsx-propose.md`, `.windsurf/workflows/opsx-apply.md`, and `.windsurf/workflows/opsx-archive.md`
- **AND** populate each file from shared templates (wrapped in OpenSpec markers) so workflow text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## MODIFIED Requirements

### Requirement: Slash Command Updates
The update command SHALL refresh existing slash command files for configured tools without creating new ones.

#### Scenario: Updating workflows for Devin Desktop
- **WHEN** `.devin/workflows/` contains `opsx-propose.md`, `opsx-apply.md`, and `opsx-archive.md`
- **THEN** refresh each file using shared templates wrapped in OpenSpec markers
- **AND** ensure templates include instructions for the relevant workflow stage
- **AND** preserve the frontmatter structure (name, description, category, tags)

#### Scenario: Updating workflows for Windsurf
- **WHEN** `.windsurf/workflows/` contains `opsx-propose.md`, `opsx-apply.md`, and `opsx-archive.md`
- **THEN** refresh each file using shared templates wrapped in OpenSpec markers
- **AND** ensure templates include instructions for the relevant workflow stage

#### Scenario: Missing workflow file
- **WHEN** a tool lacks a workflow file
- **THEN** do not create a new file during update
92 changes: 92 additions & 0 deletions openspec/changes/add-devin-desktop-support/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Implementation Tasks

## 1. Create Devin Desktop Adapter

### 1.1 Create adapter file
- Create `src/core/command-generation/adapters/devin.ts`
- Base implementation on the existing Windsurf adapter (`src/core/command-generation/adapters/windsurf.ts`)
- Use `.devin/workflows/` as the target directory
- Use `opsx-<id>.md` as the filename pattern
- Include frontmatter with: name, description, category, tags

### 1.2 Implement adapter interface
- Export `devinAdapter` object implementing `ToolCommandAdapter`
- Set `toolId` to `'devin'`
- Implement `getFilePath()` to return `.devin/workflows/opsx-<id>.md`
- Implement `formatFile()` to generate YAML frontmatter + body content

## 2. Register Adapter

### 2.1 Update registry
- Edit `src/core/command-generation/registry.ts`
- Import the new `devinAdapter`
- Register it in the static initializer: `CommandAdapterRegistry.register(devinAdapter)`

### 2.2 Export adapter
- Edit `src/core/command-generation/adapters/index.ts`
- Add export: `export { devinAdapter } from './devin.js'`
- Update main index if needed: `src/core/command-generation/index.ts`

## 3. Update CLI Tool Selection

### 3.1 Add Devin Desktop to tool picker
- Locate CLI initialization code that prompts for tool selection
- Add "Devin Desktop" option to the multi-select menu
- Ensure it appears alongside Windsurf and other tools
- Map selection to `devin` tool ID

## 4. Update Documentation

### 4.1 Update supported tools reference
- Edit `docs/supported-tools.md`
- Add Devin Desktop row to the tool directory reference table
- Include:
- Tool name and ID: `Devin Desktop (devin)`
- Skills path: `.devin/skills/openspec-*/SKILL.md`
- Command path: `.devin/workflows/opsx-<id>.md`
- Add `devin` to the available tool IDs list in the "Non-Interactive Setup" section
Comment thread
mehdishahdoost marked this conversation as resolved.

### 4.2 Update README if needed
- Check if README mentions tool count or lists specific tools
- Update any references to reflect Devin Desktop support

## 5. Add Tests

### 5.1 Test adapter functionality
- Create or update tests for the Devin adapter
- Test `getFilePath()` returns correct path
- Test `formatFile()` generates valid YAML frontmatter
- Test cross-platform path handling (Windows, macOS, Linux)

### 5.2 Test CLI integration
- Test `openspec init --tools devin` generates `.devin/workflows/` files
- Test `openspec update` refreshes existing Devin workflows
- Test that Devin Desktop appears in interactive tool selection
- Verify files are created with correct structure and content

### 5.3 Test backward compatibility
- Ensure Windsurf adapter still works
- Verify both Devin and Windsurf can be selected together
- Test that existing Windsurf installations are not affected

## 6. Verify and Polish

### 6.1 Manual testing
- Run `openspec init` and select Devin Desktop
- Verify `.devin/workflows/` directory is created
- Check that workflow files have correct frontmatter and content
- Run `openspec update` and verify files are refreshed
- Test on Windows, macOS, and Linux if possible

### 6.2 Code review checklist
- Adapter follows existing patterns (Windsurf, Cursor, Claude)
- No hardcoded paths (use `path.join()`)
- YAML escaping handles special characters
- Error handling is consistent with other adapters
- Comments are clear and helpful

### 6.3 Documentation review
- Supported tools table is accurate and complete
- Tool IDs are consistent across docs
- Examples show Devin Desktop usage
- Links and references are correct
63 changes: 63 additions & 0 deletions src/core/command-generation/adapters/devin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Devin Desktop Command Adapter
*
* Formats commands for Devin Desktop following its frontmatter specification.
* Devin Desktop uses the same Cascade workflow system as Windsurf.
*/

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

/**
* Escapes a string value for safe YAML output.
* Quotes the string if it contains special YAML characters or would be
* interpreted as an implicit YAML scalar (boolean, null, number, etc).
*/
function escapeYamlValue(value: string): string {
// Check if value needs quoting due to special YAML characters or whitespace
const hasSpecialChars = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);

// Check if value would be interpreted as an implicit YAML scalar
// Matches: booleans (true/false/yes/no/on/off), null variants, numbers, hex/octal
const isImplicitScalar = /^(true|false|yes|no|on|off|null|~|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?|0x[0-9a-fA-F]+|0o[0-7]+|-|\.?)$/.test(value);

if (hasSpecialChars || isImplicitScalar) {
// Use double quotes and escape internal double quotes and backslashes
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
return `"${escaped}"`;
}
return value;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/**
* Formats a tags array as a YAML array with proper escaping.
*/
function formatTagsArray(tags: string[]): string {
const escapedTags = tags.map((tag) => escapeYamlValue(tag));
return `[${escapedTags.join(', ')}]`;
}

/**
* Devin Desktop adapter for command generation.
* File path: .devin/workflows/opsx-<id>.md
* Frontmatter: name, description, category, tags
*/
export const devinAdapter: ToolCommandAdapter = {
toolId: 'devin',

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

formatFile(content: CommandContent): string {
return `---
name: ${escapeYamlValue(content.name)}
description: ${escapeYamlValue(content.description)}
category: ${escapeYamlValue(content.category)}
tags: ${formatTagsArray(content.tags)}
---

${content.body}
`;
},
};
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 @@ -11,6 +11,7 @@ export { bobAdapter } from './bob.js';
export { claudeAdapter } from './claude.js';
export { clineAdapter } from './cline.js';
export { codexAdapter } from './codex.js';
export { devinAdapter } from './devin.js';
export { codebuddyAdapter } from './codebuddy.js';
export { continueAdapter } from './continue.js';
export { costrictAdapter } from './costrict.js';
Expand Down
2 changes: 2 additions & 0 deletions src/core/command-generation/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { bobAdapter } from './adapters/bob.js';
import { claudeAdapter } from './adapters/claude.js';
import { clineAdapter } from './adapters/cline.js';
import { codexAdapter } from './adapters/codex.js';
import { devinAdapter } from './adapters/devin.js';
import { codebuddyAdapter } from './adapters/codebuddy.js';
import { continueAdapter } from './adapters/continue.js';
import { costrictAdapter } from './adapters/costrict.js';
Expand Down Expand Up @@ -48,6 +49,7 @@ export class CommandAdapterRegistry {
CommandAdapterRegistry.register(claudeAdapter);
CommandAdapterRegistry.register(clineAdapter);
CommandAdapterRegistry.register(codexAdapter);
CommandAdapterRegistry.register(devinAdapter);
CommandAdapterRegistry.register(codebuddyAdapter);
CommandAdapterRegistry.register(continueAdapter);
CommandAdapterRegistry.register(costrictAdapter);
Expand Down
1 change: 1 addition & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const AI_TOOLS: AIToolOption[] = [
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code', skillsDir: '.claude' },
{ name: 'Cline', value: 'cline', available: true, successLabel: 'Cline', skillsDir: '.cline' },
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex', skillsDir: '.codex' },
{ name: 'Devin Desktop', value: 'devin', available: true, successLabel: 'Devin Desktop', skillsDir: '.devin' },
{ name: 'ForgeCode', value: 'forgecode', available: true, successLabel: 'ForgeCode', skillsDir: '.forge' },
{ name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code', skillsDir: '.codebuddy' },
{ name: 'Continue', value: 'continue', available: true, successLabel: 'Continue (VS Code / JetBrains / Cli)', skillsDir: '.continue' },
Expand Down