@rune-kit/rune 2.17.1 → 2.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Aider Adapter
3
+ *
4
+ * Emits per-skill convention files into aider/rules/ and a `.aider.conf.yml`
5
+ * at the project root with a `read:` array pointing to every rule file. Aider
6
+ * loads everything in `read:` automatically on each session.
7
+ *
8
+ * Aider rules dir: aider/rules/
9
+ * Aider rule file: aider/rules/rune-{name}.md
10
+ * Aider config: .aider.conf.yml (auto-discovered: home dir → repo root → cwd)
11
+ *
12
+ * @see https://aider.chat/docs/usage/conventions.html
13
+ * @see https://aider.chat/docs/config/aider_conf.html
14
+ *
15
+ * MODEL TIER MAPPING (v2.18+):
16
+ * Aider supports any provider via env vars / CLI flags. Tier hint is emitted
17
+ * as a comment — aider does not parse model directives from rule files.
18
+ */
19
+
20
+ import { BRANDING_FOOTER } from '../transforms/branding.js';
21
+
22
+ const MODEL_MAP = {
23
+ opus: 'tier:heavy',
24
+ sonnet: 'tier:mid',
25
+ haiku: 'tier:light',
26
+ };
27
+
28
+ const TOOL_MAP = {
29
+ Read: 'read the file',
30
+ Write: 'write/create the file',
31
+ Edit: 'edit the file',
32
+ Glob: 'find files by pattern',
33
+ Grep: 'search file contents',
34
+ Bash: 'run a shell command',
35
+ TodoWrite: 'track task progress',
36
+ Skill: 'follow the referenced rune-{name} convention',
37
+ Agent: 'execute the workflow',
38
+ };
39
+
40
+ export default {
41
+ name: 'aider',
42
+ outputDir: 'aider/rules',
43
+ fileExtension: '.md',
44
+ skillPrefix: 'rune-',
45
+ skillSuffix: '',
46
+
47
+ // Aider conventions are flat per-skill markdown files.
48
+ useSkillDirectories: false,
49
+
50
+ transformReference(skillName, raw) {
51
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
52
+ const ref = `the rune-${skillName} convention`;
53
+ return isBackticked ? `\`${ref}\`` : ref;
54
+ },
55
+
56
+ transformToolName(toolName) {
57
+ return TOOL_MAP[toolName] || toolName;
58
+ },
59
+
60
+ generateHeader(skill) {
61
+ const tierHint = skill.model ? MODEL_MAP[skill.model] || skill.model : null;
62
+ const tierLine = tierHint ? ` | ${tierHint}` : '';
63
+ return `# rune-${skill.name}\n\n> Rune ${skill.layer} ${skill.group}${tierLine}\n> ${(skill.description || '').replace(/\n/g, ' ')}\n\n`;
64
+ },
65
+
66
+ generateFooter() {
67
+ return BRANDING_FOOTER;
68
+ },
69
+
70
+ transformSubagentInstruction(text) {
71
+ return text;
72
+ },
73
+
74
+ scriptsDir(skillName) {
75
+ return `rune-${skillName}-scripts`;
76
+ },
77
+
78
+ postProcess(content) {
79
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
80
+ },
81
+
82
+ // Emit `.aider.conf.yml` with `read:` array referencing every rule file +
83
+ // a CONVENTIONS.md as the high-level entry point.
84
+ generateExtraFiles({ stats }) {
85
+ // stats.files holds entries like 'rune-cook.md' (per-skill) and 'index.md' (autogen).
86
+ // Filter to per-skill rule files; aider loads everything in read[] on every session.
87
+ const ruleFiles = stats.files
88
+ .filter((f) => f.startsWith('rune-') && f.endsWith('.md') && !f.includes('/') && !f.includes('\\'))
89
+ .sort();
90
+
91
+ const conf = [
92
+ '# Auto-generated by Rune — do not edit manually.',
93
+ '# Aider auto-loads every file listed under `read:` on each session.',
94
+ 'read:',
95
+ ...ruleFiles.map((f) => ` - aider/rules/${f}`),
96
+ '',
97
+ ].join('\n');
98
+
99
+ const conventions = [
100
+ '# Rune Conventions for Aider',
101
+ '',
102
+ `Rune compiles ${stats.skillCount} skills + ${stats.packCount} packs into per-skill convention files under \`aider/rules/\`. Aider auto-loads them via \`.aider.conf.yml\`.`,
103
+ '',
104
+ 'When a request matches a skill (e.g. "implement", "refactor", "review"), follow the matching rune-<name> convention.',
105
+ '',
106
+ '## Loaded Conventions',
107
+ '',
108
+ ...ruleFiles.map((f) => `- \`aider/rules/${f}\``),
109
+ '',
110
+ '---',
111
+ '> Rune Skill Mesh — https://github.com/rune-kit/rune',
112
+ '',
113
+ ].join('\n');
114
+
115
+ return [
116
+ { path: '.aider.conf.yml', content: conf },
117
+ { path: 'CONVENTIONS.md', content: conventions },
118
+ ];
119
+ },
120
+ };
@@ -80,4 +80,37 @@ export default {
80
80
  postProcess(content) {
81
81
  return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
82
82
  },
83
+
84
+ // Codex (and OpenAI's AGENTS.md convention generally) reads AGENTS.md at the
85
+ // project root for high-level context. Migrated from emitter.js v2.18.
86
+ generateExtraFiles({ stats }) {
87
+ const agentsMd = [
88
+ '# Rune — Project Configuration',
89
+ '',
90
+ '## Overview',
91
+ '',
92
+ 'Rune is an interconnected skill ecosystem for AI coding assistants.',
93
+ `${stats.skillCount} core skills | 5-layer mesh architecture | ${stats.crossRefsResolved} connections | Multi-platform.`,
94
+ 'Philosophy: "Less skills. Deeper connections."',
95
+ '',
96
+ 'Platform: codex',
97
+ '',
98
+ '## Skills',
99
+ '',
100
+ `**${stats.skillCount} core skills** + **${stats.packCount} extension packs**`,
101
+ '',
102
+ '## Usage',
103
+ '',
104
+ 'Reference skills using the `Skill` tool or delegate to subagents using the `Agent` tool.',
105
+ '',
106
+ '## Skills Directory',
107
+ '',
108
+ 'Skills are located in: .codex/skills/',
109
+ '',
110
+ '---',
111
+ '> Rune Skill Mesh — https://github.com/rune-kit/rune',
112
+ '',
113
+ ].join('\n');
114
+ return [{ path: 'AGENTS.md', content: agentsMd }];
115
+ },
83
116
  };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * GitHub Copilot CLI Adapter
3
+ *
4
+ * Emits per-skill instruction files into .github/instructions/ — the documented
5
+ * convention for GitHub Copilot custom instructions (read by both the CLI and
6
+ * the GitHub Copilot IDE plugin family).
7
+ *
8
+ * Copilot instructions dir: .github/instructions/
9
+ * Copilot instruction file: .github/instructions/rune-{name}.instructions.md
10
+ * Copilot project context: AGENTS.md (Copilot Spaces / Coding Agent convention)
11
+ *
12
+ * Each .instructions.md uses YAML frontmatter with `applyTo` for path scoping.
13
+ * Default applyTo: "**" means "apply to all files."
14
+ *
15
+ * @see https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-custom-instructions
16
+ * @see https://docs.github.com/en/copilot/concepts/response-customization
17
+ *
18
+ * MODEL TIER MAPPING (v2.18+):
19
+ * Copilot multi-model support exposes Anthropic/OpenAI/Google providers. Tier
20
+ * names are emitted as a hint comment in the body — Copilot does not parse a
21
+ * model field, so we don't fight the platform.
22
+ */
23
+
24
+ import { BRANDING_FOOTER } from '../transforms/branding.js';
25
+
26
+ const MODEL_MAP = {
27
+ opus: 'tier:heavy',
28
+ sonnet: 'tier:mid',
29
+ haiku: 'tier:light',
30
+ };
31
+
32
+ const TOOL_MAP = {
33
+ Read: 'read the file',
34
+ Write: 'write/create the file',
35
+ Edit: 'edit the file',
36
+ Glob: 'find files by pattern',
37
+ Grep: 'search file contents',
38
+ Bash: 'run a shell command',
39
+ TodoWrite: 'track task progress',
40
+ Skill: 'follow the referenced instructions',
41
+ Agent: 'execute the workflow',
42
+ };
43
+
44
+ export default {
45
+ name: 'copilot',
46
+ outputDir: '.github/instructions',
47
+ fileExtension: '.instructions.md',
48
+ skillPrefix: 'rune-',
49
+ skillSuffix: '',
50
+
51
+ // Copilot instructions are flat per-skill files, not directories.
52
+ useSkillDirectories: false,
53
+
54
+ transformReference(skillName, raw) {
55
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
56
+ const ref = `the rune-${skillName} instructions`;
57
+ return isBackticked ? `\`${ref}\`` : ref;
58
+ },
59
+
60
+ transformToolName(toolName) {
61
+ return TOOL_MAP[toolName] || toolName;
62
+ },
63
+
64
+ generateHeader(skill) {
65
+ // Per docs.github.com Copilot CLI custom-instructions spec, the only
66
+ // documented frontmatter key for `.instructions.md` is `applyTo`. Other
67
+ // metadata (description, tier hint) belongs in the markdown body so it
68
+ // survives parsing across CLI/IDE/extensions consistently.
69
+ const desc = (skill.description || '').replace(/\n/g, ' ');
70
+ const tierHint = skill.model ? MODEL_MAP[skill.model] || skill.model : null;
71
+ const lines = ['---', 'applyTo: "**"', '---', '', `# rune-${skill.name}`, '', `> ${desc}`];
72
+ if (tierHint) lines.push('', `<!-- tier-hint: ${tierHint} -->`);
73
+ lines.push('', '');
74
+ return lines.join('\n');
75
+ },
76
+
77
+ generateFooter() {
78
+ return BRANDING_FOOTER;
79
+ },
80
+
81
+ transformSubagentInstruction(text) {
82
+ return text;
83
+ },
84
+
85
+ scriptsDir(skillName) {
86
+ return `rune-${skillName}-scripts`;
87
+ },
88
+
89
+ postProcess(content) {
90
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
91
+ },
92
+
93
+ // Emit a top-level .github/copilot-instructions.md as the entry point + an
94
+ // AGENTS.md so Copilot Spaces / Coding Agent can find the project context.
95
+ generateExtraFiles({ stats }) {
96
+ const copilotIndex = [
97
+ '# Rune — Copilot Custom Instructions',
98
+ '',
99
+ `Per-skill instructions live under \`.github/instructions/rune-<name>.instructions.md\` (${stats.skillCount} skills + ${stats.packCount} packs). Copilot loads them based on each file's \`applyTo\` glob.`,
100
+ '',
101
+ 'When a user request matches a skill\'s domain (e.g. "fix the failing test", "review this PR"), prefer following the corresponding rune-<name>.instructions.md over freestyling.',
102
+ '',
103
+ '---',
104
+ '> Rune Skill Mesh — https://github.com/rune-kit/rune',
105
+ '',
106
+ ].join('\n');
107
+
108
+ const agentsMd = [
109
+ '# Rune — Project Configuration',
110
+ '',
111
+ 'Rune is an interconnected skill ecosystem for AI coding assistants.',
112
+ `${stats.skillCount} core skills + ${stats.packCount} extension packs.`,
113
+ '',
114
+ 'Per-skill custom instructions: `.github/instructions/rune-<name>.instructions.md`',
115
+ '',
116
+ '---',
117
+ '> Rune Skill Mesh — https://github.com/rune-kit/rune',
118
+ '',
119
+ ].join('\n');
120
+
121
+ return [
122
+ { path: '.github/copilot-instructions.md', content: copilotIndex },
123
+ { path: 'AGENTS.md', content: agentsMd },
124
+ ];
125
+ },
126
+ };
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Gemini CLI Adapter
3
+ *
4
+ * Gemini CLI loads a single GEMINI.md at project root for context. It does NOT
5
+ * support per-skill imports as of writing — so the canonical output is a
6
+ * bundled GEMINI.md with every skill concatenated under `## rune-<name>` H2
7
+ * sections. Per-skill files are still emitted under gemini/skills/ for human
8
+ * inspection and forward-compatibility if Gemini adds @import support later.
9
+ *
10
+ * Gemini context file: GEMINI.md (project root)
11
+ * Gemini per-skill files: gemini/skills/rune-{name}.md (forward-compat staging)
12
+ *
13
+ * @see https://github.com/google-gemini/gemini-cli
14
+ * @see https://geminicli.com/docs/reference/configuration/
15
+ *
16
+ * MODEL TIER MAPPING (v2.18+):
17
+ * Gemini CLI exposes 1.5 Pro / 1.5 Flash / 2.0 Flash etc. Anthropic tier names
18
+ * translate to Gemini families: opus→1.5-pro, sonnet→1.5-flash, haiku→
19
+ * 2.0-flash-lite. Hint only — Gemini CLI reads model from --model flag /
20
+ * config, not from rule body.
21
+ */
22
+
23
+ import { readFile } from 'node:fs/promises';
24
+ import nodePath from 'node:path';
25
+ import { BRANDING_FOOTER } from '../transforms/branding.js';
26
+
27
+ const MODEL_MAP = {
28
+ opus: 'gemini-2.5-pro',
29
+ sonnet: 'gemini-2.5-flash',
30
+ haiku: 'gemini-2.0-flash-lite',
31
+ };
32
+
33
+ const TOOL_MAP = {
34
+ Read: 'read the file',
35
+ Write: 'write/create the file',
36
+ Edit: 'edit the file',
37
+ Glob: 'find files by pattern',
38
+ Grep: 'search file contents',
39
+ Bash: 'run a shell command',
40
+ TodoWrite: 'track task progress',
41
+ Skill: 'follow the referenced rune-{name} section in GEMINI.md',
42
+ Agent: 'execute the workflow',
43
+ };
44
+
45
+ export default {
46
+ name: 'gemini',
47
+ outputDir: 'gemini/skills',
48
+ fileExtension: '.md',
49
+ skillPrefix: 'rune-',
50
+ skillSuffix: '',
51
+
52
+ // Per-skill files staged for forward compat; canonical entry is bundled GEMINI.md.
53
+ useSkillDirectories: false,
54
+
55
+ transformReference(skillName, raw) {
56
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
57
+ const ref = `the rune-${skillName} section in GEMINI.md`;
58
+ return isBackticked ? `\`${ref}\`` : ref;
59
+ },
60
+
61
+ transformToolName(toolName) {
62
+ return TOOL_MAP[toolName] || toolName;
63
+ },
64
+
65
+ generateHeader(skill) {
66
+ const desc = (skill.description || '').replace(/"/g, '\\"');
67
+ const lines = ['---', `name: rune-${skill.name}`, `description: "${desc}"`];
68
+ const translatedModel = skill.model ? MODEL_MAP[skill.model] || skill.model : null;
69
+ if (translatedModel) lines.push(`# model-hint: ${translatedModel}`);
70
+ lines.push('---', '', '');
71
+ return lines.join('\n');
72
+ },
73
+
74
+ generateFooter() {
75
+ return BRANDING_FOOTER;
76
+ },
77
+
78
+ transformSubagentInstruction(text) {
79
+ return text;
80
+ },
81
+
82
+ scriptsDir(skillName) {
83
+ return `rune-${skillName}-scripts`;
84
+ },
85
+
86
+ postProcess(content) {
87
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
88
+ },
89
+
90
+ // Bundle every per-skill file into a single GEMINI.md with H2 section per skill.
91
+ // Gemini CLI loads GEMINI.md from project root as its context file. Hook contract
92
+ // requires relative paths (resolved against outputRoot by the emitter).
93
+ async generateExtraFiles({ stats, outputDir }) {
94
+ const skillFiles = [...stats.files]
95
+ .filter((f) => f.startsWith('rune-') && f.endsWith('.md') && !f.includes('/') && !f.includes('\\'))
96
+ .sort();
97
+
98
+ const sections = [];
99
+ for (const fname of skillFiles) {
100
+ const sourcePath = nodePath.join(outputDir, fname);
101
+ try {
102
+ const raw = await readFile(sourcePath, 'utf-8');
103
+ // Strip frontmatter — Gemini reads plain markdown.
104
+ const stripped = raw.replace(/^---\n[\s\S]*?\n---\n?/, '').trim();
105
+ const skillName = fname.replace(/^rune-/, '').replace(/\.md$/, '');
106
+ sections.push(`\n\n## rune-${skillName}\n\n${stripped}`);
107
+ } catch {
108
+ // Skip unreadable per-skill file — sections array still produces valid bundle.
109
+ }
110
+ }
111
+
112
+ const geminiMd = [
113
+ '# Rune — Project Configuration',
114
+ '',
115
+ 'Rune is an interconnected skill ecosystem for AI coding assistants.',
116
+ `${stats.skillCount} core skills + ${stats.packCount} extension packs, bundled below.`,
117
+ '',
118
+ 'When a user request matches a skill\'s domain (e.g. "implement", "review", "debug"), follow the matching `## rune-<name>` section.',
119
+ '',
120
+ '> Per-skill files are staged in `gemini/skills/` for forward-compat if Gemini CLI adds @import support.',
121
+ '',
122
+ ...sections,
123
+ '',
124
+ '---',
125
+ '> Rune Skill Mesh — https://github.com/rune-kit/rune',
126
+ '',
127
+ ].join('\n');
128
+
129
+ return [{ path: 'GEMINI.md', content: geminiMd }];
130
+ },
131
+ };
@@ -4,13 +4,18 @@
4
4
  * Central registry for all platform adapters.
5
5
  */
6
6
 
7
+ import aider from './aider.js';
7
8
  import antigravity from './antigravity.js';
8
9
  import claude from './claude.js';
9
10
  import codex from './codex.js';
11
+ import copilot from './copilot.js';
10
12
  import cursor from './cursor.js';
13
+ import gemini from './gemini.js';
11
14
  import generic from './generic.js';
12
15
  import openclaw from './openclaw.js';
13
16
  import opencode from './opencode.js';
17
+ import qoder from './qoder.js';
18
+ import qwen from './qwen.js';
14
19
  import windsurf from './windsurf.js';
15
20
 
16
21
  const adapters = {
@@ -22,6 +27,11 @@ const adapters = {
22
27
  openclaw,
23
28
  codex,
24
29
  opencode,
30
+ aider,
31
+ copilot,
32
+ gemini,
33
+ qoder,
34
+ qwen,
25
35
  };
26
36
 
27
37
  /**
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Qoder Adapter
3
+ *
4
+ * Emits per-skill rule files into .qoder/rules/ — Qoder's documented project-level
5
+ * rule directory. Qoder also reads AGENTS.md as its project-context file.
6
+ *
7
+ * Qoder rules dir: .qoder/rules/
8
+ * Qoder rule file: .qoder/rules/rune-{name}.md (one file per skill)
9
+ * Qoder project context: AGENTS.md (open AGENTS.md standard)
10
+ *
11
+ * @see https://docs.qoder.com/user-guide/rules
12
+ * @see https://agents.md/
13
+ *
14
+ * MODEL TIER MAPPING (v2.18+):
15
+ * Qoder is provider-agnostic — emits semantic tier hints rather than concrete
16
+ * model names. Qoder IDE resolves the tier to its configured provider model.
17
+ */
18
+
19
+ import { BRANDING_FOOTER } from '../transforms/branding.js';
20
+
21
+ const MODEL_MAP = {
22
+ opus: 'tier:heavy',
23
+ sonnet: 'tier:mid',
24
+ haiku: 'tier:light',
25
+ };
26
+
27
+ const TOOL_MAP = {
28
+ Read: 'read the file',
29
+ Write: 'write/create the file',
30
+ Edit: 'edit the file',
31
+ Glob: 'find files by pattern',
32
+ Grep: 'search file contents',
33
+ Bash: 'run a shell command',
34
+ TodoWrite: 'track task progress',
35
+ Skill: 'follow the referenced rune-{name} rule',
36
+ Agent: 'execute the workflow',
37
+ };
38
+
39
+ export default {
40
+ name: 'qoder',
41
+ outputDir: '.qoder/rules',
42
+ fileExtension: '.md',
43
+ skillPrefix: 'rune-',
44
+ skillSuffix: '',
45
+
46
+ // Qoder rules are flat .md files, not directory-per-skill
47
+ useSkillDirectories: false,
48
+
49
+ transformReference(skillName, raw) {
50
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
51
+ const ref = `the rune-${skillName} rule`;
52
+ return isBackticked ? `\`${ref}\`` : ref;
53
+ },
54
+
55
+ transformToolName(toolName) {
56
+ return TOOL_MAP[toolName] || toolName;
57
+ },
58
+
59
+ generateHeader(skill) {
60
+ const desc = (skill.description || '').replace(/"/g, '\\"');
61
+ const lines = ['---', `name: rune-${skill.name}`, `description: "${desc}"`];
62
+ const translatedModel = skill.model ? MODEL_MAP[skill.model] || skill.model : null;
63
+ if (translatedModel) lines.push(`model: ${translatedModel}`);
64
+ lines.push('---', '', '');
65
+ return lines.join('\n');
66
+ },
67
+
68
+ generateFooter() {
69
+ return BRANDING_FOOTER;
70
+ },
71
+
72
+ transformSubagentInstruction(text) {
73
+ return text;
74
+ },
75
+
76
+ scriptsDir(skillName) {
77
+ return `rune-${skillName}-scripts`;
78
+ },
79
+
80
+ postProcess(content) {
81
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
82
+ },
83
+
84
+ // Qoder reads AGENTS.md at the project root as its high-level context file.
85
+ generateExtraFiles({ stats }) {
86
+ const agentsMd = [
87
+ '# Rune — Project Configuration',
88
+ '',
89
+ 'Rune is an interconnected skill ecosystem for AI coding assistants.',
90
+ `${stats.skillCount} core skills + ${stats.packCount} extension packs.`,
91
+ '',
92
+ 'Per-skill rules live under `.qoder/rules/rune-<name>.md`. Qoder loads them automatically.',
93
+ '',
94
+ 'Reference a skill by name (e.g. "follow the rune-cook rule") inside any chat — the rule file is auto-injected.',
95
+ '',
96
+ '---',
97
+ '> Rune Skill Mesh — https://github.com/rune-kit/rune',
98
+ '',
99
+ ].join('\n');
100
+ return [{ path: 'AGENTS.md', content: agentsMd }];
101
+ },
102
+ };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Qwen Coder Adapter
3
+ *
4
+ * Emits per-skill rule files into qwen/skills/ and a top-level QWEN.md that
5
+ * uses Qwen Code's @import syntax to load each rule file. Qwen Code loads
6
+ * QWEN.md hierarchically (cwd → parent → ~/.qwen/QWEN.md).
7
+ *
8
+ * Qwen rules dir: qwen/skills/
9
+ * Qwen rule file: qwen/skills/rune-{name}.md
10
+ * Qwen project context: QWEN.md at project root (with @path imports)
11
+ *
12
+ * @see https://qwenlm.github.io/qwen-code-docs/en/users/configuration/settings/
13
+ * @see https://github.com/QwenLM/qwen-code
14
+ *
15
+ * MODEL TIER MAPPING (v2.18+):
16
+ * Qwen Coder defaults to Qwen3-Coder family. Anthropic-style tier names
17
+ * translate to Qwen size hints (heavy=qwen3-coder-plus, mid=qwen3-coder,
18
+ * light=qwen3-coder-flash). Hint only — Qwen Code reads model from settings.
19
+ */
20
+
21
+ import { BRANDING_FOOTER } from '../transforms/branding.js';
22
+
23
+ const MODEL_MAP = {
24
+ opus: 'qwen3-coder-plus',
25
+ sonnet: 'qwen3-coder',
26
+ haiku: 'qwen3-coder-flash',
27
+ };
28
+
29
+ const TOOL_MAP = {
30
+ Read: 'read the file',
31
+ Write: 'write/create the file',
32
+ Edit: 'edit the file',
33
+ Glob: 'find files by pattern',
34
+ Grep: 'search file contents',
35
+ Bash: 'run a shell command',
36
+ TodoWrite: 'track task progress',
37
+ Skill: 'follow the imported rune-{name} skill',
38
+ Agent: 'execute the workflow',
39
+ };
40
+
41
+ export default {
42
+ name: 'qwen',
43
+ outputDir: 'qwen/skills',
44
+ fileExtension: '.md',
45
+ skillPrefix: 'rune-',
46
+ skillSuffix: '',
47
+
48
+ // Qwen skills are flat per-skill markdown files imported from QWEN.md.
49
+ useSkillDirectories: false,
50
+
51
+ transformReference(skillName, raw) {
52
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
53
+ const ref = `the rune-${skillName} skill`;
54
+ return isBackticked ? `\`${ref}\`` : ref;
55
+ },
56
+
57
+ transformToolName(toolName) {
58
+ return TOOL_MAP[toolName] || toolName;
59
+ },
60
+
61
+ generateHeader(skill) {
62
+ const desc = (skill.description || '').replace(/"/g, '\\"');
63
+ const lines = ['---', `name: rune-${skill.name}`, `description: "${desc}"`];
64
+ const translatedModel = skill.model ? MODEL_MAP[skill.model] || skill.model : null;
65
+ if (translatedModel) lines.push(`# model-hint: ${translatedModel}`);
66
+ lines.push('---', '', '');
67
+ return lines.join('\n');
68
+ },
69
+
70
+ generateFooter() {
71
+ return BRANDING_FOOTER;
72
+ },
73
+
74
+ transformSubagentInstruction(text) {
75
+ return text;
76
+ },
77
+
78
+ scriptsDir(skillName) {
79
+ return `rune-${skillName}-scripts`;
80
+ },
81
+
82
+ postProcess(content) {
83
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
84
+ },
85
+
86
+ // Emit QWEN.md at project root with @import lines for every per-skill file.
87
+ generateExtraFiles({ stats }) {
88
+ const skillFiles = stats.files
89
+ .filter((f) => f.startsWith('rune-') && f.endsWith('.md') && !f.includes('/') && !f.includes('\\'))
90
+ .sort();
91
+
92
+ const qwenMd = [
93
+ '# Rune — Project Configuration',
94
+ '',
95
+ 'Rune is an interconnected skill ecosystem for AI coding assistants.',
96
+ `${stats.skillCount} core skills + ${stats.packCount} extension packs.`,
97
+ '',
98
+ '## Loaded Skills',
99
+ '',
100
+ 'Qwen Code loads each referenced file as part of this project context.',
101
+ '',
102
+ ...skillFiles.map((f) => `@qwen/skills/${f}`),
103
+ '',
104
+ '---',
105
+ '> Rune Skill Mesh — https://github.com/rune-kit/rune',
106
+ '',
107
+ ].join('\n');
108
+
109
+ return [{ path: 'QWEN.md', content: qwenMd }];
110
+ },
111
+ };
@@ -20,13 +20,13 @@ import { fileURLToPath } from 'node:url';
20
20
  import { getAdapter, listPlatforms } from '../adapters/index.js';
21
21
  import { getAllAnalytics } from '../analytics.js';
22
22
  import { dispatchHook } from '../commands/hook-dispatch.js';
23
+ import { checkHookDrift, formatHookDriftResult } from '../commands/hooks/drift.js';
23
24
  import { installHooks } from '../commands/hooks/install.js';
24
25
  import { hookStatus } from '../commands/hooks/status.js';
25
26
  import { uninstallHooks } from '../commands/hooks/uninstall.js';
27
+ import { formatSetupResult, runSetup } from '../commands/setup.js';
26
28
  import { generateDashboardHTML } from '../dashboard.js';
27
29
  import { checkMeshIntegrity, formatDoctorResults, formatMeshResults, runDoctor } from '../doctor.js';
28
- import { checkHookDrift, formatHookDriftResult } from '../commands/hooks/drift.js';
29
- import { runSetup, formatSetupResult } from '../commands/setup.js';
30
30
  import { buildAll } from '../emitter.js';
31
31
  import { collectStats, renderStatus, renderStatusJson } from '../status.js';
32
32
  import { collectGraphData, generateMeshHTML } from '../visualizer.js';
@@ -720,7 +720,9 @@ async function main() {
720
720
  log(' Rune CLI — Skill mesh for AI coding assistants');
721
721
  log('');
722
722
  log(' Commands:');
723
- log(' setup Interactive wizard — auto-detect tiers, pick scope, install hooks (recommended for first-time)');
723
+ log(
724
+ ' setup Interactive wizard — auto-detect tiers, pick scope, install hooks (recommended for first-time)',
725
+ );
724
726
  log(' [--here|--global] [--tier pro,business] [--preset gentle|strict] [--dry]');
725
727
  log(' init Interactive setup for build pipeline (auto-detects platform)');
726
728
  log(' build Compile skills for configured platform');