Skip to content

Commit c06afed

Browse files
authored
Merge branch 'main' into iflow
2 parents 49ada35 + 8386b91 commit c06afed

File tree

12 files changed

+356
-7
lines changed

12 files changed

+356
-7
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@fission-ai/openspec": minor
3+
---
4+
5+
Add support for new AI assistants and configuration improvements
6+
7+
- feat: add Qwen Code support with slash command integration
8+
- feat: add $ARGUMENTS support to apply slash command for dynamic variable passing
9+
- feat: add Qoder CLI support to configuration and documentation
10+
- feat: add CoStrict AI assistant support
11+
- fix: recreate missing openspec template files in extend mode
12+
- fix: prevent false 'already configured' detection for tools
13+
- fix: use change-id as fallback title instead of "Untitled Change"
14+
- docs: add guidance for populating project-level context
15+
- docs: add Crush to supported AI tools in README

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,4 @@ CLAUDE.md
149149
.DS_Store
150150

151151
# Pnpm
152-
.pnpm-store/
152+
.pnpm-store/

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ These tools have built-in OpenSpec commands. Select the OpenSpec integration whe
105105
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
106106
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
107107
| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.augment/commands/`) |
108+
| **Qwen Code** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.qwen/commands/`) |
108109

109110

110111
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"@changesets/cli": "^2.27.7",
6060
"@types/node": "^24.2.0",
6161
"@vitest/ui": "^3.2.4",
62-
"typescript": "^5.9.2",
62+
"typescript": "^5.9.3",
6363
"vitest": "^3.2.4"
6464
},
6565
"dependencies": {

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ export const AI_TOOLS: AIToolOption[] = [
3333
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex' },
3434
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot' },
3535
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer' },
36+
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code' },
3637
{ name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
3738
];

src/core/configurators/qwen.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Qwen Code configurator for OpenSpec integration.
3+
* This class handles the configuration of Qwen Code as an AI tool within OpenSpec.
4+
*
5+
* @implements {ToolConfigurator}
6+
*/
7+
import path from 'path';
8+
import { ToolConfigurator } from './base.js';
9+
import { FileSystemUtils } from '../../utils/file-system.js';
10+
import { TemplateManager } from '../templates/index.js';
11+
import { OPENSPEC_MARKERS } from '../config.js';
12+
13+
/**
14+
* QwenConfigurator class provides integration with Qwen Code
15+
* by creating and managing the necessary configuration files.
16+
* Currently configures the QWEN.md file with OpenSpec instructions.
17+
*/
18+
export class QwenConfigurator implements ToolConfigurator {
19+
/** Display name for the Qwen Code tool */
20+
name = 'Qwen Code';
21+
22+
/** Configuration file name for Qwen Code */
23+
configFileName = 'QWEN.md';
24+
25+
/** Availability status for the Qwen Code tool */
26+
isAvailable = true;
27+
28+
/**
29+
* Configures the Qwen Code integration by creating or updating the QWEN.md file
30+
* with OpenSpec instructions and markers.
31+
*
32+
* @param {string} projectPath - The path to the project root
33+
* @param {string} _openspecDir - The path to the openspec directory (unused)
34+
* @returns {Promise<void>} A promise that resolves when configuration is complete
35+
*/
36+
async configure(projectPath: string, _openspecDir: string): Promise<void> {
37+
const filePath = path.join(projectPath, this.configFileName);
38+
const content = TemplateManager.getAgentsStandardTemplate();
39+
40+
await FileSystemUtils.updateFileWithMarkers(
41+
filePath,
42+
content,
43+
OPENSPEC_MARKERS.start,
44+
OPENSPEC_MARKERS.end
45+
);
46+
}
47+
}

src/core/configurators/registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { IflowConfigurator } from './iflow.js';
66
import { CostrictConfigurator } from './costrict.js';
77
import { QoderConfigurator } from './qoder.js';
88
import { AgentsStandardConfigurator } from './agents.js';
9+
import { QwenConfigurator } from './qwen.js';
910

1011
export class ToolRegistry {
1112
private static tools: Map<string, ToolConfigurator> = new Map();
@@ -18,6 +19,7 @@ export class ToolRegistry {
1819
const costrictConfigurator = new CostrictConfigurator();
1920
const qoderConfigurator = new QoderConfigurator();
2021
const agentsConfigurator = new AgentsStandardConfigurator();
22+
const qwenConfigurator = new QwenConfigurator();
2123
// Register with the ID that matches the checkbox value
2224
this.tools.set('claude', claudeConfigurator);
2325
this.tools.set('cline', clineConfigurator);
@@ -26,6 +28,7 @@ export class ToolRegistry {
2628
this.tools.set('costrict', costrictConfigurator);
2729
this.tools.set('qoder', qoderConfigurator);
2830
this.tools.set('agents', agentsConfigurator);
31+
this.tools.set('qwen', qwenConfigurator);
2932
}
3033

3134
static register(tool: ToolConfigurator): void {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* Qwen slash command configurator for OpenSpec integration.
3+
* This class handles the generation of Qwen-specific slash command files
4+
* in the .qwen/commands directory structure.
5+
*
6+
* @implements {SlashCommandConfigurator}
7+
*/
8+
import { SlashCommandConfigurator } from './base.js';
9+
import { SlashCommandId } from '../../templates/index.js';
10+
11+
/**
12+
* Mapping of slash command IDs to their corresponding file paths in .qwen/commands directory.
13+
* @type {Record<SlashCommandId, string>}
14+
*/
15+
const FILE_PATHS: Record<SlashCommandId, string> = {
16+
proposal: '.qwen/commands/openspec-proposal.md',
17+
apply: '.qwen/commands/openspec-apply.md',
18+
archive: '.qwen/commands/openspec-archive.md'
19+
};
20+
21+
/**
22+
* YAML frontmatter definitions for Qwen command files.
23+
* These provide metadata for each slash command to ensure proper recognition by Qwen Code.
24+
* @type {Record<SlashCommandId, string>}
25+
*/
26+
const FRONTMATTER: Record<SlashCommandId, string> = {
27+
proposal: `---
28+
name: /openspec-proposal
29+
id: openspec-proposal
30+
category: OpenSpec
31+
description: Scaffold a new OpenSpec change and validate strictly.
32+
---`,
33+
apply: `---
34+
name: /openspec-apply
35+
id: openspec-apply
36+
category: OpenSpec
37+
description: Implement an approved OpenSpec change and keep tasks in sync.
38+
---`,
39+
archive: `---
40+
name: /openspec-archive
41+
id: openspec-archive
42+
category: OpenSpec
43+
description: Archive a deployed OpenSpec change and update specs.
44+
---`
45+
};
46+
47+
/**
48+
* QwenSlashCommandConfigurator class provides integration with Qwen Code
49+
* by creating the necessary slash command files in the .qwen/commands directory.
50+
*
51+
* The slash commands include:
52+
* - /openspec-proposal: Create an OpenSpec change proposal
53+
* - /openspec-apply: Apply an approved OpenSpec change
54+
* - /openspec-archive: Archive a deployed OpenSpec change
55+
*/
56+
export class QwenSlashCommandConfigurator extends SlashCommandConfigurator {
57+
/** Unique identifier for the Qwen tool */
58+
readonly toolId = 'qwen';
59+
60+
/** Availability status for the Qwen tool */
61+
readonly isAvailable = true;
62+
63+
/**
64+
* Returns the relative file path for a given slash command ID.
65+
* @param {SlashCommandId} id - The slash command identifier
66+
* @returns {string} The relative path to the command file
67+
*/
68+
protected getRelativePath(id: SlashCommandId): string {
69+
return FILE_PATHS[id];
70+
}
71+
72+
/**
73+
* Returns the YAML frontmatter for a given slash command ID.
74+
* @param {SlashCommandId} id - The slash command identifier
75+
* @returns {string} The YAML frontmatter string
76+
*/
77+
protected getFrontmatter(id: SlashCommandId): string {
78+
return FRONTMATTER[id];
79+
}
80+
}

src/core/configurators/slash/registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ClineSlashCommandConfigurator } from './cline.js';
1515
import { CrushSlashCommandConfigurator } from './crush.js';
1616
import { IflowSlashCommandConfigurator } from './iflow.js';
1717
import { CostrictSlashCommandConfigurator } from './costrict.js';
18+
import { QwenSlashCommandConfigurator } from './qwen.js';
1819

1920
export class SlashCommandRegistry {
2021
private static configurators: Map<string, SlashCommandConfigurator> = new Map();
@@ -36,6 +37,7 @@ export class SlashCommandRegistry {
3637
const crush = new CrushSlashCommandConfigurator();
3738
const iflow = new IflowSlashCommandConfigurator();
3839
const costrict = new CostrictSlashCommandConfigurator();
40+
const qwen = new QwenSlashCommandConfigurator();
3941

4042
this.configurators.set(claude.toolId, claude);
4143
this.configurators.set(codeBuddy.toolId, codeBuddy);
@@ -53,6 +55,7 @@ export class SlashCommandRegistry {
5355
this.configurators.set(crush.toolId, crush);
5456
this.configurators.set(iflow.toolId, iflow);
5557
this.configurators.set(costrict.toolId, costrict);
58+
this.configurators.set(qwen.toolId, qwen);
5659
}
5760

5861
static register(configurator: SlashCommandConfigurator): void {

0 commit comments

Comments
 (0)