@rune-kit/rune 2.4.0 → 2.7.0

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 (49) hide show
  1. package/README.md +47 -17
  2. package/compiler/__tests__/executive-dashboards.test.js +285 -0
  3. package/compiler/__tests__/inject.test.js +128 -0
  4. package/compiler/__tests__/orchestrators.test.js +151 -0
  5. package/compiler/__tests__/org-templates.test.js +447 -0
  6. package/compiler/__tests__/pack-split.test.js +141 -1
  7. package/compiler/__tests__/parser.test.js +147 -1
  8. package/compiler/__tests__/scripts-bundling.test.js +10 -11
  9. package/compiler/__tests__/skill-index.test.js +218 -0
  10. package/compiler/__tests__/status.test.js +336 -0
  11. package/compiler/__tests__/templates.test.js +245 -0
  12. package/compiler/__tests__/visualizer.test.js +325 -0
  13. package/compiler/adapters/antigravity.js +18 -4
  14. package/compiler/bin/rune.js +90 -1
  15. package/compiler/doctor.js +283 -2
  16. package/compiler/emitter.js +490 -17
  17. package/compiler/parser.js +255 -4
  18. package/compiler/status.js +342 -0
  19. package/compiler/visualizer.js +622 -0
  20. package/hooks/hooks.json +12 -0
  21. package/hooks/intent-router/index.cjs +108 -0
  22. package/hooks/pre-tool-guard/index.cjs +177 -68
  23. package/package.json +63 -63
  24. package/skills/autopsy/SKILL.md +48 -1
  25. package/skills/brainstorm/SKILL.md +2 -0
  26. package/skills/completion-gate/SKILL.md +26 -1
  27. package/skills/context-engine/SKILL.md +93 -2
  28. package/skills/cook/SKILL.md +794 -648
  29. package/skills/debug/SKILL.md +409 -392
  30. package/skills/deploy/SKILL.md +2 -0
  31. package/skills/docs/SKILL.md +28 -3
  32. package/skills/fix/SKILL.md +284 -281
  33. package/skills/mcp-builder/SKILL.md +53 -1
  34. package/skills/onboard/SKILL.md +58 -1
  35. package/skills/perf/SKILL.md +34 -1
  36. package/skills/plan/SKILL.md +372 -342
  37. package/skills/preflight/SKILL.md +396 -360
  38. package/skills/retro/SKILL.md +95 -1
  39. package/skills/review/SKILL.md +535 -489
  40. package/skills/scope-guard/SKILL.md +1 -0
  41. package/skills/scout/SKILL.md +1 -0
  42. package/skills/sentinel/SKILL.md +353 -299
  43. package/skills/sentinel/references/policy-driven-constraints.md +424 -0
  44. package/skills/session-bridge/SKILL.md +58 -2
  45. package/skills/session-bridge/references/evolutionary-memory-patterns.md +312 -0
  46. package/skills/team/SKILL.md +16 -1
  47. package/skills/test/SKILL.md +587 -585
  48. package/skills/verification/SKILL.md +1 -0
  49. package/skills/watchdog/SKILL.md +2 -0
package/README.md CHANGED
@@ -5,13 +5,14 @@
5
5
  <p align="center">
6
6
  <strong>Less skills. Deeper connections.</strong><br>
7
7
  A lean, interconnected skill ecosystem for AI coding assistants.<br>
8
- 59 skills · 200+ mesh connections · 8 platforms · MIT
8
+ 61 skills · 200+ mesh connections · 8 platforms · MIT
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
12
  <a href="https://rune-kit.github.io/rune"><img src="https://img.shields.io/badge/Landing_Page-rune--kit.github.io-blue?style=for-the-badge" alt="Landing Page"></a>
13
13
  <a href="https://rune-kit.github.io/rune#pricing"><img src="https://img.shields.io/badge/Pro_%2449-lifetime-blueviolet?style=for-the-badge" alt="Rune Pro $49"></a>
14
14
  <a href="https://rune-kit.github.io/rune#pricing"><img src="https://img.shields.io/badge/Business_%24149-lifetime-orange?style=for-the-badge" alt="Rune Business $149"></a>
15
+ <a href="https://t.me/xlabs_updates"><img src="https://img.shields.io/badge/Telegram-Updates-26A5E4?style=for-the-badge&logo=telegram&logoColor=white" alt="Telegram Updates"></a>
15
16
  </p>
16
17
 
17
18
  <p align="center">
@@ -22,7 +23,7 @@
22
23
 
23
24
  Most skill ecosystems are either **too many isolated skills** (540+ that don't talk to each other) or **rigid pipelines** (A → B → C, if B fails everything stops).
24
25
 
25
- Rune is a **mesh** — 59 skills with 200+ connections across a 5-layer architecture. Skills call each other bidirectionally, forming resilient workflows that adapt when things go wrong.
26
+ Rune is a **mesh** — 61 skills with 200+ connections across a 5-layer architecture. Skills call each other bidirectionally, forming resilient workflows that adapt when things go wrong.
26
27
 
27
28
  ```
28
29
  Pipeline: A → B → C → D (B fails = stuck)
@@ -82,19 +83,44 @@ _Methodology: Claude Code CLI headless mode (`claude -p --output-format json`),
82
83
 
83
84
  ---
84
85
 
85
- ## What's New (v2.4.0)
86
+ ## What's New (v2.7.0)
86
87
 
87
- - **UI/UX Pro Max Integration** — 161 palettes, 84 styles, 73 font pairings, 161 reasoning rules from [UI/UX Pro Max](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) (MIT, 42.8k★) meshed into `design` skill + `@rune/ui` pack
88
- - **11 Core Skill Enrichments** — deviation rules, repair operators, evidence quality gate, decision classification, debug knowledge base, CSO discipline, YAGNI pushback, formal pause/resume
89
- - **Enforcement Upgrade** — Antigravity-level IDE compliance: 5-type Request Classifier, File Ownership Matrix, Self-Verification HARD-GATE, Routing Proof, Clarification Gate, Phase Transition Protocol
90
- - **All 14 Free Packs Deep** — every L4 extension pack now exceeds 500 lines of production-ready patterns and code examples
91
- - **8 Platform Adapters** — Claude Code, Cursor, Windsurf, Google Antigravity, OpenAI Codex, OpenCode, OpenClaw, Generic
92
- - **59 Core Skills** — +4 since v2.1.0: adversary, sentinel-env, chrome-ext pack, context-pack
93
- - **Rune Pro** — 4 premium packs (product, sales, data-science, support) in private repo. $49 lifetime
88
+ - **Deep Knowledge** — 8 core skills enriched with battle-tested patterns: context compaction, structured cumulative memory, milestone analysis, multi-provider adapters, AI-driven interview, prompt-as-API-contract, token budget tracking, incremental stream processing
89
+ - **context-engine v0.8.0** — structured compaction summaries with continuation point anchoring + sentence-level stream processing pattern
90
+ - **session-bridge v0.5.0** — cumulative project notes (`.rune/cumulative-notes.md`) for living institutional memory across sessions
91
+ - **retro v0.3.0** — milestone progressive analysis at project thresholds (4/12/24/50 retros) with focal-point depth scaling
92
+ - **mcp-builder v0.4.0** — multi-provider adapter pattern reference with discriminated StreamChunk union + dual-model cost config
93
+ - **onboard v0.4.0** — AI-driven conversational interview mode (`--interview`) for ambiguous or complex projects
94
+ - **perf v0.3.0** — token budget tracking for AI-powered apps (loop detection, model mismatch, unbounded tokens, duplicate calls)
95
+ - **cook v2.0.0** — prompt-as-API-contract pattern for structured sub-skill output
96
+ - **946 Tests** — compiler + signals + hooks + scripts + status + visualizer validation
97
+
98
+ ### Previous (v2.6.0)
99
+
100
+ - **Mesh Signals** — event-driven skill communication via frontmatter. Skills declare `emit` and `listen` signals; compiler builds a signal graph in `skill-index.json`. 17 signals across 15 core skills
101
+ - **Signal Validation** — `scripts/validate-signals.js` checks orphan listeners (hard error), unlistened emitters (warning), signal naming conventions
102
+ - **Mesh Contract** (v2.5.0) — `.rune/contract.md` project-level invariants enforced by cook + sentinel as hard gates
103
+ - **Tier Override** — Pro/Business packs override Free packs with skill-level merging
104
+ - **Scripts Bundling** — compiler copies `scripts/` directories, resolves `{scripts_dir}` placeholders
105
+
106
+ ### Signal Graph
107
+
108
+ Skills communicate through declarative signals — no runtime event bus, just metadata for discovery, validation, and routing:
109
+
110
+ ```
111
+ scout ──emit:codebase.scanned──→ plan, brainstorm
112
+ fix ────emit:code.changed──────→ test, sentinel, review, preflight, verification
113
+ test ───emit:tests.passed──────→ deploy
114
+ test ───emit:tests.failed──────→ debug
115
+ sentinel─emit:security.passed──→ deploy
116
+ debug ──emit:bug.diagnosed─────→ fix
117
+ deploy ─emit:deploy.complete───→ watchdog
118
+ cook ───emit:phase.complete────→ session-bridge
119
+ ```
94
120
 
95
121
  ## What Rune Is (and Isn't)
96
122
 
97
- Rune started as a **Claude Code plugin** and now compiles to **every major AI IDE**. Same 59 skills, same mesh connections, same workflows — zero knowledge loss across platforms.
123
+ Rune started as a **Claude Code plugin** and now compiles to **every major AI IDE**. Same 61 skills, same mesh connections, same workflows — zero knowledge loss across platforms.
98
124
 
99
125
  | | Rune Provides | Claude Code Provides |
100
126
  |---|---|---|
@@ -118,7 +144,7 @@ Rune started as a **Claude Code plugin** and now compiles to **every major AI ID
118
144
  | CI quality gates | `verification` skill: lint + typecheck + tests + build (actual commands, not LLM review) |
119
145
  | Memory / state | `session-bridge` + `journal`: cross-session decisions, conventions, ADRs, module health |
120
146
  | Multi-model strategy | Every skill has assigned model: haiku (scan), sonnet (code), opus (architecture) |
121
- | Agent specialization | 58 specialized skills with dedicated roles (architect, coder, reviewer, scanner, researcher, BA, scaffolder) — each runs as a Task agent via Claude Code |
147
+ | Agent specialization | 61 specialized skills with dedicated roles (architect, coder, reviewer, scanner, researcher, BA, scaffolder) — each runs as a Task agent via Claude Code |
122
148
  | Security scanning | `sentinel`: OWASP patterns, secret scanning, dependency audit. `sast`: static analysis |
123
149
 
124
150
  ## Install
@@ -146,13 +172,13 @@ npx @rune-kit/rune init --platform windsurf
146
172
  npx @rune-kit/rune init --platform antigravity
147
173
  ```
148
174
 
149
- This compiles all 59 skills into your IDE's rules format. Same knowledge, same workflows.
175
+ This compiles all 61 skills into your IDE's rules format. Same knowledge, same workflows.
150
176
 
151
177
  ### Platform Comparison
152
178
 
153
179
  | Feature | Claude Code | Cursor / Windsurf / Others |
154
180
  |---------|-------------|---------------------------|
155
- | Skills available | 59/59 | 59/59 |
181
+ | Skills available | 61/61 | 61/61 |
156
182
  | Mesh connections | 200+ (programmatic) | 200+ (rule references) |
157
183
  | Workflows & HARD-GATEs | Full | Full |
158
184
  | Extension packs | 14 | 14 |
@@ -229,8 +255,9 @@ This compiles all 59 skills into your IDE's rules format. Same knowledge, same w
229
255
  ║ Delivery: deploy │ marketing │ incident │ docs ║
230
256
  ║ Rescue: autopsy │ safeguard │ surgeon ║
231
257
  ║ Security: adversary ║
258
+ ║ Velocity: retro ║
232
259
  ╠══════════════════════════════════════════════════════╣
233
- ║ L3: UTILITIES (26) ║
260
+ ║ L3: UTILITIES (27) ║
234
261
  ║ Stateless, pure capabilities ║
235
262
  ║ ║
236
263
  ║ Knowledge: research │ docs-seeker │ trend-scout ║
@@ -249,6 +276,8 @@ This compiles all 59 skills into your IDE's rules format. Same knowledge, same w
249
276
  ║ Documents: doc-processor ║
250
277
  ║ Security: sentinel-env ║
251
278
  ║ Memory: neural-memory ║
279
+ ║ Packs: context-pack ║
280
+ ║ Slides: slides ║
252
281
  ╠══════════════════════════════════════════════════════╣
253
282
  ║ L4: EXTENSION PACKS (14) ║
254
283
  ║ Domain-specific, install what you need ║
@@ -426,13 +455,14 @@ See [docs/MULTI-PLATFORM.md](docs/MULTI-PLATFORM.md) for the full architecture.
426
455
  ## Numbers
427
456
 
428
457
  ```
429
- Core Skills: 59 (L0: 1 │ L1: 5 │ L2: 28 │ L3: 26)
458
+ Core Skills: 61 (L0: 1 │ L1: 5 │ L2: 28 │ L3: 27)
430
459
  Extension Packs: 14 free + 4 pro + 4 business
431
460
  Mesh Connections: 200+ cross-references
461
+ Mesh Signals: 57 signals across 66 skills (emit/listen graph)
432
462
  Connections/Skill: 3.4 avg
433
463
  Platforms: 8 (Claude Code, Cursor, Windsurf, Antigravity, Codex, OpenCode, OpenClaw, Generic)
434
464
  Compiler: ~1400 LOC (parser + 8 transforms + 8 adapters + CLI)
435
- Quality: 59/59 skills with Output Format, Sharp Edges, Done When, Cost Profile
465
+ Tests: 946 (compiler + signals + status + visualizer + hooks + scripts)
436
466
  Pack Depth: 22 packs total (14 free + 4 pro + 4 business, all free packs rated Deep)
437
467
  ```
438
468
 
@@ -0,0 +1,285 @@
1
+ import assert from 'node:assert';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { describe, test } from 'node:test';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const BUSINESS_DIR = path.resolve(__dirname, '../../../Business');
9
+ const REPORT_TEMPLATES_DIR = path.join(BUSINESS_DIR, 'report-templates');
10
+ const SKILLS_DIR = path.resolve(__dirname, '../../skills');
11
+
12
+ // ─── HTML report template validation ──────────────────────────────
13
+
14
+ // Skip Business tier tests when Business repo is not available (CI)
15
+ const HAS_BUSINESS = existsSync(BUSINESS_DIR);
16
+
17
+ (HAS_BUSINESS ? describe : describe.skip)('Business report-templates directory', () => {
18
+ test('report-templates directory exists', () => {
19
+ assert.ok(existsSync(REPORT_TEMPLATES_DIR), 'Expected Business/report-templates/ to exist');
20
+ });
21
+
22
+ test('retro-business.html exists', () => {
23
+ assert.ok(
24
+ existsSync(path.join(REPORT_TEMPLATES_DIR, 'retro-business.html')),
25
+ 'Expected retro-business.html template',
26
+ );
27
+ });
28
+
29
+ test('autopsy-executive.html exists', () => {
30
+ assert.ok(
31
+ existsSync(path.join(REPORT_TEMPLATES_DIR, 'autopsy-executive.html')),
32
+ 'Expected autopsy-executive.html template',
33
+ );
34
+ });
35
+ });
36
+
37
+ describe('retro-business.html template', () => {
38
+ const filePath = path.join(REPORT_TEMPLATES_DIR, 'retro-business.html');
39
+ if (!existsSync(filePath)) return;
40
+ const content = readFileSync(filePath, 'utf-8');
41
+
42
+ test('is valid HTML with doctype', () => {
43
+ assert.ok(content.startsWith('<!DOCTYPE html>'), 'Must start with DOCTYPE');
44
+ assert.ok(content.includes('<html'), 'Must have html tag');
45
+ assert.ok(content.includes('</html>'), 'Must close html tag');
46
+ });
47
+
48
+ test('is self-contained (no external CSS/JS)', () => {
49
+ assert.ok(!content.includes('href="http'), 'No external CSS links');
50
+ assert.ok(!content.includes('src="http'), 'No external JS scripts');
51
+ assert.ok(!content.includes('cdn.'), 'No CDN references');
52
+ });
53
+
54
+ test('has inline styles', () => {
55
+ assert.ok(content.includes('<style>'), 'Must have inline style block');
56
+ });
57
+
58
+ test('has print-friendly styles', () => {
59
+ assert.ok(content.includes('@media print'), 'Must have print media query');
60
+ assert.ok(content.includes('@page'), 'Must have @page rule for PDF export');
61
+ });
62
+
63
+ test('has KPI card placeholders', () => {
64
+ assert.ok(content.includes('{{sprint_velocity}}'), 'Missing sprint_velocity placeholder');
65
+ assert.ok(content.includes('{{mrr}}'), 'Missing MRR placeholder');
66
+ assert.ok(content.includes('{{sla_pct}}'), 'Missing SLA placeholder');
67
+ assert.ok(content.includes('{{compliance_score}}'), 'Missing compliance_score placeholder');
68
+ });
69
+
70
+ test('has domain performance sections', () => {
71
+ assert.ok(content.includes('Engineering'), 'Missing Engineering domain');
72
+ assert.ok(content.includes('Revenue'), 'Missing Revenue domain');
73
+ assert.ok(content.includes('Support'), 'Missing Support domain');
74
+ assert.ok(content.includes('Finance'), 'Missing Finance domain');
75
+ });
76
+
77
+ test('has team health table', () => {
78
+ assert.ok(content.includes('Team Health'), 'Missing Team Health section');
79
+ assert.ok(content.includes('{{team_name}}'), 'Missing team_name placeholder');
80
+ assert.ok(content.includes('{{team_lead}}'), 'Missing team_lead placeholder');
81
+ });
82
+
83
+ test('has compliance status section', () => {
84
+ assert.ok(content.includes('Compliance Status'), 'Missing Compliance Status section');
85
+ assert.ok(content.includes('{{framework}}'), 'Missing framework placeholder');
86
+ });
87
+
88
+ test('has key insights section (wins + risks)', () => {
89
+ assert.ok(content.includes('Key Insights'), 'Missing Key Insights section');
90
+ assert.ok(content.includes('Wins'), 'Missing Wins subsection');
91
+ assert.ok(content.includes('Risks'), 'Missing Risks subsection');
92
+ });
93
+
94
+ test('has Rune Business footer', () => {
95
+ assert.ok(content.includes('Rune Business'), 'Missing Rune Business branding');
96
+ assert.ok(content.includes('retro --business'), 'Missing retro --business reference');
97
+ });
98
+
99
+ test('has trend delta placeholders for KPIs', () => {
100
+ assert.ok(content.includes('{{velocity_delta}}'), 'Missing velocity trend');
101
+ assert.ok(content.includes('{{mrr_delta}}'), 'Missing MRR trend');
102
+ });
103
+
104
+ test('uses CSS custom properties for theming', () => {
105
+ assert.ok(content.includes(':root'), 'Must use CSS custom properties');
106
+ assert.ok(content.includes('--bg'), 'Missing --bg variable');
107
+ assert.ok(content.includes('--accent'), 'Missing --accent variable');
108
+ });
109
+ });
110
+
111
+ describe('autopsy-executive.html template', () => {
112
+ const filePath = path.join(REPORT_TEMPLATES_DIR, 'autopsy-executive.html');
113
+ if (!existsSync(filePath)) return;
114
+ const content = readFileSync(filePath, 'utf-8');
115
+
116
+ test('is valid HTML with doctype', () => {
117
+ assert.ok(content.startsWith('<!DOCTYPE html>'), 'Must start with DOCTYPE');
118
+ assert.ok(content.includes('<html'), 'Must have html tag');
119
+ assert.ok(content.includes('</html>'), 'Must close html tag');
120
+ });
121
+
122
+ test('is self-contained (no external CSS/JS)', () => {
123
+ assert.ok(!content.includes('href="http'), 'No external CSS links');
124
+ assert.ok(!content.includes('src="http'), 'No external JS scripts');
125
+ assert.ok(!content.includes('cdn.'), 'No CDN references');
126
+ });
127
+
128
+ test('has print-friendly styles', () => {
129
+ assert.ok(content.includes('@media print'), 'Must have print media query');
130
+ assert.ok(content.includes('@page'), 'Must have @page rule');
131
+ });
132
+
133
+ test('has SVG health ring', () => {
134
+ assert.ok(content.includes('<svg'), 'Must have inline SVG');
135
+ assert.ok(content.includes('<circle'), 'Must have SVG circle for health ring');
136
+ assert.ok(content.includes('stroke-dasharray'), 'Must use dasharray for progress ring');
137
+ });
138
+
139
+ test('has health score placeholder', () => {
140
+ assert.ok(content.includes('{{health_score}}'), 'Missing health_score placeholder');
141
+ assert.ok(content.includes('{{risk_tier}}'), 'Missing risk_tier placeholder');
142
+ assert.ok(content.includes('{{score_color}}'), 'Missing score_color placeholder');
143
+ });
144
+
145
+ test('has six health dimensions', () => {
146
+ assert.ok(content.includes('Complexity'), 'Missing Complexity dimension');
147
+ assert.ok(content.includes('Test Coverage'), 'Missing Test Coverage dimension');
148
+ assert.ok(content.includes('Documentation'), 'Missing Documentation dimension');
149
+ assert.ok(content.includes('Dependencies'), 'Missing Dependencies dimension');
150
+ assert.ok(content.includes('Code Smells'), 'Missing Code Smells dimension');
151
+ assert.ok(content.includes('Maintenance'), 'Missing Maintenance dimension');
152
+ });
153
+
154
+ test('has module health table', () => {
155
+ assert.ok(content.includes('Module Health'), 'Missing Module Health section');
156
+ assert.ok(content.includes('{{module_name}}'), 'Missing module_name placeholder');
157
+ assert.ok(content.includes('{{module_score}}'), 'Missing module_score placeholder');
158
+ });
159
+
160
+ test('has surgery queue section', () => {
161
+ assert.ok(content.includes('Surgery Queue'), 'Missing Surgery Queue section');
162
+ assert.ok(content.includes('{{surgery_module}}'), 'Missing surgery_module placeholder');
163
+ assert.ok(content.includes('{{surgery_pattern}}'), 'Missing surgery_pattern placeholder');
164
+ });
165
+
166
+ test('has risk assessment section', () => {
167
+ assert.ok(content.includes('Risk Assessment'), 'Missing Risk Assessment section');
168
+ assert.ok(content.includes('Git Archaeology'), 'Missing Git Archaeology section');
169
+ });
170
+
171
+ test('has cross-domain impact table', () => {
172
+ assert.ok(content.includes('Cross-Domain Impact'), 'Missing Cross-Domain Impact section');
173
+ assert.ok(content.includes('{{domain_name}}'), 'Missing domain_name placeholder');
174
+ });
175
+
176
+ test('has recommended actions', () => {
177
+ assert.ok(content.includes('Recommended Actions'), 'Missing Recommended Actions section');
178
+ });
179
+
180
+ test('has Rune Business footer', () => {
181
+ assert.ok(content.includes('Rune Business'), 'Missing Rune Business branding');
182
+ assert.ok(content.includes('autopsy --executive'), 'Missing autopsy --executive reference');
183
+ });
184
+
185
+ test('has board-ready label', () => {
186
+ assert.ok(content.includes('Board-ready'), 'Should mention board-ready format');
187
+ });
188
+ });
189
+
190
+ // ─── Skill integration validation ─────────────────────────────────
191
+
192
+ describe('retro skill --business integration', () => {
193
+ const retroPath = path.join(SKILLS_DIR, 'retro', 'SKILL.md');
194
+ const content = readFileSync(retroPath, 'utf-8');
195
+
196
+ test('retro has --business trigger', () => {
197
+ assert.ok(content.includes('retro --business'), 'retro should document --business mode');
198
+ });
199
+
200
+ test('retro --business references HTML report', () => {
201
+ assert.ok(content.includes('.html'), 'retro --business should reference HTML output');
202
+ });
203
+
204
+ test('retro --business references cross-domain data', () => {
205
+ assert.ok(
206
+ content.includes('Revenue') || content.includes('revenue'),
207
+ 'retro --business should reference revenue domain',
208
+ );
209
+ assert.ok(
210
+ content.includes('Support') || content.includes('support'),
211
+ 'retro --business should reference support domain',
212
+ );
213
+ assert.ok(
214
+ content.includes('Finance') || content.includes('finance'),
215
+ 'retro --business should reference finance domain',
216
+ );
217
+ });
218
+
219
+ test('retro --business references org config', () => {
220
+ assert.ok(content.includes('.rune/org/org.md'), 'retro --business should reference org config');
221
+ });
222
+
223
+ test('retro --business has graceful degradation', () => {
224
+ assert.ok(
225
+ content.includes('Graceful Degradation') || content.includes('graceful'),
226
+ 'retro --business should handle missing Business pack gracefully',
227
+ );
228
+ });
229
+
230
+ test('retro --business references report template', () => {
231
+ assert.ok(content.includes('retro-business.html'), 'retro should reference the HTML template file');
232
+ });
233
+ });
234
+
235
+ describe('autopsy skill --executive integration', () => {
236
+ const autopsyPath = path.join(SKILLS_DIR, 'autopsy', 'SKILL.md');
237
+ const content = readFileSync(autopsyPath, 'utf-8');
238
+
239
+ test('autopsy has --executive mode', () => {
240
+ assert.ok(content.includes('--executive'), 'autopsy should document --executive mode');
241
+ });
242
+
243
+ test('autopsy --executive references HTML report', () => {
244
+ assert.ok(
245
+ content.includes('EXECUTIVE-HEALTH.html'),
246
+ 'autopsy --executive should reference EXECUTIVE-HEALTH.html output',
247
+ );
248
+ });
249
+
250
+ test('autopsy --executive references SVG health ring', () => {
251
+ assert.ok(
252
+ content.includes('SVG') || content.includes('svg'),
253
+ 'autopsy --executive should reference SVG health ring',
254
+ );
255
+ });
256
+
257
+ test('autopsy --executive has color coding table', () => {
258
+ assert.ok(
259
+ content.includes('Color Coding') || content.includes('color'),
260
+ 'autopsy --executive should define color coding',
261
+ );
262
+ });
263
+
264
+ test('autopsy --executive references org config', () => {
265
+ assert.ok(content.includes('.rune/org/org.md'), 'autopsy --executive should reference org config for team mapping');
266
+ });
267
+
268
+ test('autopsy --executive references report template', () => {
269
+ assert.ok(content.includes('autopsy-executive.html'), 'autopsy should reference the HTML template file');
270
+ });
271
+
272
+ test('autopsy --executive has cross-domain impact', () => {
273
+ assert.ok(
274
+ content.includes('Cross-Domain') || content.includes('cross-domain'),
275
+ 'autopsy --executive should map to business domains',
276
+ );
277
+ });
278
+
279
+ test('autopsy --executive has graceful degradation', () => {
280
+ assert.ok(
281
+ content.includes('Graceful Degradation') || content.includes('graceful'),
282
+ 'autopsy --executive should handle missing Business pack',
283
+ );
284
+ });
285
+ });
@@ -0,0 +1,128 @@
1
+ import assert from 'node:assert';
2
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { describe, test } from 'node:test';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const SKILLS_DIR = path.resolve(__dirname, '../../skills');
9
+ const PRO_DIR = path.resolve(__dirname, '../../../Pro/extensions');
10
+ const BUSINESS_DIR = path.resolve(__dirname, '../../../Business/extensions');
11
+
12
+ // Skip Pro/Business tier tests when repos are not available (CI)
13
+ const HAS_PRO = existsSync(PRO_DIR);
14
+
15
+ // Collect known Free core skill names
16
+ const knownSkills = new Set();
17
+ if (existsSync(SKILLS_DIR)) {
18
+ for (const entry of readdirSync(SKILLS_DIR, { withFileTypes: true })) {
19
+ if (entry.isDirectory() && existsSync(path.join(SKILLS_DIR, entry.name, 'SKILL.md'))) {
20
+ knownSkills.add(entry.name);
21
+ }
22
+ }
23
+ }
24
+
25
+ (HAS_PRO ? describe : describe.skip)('inject.json format validation', () => {
26
+ const packDirs = [];
27
+
28
+ // Collect all extension dirs that might have inject.json
29
+ for (const extDir of [PRO_DIR, BUSINESS_DIR]) {
30
+ if (!existsSync(extDir)) continue;
31
+ for (const entry of readdirSync(extDir, { withFileTypes: true })) {
32
+ if (!entry.isDirectory()) continue;
33
+ const injectPath = path.join(extDir, entry.name, 'inject.json');
34
+ if (existsSync(injectPath)) {
35
+ packDirs.push({ name: entry.name, dir: path.join(extDir, entry.name), injectPath });
36
+ }
37
+ }
38
+ }
39
+
40
+ test('at least one inject.json exists in Pro packs', () => {
41
+ assert.ok(packDirs.length > 0, 'Expected at least one pack with inject.json');
42
+ });
43
+
44
+ for (const pack of packDirs) {
45
+ describe(`${pack.name}/inject.json`, () => {
46
+ let config;
47
+
48
+ test('is valid JSON', () => {
49
+ const raw = readFileSync(pack.injectPath, 'utf-8');
50
+ config = JSON.parse(raw);
51
+ assert.ok(config, 'inject.json should parse as valid JSON');
52
+ });
53
+
54
+ test('has injections array', () => {
55
+ if (!config) return;
56
+ assert.ok(Array.isArray(config.injections), 'must have injections array');
57
+ assert.ok(config.injections.length > 0, 'must have at least one injection rule');
58
+ });
59
+
60
+ test('has description field', () => {
61
+ if (!config) return;
62
+ assert.ok(config.description, 'should have a description field');
63
+ });
64
+
65
+ test('each rule has required fields', () => {
66
+ if (!config) return;
67
+ for (const rule of config.injections) {
68
+ assert.ok(rule.skill, `rule must have "skill" field: ${JSON.stringify(rule)}`);
69
+ assert.ok(rule.ref, `rule must have "ref" field: ${JSON.stringify(rule)}`);
70
+ assert.ok(rule.context, `rule must have "context" field: ${JSON.stringify(rule)}`);
71
+ }
72
+ });
73
+
74
+ test('target skills exist in Free core', () => {
75
+ if (!config) return;
76
+ for (const rule of config.injections) {
77
+ assert.ok(knownSkills.has(rule.skill), `target skill "${rule.skill}" not found in Free core skills`);
78
+ }
79
+ });
80
+
81
+ test('reference files exist', () => {
82
+ if (!config) return;
83
+ for (const rule of config.injections) {
84
+ const refPath = path.join(pack.dir, rule.ref);
85
+ assert.ok(existsSync(refPath), `reference file "${rule.ref}" not found at ${refPath}`);
86
+ }
87
+ });
88
+
89
+ test('reference files are .md', () => {
90
+ if (!config) return;
91
+ for (const rule of config.injections) {
92
+ assert.ok(rule.ref.endsWith('.md'), `reference "${rule.ref}" should be a .md file`);
93
+ }
94
+ });
95
+
96
+ test('no duplicate skill targets within same pack', () => {
97
+ if (!config) return;
98
+ const seen = new Set();
99
+ for (const rule of config.injections) {
100
+ assert.ok(!seen.has(rule.skill), `duplicate injection target "${rule.skill}" in ${pack.name}`);
101
+ seen.add(rule.skill);
102
+ }
103
+ });
104
+ });
105
+ }
106
+ });
107
+
108
+ (HAS_PRO ? describe : describe.skip)('injection rule coverage', () => {
109
+ const expectedPacks = ['pro-product', 'pro-data-science', 'pro-sales', 'pro-support'];
110
+
111
+ for (const packName of expectedPacks) {
112
+ test(`${packName} has inject.json`, () => {
113
+ const injectPath = path.join(PRO_DIR, packName, 'inject.json');
114
+ assert.ok(existsSync(injectPath), `${packName} should have inject.json`);
115
+ });
116
+ }
117
+
118
+ test('total injection rules across all packs', () => {
119
+ let totalRules = 0;
120
+ for (const packName of expectedPacks) {
121
+ const injectPath = path.join(PRO_DIR, packName, 'inject.json');
122
+ if (!existsSync(injectPath)) continue;
123
+ const config = JSON.parse(readFileSync(injectPath, 'utf-8'));
124
+ totalRules += (config.injections || []).length;
125
+ }
126
+ assert.ok(totalRules >= 8, `should have at least 8 injection rules total, got ${totalRules}`);
127
+ });
128
+ });