@rune-kit/rune 2.2.0 → 2.2.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.
Files changed (144) hide show
  1. package/README.md +40 -34
  2. package/compiler/__tests__/pack-split.test.js +145 -0
  3. package/compiler/bin/rune.js +0 -5
  4. package/compiler/doctor.js +42 -0
  5. package/compiler/emitter.js +27 -4
  6. package/compiler/parser.js +41 -3
  7. package/compiler/transformer.js +10 -6
  8. package/compiler/transforms/compliance.js +40 -0
  9. package/extensions/ai-ml/PACK.md +38 -474
  10. package/extensions/ai-ml/skills/ai-agents.md +172 -0
  11. package/extensions/ai-ml/skills/code-sandbox.md +187 -0
  12. package/extensions/ai-ml/skills/deep-research.md +146 -0
  13. package/extensions/ai-ml/skills/embedding-search.md +66 -0
  14. package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -0
  15. package/extensions/ai-ml/skills/llm-architect.md +125 -0
  16. package/extensions/ai-ml/skills/llm-integration.md +64 -0
  17. package/extensions/ai-ml/skills/prompt-patterns.md +72 -0
  18. package/extensions/ai-ml/skills/rag-patterns.md +66 -0
  19. package/extensions/ai-ml/skills/web-extraction.md +114 -0
  20. package/extensions/analytics/PACK.md +19 -484
  21. package/extensions/analytics/skills/ab-testing.md +72 -0
  22. package/extensions/analytics/skills/dashboard-patterns.md +83 -0
  23. package/extensions/analytics/skills/data-validation.md +68 -0
  24. package/extensions/analytics/skills/funnel-analysis.md +81 -0
  25. package/extensions/analytics/skills/sql-patterns.md +57 -0
  26. package/extensions/analytics/skills/statistical-analysis.md +79 -0
  27. package/extensions/analytics/skills/tracking-setup.md +71 -0
  28. package/extensions/backend/PACK.md +44 -618
  29. package/extensions/backend/skills/api-patterns.md +84 -0
  30. package/extensions/backend/skills/async-pipeline.md +193 -0
  31. package/extensions/backend/skills/auth-patterns.md +97 -0
  32. package/extensions/backend/skills/background-jobs.md +133 -0
  33. package/extensions/backend/skills/caching-patterns.md +108 -0
  34. package/extensions/backend/skills/cli-generation.md +133 -0
  35. package/extensions/backend/skills/database-patterns.md +87 -0
  36. package/extensions/backend/skills/middleware-patterns.md +104 -0
  37. package/extensions/chrome-ext/PACK.md +19 -921
  38. package/extensions/chrome-ext/skills/cws-preflight.md +143 -0
  39. package/extensions/chrome-ext/skills/cws-publish.md +104 -0
  40. package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -0
  41. package/extensions/chrome-ext/skills/ext-messaging.md +139 -0
  42. package/extensions/chrome-ext/skills/ext-storage.md +133 -0
  43. package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -0
  44. package/extensions/content/PACK.md +43 -335
  45. package/extensions/content/skills/blog-patterns.md +88 -0
  46. package/extensions/content/skills/cms-integration.md +131 -0
  47. package/extensions/content/skills/content-scoring.md +107 -0
  48. package/extensions/content/skills/i18n.md +83 -0
  49. package/extensions/content/skills/mdx-authoring.md +137 -0
  50. package/extensions/content/skills/reference.md +1014 -0
  51. package/extensions/content/skills/seo-patterns.md +67 -0
  52. package/extensions/content/skills/video-repurpose.md +153 -0
  53. package/extensions/devops/PACK.md +38 -457
  54. package/extensions/devops/skills/chaos-testing.md +67 -0
  55. package/extensions/devops/skills/ci-cd.md +75 -0
  56. package/extensions/devops/skills/docker.md +58 -0
  57. package/extensions/devops/skills/edge-serverless.md +163 -0
  58. package/extensions/devops/skills/infra-as-code.md +158 -0
  59. package/extensions/devops/skills/kubernetes.md +110 -0
  60. package/extensions/devops/skills/monitoring.md +57 -0
  61. package/extensions/devops/skills/server-setup.md +64 -0
  62. package/extensions/devops/skills/ssl-domain.md +42 -0
  63. package/extensions/ecommerce/PACK.md +62 -226
  64. package/extensions/ecommerce/skills/cart-system.md +79 -0
  65. package/extensions/ecommerce/skills/inventory-mgmt.md +102 -0
  66. package/extensions/ecommerce/skills/order-management.md +126 -0
  67. package/extensions/ecommerce/skills/payment-integration.md +472 -0
  68. package/extensions/ecommerce/skills/shopify-dev.md +69 -0
  69. package/extensions/ecommerce/skills/subscription-billing.md +93 -0
  70. package/extensions/ecommerce/skills/tax-compliance.md +117 -0
  71. package/extensions/gamedev/PACK.md +66 -317
  72. package/extensions/gamedev/skills/asset-pipeline.md +74 -0
  73. package/extensions/gamedev/skills/audio-system.md +129 -0
  74. package/extensions/gamedev/skills/camera-system.md +87 -0
  75. package/extensions/gamedev/skills/ecs.md +98 -0
  76. package/extensions/gamedev/skills/game-loops.md +72 -0
  77. package/extensions/gamedev/skills/input-system.md +199 -0
  78. package/extensions/gamedev/skills/multiplayer.md +180 -0
  79. package/extensions/gamedev/skills/particles.md +105 -0
  80. package/extensions/gamedev/skills/physics-engine.md +89 -0
  81. package/extensions/gamedev/skills/scene-management.md +146 -0
  82. package/extensions/gamedev/skills/threejs-patterns.md +90 -0
  83. package/extensions/gamedev/skills/webgl.md +71 -0
  84. package/extensions/mobile/PACK.md +56 -223
  85. package/extensions/mobile/skills/app-store-connect.md +152 -0
  86. package/extensions/mobile/skills/app-store-prep.md +66 -0
  87. package/extensions/mobile/skills/deep-linking.md +109 -0
  88. package/extensions/mobile/skills/flutter.md +60 -0
  89. package/extensions/mobile/skills/ios-build-pipeline.md +142 -0
  90. package/extensions/mobile/skills/native-bridge.md +66 -0
  91. package/extensions/mobile/skills/ota-updates.md +97 -0
  92. package/extensions/mobile/skills/push-notifications.md +111 -0
  93. package/extensions/mobile/skills/react-native.md +82 -0
  94. package/extensions/saas/PACK.md +26 -720
  95. package/extensions/saas/skills/billing-integration.md +121 -0
  96. package/extensions/saas/skills/feature-flags.md +130 -0
  97. package/extensions/saas/skills/multi-tenant.md +103 -0
  98. package/extensions/saas/skills/onboarding-flow.md +139 -0
  99. package/extensions/saas/skills/subscription-flow.md +95 -0
  100. package/extensions/saas/skills/team-management.md +144 -0
  101. package/extensions/security/PACK.md +10 -448
  102. package/extensions/security/skills/api-security.md +140 -0
  103. package/extensions/security/skills/compliance.md +68 -0
  104. package/extensions/security/skills/owasp-audit.md +64 -0
  105. package/extensions/security/skills/pentest-patterns.md +77 -0
  106. package/extensions/security/skills/secret-mgmt.md +65 -0
  107. package/extensions/security/skills/supply-chain.md +65 -0
  108. package/extensions/trading/PACK.md +18 -535
  109. package/extensions/trading/skills/chart-components.md +55 -0
  110. package/extensions/trading/skills/experiment-loop.md +125 -0
  111. package/extensions/trading/skills/fintech-patterns.md +47 -0
  112. package/extensions/trading/skills/indicator-library.md +58 -0
  113. package/extensions/trading/skills/quant-analysis.md +111 -0
  114. package/extensions/trading/skills/realtime-data.md +58 -0
  115. package/extensions/trading/skills/trade-logic.md +104 -0
  116. package/extensions/ui/PACK.md +34 -853
  117. package/extensions/ui/skills/a11y-audit.md +91 -0
  118. package/extensions/ui/skills/animation-patterns.md +106 -0
  119. package/extensions/ui/skills/component-patterns.md +75 -0
  120. package/extensions/ui/skills/design-decision.md +98 -0
  121. package/extensions/ui/skills/design-system.md +68 -0
  122. package/extensions/ui/skills/landing-patterns.md +155 -0
  123. package/extensions/ui/skills/palette-picker.md +162 -0
  124. package/extensions/ui/skills/react-health.md +90 -0
  125. package/extensions/ui/skills/type-system.md +125 -0
  126. package/extensions/ui/skills/web-vitals.md +153 -0
  127. package/extensions/zalo/PACK.md +117 -0
  128. package/extensions/zalo/skills/zalo-oa-mcp.md +317 -0
  129. package/extensions/zalo/skills/zalo-oa-messaging.md +429 -0
  130. package/extensions/zalo/skills/zalo-oa-setup.md +236 -0
  131. package/extensions/zalo/skills/zalo-oa-webhook.md +189 -0
  132. package/extensions/zalo/skills/zalo-personal-messaging.md +194 -0
  133. package/extensions/zalo/skills/zalo-personal-setup.md +153 -0
  134. package/extensions/zalo/skills/zalo-rate-guard.md +219 -0
  135. package/package.json +1 -1
  136. package/skills/brainstorm/SKILL.md +63 -1
  137. package/skills/cook/SKILL.md +84 -5
  138. package/skills/mcp-builder/SKILL.md +48 -1
  139. package/skills/review/SKILL.md +42 -5
  140. package/skills/review-intake/SKILL.md +17 -1
  141. package/skills/skill-router/SKILL.md +89 -7
  142. package/skills/team/SKILL.md +24 -1
  143. package/skills/test/SKILL.md +18 -0
  144. package/skills/verification/SKILL.md +40 -1
package/README.md CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  **Less skills. Deeper connections.**
4
4
 
5
- A lean, interconnected skill ecosystem for AI coding assistants. 55 skills, 200+ mesh connections, full project lifecycle — from idea to production.
5
+ A lean, interconnected skill ecosystem for AI coding assistants. 58 skills, 200+ mesh connections, full project lifecycle — from idea to production.
6
6
 
7
- Works on **Claude Code** (native plugin) · **Cursor** · **Windsurf** · **Google Antigravity** · any AI IDE
7
+ Works on **Claude Code** (native plugin) · **Cursor** · **Windsurf** · **Google Antigravity** · **OpenAI Codex** · **OpenCode** · any AI IDE
8
8
 
9
9
  ## Why Rune?
10
10
 
11
11
  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).
12
12
 
13
- Rune is a **mesh** — 55 skills with 200+ connections across a 5-layer architecture. Skills call each other bidirectionally, forming resilient workflows that adapt when things go wrong.
13
+ Rune is a **mesh** — 58 skills with 200+ connections across a 5-layer architecture. Skills call each other bidirectionally, forming resilient workflows that adapt when things go wrong.
14
14
 
15
15
  ```
16
16
  Pipeline: A → B → C → D (B fails = stuck)
@@ -20,24 +20,23 @@ Mesh: A ↔ B ↔ C (B fails = A reaches C via D→E)
20
20
  D ↔ E ↔ F
21
21
  ```
22
22
 
23
- ## What's New (v2.1.0)
23
+ ## What's New (v2.2.1)
24
24
 
25
- - **Phase-Aware Execution** — cook v0.5.0 resumes multi-session plans automatically. Master plan + phase files = one phase per session, any model executes accurately
26
- - **Amateur-Proof Plans** plan v0.4.0 generates self-contained phase files with data flow diagrams, code contracts, failure scenarios, and rejection criteria. Designed so even the weakest model succeeds
27
- - **Cognitive Bias Detection** — problem-solver v0.3.0 runs mandatory bias checks (12 biases) before every analysis. Reversibility Filter, Pre-Mortem, Weighted Matrix, MECE decomposition
28
- - **Second-Order Thinking** — sequential-thinking v0.3.0 classifies decision reversibility, traces cascading effects, and cross-checks for anchoring/status-quo/overconfidence biases
29
- - **6 New Skills** — ba (business analyst), scaffold (project bootstrap), docs (documentation lifecycle), git (semantic commits), mcp-builder (MCP server generator), doc-processor (PDF/DOCX/XLSX)
30
- - **Multi-Platform Compiler** — all 55 skills compile to Cursor, Windsurf, Antigravity, and generic formats with zero knowledge loss
25
+ - **Enforcement Upgrade** — Antigravity-level IDE compliance: 5-type Request Classifier, File Ownership Matrix, Self-Verification HARD-GATE, Routing Proof, Clarification Gate, Phase Transition Protocol. Distributed via compiler every skill file gets compliance preamble on non-Claude platforms
26
+ - **All 14 Free Packs Deep** every L4 extension pack now exceeds 500 lines of production-ready patterns and code examples
27
+ - **8 Platform Adapters** — Claude Code, Cursor, Windsurf, Google Antigravity, OpenAI Codex, OpenCode, OpenClaw, Generic
28
+ - **58 Core Skills** — +3 since v2.1.0: adversary (red team), sentinel-env (env validation), chrome-ext pack
29
+ - **Rune Pro** — 4 premium packs (product, sales, data-science, support) in private repo. $49 lifetime
31
30
 
32
31
  ## What Rune Is (and Isn't)
33
32
 
34
- Rune started as a **Claude Code plugin** and now compiles to **every major AI IDE**. Same 55 skills, same mesh connections, same workflows — zero knowledge loss across platforms.
33
+ Rune started as a **Claude Code plugin** and now compiles to **every major AI IDE**. Same 58 skills, same mesh connections, same workflows — zero knowledge loss across platforms.
35
34
 
36
35
  | | Rune Provides | Claude Code Provides |
37
36
  |---|---|---|
38
37
  | **Workflows** | 8-phase TDD cycle (cook), parallel DAG execution (team), rescue pipelines | Basic tool calling |
39
38
  | **Quality Gates** | preflight + sentinel + review + completion-gate (parallel) | None built-in |
40
- | **Domain Knowledge** | 12 extension packs (trading, SaaS, mobile, etc.) | General-purpose |
39
+ | **Domain Knowledge** | 14 extension packs (trading, SaaS, mobile, etc.) | General-purpose |
41
40
  | **Cross-Session State** | .rune/ directory (decisions, conventions, progress) | Conversation only |
42
41
  | **Mesh Resilience** | 200+ skill connections, fail-loud-route-around | Linear execution |
43
42
  | **Cost Optimization** | Auto model selection (haiku/sonnet/opus per task) | Single model |
@@ -55,7 +54,7 @@ Rune started as a **Claude Code plugin** and now compiles to **every major AI ID
55
54
  | CI quality gates | `verification` skill: lint + typecheck + tests + build (actual commands, not LLM review) |
56
55
  | Memory / state | `session-bridge` + `journal`: cross-session decisions, conventions, ADRs, module health |
57
56
  | Multi-model strategy | Every skill has assigned model: haiku (scan), sonnet (code), opus (architecture) |
58
- | Agent specialization | 55 specialized skills with dedicated roles (architect, coder, reviewer, scanner, researcher, BA, scaffolder) — each runs as a Task agent via Claude Code |
57
+ | Agent specialization | 58 specialized skills with dedicated roles (architect, coder, reviewer, scanner, researcher, BA, scaffolder) — each runs as a Task agent via Claude Code |
59
58
  | Security scanning | `sentinel`: OWASP patterns, secret scanning, dependency audit. `sast`: static analysis |
60
59
 
61
60
  ## Install
@@ -82,16 +81,16 @@ npx @rune-kit/rune init --platform windsurf
82
81
  npx @rune-kit/rune init --platform antigravity
83
82
  ```
84
83
 
85
- This compiles all 55 skills into your IDE's rules format. Same knowledge, same workflows.
84
+ This compiles all 58 skills into your IDE's rules format. Same knowledge, same workflows.
86
85
 
87
86
  ### Platform Comparison
88
87
 
89
88
  | Feature | Claude Code | Cursor / Windsurf / Others |
90
89
  |---------|-------------|---------------------------|
91
- | Skills available | 55/55 | 55/55 |
90
+ | Skills available | 58/58 | 58/58 |
92
91
  | Mesh connections | 200+ (programmatic) | 200+ (rule references) |
93
92
  | Workflows & HARD-GATEs | Full | Full |
94
- | Extension packs | 12 | 12 |
93
+ | Extension packs | 14 | 14 |
95
94
  | Subagent parallelism | Native | Sequential fallback |
96
95
  | Lifecycle hooks | 8 hooks (JS runtime) | Inline MUST/NEVER constraints |
97
96
  | Adaptive model routing | haiku/sonnet/opus | Single model |
@@ -153,7 +152,7 @@ This compiles all 55 skills into your IDE's rules format. Same knowledge, same w
153
152
  ║ Full lifecycle workflows ║
154
153
  ║ cook │ team │ launch │ rescue │ scaffold ║
155
154
  ╠══════════════════════════════════════════════════════╣
156
- ║ L2: WORKFLOW HUBS (26) ║
155
+ ║ L2: WORKFLOW HUBS (27) ║
157
156
  ║ Cross-hub mesh — the key differentiator ║
158
157
  ║ ║
159
158
  ║ Creation: plan │ scout │ brainstorm │ design │ ║
@@ -164,8 +163,9 @@ This compiles all 55 skills into your IDE's rules format. Same knowledge, same w
164
163
  ║ logic-guardian ║
165
164
  ║ Delivery: deploy │ marketing │ incident │ docs ║
166
165
  ║ Rescue: autopsy │ safeguard │ surgeon ║
166
+ ║ Security: adversary ║
167
167
  ╠══════════════════════════════════════════════════════╣
168
- ║ L3: UTILITIES (23) ║
168
+ ║ L3: UTILITIES (25) ║
169
169
  ║ Stateless, pure capabilities ║
170
170
  ║ ║
171
171
  ║ Knowledge: research │ docs-seeker │ trend-scout ║
@@ -182,14 +182,17 @@ This compiles all 55 skills into your IDE's rules format. Same knowledge, same w
182
182
  ║ Workspace: worktree ║
183
183
  ║ Git: git ║
184
184
  ║ Documents: doc-processor ║
185
+ ║ Security: sentinel-env ║
186
+ ║ Memory: neural-memory ║
185
187
  ╠══════════════════════════════════════════════════════╣
186
- ║ L4: EXTENSION PACKS (12) ║
188
+ ║ L4: EXTENSION PACKS (14) ║
187
189
  ║ Domain-specific, install what you need ║
188
190
  ║ ║
189
191
  ║ @rune/ui │ @rune/backend │ @rune/devops │ ║
190
192
  ║ @rune/mobile │ @rune/security │ @rune/trading │ ║
191
193
  ║ @rune/saas │ @rune/ecommerce │ @rune/ai-ml │ ║
192
- ║ @rune/gamedev │ @rune/content │ @rune/analytics
194
+ ║ @rune/gamedev │ @rune/content │ @rune/analytics
195
+ ║ @rune/chrome-ext │ @rune/zalo ║
193
196
  ╚══════════════════════════════════════════════════════╝
194
197
  ```
195
198
 
@@ -310,8 +313,9 @@ Business department packs for product, sales, and data teams. Same PACK.md forma
310
313
  | @rune-pro/product | feature-spec, roadmap, metrics, release-comms, user-research, competitive | Product Management |
311
314
  | @rune-pro/sales | account-research, call-prep, outreach, pipeline-review, competitive-intel | Sales Enablement |
312
315
  | @rune-pro/data-science | data-exploration, sql-advanced, visualization, statistical-testing, dashboards | Data Science |
316
+ | @rune-pro/support | ticket-triage, response-drafting, knowledge-base, escalation, faq, metrics | Customer Support |
313
317
 
314
- Coming soon. See [docs/plans/RUNE-PRO-PLAN.md](docs/plans/RUNE-PRO-PLAN.md) for the full roadmap.
318
+ **$49 lifetime** (Pro) · **$149 lifetime** (Business). See [docs/plans/RUNE-PRO-PLAN.md](docs/plans/RUNE-PRO-PLAN.md).
315
319
 
316
320
  ## Multi-Platform Compiler
317
321
 
@@ -321,13 +325,14 @@ Rune includes a 3-stage compiler that transforms SKILL.md files into platform-na
321
325
  skills/*.md → PARSE → TRANSFORM → EMIT → platform rules
322
326
  ```
323
327
 
324
- **6 transforms applied per platform:**
325
- 1. Cross-references: `rune:cook` `@rune-cook.mdc` (Cursor) / prose ref (Windsurf)
326
- 2. Tool names: `Read`, `Edit`, `Bash` generic language
327
- 3. Frontmatter: strip Claude Code-specific directives
328
+ **8 transforms applied per platform:**
329
+ 1. Frontmatter: strip Claude Code-specific directives
330
+ 2. Cross-references: `rune:cook` → `@rune-cook.mdc` (Cursor) / prose ref (Windsurf)
331
+ 3. Tool names: `Read`, `Edit`, `Bash` → generic language
328
332
  4. Subagents: parallel → sequential workflow
329
- 5. Hooks: runtime hooks inline MUST/NEVER constraints
330
- 6. Branding: Rune attribution footer
333
+ 5. Compliance: inject enforcement preamble (non-Claude platforms)
334
+ 6. Hooks: runtime hooks → inline MUST/NEVER constraints
335
+ 7. Branding: Rune attribution footer
331
336
 
332
337
  ```bash
333
338
  # Build for any platform
@@ -343,13 +348,14 @@ See [docs/MULTI-PLATFORM.md](docs/MULTI-PLATFORM.md) for the full architecture.
343
348
  ## Numbers
344
349
 
345
350
  ```
346
- Core Skills: 55 (L0: 1 │ L1: 5 │ L2: 26 │ L3: 23)
347
- Extension Packs: 12 free + 3 pro (coming soon)
348
- Mesh Connections: 206 cross-references + 151 tool references
349
- Connections/Skill: 3.7 avg
350
- Platforms: 5 (Claude Code, Cursor, Windsurf, Antigravity, Generic)
351
- Compiler: ~1200 LOC (parser + transforms + adapters + CLI)
352
- Quality: 55/55 skills with Output Format, Sharp Edges, Done When, Cost Profile
351
+ Core Skills: 58 (L0: 1 │ L1: 5 │ L2: 27 │ L3: 25)
352
+ Extension Packs: 14 free + 4 pro + 2 business
353
+ Mesh Connections: 200+ cross-references
354
+ Connections/Skill: 3.4 avg
355
+ Platforms: 8 (Claude Code, Cursor, Windsurf, Antigravity, Codex, OpenCode, OpenClaw, Generic)
356
+ Compiler: ~1400 LOC (parser + 8 transforms + 8 adapters + CLI)
357
+ Quality: 58/58 skills with Output Format, Sharp Edges, Done When, Cost Profile
358
+ Pack Depth: 20 packs total (14 free + 4 pro + 2 business, all free packs rated Deep)
353
359
  ```
354
360
 
355
361
  ## License
@@ -0,0 +1,145 @@
1
+ import { parsePack, parseSkill } from '../parser.js';
2
+ import { test } from 'node:test';
3
+ import assert from 'node:assert';
4
+ import { readFileSync, existsSync } from 'node:fs';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const EXTENSIONS_DIR = path.resolve(__dirname, '../../extensions');
10
+
11
+ // --- Monolith format (regression) ---
12
+
13
+ test('parsePack: monolith format has isSplit=false', () => {
14
+ const content = `---
15
+ name: "@rune/trading"
16
+ description: "Trading patterns"
17
+ metadata:
18
+ version: "0.2.0"
19
+ layer: L4
20
+ price: free
21
+ target: Traders
22
+ ---
23
+
24
+ # @rune/trading
25
+
26
+ ## Purpose
27
+ Trading pack.
28
+
29
+ ## Skills Included
30
+
31
+ ### risk-management
32
+ Risk management workflow.
33
+ `;
34
+
35
+ const parsed = parsePack(content, 'extensions/trading/PACK.md');
36
+
37
+ assert.strictEqual(parsed.isSplit, false);
38
+ assert.deepStrictEqual(parsed.skillManifest, []);
39
+ assert.strictEqual(parsed.name, '@rune/trading');
40
+ assert.strictEqual(parsed.version, '0.2.0');
41
+ assert.strictEqual(parsed.layer, 'L4');
42
+ assert.ok(parsed.body.includes('risk-management'));
43
+ });
44
+
45
+ test('parsePack: monolith without metadata block defaults correctly', () => {
46
+ const content = `---
47
+ name: "@rune/test"
48
+ description: "Test pack"
49
+ ---
50
+
51
+ # @rune/test
52
+ Body here.
53
+ `;
54
+
55
+ const parsed = parsePack(content, 'test/PACK.md');
56
+
57
+ assert.strictEqual(parsed.isSplit, false);
58
+ assert.strictEqual(parsed.version, '1.0.0');
59
+ });
60
+
61
+ // --- Split format detection ---
62
+
63
+ test('parsePack: split format detected via metadata.format', () => {
64
+ const content = `---
65
+ name: "@rune/backend"
66
+ description: "Backend patterns"
67
+ metadata:
68
+ version: "0.3.0"
69
+ layer: L4
70
+ price: free
71
+ target: Backend developers
72
+ format: split
73
+ ---
74
+
75
+ # @rune/backend
76
+
77
+ ## Purpose
78
+ Backend pack index.
79
+
80
+ ## Skills Included
81
+ | Skill | Model | Description |
82
+ |-------|-------|-------------|
83
+ | api-design | sonnet | API patterns |
84
+ | auth | sonnet | Auth patterns |
85
+ `;
86
+
87
+ const parsed = parsePack(content, 'extensions/backend/PACK.md');
88
+
89
+ assert.strictEqual(parsed.isSplit, true);
90
+ assert.strictEqual(parsed.version, '0.3.0');
91
+ assert.strictEqual(parsed.name, '@rune/backend');
92
+ });
93
+
94
+ test('parsePack: skill manifest parsed from string array', () => {
95
+ // Note: our YAML parser is simple — it handles nested objects but not arrays.
96
+ // This test validates the parseSkillManifest function directly with object skills.
97
+ const content = `---
98
+ name: "@rune/test"
99
+ description: "Test"
100
+ metadata:
101
+ format: split
102
+ ---
103
+
104
+ Body.
105
+ `;
106
+
107
+ const parsed = parsePack(content, 'test/PACK.md');
108
+ assert.strictEqual(parsed.isSplit, true);
109
+ // With simple YAML parser, skills array won't parse — manifest will be empty
110
+ // Real usage will use the skills/ directory detection as fallback
111
+ assert.ok(Array.isArray(parsed.skillManifest));
112
+ });
113
+
114
+ // --- Real pack regression: existing monolith packs parse correctly ---
115
+
116
+ test('parsePack: real trading PACK.md parses as split', () => {
117
+ const tradingPath = path.join(EXTENSIONS_DIR, 'trading', 'PACK.md');
118
+ if (!existsSync(tradingPath)) {
119
+ console.log(' skip: trading PACK.md not found');
120
+ return;
121
+ }
122
+
123
+ const content = readFileSync(tradingPath, 'utf-8');
124
+ const parsed = parsePack(content, tradingPath);
125
+
126
+ assert.strictEqual(parsed.isSplit, true);
127
+ assert.strictEqual(parsed.layer, 'L4');
128
+ assert.strictEqual(parsed.group, 'extension');
129
+ assert.ok(parsed.body.length > 50);
130
+ });
131
+
132
+ test('parsePack: real backend PACK.md parses as split (post-split)', () => {
133
+ const backendPath = path.join(EXTENSIONS_DIR, 'backend', 'PACK.md');
134
+ if (!existsSync(backendPath)) {
135
+ console.log(' skip: backend PACK.md not found');
136
+ return;
137
+ }
138
+
139
+ const content = readFileSync(backendPath, 'utf-8');
140
+ const parsed = parsePack(content, backendPath);
141
+
142
+ assert.strictEqual(parsed.isSplit, true);
143
+ assert.strictEqual(parsed.layer, 'L4');
144
+ assert.ok(parsed.sections.size > 0);
145
+ });
@@ -51,11 +51,6 @@ function detectPlatform(projectRoot) {
51
51
  return null;
52
52
  }
53
53
 
54
- function discoverExtensions() {
55
- const extDir = path.join(RUNE_ROOT, 'extensions');
56
- if (!existsSync(extDir)) return [];
57
- return [];
58
- }
59
54
 
60
55
  async function prompt(question) {
61
56
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -7,6 +7,7 @@
7
7
  import { readdir, readFile, stat } from 'node:fs/promises';
8
8
  import { existsSync } from 'node:fs';
9
9
  import path from 'node:path';
10
+ import { parsePack } from './parser.js';
10
11
 
11
12
  /**
12
13
  * Run doctor checks on compiled output
@@ -89,6 +90,18 @@ export async function runDoctor({ outputRoot, adapter, config, runeRoot }) {
89
90
  results.warnings.push(`${disabled.length} skills disabled: ${disabled.join(', ')}`);
90
91
  }
91
92
 
93
+ // Check 7: Split pack integrity (validate skill manifest files exist)
94
+ const extensionsDir = path.join(runeRoot, 'extensions');
95
+ if (existsSync(extensionsDir)) {
96
+ const splitPackErrors = await checkSplitPacks(extensionsDir);
97
+ if (splitPackErrors.length === 0) {
98
+ results.checks.push({ name: 'Split packs', status: 'pass' });
99
+ } else {
100
+ results.checks.push({ name: 'Split packs', status: 'fail', detail: `${splitPackErrors.length} missing skill files` });
101
+ results.errors.push(...splitPackErrors);
102
+ }
103
+ }
104
+
92
105
  if (results.errors.length > 0) results.healthy = false;
93
106
 
94
107
  return results;
@@ -119,6 +132,35 @@ async function checkCrossRefs(outputDir, files, adapter) {
119
132
  return [...new Set(errors)]; // deduplicate
120
133
  }
121
134
 
135
+ /**
136
+ * Check that all split packs have their declared skill files present
137
+ */
138
+ async function checkSplitPacks(extensionsDir) {
139
+ const errors = [];
140
+ const entries = await readdir(extensionsDir, { withFileTypes: true });
141
+
142
+ for (const entry of entries) {
143
+ if (!entry.isDirectory()) continue;
144
+ const packFile = path.join(extensionsDir, entry.name, 'PACK.md');
145
+ if (!existsSync(packFile)) continue;
146
+
147
+ const content = await readFile(packFile, 'utf-8');
148
+ const parsed = parsePack(content, packFile);
149
+
150
+ if (!parsed.isSplit) continue;
151
+
152
+ const packDir = path.dirname(packFile);
153
+ for (const skill of parsed.skillManifest) {
154
+ const skillPath = path.join(packDir, skill.file);
155
+ if (!existsSync(skillPath)) {
156
+ errors.push(`@rune/${entry.name}: skill file "${skill.file}" declared in manifest but not found`);
157
+ }
158
+ }
159
+ }
160
+
161
+ return errors;
162
+ }
163
+
122
164
  /**
123
165
  * Format doctor results for console output
124
166
  */
@@ -8,7 +8,7 @@
8
8
  import { readdir, readFile, mkdir, writeFile } from 'node:fs/promises';
9
9
  import { existsSync } from 'node:fs';
10
10
  import path from 'node:path';
11
- import { parseSkill, parsePack } from './parser.js';
11
+ import { parseSkill, parsePack, extractCrossRefs, extractToolRefs } from './parser.js';
12
12
  import { transformSkill } from './transformer.js';
13
13
 
14
14
  /**
@@ -158,6 +158,29 @@ export async function buildAll({ runeRoot, outputRoot, adapter, disabledSkills =
158
158
  const content = await readFile(packPath, 'utf-8');
159
159
  const parsed = parsePack(content, packPath);
160
160
  const packName = path.basename(path.dirname(packPath));
161
+ const packDir = path.dirname(packPath);
162
+
163
+ // For split packs, load individual skill files and concatenate into body
164
+ if (parsed.isSplit && parsed.skillManifest.length > 0) {
165
+ const skillBodies = [];
166
+ for (const skill of parsed.skillManifest) {
167
+ const skillPath = path.join(packDir, skill.file);
168
+ if (existsSync(skillPath)) {
169
+ const skillContent = await readFile(skillPath, 'utf-8');
170
+ // Strip frontmatter from skill file — we only need the body
171
+ const skillBodyMatch = skillContent.replace(/\r\n/g, '\n').match(/^---\n[\s\S]*?\n---\n?([\s\S]*)$/);
172
+ const skillBody = skillBodyMatch ? skillBodyMatch[1].trim() : skillContent.trim();
173
+ skillBodies.push(skillBody);
174
+ } else {
175
+ stats.errors.push({ file: skillPath, error: `Skill file not found (listed in ${packPath} manifest)` });
176
+ }
177
+ }
178
+ // Concatenate: index body + all skill bodies
179
+ parsed.body = parsed.body + '\n\n' + skillBodies.join('\n\n---\n\n');
180
+ // Re-extract refs from the full concatenated body
181
+ parsed.crossRefs = extractCrossRefs(parsed.body);
182
+ parsed.toolRefs = extractToolRefs(parsed.body);
183
+ }
161
184
 
162
185
  // Normalize pack name for headers (ext-trading instead of @rune/trading)
163
186
  parsed.name = `ext-${packName}`;
@@ -170,9 +193,9 @@ export async function buildAll({ runeRoot, outputRoot, adapter, disabledSkills =
170
193
 
171
194
  if (adapter.useSkillDirectories) {
172
195
  const dirName = `${adapter.skillPrefix}ext-${packName}`;
173
- const packDir = path.join(outputDir, dirName);
174
- await mkdir(packDir, { recursive: true });
175
- outputPath = path.join(packDir, adapter.skillFileName || 'SKILL.md');
196
+ const outPackDir = path.join(outputDir, dirName);
197
+ await mkdir(outPackDir, { recursive: true });
198
+ outputPath = path.join(outPackDir, adapter.skillFileName || 'SKILL.md');
176
199
  displayName = `${dirName}/${adapter.skillFileName || 'SKILL.md'}`;
177
200
  } else {
178
201
  const fileName = outputFileName(`ext-${packName}`, adapter);
@@ -65,7 +65,7 @@ function parseFrontmatter(content) {
65
65
  /**
66
66
  * Extract all rune:<name> cross-references from body
67
67
  */
68
- function extractCrossRefs(body) {
68
+ export function extractCrossRefs(body) {
69
69
  const refs = [];
70
70
  const lines = body.split('\n');
71
71
 
@@ -90,7 +90,7 @@ function extractCrossRefs(body) {
90
90
  /**
91
91
  * Extract all Claude Code tool references from body
92
92
  */
93
- function extractToolRefs(body) {
93
+ export function extractToolRefs(body) {
94
94
  const refs = [];
95
95
  const lines = body.split('\n');
96
96
 
@@ -188,16 +188,25 @@ export function parseSkill(content, filePath = '') {
188
188
 
189
189
  /**
190
190
  * Parse a PACK.md extension file (same format, slightly different metadata)
191
+ * Supports both monolith format (all skills inline) and split format (skills in separate files)
192
+ *
193
+ * Split format detection: metadata.format === "split" OR metadata.skills array present
191
194
  */
192
195
  export function parsePack(content, filePath = '') {
193
196
  const { frontmatter, body } = parseFrontmatter(content);
197
+ const metadata = frontmatter.metadata || {};
198
+
199
+ // Detect split format
200
+ const isSplit = metadata.format === 'split' || Array.isArray(metadata.skills);
194
201
 
195
202
  return {
196
203
  name: frontmatter.name || '',
197
204
  description: frontmatter.description || '',
198
- version: frontmatter.version || '1.0.0',
205
+ version: metadata.version || frontmatter.version || '1.0.0',
199
206
  layer: 'L4',
200
207
  group: 'extension',
208
+ isSplit,
209
+ skillManifest: isSplit ? parseSkillManifest(metadata.skills || []) : [],
201
210
  body,
202
211
  crossRefs: extractCrossRefs(body),
203
212
  toolRefs: extractToolRefs(body),
@@ -207,3 +216,32 @@ export function parsePack(content, filePath = '') {
207
216
  frontmatter,
208
217
  };
209
218
  }
219
+
220
+ /**
221
+ * Parse the skills manifest from split PACK.md frontmatter
222
+ *
223
+ * @param {Array} skills - array of skill entries from frontmatter metadata.skills
224
+ * @returns {Array<{name: string, file: string, model: string, description: string}>}
225
+ */
226
+ function parseSkillManifest(skills) {
227
+ if (!Array.isArray(skills)) return [];
228
+
229
+ return skills.map(skill => {
230
+ // Handle both string format ("skill-name") and object format ({name, file, model, description})
231
+ if (typeof skill === 'string') {
232
+ return {
233
+ name: skill,
234
+ file: `skills/${skill}.md`,
235
+ model: 'sonnet',
236
+ description: '',
237
+ };
238
+ }
239
+
240
+ return {
241
+ name: skill.name || '',
242
+ file: skill.file || `skills/${skill.name}.md`,
243
+ model: skill.model || 'sonnet',
244
+ description: skill.description || '',
245
+ };
246
+ });
247
+ }
@@ -2,7 +2,7 @@
2
2
  * Transform Pipeline
3
3
  *
4
4
  * Applies all transforms in order to convert a parsed skill into platform-specific output.
5
- * Pipeline: frontmatter → cross-refs → tool-names → subagents → hooks → branding
5
+ * Pipeline: frontmatter → cross-refs → tool-names → subagents → compliance → hooks → branding
6
6
  */
7
7
 
8
8
  import { transformCrossReferences } from './transforms/cross-references.js';
@@ -10,6 +10,7 @@ import { transformToolNames } from './transforms/tool-names.js';
10
10
  import { transformFrontmatter } from './transforms/frontmatter.js';
11
11
  import { transformSubagents } from './transforms/subagents.js';
12
12
  import { generateHookConstraints } from './transforms/hooks.js';
13
+ import { transformCompliance } from './transforms/compliance.js';
13
14
  import { addBranding } from './transforms/branding.js';
14
15
 
15
16
  /**
@@ -40,16 +41,19 @@ export function transformSkill(parsedSkill, adapter) {
40
41
  // 3. Convert subagent/parallel instructions to sequential
41
42
  body = transformSubagents(body, adapter);
42
43
 
43
- // 4. Platform-specific post-processing
44
+ // 4. Inject compliance preamble (enforcement for non-Claude platforms)
45
+ body = transformCompliance(body, adapter);
46
+
47
+ // 5. Platform-specific post-processing
44
48
  body = adapter.postProcess(body);
45
49
 
46
- // 5. Generate header (platform-specific frontmatter/preamble)
50
+ // 6. Generate header (platform-specific frontmatter/preamble)
47
51
  const header = adapter.generateHeader(parsedSkill);
48
52
 
49
- // 6. Generate hook constraints section
53
+ // 7. Generate hook constraints section
50
54
  const hookConstraints = generateHookConstraints(parsedSkill, adapter);
51
55
 
52
- // 7. Inject hook constraints after the first heading
56
+ // 8. Inject hook constraints after the first heading
53
57
  if (hookConstraints) {
54
58
  const firstHeadingEnd = body.indexOf('\n## ');
55
59
  if (firstHeadingEnd !== -1) {
@@ -62,7 +66,7 @@ export function transformSkill(parsedSkill, adapter) {
62
66
  }
63
67
  }
64
68
 
65
- // 8. Generate footer (branding + CTA)
69
+ // 9. Generate footer (branding + CTA)
66
70
  const footer = adapter.generateFooter();
67
71
 
68
72
  return { header, body, footer };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Compliance Transform
3
+ *
4
+ * Injects a compliance preamble into every compiled skill file.
5
+ * This distributes enforcement to ALL platforms (Cursor, Windsurf, etc.)
6
+ * so the AI is reminded of routing discipline even if skill-router
7
+ * itself is not loaded in context.
8
+ *
9
+ * Only applies to non-Claude platforms (Claude has native plugin enforcement).
10
+ */
11
+
12
+ const COMPLIANCE_PREAMBLE = [
13
+ '',
14
+ '> **RUNE COMPLIANCE**: Before ANY code response, you MUST:',
15
+ '> 1. Classify this request (CODE_CHANGE | QUESTION | DEBUG | REVIEW | EXPLORE)',
16
+ '> 2. Route through the correct Rune skill (see skill-router routing table)',
17
+ "> 3. Follow the skill's workflow — do NOT freelance or skip steps",
18
+ '> Violation: writing code without skill routing = incorrect behavior.',
19
+ '',
20
+ ].join('\n');
21
+
22
+ /**
23
+ * Inject compliance preamble after the first heading in a skill body.
24
+ *
25
+ * @param {string} body - skill body text
26
+ * @param {object} adapter - platform adapter
27
+ * @returns {string} body with compliance preamble injected
28
+ */
29
+ export function transformCompliance(body, adapter) {
30
+ if (adapter.name === 'claude') return body;
31
+
32
+ // Insert after first heading (# skill-name), before first ## section
33
+ const firstH2 = body.indexOf('\n## ');
34
+ if (firstH2 !== -1) {
35
+ return body.slice(0, firstH2) + COMPLIANCE_PREAMBLE + body.slice(firstH2);
36
+ }
37
+
38
+ // Fallback: append at end if no ## sections found
39
+ return body + COMPLIANCE_PREAMBLE;
40
+ }