@rune-kit/rune 2.2.2 → 2.2.4

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.
Files changed (45) hide show
  1. package/compiler/__tests__/adapters.test.js +109 -0
  2. package/compiler/__tests__/openclaw-adapter.test.js +149 -140
  3. package/compiler/__tests__/pack-split.test.js +145 -145
  4. package/compiler/__tests__/parser.test.js +3 -3
  5. package/compiler/__tests__/pipeline.test.js +110 -0
  6. package/compiler/__tests__/skill-validation.test.js +148 -0
  7. package/compiler/__tests__/transformer.test.js +70 -0
  8. package/compiler/__tests__/transforms.test.js +92 -0
  9. package/compiler/adapters/antigravity.js +5 -6
  10. package/compiler/adapters/claude.js +1 -1
  11. package/compiler/adapters/codex.js +69 -77
  12. package/compiler/adapters/cursor.js +5 -6
  13. package/compiler/adapters/generic.js +5 -6
  14. package/compiler/adapters/index.js +3 -3
  15. package/compiler/adapters/openclaw.js +146 -150
  16. package/compiler/adapters/opencode.js +78 -86
  17. package/compiler/adapters/windsurf.js +5 -6
  18. package/compiler/bin/rune.js +32 -23
  19. package/compiler/doctor.js +19 -7
  20. package/compiler/emitter.js +11 -18
  21. package/compiler/parser.js +5 -5
  22. package/compiler/transformer.js +3 -5
  23. package/compiler/transforms/branding.js +5 -4
  24. package/compiler/transforms/compliance.js +40 -40
  25. package/compiler/transforms/frontmatter.js +1 -1
  26. package/compiler/transforms/hooks.js +12 -14
  27. package/compiler/transforms/subagents.js +1 -3
  28. package/compiler/transforms/tool-names.js +1 -1
  29. package/docs/guides/index.html +48 -7
  30. package/docs/index.html +67 -2
  31. package/docs/style.css +18 -1
  32. package/extensions/security/PACK.md +4 -3
  33. package/extensions/security/skills/defense-in-depth.md +103 -0
  34. package/package.json +8 -1
  35. package/skills/completion-gate/SKILL.md +31 -1
  36. package/skills/cook/SKILL.md +20 -2
  37. package/skills/debug/SKILL.md +16 -1
  38. package/skills/hallucination-guard/SKILL.md +21 -6
  39. package/skills/plan/SKILL.md +90 -1
  40. package/skills/review/SKILL.md +1 -0
  41. package/skills/sentinel-env/SKILL.md +31 -1
  42. package/skills/skill-forge/SKILL.md +57 -1
  43. package/skills/team/SKILL.md +61 -15
  44. package/skills/test/SKILL.md +101 -10
  45. package/skills/verification/SKILL.md +29 -1
@@ -0,0 +1,92 @@
1
+ import assert from 'node:assert';
2
+ import { describe, test } from 'node:test';
3
+ import { addBranding } from '../transforms/branding.js';
4
+ import { transformCrossReferences } from '../transforms/cross-references.js';
5
+ import { transformToolNames } from '../transforms/tool-names.js';
6
+
7
+ // --- Mock adapter ---
8
+
9
+ const mockAdapter = {
10
+ name: 'test',
11
+ transformReference(skillName, raw) {
12
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
13
+ const ref = `@rune-${skillName}.mdc`;
14
+ return isBackticked ? `\`${ref}\`` : ref;
15
+ },
16
+ transformToolName(toolName) {
17
+ const map = { Read: 'read file', Write: 'write file', Bash: 'run command' };
18
+ return map[toolName] || toolName;
19
+ },
20
+ generateFooter() {
21
+ return '\n---\n> Rune Test Footer';
22
+ },
23
+ };
24
+
25
+ const claudeAdapter = {
26
+ name: 'claude',
27
+ };
28
+
29
+ // --- Cross-reference transform ---
30
+
31
+ describe('transformCrossReferences', () => {
32
+ test('rewrites backticked rune:cook reference', () => {
33
+ const input = 'Use `rune:cook` for features.';
34
+ const result = transformCrossReferences(input, mockAdapter);
35
+ assert.ok(result.includes('`@rune-cook.mdc`'));
36
+ assert.ok(!result.includes('rune:cook'));
37
+ });
38
+
39
+ test('rewrites bare rune:plan reference', () => {
40
+ const input = 'Delegate to rune:plan for planning.';
41
+ const result = transformCrossReferences(input, mockAdapter);
42
+ assert.ok(result.includes('@rune-plan.mdc'));
43
+ assert.ok(!result.includes('rune:plan'));
44
+ });
45
+
46
+ test('handles multiple refs in same line', () => {
47
+ const input = 'Use `rune:cook` then `rune:test` then `rune:fix`.';
48
+ const result = transformCrossReferences(input, mockAdapter);
49
+ assert.ok(result.includes('@rune-cook.mdc'));
50
+ assert.ok(result.includes('@rune-test.mdc'));
51
+ assert.ok(result.includes('@rune-fix.mdc'));
52
+ });
53
+
54
+ test('does not modify text without refs', () => {
55
+ const input = 'Regular markdown without any references.';
56
+ const result = transformCrossReferences(input, mockAdapter);
57
+ assert.strictEqual(result, input);
58
+ });
59
+ });
60
+
61
+ // --- Tool name transform ---
62
+
63
+ describe('transformToolNames', () => {
64
+ test('rewrites tool references in backticks', () => {
65
+ const input = 'Use `Read` to check the file.';
66
+ const result = transformToolNames(input, mockAdapter);
67
+ assert.ok(!result.includes('`Read`'));
68
+ });
69
+
70
+ test('does not modify non-tool words', () => {
71
+ const input = 'Read the documentation carefully.';
72
+ const result = transformToolNames(input, mockAdapter);
73
+ // Should not touch bare "Read" without backticks in tool pattern
74
+ assert.ok(result.includes('Read'));
75
+ });
76
+ });
77
+
78
+ // --- Branding transform ---
79
+
80
+ describe('addBranding', () => {
81
+ test('adds footer for non-Claude adapters', () => {
82
+ const body = '# Skill\n\nSome content.';
83
+ const result = addBranding(body, mockAdapter);
84
+ assert.ok(result.includes('Rune Test Footer'));
85
+ });
86
+
87
+ test('skips footer for Claude adapter', () => {
88
+ const body = '# Skill\n\nSome content.';
89
+ const result = addBranding(body, claudeAdapter);
90
+ assert.strictEqual(result, body);
91
+ });
92
+ });
@@ -41,9 +41,10 @@ export default {
41
41
  return [
42
42
  '',
43
43
  '---',
44
- '> **Rune Skill Mesh** — 58 skills, 200+ connections',
45
- '> Source: https://github.com/rune-kit/rune',
46
- '> Full experience with subagents, hooks, adaptive routinguse Rune on Claude Code.',
44
+ '> **Rune Skill Mesh** — 58 skills, 200+ connections, 14 extension packs',
45
+ '> Source: https://github.com/rune-kit/rune (MIT)',
46
+ '> **Rune Pro** ($49 lifetime) — product, sales, data-science, support packs [rune-kit/rune-pro](https://github.com/rune-kit/rune-pro)',
47
+ '> **Rune Business** ($149 lifetime) — finance, legal, HR, enterprise-search packs → [rune-kit/rune-business](https://github.com/rune-kit/rune-business)',
47
48
  ].join('\n');
48
49
  },
49
50
 
@@ -52,8 +53,6 @@ export default {
52
53
  },
53
54
 
54
55
  postProcess(content) {
55
- return content
56
- .replace(/^context: fork\n/gm, '')
57
- .replace(/^agent: general-purpose\n/gm, '');
56
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
58
57
  },
59
58
  };
@@ -11,7 +11,7 @@ export default {
11
11
  skillPrefix: '',
12
12
  skillSuffix: '',
13
13
 
14
- transformReference(skillName, raw) {
14
+ transformReference(_skillName, raw) {
15
15
  return raw;
16
16
  },
17
17
 
@@ -1,77 +1,69 @@
1
- /**
2
- * OpenAI Codex Adapter
3
- *
4
- * Emits SKILL.md files into .codex/skills/{name}/ directories.
5
- * Codex uses the same SKILL.md frontmatter format (name, description)
6
- * with markdown body — very close to Rune's native format.
7
- *
8
- * Codex project context: AGENTS.md (equivalent to CLAUDE.md)
9
- * Codex skills dir: .codex/skills/
10
- * Codex skill format: .codex/skills/{name}/SKILL.md
11
- */
12
-
13
- const TOOL_MAP = {
14
- Read: 'read the file',
15
- Write: 'write/create the file',
16
- Edit: 'edit the file',
17
- Glob: 'find files by pattern',
18
- Grep: 'search file contents',
19
- Bash: 'run a shell command',
20
- TodoWrite: 'track task progress',
21
- Skill: 'follow the referenced skill',
22
- Agent: 'execute the workflow',
23
- };
24
-
25
- export default {
26
- name: 'codex',
27
- outputDir: '.codex/skills',
28
- fileExtension: '.md',
29
- skillPrefix: 'rune-',
30
- skillSuffix: '',
31
-
32
- // Codex uses directory-per-skill: .codex/skills/{name}/SKILL.md
33
- useSkillDirectories: true,
34
- skillFileName: 'SKILL.md',
35
-
36
- transformReference(skillName, raw) {
37
- const isBackticked = raw.startsWith('`') && raw.endsWith('`');
38
- const ref = `the rune-${skillName} skill`;
39
- return isBackticked ? `\`${ref}\`` : ref;
40
- },
41
-
42
- transformToolName(toolName) {
43
- return TOOL_MAP[toolName] || toolName;
44
- },
45
-
46
- generateHeader(skill) {
47
- const desc = (skill.description || '').replace(/"/g, '\\"');
48
- return [
49
- '---',
50
- `name: rune-${skill.name}`,
51
- `description: "${desc}"`,
52
- '---',
53
- '',
54
- '',
55
- ].join('\n');
56
- },
57
-
58
- generateFooter() {
59
- return [
60
- '',
61
- '---',
62
- '> **Rune Skill Mesh** — 58 skills, 200+ connections',
63
- '> Source: https://github.com/rune-kit/rune',
64
- '> Full experience with subagents, hooks, adaptive routing → use Rune on Claude Code.',
65
- ].join('\n');
66
- },
67
-
68
- transformSubagentInstruction(text) {
69
- return text;
70
- },
71
-
72
- postProcess(content) {
73
- return content
74
- .replace(/^context: fork\n/gm, '')
75
- .replace(/^agent: general-purpose\n/gm, '');
76
- },
77
- };
1
+ /**
2
+ * OpenAI Codex Adapter
3
+ *
4
+ * Emits SKILL.md files into .codex/skills/{name}/ directories.
5
+ * Codex uses the same SKILL.md frontmatter format (name, description)
6
+ * with markdown body — very close to Rune's native format.
7
+ *
8
+ * Codex project context: AGENTS.md (equivalent to CLAUDE.md)
9
+ * Codex skills dir: .codex/skills/
10
+ * Codex skill format: .codex/skills/{name}/SKILL.md
11
+ */
12
+
13
+ const TOOL_MAP = {
14
+ Read: 'read the file',
15
+ Write: 'write/create the file',
16
+ Edit: 'edit the file',
17
+ Glob: 'find files by pattern',
18
+ Grep: 'search file contents',
19
+ Bash: 'run a shell command',
20
+ TodoWrite: 'track task progress',
21
+ Skill: 'follow the referenced skill',
22
+ Agent: 'execute the workflow',
23
+ };
24
+
25
+ export default {
26
+ name: 'codex',
27
+ outputDir: '.codex/skills',
28
+ fileExtension: '.md',
29
+ skillPrefix: 'rune-',
30
+ skillSuffix: '',
31
+
32
+ // Codex uses directory-per-skill: .codex/skills/{name}/SKILL.md
33
+ useSkillDirectories: true,
34
+ skillFileName: 'SKILL.md',
35
+
36
+ transformReference(skillName, raw) {
37
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
38
+ const ref = `the rune-${skillName} skill`;
39
+ return isBackticked ? `\`${ref}\`` : ref;
40
+ },
41
+
42
+ transformToolName(toolName) {
43
+ return TOOL_MAP[toolName] || toolName;
44
+ },
45
+
46
+ generateHeader(skill) {
47
+ const desc = (skill.description || '').replace(/"/g, '\\"');
48
+ return ['---', `name: rune-${skill.name}`, `description: "${desc}"`, '---', '', ''].join('\n');
49
+ },
50
+
51
+ generateFooter() {
52
+ return [
53
+ '',
54
+ '---',
55
+ '> **Rune Skill Mesh** — 58 skills, 200+ connections, 14 extension packs',
56
+ '> Source: https://github.com/rune-kit/rune (MIT)',
57
+ '> **Rune Pro** ($49 lifetime) — product, sales, data-science, support packs → [rune-kit/rune-pro](https://github.com/rune-kit/rune-pro)',
58
+ '> **Rune Business** ($149 lifetime) — finance, legal, HR, enterprise-search packs → [rune-kit/rune-business](https://github.com/rune-kit/rune-business)',
59
+ ].join('\n');
60
+ },
61
+
62
+ transformSubagentInstruction(text) {
63
+ return text;
64
+ },
65
+
66
+ postProcess(content) {
67
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
68
+ },
69
+ };
@@ -49,9 +49,10 @@ export default {
49
49
  return [
50
50
  '',
51
51
  '---',
52
- '> **Rune Skill Mesh** — 58 skills, 200+ connections',
53
- '> Source: https://github.com/rune-kit/rune',
54
- '> Full experience with subagents, hooks, adaptive routinguse Rune on Claude Code.',
52
+ '> **Rune Skill Mesh** — 58 skills, 200+ connections, 14 extension packs',
53
+ '> Source: https://github.com/rune-kit/rune (MIT)',
54
+ '> **Rune Pro** ($49 lifetime) — product, sales, data-science, support packs [rune-kit/rune-pro](https://github.com/rune-kit/rune-pro)',
55
+ '> **Rune Business** ($149 lifetime) — finance, legal, HR, enterprise-search packs → [rune-kit/rune-business](https://github.com/rune-kit/rune-business)',
55
56
  ].join('\n');
56
57
  },
57
58
 
@@ -60,8 +61,6 @@ export default {
60
61
  },
61
62
 
62
63
  postProcess(content) {
63
- return content
64
- .replace(/^context: fork\n/gm, '')
65
- .replace(/^agent: general-purpose\n/gm, '');
64
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
66
65
  },
67
66
  };
@@ -42,9 +42,10 @@ export default {
42
42
  return [
43
43
  '',
44
44
  '---',
45
- '> **Rune Skill Mesh** — 58 skills, 200+ connections',
46
- '> Source: https://github.com/rune-kit/rune',
47
- '> Full experience with subagents, hooks, adaptive routinguse Rune on Claude Code.',
45
+ '> **Rune Skill Mesh** — 58 skills, 200+ connections, 14 extension packs',
46
+ '> Source: https://github.com/rune-kit/rune (MIT)',
47
+ '> **Rune Pro** ($49 lifetime) — product, sales, data-science, support packs [rune-kit/rune-pro](https://github.com/rune-kit/rune-pro)',
48
+ '> **Rune Business** ($149 lifetime) — finance, legal, HR, enterprise-search packs → [rune-kit/rune-business](https://github.com/rune-kit/rune-business)',
48
49
  ].join('\n');
49
50
  },
50
51
 
@@ -53,8 +54,6 @@ export default {
53
54
  },
54
55
 
55
56
  postProcess(content) {
56
- return content
57
- .replace(/^context: fork\n/gm, '')
58
- .replace(/^agent: general-purpose\n/gm, '');
57
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
59
58
  },
60
59
  };
@@ -4,14 +4,14 @@
4
4
  * Central registry for all platform adapters.
5
5
  */
6
6
 
7
+ import antigravity from './antigravity.js';
7
8
  import claude from './claude.js';
9
+ import codex from './codex.js';
8
10
  import cursor from './cursor.js';
9
- import windsurf from './windsurf.js';
10
- import antigravity from './antigravity.js';
11
11
  import generic from './generic.js';
12
12
  import openclaw from './openclaw.js';
13
- import codex from './codex.js';
14
13
  import opencode from './opencode.js';
14
+ import windsurf from './windsurf.js';
15
15
 
16
16
  const adapters = {
17
17
  claude,
@@ -1,150 +1,146 @@
1
- /**
2
- * OpenClaw Adapter
3
- *
4
- * Emits an OpenClaw plugin structure:
5
- * .openclaw/rune/openclaw.plugin.json (manifest)
6
- * .openclaw/rune/src/index.ts (register entrypoint)
7
- * .openclaw/rune/skills/*.md (transformed skill files)
8
- *
9
- * Follows the NeuralMemory OpenClaw plugin pattern.
10
- */
11
-
12
- const TOOL_MAP = {
13
- Read: 'read_file',
14
- Write: 'write_file',
15
- Edit: 'edit_file',
16
- Glob: 'glob',
17
- Grep: 'grep',
18
- Bash: 'run_command',
19
- TodoWrite: 'todo_write',
20
- Skill: 'follow the referenced skill',
21
- Agent: 'execute the workflow',
22
- };
23
-
24
- export default {
25
- name: 'openclaw',
26
- outputDir: '.openclaw/rune/skills',
27
- fileExtension: '.md',
28
- skillPrefix: 'rune-',
29
- skillSuffix: '',
30
-
31
- transformReference(skillName, raw) {
32
- const isBackticked = raw.startsWith('`') && raw.endsWith('`');
33
- const ref = `rune-${skillName}.md`;
34
- return isBackticked ? `\`${ref}\`` : ref;
35
- },
36
-
37
- transformToolName(toolName) {
38
- return TOOL_MAP[toolName] || toolName;
39
- },
40
-
41
- generateHeader(skill) {
42
- return `# rune-${skill.name}\n\n> Rune ${skill.layer} Skill | ${skill.group}\n\n`;
43
- },
44
-
45
- generateFooter() {
46
- return [
47
- '',
48
- '---',
49
- '> **Rune Skill Mesh** — 55 skills, 200+ connections',
50
- '> Source: https://github.com/rune-kit/rune',
51
- ].join('\n');
52
- },
53
-
54
- transformSubagentInstruction(text) {
55
- return text;
56
- },
57
-
58
- postProcess(content) {
59
- return content
60
- .replace(/^context: fork\n/gm, '')
61
- .replace(/^agent: general-purpose\n/gm, '');
62
- },
63
-
64
- /**
65
- * Generate openclaw.plugin.json manifest
66
- *
67
- * @param {object[]} skills - parsed skill objects
68
- * @param {object} pluginJson - Rune's .claude-plugin/plugin.json
69
- * @returns {object} manifest object
70
- */
71
- generateManifest(skills, pluginJson) {
72
- return {
73
- id: 'rune',
74
- name: 'Rune',
75
- kind: 'skills',
76
- description: '55-skill mesh for AI coding assistants. Routes all code tasks through specialized skills.',
77
- version: pluginJson.version || '0.0.0',
78
- skills: ['./skills'],
79
- configSchema: {
80
- jsonSchema: {
81
- type: 'object',
82
- properties: {
83
- disabledSkills: {
84
- type: 'array',
85
- items: { type: 'string' },
86
- description: 'Skills to disable (by name)',
87
- default: [],
88
- },
89
- },
90
- additionalProperties: false,
91
- },
92
- uiHints: {
93
- disabledSkills: {
94
- label: 'Disabled Skills',
95
- help: 'Comma-separated list of skill names to exclude from routing',
96
- },
97
- },
98
- },
99
- };
100
- },
101
-
102
- /**
103
- * Generate src/index.ts entry point with register(api) pattern
104
- *
105
- * @param {object[]} skills - parsed skill objects
106
- * @param {string} routerContent - skill-router SKILL.md content for injection
107
- * @returns {string} TypeScript source
108
- */
109
- generateEntryPoint(skills, routerContent) {
110
- const skillNames = skills.map(s => s.name);
111
- const routingTable = skills
112
- .map(s => `// ${s.name} (${s.layer}) — ${s.description || s.group}`)
113
- .join('\n');
114
-
115
- // Escape backticks and backslashes in router content for template literal
116
- const escapedRouter = (routerContent || '')
117
- .replace(/\\/g, '\\\\')
118
- .replace(/`/g, '\\`')
119
- .replace(/\$/g, '\\$');
120
-
121
- return `/**
122
- * Rune — OpenClaw Plugin Entry Point
123
- *
124
- * Auto-generated by Rune compiler.
125
- * Do not edit manually — regenerate with: rune build --platform openclaw
126
- *
127
- * Skills (${skillNames.length}):
128
- ${routingTable}
129
- */
130
-
131
- const SKILL_ROUTER_INSTRUCTIONS = \`${escapedRouter}\`;
132
-
133
- const plugin = {
134
- id: 'rune',
135
- name: 'Rune',
136
-
137
- register(api: any): void {
138
- // Inject skill-router instructions so the agent routes through Rune skills
139
- api.on('before_agent_start', async () => {
140
- return {
141
- prependSystemContext: SKILL_ROUTER_INSTRUCTIONS,
142
- };
143
- }, { priority: 5 });
144
- },
145
- };
146
-
147
- export default plugin;
148
- `;
149
- },
150
- };
1
+ /**
2
+ * OpenClaw Adapter
3
+ *
4
+ * Emits an OpenClaw plugin structure:
5
+ * .openclaw/rune/openclaw.plugin.json (manifest)
6
+ * .openclaw/rune/src/index.ts (register entrypoint)
7
+ * .openclaw/rune/skills/*.md (transformed skill files)
8
+ *
9
+ * Follows the NeuralMemory OpenClaw plugin pattern.
10
+ */
11
+
12
+ const TOOL_MAP = {
13
+ Read: 'read_file',
14
+ Write: 'write_file',
15
+ Edit: 'edit_file',
16
+ Glob: 'glob',
17
+ Grep: 'grep',
18
+ Bash: 'run_command',
19
+ TodoWrite: 'todo_write',
20
+ Skill: 'follow the referenced skill',
21
+ Agent: 'execute the workflow',
22
+ };
23
+
24
+ export default {
25
+ name: 'openclaw',
26
+ outputDir: '.openclaw/rune/skills',
27
+ fileExtension: '.md',
28
+ skillPrefix: 'rune-',
29
+ skillSuffix: '',
30
+
31
+ transformReference(skillName, raw) {
32
+ const isBackticked = raw.startsWith('`') && raw.endsWith('`');
33
+ const ref = `rune-${skillName}.md`;
34
+ return isBackticked ? `\`${ref}\`` : ref;
35
+ },
36
+
37
+ transformToolName(toolName) {
38
+ return TOOL_MAP[toolName] || toolName;
39
+ },
40
+
41
+ generateHeader(skill) {
42
+ return `# rune-${skill.name}\n\n> Rune ${skill.layer} Skill | ${skill.group}\n\n`;
43
+ },
44
+
45
+ generateFooter() {
46
+ return [
47
+ '',
48
+ '---',
49
+ '> **Rune Skill Mesh** — 58 skills, 200+ connections, 14 extension packs',
50
+ '> Source: https://github.com/rune-kit/rune (MIT)',
51
+ '> **Rune Pro** ($49 lifetime) — product, sales, data-science, support packs → [rune-kit/rune-pro](https://github.com/rune-kit/rune-pro)',
52
+ '> **Rune Business** ($149 lifetime) — finance, legal, HR, enterprise-search packs → [rune-kit/rune-business](https://github.com/rune-kit/rune-business)',
53
+ ].join('\n');
54
+ },
55
+
56
+ transformSubagentInstruction(text) {
57
+ return text;
58
+ },
59
+
60
+ postProcess(content) {
61
+ return content.replace(/^context: fork\n/gm, '').replace(/^agent: general-purpose\n/gm, '');
62
+ },
63
+
64
+ /**
65
+ * Generate openclaw.plugin.json manifest
66
+ *
67
+ * @param {object[]} skills - parsed skill objects
68
+ * @param {object} pluginJson - Rune's .claude-plugin/plugin.json
69
+ * @returns {object} manifest object
70
+ */
71
+ generateManifest(_skills, pluginJson) {
72
+ return {
73
+ id: 'rune',
74
+ name: 'Rune',
75
+ kind: 'skills',
76
+ description:
77
+ '58-skill mesh for AI coding assistants. Routes all code tasks through specialized skills. 200+ connections, 14 extension packs.',
78
+ version: pluginJson.version || '0.0.0',
79
+ skills: ['./skills'],
80
+ configSchema: {
81
+ jsonSchema: {
82
+ type: 'object',
83
+ properties: {
84
+ disabledSkills: {
85
+ type: 'array',
86
+ items: { type: 'string' },
87
+ description: 'Skills to disable (by name)',
88
+ default: [],
89
+ },
90
+ },
91
+ additionalProperties: false,
92
+ },
93
+ uiHints: {
94
+ disabledSkills: {
95
+ label: 'Disabled Skills',
96
+ help: 'Comma-separated list of skill names to exclude from routing',
97
+ },
98
+ },
99
+ },
100
+ };
101
+ },
102
+
103
+ /**
104
+ * Generate src/index.ts entry point with register(api) pattern
105
+ *
106
+ * @param {object[]} skills - parsed skill objects
107
+ * @param {string} routerContent - skill-router SKILL.md content for injection
108
+ * @returns {string} TypeScript source
109
+ */
110
+ generateEntryPoint(skills, routerContent) {
111
+ const skillNames = skills.map((s) => s.name);
112
+ const routingTable = skills.map((s) => `// ${s.name} (${s.layer}) — ${s.description || s.group}`).join('\n');
113
+
114
+ // Escape backticks and backslashes in router content for template literal
115
+ const escapedRouter = (routerContent || '').replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
116
+
117
+ return `/**
118
+ * Rune — OpenClaw Plugin Entry Point
119
+ *
120
+ * Auto-generated by Rune compiler.
121
+ * Do not edit manually — regenerate with: rune build --platform openclaw
122
+ *
123
+ * Skills (${skillNames.length}):
124
+ ${routingTable}
125
+ */
126
+
127
+ const SKILL_ROUTER_INSTRUCTIONS = \`${escapedRouter}\`;
128
+
129
+ const plugin = {
130
+ id: 'rune',
131
+ name: 'Rune',
132
+
133
+ register(api: any): void {
134
+ // Inject skill-router instructions so the agent routes through Rune skills
135
+ api.on('before_agent_start', async () => {
136
+ return {
137
+ prependSystemContext: SKILL_ROUTER_INSTRUCTIONS,
138
+ };
139
+ }, { priority: 5 });
140
+ },
141
+ };
142
+
143
+ export default plugin;
144
+ `;
145
+ },
146
+ };