@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.
- package/README.md +40 -34
- package/compiler/__tests__/pack-split.test.js +145 -0
- package/compiler/bin/rune.js +0 -5
- package/compiler/doctor.js +42 -0
- package/compiler/emitter.js +27 -4
- package/compiler/parser.js +41 -3
- package/compiler/transformer.js +10 -6
- package/compiler/transforms/compliance.js +40 -0
- package/extensions/ai-ml/PACK.md +38 -474
- package/extensions/ai-ml/skills/ai-agents.md +172 -0
- package/extensions/ai-ml/skills/code-sandbox.md +187 -0
- package/extensions/ai-ml/skills/deep-research.md +146 -0
- package/extensions/ai-ml/skills/embedding-search.md +66 -0
- package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -0
- package/extensions/ai-ml/skills/llm-architect.md +125 -0
- package/extensions/ai-ml/skills/llm-integration.md +64 -0
- package/extensions/ai-ml/skills/prompt-patterns.md +72 -0
- package/extensions/ai-ml/skills/rag-patterns.md +66 -0
- package/extensions/ai-ml/skills/web-extraction.md +114 -0
- package/extensions/analytics/PACK.md +19 -484
- package/extensions/analytics/skills/ab-testing.md +72 -0
- package/extensions/analytics/skills/dashboard-patterns.md +83 -0
- package/extensions/analytics/skills/data-validation.md +68 -0
- package/extensions/analytics/skills/funnel-analysis.md +81 -0
- package/extensions/analytics/skills/sql-patterns.md +57 -0
- package/extensions/analytics/skills/statistical-analysis.md +79 -0
- package/extensions/analytics/skills/tracking-setup.md +71 -0
- package/extensions/backend/PACK.md +44 -618
- package/extensions/backend/skills/api-patterns.md +84 -0
- package/extensions/backend/skills/async-pipeline.md +193 -0
- package/extensions/backend/skills/auth-patterns.md +97 -0
- package/extensions/backend/skills/background-jobs.md +133 -0
- package/extensions/backend/skills/caching-patterns.md +108 -0
- package/extensions/backend/skills/cli-generation.md +133 -0
- package/extensions/backend/skills/database-patterns.md +87 -0
- package/extensions/backend/skills/middleware-patterns.md +104 -0
- package/extensions/chrome-ext/PACK.md +19 -921
- package/extensions/chrome-ext/skills/cws-preflight.md +143 -0
- package/extensions/chrome-ext/skills/cws-publish.md +104 -0
- package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -0
- package/extensions/chrome-ext/skills/ext-messaging.md +139 -0
- package/extensions/chrome-ext/skills/ext-storage.md +133 -0
- package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -0
- package/extensions/content/PACK.md +43 -335
- package/extensions/content/skills/blog-patterns.md +88 -0
- package/extensions/content/skills/cms-integration.md +131 -0
- package/extensions/content/skills/content-scoring.md +107 -0
- package/extensions/content/skills/i18n.md +83 -0
- package/extensions/content/skills/mdx-authoring.md +137 -0
- package/extensions/content/skills/reference.md +1014 -0
- package/extensions/content/skills/seo-patterns.md +67 -0
- package/extensions/content/skills/video-repurpose.md +153 -0
- package/extensions/devops/PACK.md +38 -457
- package/extensions/devops/skills/chaos-testing.md +67 -0
- package/extensions/devops/skills/ci-cd.md +75 -0
- package/extensions/devops/skills/docker.md +58 -0
- package/extensions/devops/skills/edge-serverless.md +163 -0
- package/extensions/devops/skills/infra-as-code.md +158 -0
- package/extensions/devops/skills/kubernetes.md +110 -0
- package/extensions/devops/skills/monitoring.md +57 -0
- package/extensions/devops/skills/server-setup.md +64 -0
- package/extensions/devops/skills/ssl-domain.md +42 -0
- package/extensions/ecommerce/PACK.md +62 -226
- package/extensions/ecommerce/skills/cart-system.md +79 -0
- package/extensions/ecommerce/skills/inventory-mgmt.md +102 -0
- package/extensions/ecommerce/skills/order-management.md +126 -0
- package/extensions/ecommerce/skills/payment-integration.md +472 -0
- package/extensions/ecommerce/skills/shopify-dev.md +69 -0
- package/extensions/ecommerce/skills/subscription-billing.md +93 -0
- package/extensions/ecommerce/skills/tax-compliance.md +117 -0
- package/extensions/gamedev/PACK.md +66 -317
- package/extensions/gamedev/skills/asset-pipeline.md +74 -0
- package/extensions/gamedev/skills/audio-system.md +129 -0
- package/extensions/gamedev/skills/camera-system.md +87 -0
- package/extensions/gamedev/skills/ecs.md +98 -0
- package/extensions/gamedev/skills/game-loops.md +72 -0
- package/extensions/gamedev/skills/input-system.md +199 -0
- package/extensions/gamedev/skills/multiplayer.md +180 -0
- package/extensions/gamedev/skills/particles.md +105 -0
- package/extensions/gamedev/skills/physics-engine.md +89 -0
- package/extensions/gamedev/skills/scene-management.md +146 -0
- package/extensions/gamedev/skills/threejs-patterns.md +90 -0
- package/extensions/gamedev/skills/webgl.md +71 -0
- package/extensions/mobile/PACK.md +56 -223
- package/extensions/mobile/skills/app-store-connect.md +152 -0
- package/extensions/mobile/skills/app-store-prep.md +66 -0
- package/extensions/mobile/skills/deep-linking.md +109 -0
- package/extensions/mobile/skills/flutter.md +60 -0
- package/extensions/mobile/skills/ios-build-pipeline.md +142 -0
- package/extensions/mobile/skills/native-bridge.md +66 -0
- package/extensions/mobile/skills/ota-updates.md +97 -0
- package/extensions/mobile/skills/push-notifications.md +111 -0
- package/extensions/mobile/skills/react-native.md +82 -0
- package/extensions/saas/PACK.md +26 -720
- package/extensions/saas/skills/billing-integration.md +121 -0
- package/extensions/saas/skills/feature-flags.md +130 -0
- package/extensions/saas/skills/multi-tenant.md +103 -0
- package/extensions/saas/skills/onboarding-flow.md +139 -0
- package/extensions/saas/skills/subscription-flow.md +95 -0
- package/extensions/saas/skills/team-management.md +144 -0
- package/extensions/security/PACK.md +10 -448
- package/extensions/security/skills/api-security.md +140 -0
- package/extensions/security/skills/compliance.md +68 -0
- package/extensions/security/skills/owasp-audit.md +64 -0
- package/extensions/security/skills/pentest-patterns.md +77 -0
- package/extensions/security/skills/secret-mgmt.md +65 -0
- package/extensions/security/skills/supply-chain.md +65 -0
- package/extensions/trading/PACK.md +18 -535
- package/extensions/trading/skills/chart-components.md +55 -0
- package/extensions/trading/skills/experiment-loop.md +125 -0
- package/extensions/trading/skills/fintech-patterns.md +47 -0
- package/extensions/trading/skills/indicator-library.md +58 -0
- package/extensions/trading/skills/quant-analysis.md +111 -0
- package/extensions/trading/skills/realtime-data.md +58 -0
- package/extensions/trading/skills/trade-logic.md +104 -0
- package/extensions/ui/PACK.md +34 -853
- package/extensions/ui/skills/a11y-audit.md +91 -0
- package/extensions/ui/skills/animation-patterns.md +106 -0
- package/extensions/ui/skills/component-patterns.md +75 -0
- package/extensions/ui/skills/design-decision.md +98 -0
- package/extensions/ui/skills/design-system.md +68 -0
- package/extensions/ui/skills/landing-patterns.md +155 -0
- package/extensions/ui/skills/palette-picker.md +162 -0
- package/extensions/ui/skills/react-health.md +90 -0
- package/extensions/ui/skills/type-system.md +125 -0
- package/extensions/ui/skills/web-vitals.md +153 -0
- package/extensions/zalo/PACK.md +117 -0
- package/extensions/zalo/skills/zalo-oa-mcp.md +317 -0
- package/extensions/zalo/skills/zalo-oa-messaging.md +429 -0
- package/extensions/zalo/skills/zalo-oa-setup.md +236 -0
- package/extensions/zalo/skills/zalo-oa-webhook.md +189 -0
- package/extensions/zalo/skills/zalo-personal-messaging.md +194 -0
- package/extensions/zalo/skills/zalo-personal-setup.md +153 -0
- package/extensions/zalo/skills/zalo-rate-guard.md +219 -0
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +63 -1
- package/skills/cook/SKILL.md +84 -5
- package/skills/mcp-builder/SKILL.md +48 -1
- package/skills/review/SKILL.md +42 -5
- package/skills/review-intake/SKILL.md +17 -1
- package/skills/skill-router/SKILL.md +89 -7
- package/skills/team/SKILL.md +24 -1
- package/skills/test/SKILL.md +18 -0
- 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.
|
|
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** —
|
|
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
|
|
23
|
+
## What's New (v2.2.1)
|
|
24
24
|
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
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
|
|
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** |
|
|
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 |
|
|
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
|
|
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 |
|
|
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 |
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
**
|
|
325
|
-
1.
|
|
326
|
-
2.
|
|
327
|
-
3.
|
|
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.
|
|
330
|
-
6.
|
|
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:
|
|
347
|
-
Extension Packs:
|
|
348
|
-
Mesh Connections:
|
|
349
|
-
Connections/Skill: 3.
|
|
350
|
-
Platforms:
|
|
351
|
-
Compiler: ~
|
|
352
|
-
Quality:
|
|
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
|
+
});
|
package/compiler/bin/rune.js
CHANGED
|
@@ -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 });
|
package/compiler/doctor.js
CHANGED
|
@@ -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
|
*/
|
package/compiler/emitter.js
CHANGED
|
@@ -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
|
|
174
|
-
await mkdir(
|
|
175
|
-
outputPath = path.join(
|
|
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);
|
package/compiler/parser.js
CHANGED
|
@@ -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
|
+
}
|
package/compiler/transformer.js
CHANGED
|
@@ -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.
|
|
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
|
-
//
|
|
50
|
+
// 6. Generate header (platform-specific frontmatter/preamble)
|
|
47
51
|
const header = adapter.generateHeader(parsedSkill);
|
|
48
52
|
|
|
49
|
-
//
|
|
53
|
+
// 7. Generate hook constraints section
|
|
50
54
|
const hookConstraints = generateHookConstraints(parsedSkill, adapter);
|
|
51
55
|
|
|
52
|
-
//
|
|
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
|
-
//
|
|
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
|
+
}
|