@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.
- package/README.md +47 -17
- package/compiler/__tests__/executive-dashboards.test.js +285 -0
- package/compiler/__tests__/inject.test.js +128 -0
- package/compiler/__tests__/orchestrators.test.js +151 -0
- package/compiler/__tests__/org-templates.test.js +447 -0
- package/compiler/__tests__/pack-split.test.js +141 -1
- package/compiler/__tests__/parser.test.js +147 -1
- package/compiler/__tests__/scripts-bundling.test.js +10 -11
- package/compiler/__tests__/skill-index.test.js +218 -0
- package/compiler/__tests__/status.test.js +336 -0
- package/compiler/__tests__/templates.test.js +245 -0
- package/compiler/__tests__/visualizer.test.js +325 -0
- package/compiler/adapters/antigravity.js +18 -4
- package/compiler/bin/rune.js +90 -1
- package/compiler/doctor.js +283 -2
- package/compiler/emitter.js +490 -17
- package/compiler/parser.js +255 -4
- package/compiler/status.js +342 -0
- package/compiler/visualizer.js +622 -0
- package/hooks/hooks.json +12 -0
- package/hooks/intent-router/index.cjs +108 -0
- package/hooks/pre-tool-guard/index.cjs +177 -68
- package/package.json +63 -63
- package/skills/autopsy/SKILL.md +48 -1
- package/skills/brainstorm/SKILL.md +2 -0
- package/skills/completion-gate/SKILL.md +26 -1
- package/skills/context-engine/SKILL.md +93 -2
- package/skills/cook/SKILL.md +794 -648
- package/skills/debug/SKILL.md +409 -392
- package/skills/deploy/SKILL.md +2 -0
- package/skills/docs/SKILL.md +28 -3
- package/skills/fix/SKILL.md +284 -281
- package/skills/mcp-builder/SKILL.md +53 -1
- package/skills/onboard/SKILL.md +58 -1
- package/skills/perf/SKILL.md +34 -1
- package/skills/plan/SKILL.md +372 -342
- package/skills/preflight/SKILL.md +396 -360
- package/skills/retro/SKILL.md +95 -1
- package/skills/review/SKILL.md +535 -489
- package/skills/scope-guard/SKILL.md +1 -0
- package/skills/scout/SKILL.md +1 -0
- package/skills/sentinel/SKILL.md +353 -299
- package/skills/sentinel/references/policy-driven-constraints.md +424 -0
- package/skills/session-bridge/SKILL.md +58 -2
- package/skills/session-bridge/references/evolutionary-memory-patterns.md +312 -0
- package/skills/team/SKILL.md +16 -1
- package/skills/test/SKILL.md +587 -585
- package/skills/verification/SKILL.md +1 -0
- 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
|
-
|
|
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** —
|
|
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.
|
|
86
|
+
## What's New (v2.7.0)
|
|
86
87
|
|
|
87
|
-
- **
|
|
88
|
-
- **
|
|
89
|
-
- **
|
|
90
|
-
- **
|
|
91
|
-
- **
|
|
92
|
-
- **
|
|
93
|
-
- **
|
|
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
|
|
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 |
|
|
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
|
|
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 |
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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
|
+
});
|