@rune-kit/rune 2.3.0 → 2.3.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 (46) hide show
  1. package/README.md +395 -389
  2. package/compiler/__tests__/tier-override.test.js +158 -0
  3. package/compiler/adapters/antigravity.js +3 -8
  4. package/compiler/adapters/codex.js +3 -8
  5. package/compiler/adapters/cursor.js +3 -8
  6. package/compiler/adapters/generic.js +3 -8
  7. package/compiler/adapters/openclaw.js +4 -9
  8. package/compiler/adapters/opencode.js +3 -8
  9. package/compiler/adapters/windsurf.js +3 -8
  10. package/compiler/bin/rune.js +34 -1
  11. package/compiler/emitter.js +94 -5
  12. package/compiler/transforms/branding.js +10 -3
  13. package/docs/ARCHITECTURE.md +3 -3
  14. package/docs/VISION.md +3 -3
  15. package/docs/guides/index.html +14 -14
  16. package/docs/index.html +7 -7
  17. package/docs/skills/index.html +832 -832
  18. package/extensions/ai-ml/PACK.md +7 -0
  19. package/extensions/content/PACK.md +7 -0
  20. package/extensions/mobile/PACK.md +9 -9
  21. package/extensions/zalo/PACK.md +9 -0
  22. package/package.json +2 -2
  23. package/skills/audit/SKILL.md +526 -529
  24. package/skills/ba/SKILL.md +349 -351
  25. package/skills/completion-gate/SKILL.md +260 -263
  26. package/skills/context-engine/SKILL.md +0 -6
  27. package/skills/cook/SKILL.md +2 -11
  28. package/skills/debug/SKILL.md +392 -394
  29. package/skills/deploy/references/post-deploy-integration.md +192 -0
  30. package/skills/fix/SKILL.md +281 -282
  31. package/skills/onboard/SKILL.md +0 -4
  32. package/skills/plan/references/completeness-scoring.md +0 -1
  33. package/skills/plan/references/outcome-block.md +0 -1
  34. package/skills/plan/references/workflow-registry.md +0 -1
  35. package/skills/preflight/SKILL.md +360 -365
  36. package/skills/rescue/SKILL.md +1 -0
  37. package/skills/research/SKILL.md +149 -150
  38. package/skills/review/SKILL.md +489 -495
  39. package/skills/sentinel/SKILL.md +0 -11
  40. package/skills/sentinel/references/destructive-commands.md +0 -1
  41. package/skills/sentinel/references/skill-content-guard.md +0 -1
  42. package/skills/session-bridge/SKILL.md +0 -4
  43. package/skills/skill-router/{SKILL.md → skill.md} +446 -397
  44. package/skills/team/SKILL.md +1 -2
  45. package/skills/test/SKILL.md +585 -593
  46. package/skills/watchdog/references/webhook-health-checks.md +243 -0
@@ -1,832 +1,832 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Rune Skills Catalog — 58 skills · 5 layers · 200+ connections</title>
7
- <meta name="description" content="Visual reference of all 58 Rune skills organized by layer. Filter by layer, search by name, sort by model.">
8
- <meta property="og:title" content="Rune Skills Catalog">
9
- <meta property="og:description" content="58 skills · 5 layers · 200+ connections. The complete Rune skill reference.">
10
- <meta property="og:type" content="website">
11
- <link rel="preconnect" href="https://fonts.googleapis.com">
12
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
- <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
14
- <style>
15
- /* ─── DESIGN TOKENS ─── */
16
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
17
-
18
- :root {
19
- --bg-base: #0a0f1a;
20
- --bg-card: #111827;
21
- --bg-elevated: #1a2332;
22
- --bg-section-alt: #080c14;
23
- --bg-code: rgba(0,0,0,0.4);
24
- --text-primary: #f0f4f8;
25
- --text-secondary: #8896a8;
26
- --text-muted: #5a6a7a;
27
- --border: #1e2d3d;
28
- --accent: #10b981;
29
- --accent-dim: rgba(16, 185, 129, 0.15);
30
- --accent-glow: rgba(16, 185, 129, 0.3);
31
- --cyan: #06b6d4;
32
- --nav-bg: rgba(10, 15, 26, 0.85);
33
- --font-display: 'Space Grotesk', system-ui, sans-serif;
34
- --font-body: 'Inter', system-ui, sans-serif;
35
- --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
36
- --radius-sm: 6px;
37
- --radius-md: 10px;
38
- --radius-lg: 16px;
39
- --shadow-card: 0 4px 24px rgba(0,0,0,0.3);
40
- --transition: 250ms ease;
41
- --max-width: 1280px;
42
- --nav-h: 56px;
43
-
44
- /* Layer colors */
45
- --l0-color: #10b981;
46
- --l0-bg: rgba(16, 185, 129, 0.12);
47
- --l1-color: #ef4444;
48
- --l1-bg: rgba(239, 68, 68, 0.12);
49
- --l2-color: #3b82f6;
50
- --l2-bg: rgba(59, 130, 246, 0.12);
51
- --l3-color: #10b981;
52
- --l3-bg: rgba(16, 185, 129, 0.1);
53
- --l4-color: #f59e0b;
54
- --l4-bg: rgba(245, 158, 11, 0.12);
55
-
56
- /* Model colors */
57
- --haiku-color: #10b981;
58
- --haiku-bg: rgba(16, 185, 129, 0.1);
59
- --sonnet-color: #3b82f6;
60
- --sonnet-bg: rgba(59, 130, 246, 0.1);
61
- --opus-color: #8b5cf6;
62
- --opus-bg: rgba(139, 92, 246, 0.1);
63
- }
64
-
65
- /* ─── LIGHT MODE ─── */
66
- [data-theme="light"] {
67
- --bg-base: #f8fafb;
68
- --bg-card: #ffffff;
69
- --bg-elevated: #f1f5f9;
70
- --bg-section-alt: #eef2f6;
71
- --bg-code: rgba(0,0,0,0.04);
72
- --text-primary: #0f172a;
73
- --text-secondary: #475569;
74
- --text-muted: #94a3b8;
75
- --border: #e2e8f0;
76
- --accent: #059669;
77
- --accent-dim: rgba(5, 150, 105, 0.1);
78
- --accent-glow: rgba(5, 150, 105, 0.2);
79
- --nav-bg: rgba(248, 250, 251, 0.88);
80
- --shadow-card: 0 4px 24px rgba(0,0,0,0.08);
81
- --l0-bg: rgba(5, 150, 105, 0.08);
82
- --l1-bg: rgba(220, 38, 38, 0.08);
83
- --l2-bg: rgba(37, 99, 235, 0.08);
84
- --l3-bg: rgba(5, 150, 105, 0.06);
85
- --l4-bg: rgba(217, 119, 6, 0.08);
86
- --haiku-bg: rgba(5, 150, 105, 0.07);
87
- --sonnet-bg: rgba(37, 99, 235, 0.07);
88
- --opus-bg: rgba(109, 40, 217, 0.07);
89
- }
90
- [data-theme="light"] .search-input { background: var(--bg-card); }
91
- [data-theme="light"] .skill-card:hover { box-shadow: 0 8px 32px rgba(0,0,0,0.1); }
92
- [data-theme="light"] .pack-card:hover { box-shadow: 0 8px 32px rgba(0,0,0,0.1); }
93
-
94
- html { scroll-behavior: smooth; scroll-padding-top: 80px; }
95
-
96
- body {
97
- font-family: var(--font-body);
98
- background: var(--bg-base);
99
- color: var(--text-primary);
100
- line-height: 1.6;
101
- -webkit-font-smoothing: antialiased;
102
- overflow-x: hidden;
103
- }
104
-
105
- a { color: var(--accent); text-decoration: none; transition: opacity var(--transition); }
106
- a:hover { opacity: 0.8; }
107
-
108
- /* ─── NAV ─── */
109
- .nav {
110
- position: fixed; top: 0; left: 0; right: 0; z-index: 100;
111
- background: var(--nav-bg);
112
- backdrop-filter: blur(12px);
113
- border-bottom: 1px solid var(--border);
114
- height: var(--nav-h);
115
- }
116
- .nav-inner {
117
- max-width: var(--max-width); margin: 0 auto;
118
- display: flex; align-items: center; justify-content: space-between;
119
- padding: 0 24px; height: 100%;
120
- }
121
- .nav-logo {
122
- font-family: var(--font-display); font-weight: 700; font-size: 20px;
123
- color: var(--text-primary); display: flex; align-items: center; gap: 8px;
124
- text-decoration: none;
125
- }
126
- .logo-rune { color: var(--accent); }
127
- .nav-links { display: flex; align-items: center; gap: 24px; }
128
- .nav-links a {
129
- color: var(--text-secondary); font-size: 14px; font-weight: 500;
130
- transition: color var(--transition);
131
- }
132
- .nav-links a:hover { color: var(--text-primary); opacity: 1; }
133
- .nav-links a.active { color: var(--accent); }
134
- .nav-controls { display: flex; align-items: center; gap: 8px; }
135
- .theme-toggle {
136
- background: none; border: 1px solid var(--border); color: var(--text-secondary);
137
- width: 34px; height: 34px; border-radius: var(--radius-sm);
138
- cursor: pointer; display: flex; align-items: center; justify-content: center;
139
- font-size: 16px; transition: all var(--transition);
140
- }
141
- .theme-toggle:hover { border-color: var(--accent); color: var(--text-primary); }
142
- .btn {
143
- display: inline-flex; align-items: center; justify-content: center;
144
- padding: 8px 18px; border-radius: var(--radius-sm);
145
- font-family: var(--font-body); font-weight: 600; font-size: 13px;
146
- cursor: pointer; transition: all var(--transition); border: 1px solid var(--border);
147
- background: transparent; color: var(--text-secondary);
148
- }
149
- .btn:hover { border-color: var(--accent); color: var(--text-primary); opacity: 1; }
150
-
151
- /* ─── PAGE HEADER ─── */
152
- .page-header {
153
- padding: calc(var(--nav-h) + 56px) 24px 48px;
154
- max-width: var(--max-width); margin: 0 auto;
155
- }
156
- .page-header-badge {
157
- display: inline-block; font-family: var(--font-mono); font-size: 12px;
158
- color: var(--accent); background: var(--accent-dim);
159
- padding: 4px 14px; border-radius: 999px; margin-bottom: 20px;
160
- border: 1px solid rgba(16, 185, 129, 0.2);
161
- letter-spacing: 0.02em;
162
- }
163
- .page-header h1 {
164
- font-family: var(--font-display); font-size: clamp(32px, 5vw, 52px);
165
- font-weight: 700; line-height: 1.15; margin-bottom: 12px;
166
- }
167
- .page-header h1 span { color: var(--accent); }
168
- .page-header p {
169
- font-size: 17px; color: var(--text-secondary); max-width: 560px;
170
- }
171
-
172
- /* ─── STATS BAR ─── */
173
- .stats-bar {
174
- max-width: var(--max-width); margin: 0 auto 40px;
175
- padding: 0 24px;
176
- display: flex; gap: 0; flex-wrap: wrap;
177
- background: var(--bg-card); border: 1px solid var(--border);
178
- border-radius: var(--radius-lg); overflow: hidden;
179
- }
180
- .stat-item {
181
- flex: 1; min-width: 120px;
182
- padding: 18px 24px;
183
- display: flex; flex-direction: column; gap: 4px;
184
- border-right: 1px solid var(--border);
185
- }
186
- .stat-item:last-child { border-right: none; }
187
- .stat-value {
188
- font-family: var(--font-mono); font-size: 26px; font-weight: 700;
189
- color: var(--accent); line-height: 1;
190
- }
191
- .stat-label { font-size: 12px; color: var(--text-muted); font-weight: 500; text-transform: uppercase; letter-spacing: 0.05em; }
192
-
193
- /* ─── CONTROLS ─── */
194
- .catalog-controls {
195
- max-width: var(--max-width); margin: 0 auto 28px;
196
- padding: 0 24px;
197
- display: flex; flex-direction: column; gap: 16px;
198
- }
199
- .filter-row {
200
- display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
201
- }
202
- .filter-label {
203
- font-size: 12px; font-weight: 600; color: var(--text-muted);
204
- text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap;
205
- }
206
- .filter-tabs {
207
- display: flex; gap: 6px; flex-wrap: wrap;
208
- }
209
- .filter-tab {
210
- padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 500;
211
- cursor: pointer; border: 1px solid var(--border); background: transparent;
212
- color: var(--text-secondary); transition: all var(--transition); font-family: var(--font-body);
213
- }
214
- .filter-tab:hover { border-color: var(--accent); color: var(--text-primary); }
215
- .filter-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }
216
- .filter-tab[data-layer="L0"].active { background: var(--l0-color); border-color: var(--l0-color); }
217
- .filter-tab[data-layer="L1"].active { background: var(--l1-color); border-color: var(--l1-color); }
218
- .filter-tab[data-layer="L2"].active { background: var(--l2-color); border-color: var(--l2-color); }
219
- .filter-tab[data-layer="L3"].active { background: var(--l3-color); border-color: var(--l3-color); }
220
- .filter-tab[data-layer="L4"].active { background: var(--l4-color); border-color: var(--l4-color); color: #000; }
221
-
222
- .search-row {
223
- display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
224
- }
225
- .search-wrap { position: relative; flex: 1; min-width: 220px; max-width: 400px; }
226
- .search-icon {
227
- position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
228
- color: var(--text-muted); font-size: 14px; pointer-events: none;
229
- }
230
- .search-input {
231
- width: 100%; padding: 8px 12px 8px 36px;
232
- background: var(--bg-elevated); border: 1px solid var(--border);
233
- border-radius: var(--radius-md); color: var(--text-primary);
234
- font-family: var(--font-body); font-size: 14px;
235
- transition: border-color var(--transition); outline: none;
236
- }
237
- .search-input::placeholder { color: var(--text-muted); }
238
- .search-input:focus { border-color: var(--accent); }
239
-
240
- .sort-wrap { display: flex; align-items: center; gap: 8px; }
241
- .sort-label { font-size: 12px; color: var(--text-muted); font-weight: 500; white-space: nowrap; }
242
- .sort-select {
243
- padding: 7px 28px 7px 10px; background: var(--bg-elevated);
244
- border: 1px solid var(--border); border-radius: var(--radius-sm);
245
- color: var(--text-secondary); font-family: var(--font-body); font-size: 13px;
246
- cursor: pointer; outline: none; appearance: none; -webkit-appearance: none;
247
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='%235a6a7a'%3E%3Cpath d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
248
- background-repeat: no-repeat; background-position: right 10px center;
249
- transition: border-color var(--transition);
250
- }
251
- .sort-select:focus { border-color: var(--accent); }
252
-
253
- .results-count {
254
- font-size: 13px; color: var(--text-muted); white-space: nowrap;
255
- font-family: var(--font-mono);
256
- }
257
-
258
- /* ─── SKILLS GRID ─── */
259
- .catalog-section {
260
- max-width: var(--max-width); margin: 0 auto;
261
- padding: 0 24px;
262
- }
263
- .section-divider {
264
- display: flex; align-items: center; gap: 14px;
265
- margin-bottom: 20px; margin-top: 40px;
266
- }
267
- .section-divider:first-of-type { margin-top: 0; }
268
- .section-divider-label {
269
- font-family: var(--font-display); font-size: 14px; font-weight: 700;
270
- color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em;
271
- white-space: nowrap;
272
- }
273
- .section-divider-line {
274
- flex: 1; height: 1px; background: var(--border);
275
- }
276
- .section-divider-count {
277
- font-family: var(--font-mono); font-size: 12px; color: var(--text-muted);
278
- background: var(--bg-elevated); border: 1px solid var(--border);
279
- padding: 2px 8px; border-radius: 4px; white-space: nowrap;
280
- }
281
-
282
- .skills-grid {
283
- display: grid;
284
- grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
285
- gap: 12px;
286
- margin-bottom: 8px;
287
- }
288
-
289
- .skill-card {
290
- background: var(--bg-card); border: 1px solid var(--border);
291
- border-radius: var(--radius-md); padding: 16px 18px;
292
- display: flex; flex-direction: column; gap: 10px;
293
- transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
294
- cursor: default;
295
- }
296
- .skill-card:hover {
297
- border-color: var(--accent); transform: translateY(-1px);
298
- box-shadow: 0 6px 24px rgba(0,0,0,0.2);
299
- }
300
- .skill-card-top {
301
- display: flex; align-items: flex-start; justify-content: space-between; gap: 8px;
302
- }
303
- .skill-name {
304
- font-family: var(--font-mono); font-size: 14px; font-weight: 700;
305
- color: var(--text-primary); letter-spacing: -0.01em;
306
- }
307
- .skill-badges { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; flex-shrink: 0; }
308
- .badge {
309
- display: inline-flex; align-items: center;
310
- padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;
311
- font-family: var(--font-mono); letter-spacing: 0.02em; white-space: nowrap;
312
- }
313
- .badge-l0 { background: var(--l0-bg); color: var(--l0-color); }
314
- .badge-l1 { background: var(--l1-bg); color: var(--l1-color); }
315
- .badge-l2 { background: var(--l2-bg); color: var(--l2-color); }
316
- .badge-l3 { background: var(--l3-bg); color: var(--l3-color); }
317
- .badge-l4 { background: var(--l4-bg); color: var(--l4-color); }
318
- .badge-haiku { background: var(--haiku-bg); color: var(--haiku-color); }
319
- .badge-sonnet { background: var(--sonnet-bg); color: var(--sonnet-color); }
320
- .badge-opus { background: var(--opus-bg); color: var(--opus-color); }
321
-
322
- .skill-desc {
323
- font-size: 13px; color: var(--text-secondary); line-height: 1.55;
324
- display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
325
- overflow: hidden;
326
- }
327
- .skill-footer {
328
- display: flex; align-items: center; justify-content: space-between;
329
- padding-top: 8px; border-top: 1px solid var(--border);
330
- }
331
- .skill-tools {
332
- font-family: var(--font-mono); font-size: 11px; color: var(--text-muted);
333
- display: flex; align-items: center; gap: 5px;
334
- }
335
- .skill-tools-dot {
336
- width: 3px; height: 3px; border-radius: 50%;
337
- background: var(--text-muted); display: inline-block;
338
- }
339
-
340
- /* ─── EMPTY STATE ─── */
341
- .empty-state {
342
- text-align: center; padding: 60px 24px; color: var(--text-muted);
343
- display: none;
344
- }
345
- .empty-state.visible { display: block; }
346
- .empty-state-icon { font-size: 40px; margin-bottom: 14px; opacity: 0.5; }
347
- .empty-state p { font-size: 15px; }
348
-
349
- /* ─── L4 PACKS SECTION ─── */
350
- .packs-section {
351
- max-width: var(--max-width); margin: 0 auto;
352
- padding: 60px 24px 40px;
353
- }
354
- .packs-header { margin-bottom: 28px; }
355
- .packs-header h2 {
356
- font-family: var(--font-display); font-size: 28px; font-weight: 700;
357
- margin-bottom: 8px;
358
- }
359
- .packs-header p { font-size: 15px; color: var(--text-secondary); }
360
- .packs-grid {
361
- display: grid;
362
- grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
363
- gap: 14px;
364
- }
365
- .pack-card {
366
- background: var(--bg-card); border: 1px solid var(--border);
367
- border-radius: var(--radius-lg); padding: 22px;
368
- display: flex; flex-direction: column; gap: 10px;
369
- transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
370
- }
371
- .pack-card:hover {
372
- border-color: var(--l4-color); transform: translateY(-2px);
373
- box-shadow: 0 8px 28px rgba(245, 158, 11, 0.08);
374
- }
375
- .pack-name {
376
- font-family: var(--font-mono); font-size: 13px; font-weight: 700;
377
- color: var(--l4-color);
378
- }
379
- .pack-meta {
380
- display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
381
- }
382
- .pack-badge {
383
- font-family: var(--font-mono); font-size: 10px; font-weight: 700;
384
- padding: 2px 8px; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.05em;
385
- }
386
- .pack-badge-free { background: var(--accent-dim); color: var(--accent); }
387
- .pack-badge-paid { background: var(--l4-bg); color: var(--l4-color); }
388
- .pack-skills-count {
389
- font-size: 12px; color: var(--text-muted); font-family: var(--font-mono);
390
- }
391
- .pack-desc {
392
- font-size: 13px; color: var(--text-secondary); line-height: 1.55;
393
- display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
394
- overflow: hidden;
395
- }
396
- .pack-target {
397
- font-size: 11px; color: var(--text-muted); margin-top: 4px;
398
- }
399
-
400
- /* ─── FOOTER ─── */
401
- .footer {
402
- border-top: 1px solid var(--border); padding: 32px 24px;
403
- max-width: var(--max-width); margin: 60px auto 0;
404
- }
405
- .footer-inner {
406
- display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px;
407
- }
408
- .footer-inner p { font-size: 14px; color: var(--text-secondary); }
409
- .footer-links { display: flex; gap: 24px; }
410
- .footer-links a { font-size: 13px; color: var(--text-muted); }
411
- .footer-links a:hover { color: var(--text-primary); }
412
-
413
- /* ─── FADE-IN ANIMATION ─── */
414
- @media (prefers-reduced-motion: no-preference) {
415
- .skill-card {
416
- opacity: 0; transform: translateY(12px);
417
- animation: fadeUp 0.35s ease forwards;
418
- }
419
- .pack-card {
420
- opacity: 0; transform: translateY(12px);
421
- animation: fadeUp 0.35s ease forwards;
422
- }
423
- }
424
- @keyframes fadeUp {
425
- to { opacity: 1; transform: translateY(0); }
426
- }
427
-
428
- /* ─── RESPONSIVE ─── */
429
- @media (max-width: 768px) {
430
- .stats-bar { flex-direction: column; gap: 0; }
431
- .stat-item { border-right: none; border-bottom: 1px solid var(--border); }
432
- .stat-item:last-child { border-bottom: none; }
433
- .skills-grid { grid-template-columns: 1fr; }
434
- .packs-grid { grid-template-columns: 1fr; }
435
- .nav-links a:not(.btn):not([data-keep]) { display: none; }
436
- .page-header { padding-top: calc(var(--nav-h) + 36px); }
437
- }
438
- @media (max-width: 480px) {
439
- .filter-tabs { gap: 4px; }
440
- .filter-tab { padding: 5px 10px; font-size: 12px; }
441
- .search-wrap { max-width: 100%; }
442
- }
443
-
444
- /* Highlight for search matches */
445
- mark {
446
- background: rgba(16, 185, 129, 0.25); color: inherit;
447
- border-radius: 2px; padding: 0 1px;
448
- }
449
- </style>
450
- </head>
451
- <body>
452
-
453
- <!-- NAV -->
454
- <nav class="nav">
455
- <div class="nav-inner">
456
- <a href="../index.html" class="nav-logo">
457
- <span class="logo-rune">&#9670;</span> Rune
458
- </a>
459
- <div class="nav-links">
460
- <a href="../index.html">Home</a>
461
- <a href="../guides/">Guides</a>
462
- <a href="index.html" class="active" data-keep>Skills</a>
463
- <div class="nav-controls">
464
- <button class="theme-toggle" id="theme-toggle" aria-label="Toggle light/dark mode" title="Toggle theme">&#9790;</button>
465
- </div>
466
- <a href="https://github.com/rune-kit/rune" class="btn" target="_blank" rel="noopener">GitHub</a>
467
- </div>
468
- </div>
469
- </nav>
470
-
471
- <!-- PAGE HEADER -->
472
- <div class="page-header">
473
- <p class="page-header-badge">v2.2.0 — Skills Reference</p>
474
- <h1>Rune <span>Skills</span> Catalog</h1>
475
- <p>58 skills &middot; 5 layers &middot; 200+ mesh connections. Every skill has a clear responsibility and knows exactly which skills to call.</p>
476
- </div>
477
-
478
- <!-- STATS BAR -->
479
- <div class="stats-bar">
480
- <div class="stat-item">
481
- <span class="stat-value">58</span>
482
- <span class="stat-label">Total Skills</span>
483
- </div>
484
- <div class="stat-item">
485
- <span class="stat-value" style="color: var(--l1-color)">1+5</span>
486
- <span class="stat-label">L0 Router + L1 Orchestrators</span>
487
- </div>
488
- <div class="stat-item">
489
- <span class="stat-value" style="color: var(--l2-color)">27</span>
490
- <span class="stat-label">L2 Workflow Hubs</span>
491
- </div>
492
- <div class="stat-item">
493
- <span class="stat-value">25</span>
494
- <span class="stat-label">L3 Utilities</span>
495
- </div>
496
- <div class="stat-item">
497
- <span class="stat-value" style="color: var(--l4-color)">14</span>
498
- <span class="stat-label">L4 Extension Packs</span>
499
- </div>
500
- <div class="stat-item">
501
- <span class="stat-value">200+</span>
502
- <span class="stat-label">Mesh Connections</span>
503
- </div>
504
- </div>
505
-
506
- <!-- CONTROLS -->
507
- <div class="catalog-controls">
508
- <div class="filter-row">
509
- <span class="filter-label">Layer</span>
510
- <div class="filter-tabs" id="layer-tabs">
511
- <button class="filter-tab active" data-layer="all">All</button>
512
- <button class="filter-tab" data-layer="L0">L0 Router</button>
513
- <button class="filter-tab" data-layer="L1">L1 Orchestrators</button>
514
- <button class="filter-tab" data-layer="L2">L2 Workflows</button>
515
- <button class="filter-tab" data-layer="L3">L3 Utilities</button>
516
- </div>
517
- </div>
518
- <div class="search-row">
519
- <div class="search-wrap">
520
- <span class="search-icon">&#128269;</span>
521
- <input type="search" class="search-input" id="skill-search" placeholder="Search skills by name or description..." aria-label="Search skills">
522
- </div>
523
- <div class="sort-wrap">
524
- <span class="sort-label">Sort</span>
525
- <select class="sort-select" id="sort-select" aria-label="Sort skills">
526
- <option value="layer">By layer</option>
527
- <option value="name">By name</option>
528
- <option value="model">By model</option>
529
- <option value="tools">By tools count</option>
530
- </select>
531
- </div>
532
- <span class="results-count" id="results-count">57 skills</span>
533
- </div>
534
- </div>
535
-
536
- <!-- SKILLS CATALOG -->
537
- <div class="catalog-section" id="catalog-section">
538
- <!-- populated by JS -->
539
- </div>
540
-
541
- <div class="empty-state" id="empty-state">
542
- <div class="empty-state-icon">&#128269;</div>
543
- <p>No skills match your search. Try a different query.</p>
544
- </div>
545
-
546
- <!-- L4 EXTENSION PACKS -->
547
- <div class="packs-section" id="packs-section">
548
- <div class="packs-header">
549
- <h2>L4 Extension Packs</h2>
550
- <p>Domain-specific skill bundles. Install only what you need. Free packs are MIT-licensed — Pro packs require Rune Pro.</p>
551
- </div>
552
- <div class="packs-grid" id="packs-grid">
553
- <!-- populated by JS -->
554
- </div>
555
- </div>
556
-
557
- <!-- FOOTER -->
558
- <div class="footer">
559
- <div class="footer-inner">
560
- <p>Rune &mdash; Less skills. Deeper connections. &copy; 2025 rune-kit</p>
561
- <div class="footer-links">
562
- <a href="https://github.com/rune-kit/rune" target="_blank" rel="noopener">GitHub</a>
563
- <a href="../index.html">Home</a>
564
- <a href="../guides/">Guides</a>
565
- </div>
566
- </div>
567
- </div>
568
-
569
- <script>
570
- // ─── THEME TOGGLE ───
571
- const themeToggle = document.getElementById('theme-toggle');
572
- const saved = localStorage.getItem('rune-theme') || 'dark';
573
- document.documentElement.setAttribute('data-theme', saved);
574
- themeToggle.textContent = saved === 'dark' ? '☀' : '☾';
575
- themeToggle.addEventListener('click', () => {
576
- const cur = document.documentElement.getAttribute('data-theme');
577
- const next = cur === 'dark' ? 'light' : 'dark';
578
- document.documentElement.setAttribute('data-theme', next);
579
- localStorage.setItem('rune-theme', next);
580
- themeToggle.textContent = next === 'dark' ? '☀' : '☾';
581
- });
582
-
583
- // ─── SKILL DATA ───
584
- const SKILLS = [
585
- // L0
586
- { name: 'skill-router', layer: 'L0', model: 'haiku', tools: 3, desc: 'Meta-enforcement layer that routes every agent action through the correct skill. Prevents hallucinated commands by mapping intent to the canonical skill.' },
587
- // L1
588
- { name: 'cook', layer: 'L1', model: 'sonnet', tools: 6, desc: 'Feature implementation orchestrator. Use for ANY code change — the primary entry point for development work with phase-aware execution.' },
589
- { name: 'team', layer: 'L1', model: 'opus', tools: 6, desc: 'Multi-agent meta-orchestrator for tasks spanning 5+ files or 3+ modules. Spawns and coordinates parallel specialist agents.' },
590
- { name: 'launch', layer: 'L1', model: 'sonnet', tools: 6, desc: 'Deploy + marketing orchestrator. Handles production deployment plus announcement strategy in one coordinated workflow.' },
591
- { name: 'rescue', layer: 'L1', model: 'sonnet', tools: 6, desc: 'Legacy refactoring orchestrator. Assesses technical debt, creates safety nets, and incrementally modernizes codebases.' },
592
- { name: 'scaffold', layer: 'L1', model: 'sonnet', tools: 8, desc: 'Autonomous project bootstrapper. Generates a complete project from a description — structure, code, tests, and CI/CD config.' },
593
- // L2
594
- { name: 'adversary', layer: 'L2', model: 'opus', tools: 3, desc: 'Pre-implementation red-team analysis. Stress-tests plans across edge cases, security, scalability, and integration risk before code is written.' },
595
- { name: 'audit', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Comprehensive project audit across security, dependencies, code quality, architecture, and performance.' },
596
- { name: 'autopsy', layer: 'L2', model: 'opus', tools: 4, desc: 'Full codebase health assessment. Analyzes complexity, dependencies, dead code, tech debt, and git history hotspots.' },
597
- { name: 'ba', layer: 'L2', model: 'opus', tools: 3, desc: 'Business Analyst agent. Elicits and validates requirements before planning begins — uncovers hidden constraints and ambiguities.' },
598
- { name: 'brainstorm', layer: 'L2', model: 'opus', tools: 3, desc: 'Creative ideation and solution exploration. Generates multiple approaches with trade-offs; includes Rescue Mode for divergent alternatives.' },
599
- { name: 'db', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Database workflow specialist. Generates migration files with rollback scripts, detects breaking schema changes, and audits query performance.' },
600
- { name: 'debug', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Root cause analysis for bugs and unexpected behavior. Traces symptoms to origin and classifies whether escalation is needed.' },
601
- { name: 'deploy', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Deploy application to target platform. Handles Vercel, Railway, Fly.io, AWS, and Docker with environment validation.' },
602
- { name: 'design', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Design system reasoning. Maps product domain to style, palette, typography, and platform-specific patterns.' },
603
- { name: 'docs', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Auto-generate and maintain project documentation — README, API docs, architecture docs, and changelogs.' },
604
- { name: 'fix', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Apply code changes and bug fixes. Writes implementation code and verifies changes with targeted tests.' },
605
- { name: 'incident', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Structured incident response. Triage, mitigation, root cause analysis, and post-mortem generation.' },
606
- { name: 'logic-guardian', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Protects complex business logic from accidental deletion or overwrite during refactoring.' },
607
- { name: 'marketing', layer: 'L2', model: 'sonnet', tools: 7, desc: 'Create marketing assets and execute launch strategy — copy, social posts, landing pages, and SEO audits.' },
608
- { name: 'mcp-builder', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Build Model Context Protocol servers from specifications in TypeScript or Python with tests and docs.' },
609
- { name: 'onboard', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Auto-generate project context for AI sessions — scans codebase, creates CLAUDE.md and architecture summaries.' },
610
- { name: 'perf', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Performance regression gate. Detects N+1 queries, sync-in-async patterns, missing indexes, and memory leaks.' },
611
- { name: 'plan', layer: 'L2', model: 'opus', tools: 5, desc: 'Create structured implementation plans from requirements. Supports Feature Spec Mode and Roadmap Mode with ICE scoring.' },
612
- { name: 'preflight', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Pre-commit quality gate. Catches "almost right" code — type errors, missing tests, lint failures, and broken imports.' },
613
- { name: 'review', layer: 'L2', model: 'sonnet', tools: 3, desc: 'Code quality review covering patterns, security vulnerabilities, performance, and correctness.' },
614
- { name: 'review-intake', layer: 'L2', model: 'sonnet', tools: 3, desc: 'Process external code review feedback or PR comments before implementing changes.' },
615
- { name: 'safeguard', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Build safety nets before refactoring — characterization tests, boundary markers, and config freeze snapshots.' },
616
- { name: 'scout', layer: 'L2', model: 'haiku', tools: 3, desc: 'Fast codebase scanner. Provides architecture context to other skills without loading full file contents.' },
617
- { name: 'sentinel', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Automated security gatekeeper. Blocks unsafe code — secret scanning, OWASP top 10, dependency vulnerabilities.' },
618
- { name: 'sentinel-env', layer: 'L3', model: 'haiku', tools: 4, desc: 'Environment-aware pre-flight check. Validates OS, runtime versions, installed tools, and port availability.' },
619
- { name: 'skill-forge', layer: 'L2', model: 'opus', tools: 6, desc: 'Create new Rune skills, edit existing ones, and verify skill quality before deployment.' },
620
- { name: 'surgeon', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Incremental refactorer. Refactors one module per session using Strangler Fig, Branch by Abstraction, and Parallel Run patterns.' },
621
- { name: 'test', layer: 'L2', model: 'sonnet', tools: 6, desc: 'TDD test writer. Writes failing tests first (red), verifies they pass after implementation (green), enforces 80% coverage.' },
622
- // L3
623
- { name: 'asset-creator', layer: 'L3', model: 'sonnet', tools: 5, desc: 'Creates code-based visual assets — SVG icons, OG image HTML templates, social banners, and icon sets.' },
624
- { name: 'browser-pilot', layer: 'L3', model: 'sonnet', tools: 4, desc: 'Playwright browser automation. Navigates URLs, takes screenshots, checks accessibility, and interacts with web UIs.' },
625
- { name: 'completion-gate', layer: 'L3', model: 'haiku', tools: 4, desc: 'Validates agent claims against evidence trail. Catches "done" without proof and "tests pass" without output.' },
626
- { name: 'constraint-check', layer: 'L3', model: 'haiku', tools: 3, desc: 'Meta-validator for HARD-GATEs. Verifies that skills\' mandatory constraints were followed during a workflow step.' },
627
- { name: 'context-engine', layer: 'L3', model: 'haiku', tools: 3, desc: 'Context window management. Auto-triggered when context is filling up — summarizes and compacts without losing state.' },
628
- { name: 'dependency-doctor', layer: 'L3', model: 'haiku', tools: 4, desc: 'Dependency health management. Detects outdated packages and vulnerabilities across npm, pip, cargo, and go modules.' },
629
- { name: 'doc-processor', layer: 'L3', model: 'sonnet', tools: 6, desc: 'Generate and parse office documents — PDF, DOCX, XLSX, PPTX, and CSV files.' },
630
- { name: 'docs-seeker', layer: 'L3', model: 'haiku', tools: 5, desc: 'Find documentation for APIs, libraries, and error messages via web search and curated sources.' },
631
- { name: 'git', layer: 'L3', model: 'haiku', tools: 4, desc: 'Specialized git operations — semantic commits, PR descriptions, branch management, and conflict resolution.' },
632
- { name: 'hallucination-guard', layer: 'L3', model: 'haiku', tools: 4, desc: 'Verify AI-generated imports, API calls, and packages actually exist before code is written.' },
633
- { name: 'integrity-check', layer: 'L3', model: 'haiku', tools: 3, desc: 'Verify integrity of persisted state, skill outputs, and context bus data across workflow steps.' },
634
- { name: 'journal', layer: 'L3', model: 'haiku', tools: 5, desc: 'Persistent state tracking and Architecture Decision Records. Maintains project history across sessions.' },
635
- { name: 'neural-memory', layer: 'L3', model: 'haiku', tools: 1, desc: 'Cross-session cognitive persistence via Neural Memory MCP. Stores and recalls decisions, patterns, and context.' },
636
- { name: 'problem-solver', layer: 'L3', model: 'sonnet', tools: 3, desc: 'Structured reasoning frameworks for complex multi-variable problems. Applies first-principles and systems thinking.' },
637
- { name: 'research', layer: 'L3', model: 'haiku', tools: 5, desc: 'Web search and external knowledge lookup. Gathers data on technologies, libraries, and best practices.' },
638
- { name: 'sast', layer: 'L3', model: 'haiku', tools: 4, desc: 'Static analysis tool runner. Wraps ESLint, Semgrep, Bandit, Clippy, and language-specific analyzers.' },
639
- { name: 'scope-guard', layer: 'L3', model: 'haiku', tools: 3, desc: 'Detects scope creep. Auto-triggered by L1 orchestrators when files exceed the original plan.' },
640
- { name: 'sequential-thinking', layer: 'L3', model: 'sonnet', tools: 3, desc: 'Step-by-step complex reasoning for multi-variable problems with explicit dependency tracking.' },
641
- { name: 'session-bridge', layer: 'L3', model: 'haiku', tools: 5, desc: 'Universal context persistence across sessions. Auto-saves decisions, conventions, and progress to project files.' },
642
- { name: 'trend-scout', layer: 'L3', model: 'haiku', tools: 5, desc: 'Scan market trends, competitor activity, and emerging technology patterns.' },
643
- { name: 'verification', layer: 'L3', model: 'haiku', tools: 4, desc: 'Universal verification runner. Runs lint, type-check, tests, and build in one coordinated pass.' },
644
- { name: 'video-creator', layer: 'L3', model: 'sonnet', tools: 5, desc: 'Video content planning. Writes narration scripts, storyboards, shot lists, and asset checklists.' },
645
- { name: 'watchdog', layer: 'L3', model: 'sonnet', tools: 4, desc: 'Post-deploy monitoring. Checks HTTP status, response times, error rates, and service health after deployment.' },
646
- { name: 'worktree', layer: 'L3', model: 'haiku', tools: 4, desc: 'Git worktree lifecycle management. Creates isolated workspaces for parallel feature development.' },
647
- ];
648
-
649
- const PACKS = [
650
- { name: '@rune/ui', desc: 'Frontend UI patterns — design systems, color palettes, typography, component architecture, landing page sections, accessibility audits, and animation patterns.', target: 'Frontend developers', price: 'free', skills: 8 },
651
- { name: '@rune/backend', desc: 'Backend patterns — API design, authentication, database patterns, middleware architecture, caching strategies, and background job processing.', target: 'Backend developers', price: 'free', skills: 7 },
652
- { name: '@rune/devops', desc: 'DevOps patterns — Docker, CI/CD pipelines, monitoring setup, server configuration, and SSL/domain management.', target: 'DevOps engineers', price: '$12', skills: 7 },
653
- { name: '@rune/mobile', desc: 'Mobile development patterns — React Native, Flutter, app store preparation, and native bridge integration.', target: 'Mobile developers', price: '$15', skills: 5 },
654
- { name: '@rune/security', desc: 'Deep security analysis — OWASP audit, penetration testing patterns, secret management, compliance checking, and API hardening.', target: 'Security engineers', price: '$15', skills: 6 },
655
- { name: '@rune/trading', desc: 'Fintech and trading patterns — real-time data, financial dashboards, technical indicators, WebSocket architecture, and strategy development.', target: 'Fintech developers', price: '$15', skills: 7 },
656
- { name: '@rune/saas', desc: 'SaaS patterns — multi-tenancy, billing integration, subscription management, feature flags, team permissions, and user onboarding flows.', target: 'SaaS builders', price: '$12', skills: 6 },
657
- { name: '@rune/ecommerce', desc: 'E-commerce patterns — Shopify development, payment integration, shopping cart state machines, and inventory management.', target: 'E-commerce developers', price: '$12', skills: 5 },
658
- { name: '@rune/ai-ml', desc: 'AI/ML integration patterns — LLM integration, RAG pipelines, embeddings, fine-tuning workflows, and prompt engineering.', target: 'AI engineers', price: '$15', skills: 6 },
659
- { name: '@rune/gamedev', desc: 'Game development patterns — Three.js, WebGL, game loops, physics engines, and asset pipelines.', target: 'Game developers', price: '$12', skills: 5 },
660
- { name: '@rune/content', desc: 'Content platform patterns — blog systems, CMS integration, MDX authoring, internationalization, and SEO.', target: 'Content creators', price: '$9', skills: 5 },
661
- { name: '@rune/analytics', desc: 'Analytics patterns — tracking setup, A/B testing, funnel analysis, statistical significance, and dashboard design.', target: 'Growth engineers', price: '$12', skills: 6 },
662
- { name: '@rune/chrome-ext', desc: 'Chrome extension development — Manifest V3 scaffolding, service worker lifecycle, message passing, CWS compliance, and Chrome AI integration.', target: 'Chrome extension developers', price: 'free', skills: 6 },
663
- { name: '@rune/zalo', desc: 'Zalo platform integration — Official Account API (OAuth2, messaging, webhooks, MCP server) and personal account automation (zca-js). Dual-track with risk gating.', target: 'Vietnamese developers', price: 'free', skills: 7 },
664
- ];
665
-
666
- // ─── LAYER ORDER ───
667
- const LAYER_ORDER = { L0: 0, L1: 1, L2: 2, L3: 3, L4: 4 };
668
- const LAYER_LABELS = {
669
- L0: 'L0 — Router',
670
- L1: 'L1 — Orchestrators',
671
- L2: 'L2 — Workflow Hubs',
672
- L3: 'L3 — Utilities',
673
- };
674
-
675
- // ─── STATE ───
676
- let activeLayer = 'all';
677
- let searchQuery = '';
678
- let sortBy = 'layer';
679
-
680
- // ─── RENDER ───
681
- function escapeHTML(str) {
682
- return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
683
- }
684
-
685
- function highlight(text, query) {
686
- if (!query) return escapeHTML(text);
687
- const escaped = escapeHTML(text);
688
- const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
689
- return escaped.replace(new RegExp(escapedQuery, 'gi'), match => `<mark>${match}</mark>`);
690
- }
691
-
692
- function renderSkillCard(skill, query, delay) {
693
- const layerClass = `badge-${skill.layer.toLowerCase()}`;
694
- const modelClass = `badge-${skill.model}`;
695
- return `
696
- <div class="skill-card" data-layer="${skill.layer}" style="animation-delay:${delay}ms">
697
- <div class="skill-card-top">
698
- <span class="skill-name">${highlight(skill.name, query)}</span>
699
- <div class="skill-badges">
700
- <span class="badge ${layerClass}">${skill.layer}</span>
701
- <span class="badge ${modelClass}">${skill.model}</span>
702
- </div>
703
- </div>
704
- <p class="skill-desc">${highlight(skill.desc, query)}</p>
705
- <div class="skill-footer">
706
- <span class="skill-tools">
707
- <span class="skill-tools-dot"></span>
708
- ${skill.tools} tool${skill.tools !== 1 ? 's' : ''}
709
- </span>
710
- </div>
711
- </div>`;
712
- }
713
-
714
- function getFilteredSorted() {
715
- let filtered = SKILLS.filter(s => {
716
- if (activeLayer !== 'all' && s.layer !== activeLayer) return false;
717
- if (searchQuery) {
718
- const q = searchQuery.toLowerCase();
719
- return s.name.includes(q) || s.desc.toLowerCase().includes(q);
720
- }
721
- return true;
722
- });
723
-
724
- filtered.sort((a, b) => {
725
- if (sortBy === 'name') return a.name.localeCompare(b.name);
726
- if (sortBy === 'model') {
727
- const modelOrder = { haiku: 0, sonnet: 1, opus: 2 };
728
- return modelOrder[a.model] - modelOrder[b.model] || a.name.localeCompare(b.name);
729
- }
730
- if (sortBy === 'tools') return b.tools - a.tools;
731
- // default: layer
732
- return (LAYER_ORDER[a.layer] - LAYER_ORDER[b.layer]) || a.name.localeCompare(b.name);
733
- });
734
-
735
- return filtered;
736
- }
737
-
738
- function render() {
739
- const filtered = getFilteredSorted();
740
- const section = document.getElementById('catalog-section');
741
- const emptyState = document.getElementById('empty-state');
742
- const countEl = document.getElementById('results-count');
743
- const packsSection = document.getElementById('packs-section');
744
-
745
- countEl.textContent = `${filtered.length} skill${filtered.length !== 1 ? 's' : ''}`;
746
-
747
- if (filtered.length === 0) {
748
- section.innerHTML = '';
749
- emptyState.classList.add('visible');
750
- return;
751
- }
752
- emptyState.classList.remove('visible');
753
-
754
- // Group by layer when sort=layer and no specific layer filter
755
- const groupByLayer = sortBy === 'layer' && activeLayer === 'all';
756
-
757
- if (groupByLayer) {
758
- const groups = {};
759
- for (const skill of filtered) {
760
- if (!groups[skill.layer]) groups[skill.layer] = [];
761
- groups[skill.layer].push(skill);
762
- }
763
- let html = '';
764
- let globalDelay = 0;
765
- for (const layer of ['L0', 'L1', 'L2', 'L3']) {
766
- if (!groups[layer]) continue;
767
- html += `
768
- <div class="section-divider">
769
- <span class="section-divider-label">${LAYER_LABELS[layer]}</span>
770
- <div class="section-divider-line"></div>
771
- <span class="section-divider-count">${groups[layer].length} skill${groups[layer].length !== 1 ? 's' : ''}</span>
772
- </div>
773
- <div class="skills-grid">
774
- ${groups[layer].map((s, i) => renderSkillCard(s, searchQuery, (globalDelay + i) * 30)).join('')}
775
- </div>`;
776
- globalDelay += groups[layer].length;
777
- }
778
- section.innerHTML = html;
779
- } else {
780
- section.innerHTML = `
781
- <div class="skills-grid">
782
- ${filtered.map((s, i) => renderSkillCard(s, searchQuery, i * 25)).join('')}
783
- </div>`;
784
- }
785
-
786
- // Show/hide L4 packs section based on filter
787
- packsSection.style.display = (activeLayer === 'all' || activeLayer === 'L4') ? '' : 'none';
788
- }
789
-
790
- function renderPacks() {
791
- const grid = document.getElementById('packs-grid');
792
- grid.innerHTML = PACKS.map((p, i) => {
793
- const isFree = p.price === 'free';
794
- return `
795
- <div class="pack-card" style="animation-delay:${i * 40}ms">
796
- <span class="pack-name">${escapeHTML(p.name)}</span>
797
- <div class="pack-meta">
798
- <span class="pack-badge ${isFree ? 'pack-badge-free' : 'pack-badge-paid'}">${isFree ? 'FREE' : p.price}</span>
799
- <span class="pack-skills-count">${p.skills} skills</span>
800
- </div>
801
- <p class="pack-desc">${escapeHTML(p.desc)}</p>
802
- <span class="pack-target">&#128100; ${escapeHTML(p.target)}</span>
803
- </div>`;
804
- }).join('');
805
- }
806
-
807
- // ─── EVENT LISTENERS ───
808
- document.getElementById('layer-tabs').addEventListener('click', e => {
809
- const tab = e.target.closest('.filter-tab');
810
- if (!tab) return;
811
- document.querySelectorAll('.filter-tab').forEach(t => t.classList.remove('active'));
812
- tab.classList.add('active');
813
- activeLayer = tab.dataset.layer;
814
- render();
815
- });
816
-
817
- document.getElementById('skill-search').addEventListener('input', e => {
818
- searchQuery = e.target.value.trim();
819
- render();
820
- });
821
-
822
- document.getElementById('sort-select').addEventListener('change', e => {
823
- sortBy = e.target.value;
824
- render();
825
- });
826
-
827
- // ─── INIT ───
828
- render();
829
- renderPacks();
830
- </script>
831
- </body>
832
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Rune Skills Catalog — 59 skills · 5 layers · 200+ connections</title>
7
+ <meta name="description" content="Visual reference of all 58 Rune skills organized by layer. Filter by layer, search by name, sort by model.">
8
+ <meta property="og:title" content="Rune Skills Catalog">
9
+ <meta property="og:description" content="59 skills · 5 layers · 200+ connections. The complete Rune skill reference.">
10
+ <meta property="og:type" content="website">
11
+ <link rel="preconnect" href="https://fonts.googleapis.com">
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
14
+ <style>
15
+ /* ─── DESIGN TOKENS ─── */
16
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
17
+
18
+ :root {
19
+ --bg-base: #0a0f1a;
20
+ --bg-card: #111827;
21
+ --bg-elevated: #1a2332;
22
+ --bg-section-alt: #080c14;
23
+ --bg-code: rgba(0,0,0,0.4);
24
+ --text-primary: #f0f4f8;
25
+ --text-secondary: #8896a8;
26
+ --text-muted: #5a6a7a;
27
+ --border: #1e2d3d;
28
+ --accent: #10b981;
29
+ --accent-dim: rgba(16, 185, 129, 0.15);
30
+ --accent-glow: rgba(16, 185, 129, 0.3);
31
+ --cyan: #06b6d4;
32
+ --nav-bg: rgba(10, 15, 26, 0.85);
33
+ --font-display: 'Space Grotesk', system-ui, sans-serif;
34
+ --font-body: 'Inter', system-ui, sans-serif;
35
+ --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
36
+ --radius-sm: 6px;
37
+ --radius-md: 10px;
38
+ --radius-lg: 16px;
39
+ --shadow-card: 0 4px 24px rgba(0,0,0,0.3);
40
+ --transition: 250ms ease;
41
+ --max-width: 1280px;
42
+ --nav-h: 56px;
43
+
44
+ /* Layer colors */
45
+ --l0-color: #10b981;
46
+ --l0-bg: rgba(16, 185, 129, 0.12);
47
+ --l1-color: #ef4444;
48
+ --l1-bg: rgba(239, 68, 68, 0.12);
49
+ --l2-color: #3b82f6;
50
+ --l2-bg: rgba(59, 130, 246, 0.12);
51
+ --l3-color: #10b981;
52
+ --l3-bg: rgba(16, 185, 129, 0.1);
53
+ --l4-color: #f59e0b;
54
+ --l4-bg: rgba(245, 158, 11, 0.12);
55
+
56
+ /* Model colors */
57
+ --haiku-color: #10b981;
58
+ --haiku-bg: rgba(16, 185, 129, 0.1);
59
+ --sonnet-color: #3b82f6;
60
+ --sonnet-bg: rgba(59, 130, 246, 0.1);
61
+ --opus-color: #8b5cf6;
62
+ --opus-bg: rgba(139, 92, 246, 0.1);
63
+ }
64
+
65
+ /* ─── LIGHT MODE ─── */
66
+ [data-theme="light"] {
67
+ --bg-base: #f8fafb;
68
+ --bg-card: #ffffff;
69
+ --bg-elevated: #f1f5f9;
70
+ --bg-section-alt: #eef2f6;
71
+ --bg-code: rgba(0,0,0,0.04);
72
+ --text-primary: #0f172a;
73
+ --text-secondary: #475569;
74
+ --text-muted: #94a3b8;
75
+ --border: #e2e8f0;
76
+ --accent: #059669;
77
+ --accent-dim: rgba(5, 150, 105, 0.1);
78
+ --accent-glow: rgba(5, 150, 105, 0.2);
79
+ --nav-bg: rgba(248, 250, 251, 0.88);
80
+ --shadow-card: 0 4px 24px rgba(0,0,0,0.08);
81
+ --l0-bg: rgba(5, 150, 105, 0.08);
82
+ --l1-bg: rgba(220, 38, 38, 0.08);
83
+ --l2-bg: rgba(37, 99, 235, 0.08);
84
+ --l3-bg: rgba(5, 150, 105, 0.06);
85
+ --l4-bg: rgba(217, 119, 6, 0.08);
86
+ --haiku-bg: rgba(5, 150, 105, 0.07);
87
+ --sonnet-bg: rgba(37, 99, 235, 0.07);
88
+ --opus-bg: rgba(109, 40, 217, 0.07);
89
+ }
90
+ [data-theme="light"] .search-input { background: var(--bg-card); }
91
+ [data-theme="light"] .skill-card:hover { box-shadow: 0 8px 32px rgba(0,0,0,0.1); }
92
+ [data-theme="light"] .pack-card:hover { box-shadow: 0 8px 32px rgba(0,0,0,0.1); }
93
+
94
+ html { scroll-behavior: smooth; scroll-padding-top: 80px; }
95
+
96
+ body {
97
+ font-family: var(--font-body);
98
+ background: var(--bg-base);
99
+ color: var(--text-primary);
100
+ line-height: 1.6;
101
+ -webkit-font-smoothing: antialiased;
102
+ overflow-x: hidden;
103
+ }
104
+
105
+ a { color: var(--accent); text-decoration: none; transition: opacity var(--transition); }
106
+ a:hover { opacity: 0.8; }
107
+
108
+ /* ─── NAV ─── */
109
+ .nav {
110
+ position: fixed; top: 0; left: 0; right: 0; z-index: 100;
111
+ background: var(--nav-bg);
112
+ backdrop-filter: blur(12px);
113
+ border-bottom: 1px solid var(--border);
114
+ height: var(--nav-h);
115
+ }
116
+ .nav-inner {
117
+ max-width: var(--max-width); margin: 0 auto;
118
+ display: flex; align-items: center; justify-content: space-between;
119
+ padding: 0 24px; height: 100%;
120
+ }
121
+ .nav-logo {
122
+ font-family: var(--font-display); font-weight: 700; font-size: 20px;
123
+ color: var(--text-primary); display: flex; align-items: center; gap: 8px;
124
+ text-decoration: none;
125
+ }
126
+ .logo-rune { color: var(--accent); }
127
+ .nav-links { display: flex; align-items: center; gap: 24px; }
128
+ .nav-links a {
129
+ color: var(--text-secondary); font-size: 14px; font-weight: 500;
130
+ transition: color var(--transition);
131
+ }
132
+ .nav-links a:hover { color: var(--text-primary); opacity: 1; }
133
+ .nav-links a.active { color: var(--accent); }
134
+ .nav-controls { display: flex; align-items: center; gap: 8px; }
135
+ .theme-toggle {
136
+ background: none; border: 1px solid var(--border); color: var(--text-secondary);
137
+ width: 34px; height: 34px; border-radius: var(--radius-sm);
138
+ cursor: pointer; display: flex; align-items: center; justify-content: center;
139
+ font-size: 16px; transition: all var(--transition);
140
+ }
141
+ .theme-toggle:hover { border-color: var(--accent); color: var(--text-primary); }
142
+ .btn {
143
+ display: inline-flex; align-items: center; justify-content: center;
144
+ padding: 8px 18px; border-radius: var(--radius-sm);
145
+ font-family: var(--font-body); font-weight: 600; font-size: 13px;
146
+ cursor: pointer; transition: all var(--transition); border: 1px solid var(--border);
147
+ background: transparent; color: var(--text-secondary);
148
+ }
149
+ .btn:hover { border-color: var(--accent); color: var(--text-primary); opacity: 1; }
150
+
151
+ /* ─── PAGE HEADER ─── */
152
+ .page-header {
153
+ padding: calc(var(--nav-h) + 56px) 24px 48px;
154
+ max-width: var(--max-width); margin: 0 auto;
155
+ }
156
+ .page-header-badge {
157
+ display: inline-block; font-family: var(--font-mono); font-size: 12px;
158
+ color: var(--accent); background: var(--accent-dim);
159
+ padding: 4px 14px; border-radius: 999px; margin-bottom: 20px;
160
+ border: 1px solid rgba(16, 185, 129, 0.2);
161
+ letter-spacing: 0.02em;
162
+ }
163
+ .page-header h1 {
164
+ font-family: var(--font-display); font-size: clamp(32px, 5vw, 52px);
165
+ font-weight: 700; line-height: 1.15; margin-bottom: 12px;
166
+ }
167
+ .page-header h1 span { color: var(--accent); }
168
+ .page-header p {
169
+ font-size: 17px; color: var(--text-secondary); max-width: 560px;
170
+ }
171
+
172
+ /* ─── STATS BAR ─── */
173
+ .stats-bar {
174
+ max-width: var(--max-width); margin: 0 auto 40px;
175
+ padding: 0 24px;
176
+ display: flex; gap: 0; flex-wrap: wrap;
177
+ background: var(--bg-card); border: 1px solid var(--border);
178
+ border-radius: var(--radius-lg); overflow: hidden;
179
+ }
180
+ .stat-item {
181
+ flex: 1; min-width: 120px;
182
+ padding: 18px 24px;
183
+ display: flex; flex-direction: column; gap: 4px;
184
+ border-right: 1px solid var(--border);
185
+ }
186
+ .stat-item:last-child { border-right: none; }
187
+ .stat-value {
188
+ font-family: var(--font-mono); font-size: 26px; font-weight: 700;
189
+ color: var(--accent); line-height: 1;
190
+ }
191
+ .stat-label { font-size: 12px; color: var(--text-muted); font-weight: 500; text-transform: uppercase; letter-spacing: 0.05em; }
192
+
193
+ /* ─── CONTROLS ─── */
194
+ .catalog-controls {
195
+ max-width: var(--max-width); margin: 0 auto 28px;
196
+ padding: 0 24px;
197
+ display: flex; flex-direction: column; gap: 16px;
198
+ }
199
+ .filter-row {
200
+ display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
201
+ }
202
+ .filter-label {
203
+ font-size: 12px; font-weight: 600; color: var(--text-muted);
204
+ text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap;
205
+ }
206
+ .filter-tabs {
207
+ display: flex; gap: 6px; flex-wrap: wrap;
208
+ }
209
+ .filter-tab {
210
+ padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 500;
211
+ cursor: pointer; border: 1px solid var(--border); background: transparent;
212
+ color: var(--text-secondary); transition: all var(--transition); font-family: var(--font-body);
213
+ }
214
+ .filter-tab:hover { border-color: var(--accent); color: var(--text-primary); }
215
+ .filter-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }
216
+ .filter-tab[data-layer="L0"].active { background: var(--l0-color); border-color: var(--l0-color); }
217
+ .filter-tab[data-layer="L1"].active { background: var(--l1-color); border-color: var(--l1-color); }
218
+ .filter-tab[data-layer="L2"].active { background: var(--l2-color); border-color: var(--l2-color); }
219
+ .filter-tab[data-layer="L3"].active { background: var(--l3-color); border-color: var(--l3-color); }
220
+ .filter-tab[data-layer="L4"].active { background: var(--l4-color); border-color: var(--l4-color); color: #000; }
221
+
222
+ .search-row {
223
+ display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
224
+ }
225
+ .search-wrap { position: relative; flex: 1; min-width: 220px; max-width: 400px; }
226
+ .search-icon {
227
+ position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
228
+ color: var(--text-muted); font-size: 14px; pointer-events: none;
229
+ }
230
+ .search-input {
231
+ width: 100%; padding: 8px 12px 8px 36px;
232
+ background: var(--bg-elevated); border: 1px solid var(--border);
233
+ border-radius: var(--radius-md); color: var(--text-primary);
234
+ font-family: var(--font-body); font-size: 14px;
235
+ transition: border-color var(--transition); outline: none;
236
+ }
237
+ .search-input::placeholder { color: var(--text-muted); }
238
+ .search-input:focus { border-color: var(--accent); }
239
+
240
+ .sort-wrap { display: flex; align-items: center; gap: 8px; }
241
+ .sort-label { font-size: 12px; color: var(--text-muted); font-weight: 500; white-space: nowrap; }
242
+ .sort-select {
243
+ padding: 7px 28px 7px 10px; background: var(--bg-elevated);
244
+ border: 1px solid var(--border); border-radius: var(--radius-sm);
245
+ color: var(--text-secondary); font-family: var(--font-body); font-size: 13px;
246
+ cursor: pointer; outline: none; appearance: none; -webkit-appearance: none;
247
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='%235a6a7a'%3E%3Cpath d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
248
+ background-repeat: no-repeat; background-position: right 10px center;
249
+ transition: border-color var(--transition);
250
+ }
251
+ .sort-select:focus { border-color: var(--accent); }
252
+
253
+ .results-count {
254
+ font-size: 13px; color: var(--text-muted); white-space: nowrap;
255
+ font-family: var(--font-mono);
256
+ }
257
+
258
+ /* ─── SKILLS GRID ─── */
259
+ .catalog-section {
260
+ max-width: var(--max-width); margin: 0 auto;
261
+ padding: 0 24px;
262
+ }
263
+ .section-divider {
264
+ display: flex; align-items: center; gap: 14px;
265
+ margin-bottom: 20px; margin-top: 40px;
266
+ }
267
+ .section-divider:first-of-type { margin-top: 0; }
268
+ .section-divider-label {
269
+ font-family: var(--font-display); font-size: 14px; font-weight: 700;
270
+ color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em;
271
+ white-space: nowrap;
272
+ }
273
+ .section-divider-line {
274
+ flex: 1; height: 1px; background: var(--border);
275
+ }
276
+ .section-divider-count {
277
+ font-family: var(--font-mono); font-size: 12px; color: var(--text-muted);
278
+ background: var(--bg-elevated); border: 1px solid var(--border);
279
+ padding: 2px 8px; border-radius: 4px; white-space: nowrap;
280
+ }
281
+
282
+ .skills-grid {
283
+ display: grid;
284
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
285
+ gap: 12px;
286
+ margin-bottom: 8px;
287
+ }
288
+
289
+ .skill-card {
290
+ background: var(--bg-card); border: 1px solid var(--border);
291
+ border-radius: var(--radius-md); padding: 16px 18px;
292
+ display: flex; flex-direction: column; gap: 10px;
293
+ transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
294
+ cursor: default;
295
+ }
296
+ .skill-card:hover {
297
+ border-color: var(--accent); transform: translateY(-1px);
298
+ box-shadow: 0 6px 24px rgba(0,0,0,0.2);
299
+ }
300
+ .skill-card-top {
301
+ display: flex; align-items: flex-start; justify-content: space-between; gap: 8px;
302
+ }
303
+ .skill-name {
304
+ font-family: var(--font-mono); font-size: 14px; font-weight: 700;
305
+ color: var(--text-primary); letter-spacing: -0.01em;
306
+ }
307
+ .skill-badges { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; flex-shrink: 0; }
308
+ .badge {
309
+ display: inline-flex; align-items: center;
310
+ padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600;
311
+ font-family: var(--font-mono); letter-spacing: 0.02em; white-space: nowrap;
312
+ }
313
+ .badge-l0 { background: var(--l0-bg); color: var(--l0-color); }
314
+ .badge-l1 { background: var(--l1-bg); color: var(--l1-color); }
315
+ .badge-l2 { background: var(--l2-bg); color: var(--l2-color); }
316
+ .badge-l3 { background: var(--l3-bg); color: var(--l3-color); }
317
+ .badge-l4 { background: var(--l4-bg); color: var(--l4-color); }
318
+ .badge-haiku { background: var(--haiku-bg); color: var(--haiku-color); }
319
+ .badge-sonnet { background: var(--sonnet-bg); color: var(--sonnet-color); }
320
+ .badge-opus { background: var(--opus-bg); color: var(--opus-color); }
321
+
322
+ .skill-desc {
323
+ font-size: 13px; color: var(--text-secondary); line-height: 1.55;
324
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
325
+ overflow: hidden;
326
+ }
327
+ .skill-footer {
328
+ display: flex; align-items: center; justify-content: space-between;
329
+ padding-top: 8px; border-top: 1px solid var(--border);
330
+ }
331
+ .skill-tools {
332
+ font-family: var(--font-mono); font-size: 11px; color: var(--text-muted);
333
+ display: flex; align-items: center; gap: 5px;
334
+ }
335
+ .skill-tools-dot {
336
+ width: 3px; height: 3px; border-radius: 50%;
337
+ background: var(--text-muted); display: inline-block;
338
+ }
339
+
340
+ /* ─── EMPTY STATE ─── */
341
+ .empty-state {
342
+ text-align: center; padding: 60px 24px; color: var(--text-muted);
343
+ display: none;
344
+ }
345
+ .empty-state.visible { display: block; }
346
+ .empty-state-icon { font-size: 40px; margin-bottom: 14px; opacity: 0.5; }
347
+ .empty-state p { font-size: 15px; }
348
+
349
+ /* ─── L4 PACKS SECTION ─── */
350
+ .packs-section {
351
+ max-width: var(--max-width); margin: 0 auto;
352
+ padding: 60px 24px 40px;
353
+ }
354
+ .packs-header { margin-bottom: 28px; }
355
+ .packs-header h2 {
356
+ font-family: var(--font-display); font-size: 28px; font-weight: 700;
357
+ margin-bottom: 8px;
358
+ }
359
+ .packs-header p { font-size: 15px; color: var(--text-secondary); }
360
+ .packs-grid {
361
+ display: grid;
362
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
363
+ gap: 14px;
364
+ }
365
+ .pack-card {
366
+ background: var(--bg-card); border: 1px solid var(--border);
367
+ border-radius: var(--radius-lg); padding: 22px;
368
+ display: flex; flex-direction: column; gap: 10px;
369
+ transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
370
+ }
371
+ .pack-card:hover {
372
+ border-color: var(--l4-color); transform: translateY(-2px);
373
+ box-shadow: 0 8px 28px rgba(245, 158, 11, 0.08);
374
+ }
375
+ .pack-name {
376
+ font-family: var(--font-mono); font-size: 13px; font-weight: 700;
377
+ color: var(--l4-color);
378
+ }
379
+ .pack-meta {
380
+ display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
381
+ }
382
+ .pack-badge {
383
+ font-family: var(--font-mono); font-size: 10px; font-weight: 700;
384
+ padding: 2px 8px; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.05em;
385
+ }
386
+ .pack-badge-free { background: var(--accent-dim); color: var(--accent); }
387
+ .pack-badge-paid { background: var(--l4-bg); color: var(--l4-color); }
388
+ .pack-skills-count {
389
+ font-size: 12px; color: var(--text-muted); font-family: var(--font-mono);
390
+ }
391
+ .pack-desc {
392
+ font-size: 13px; color: var(--text-secondary); line-height: 1.55;
393
+ display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
394
+ overflow: hidden;
395
+ }
396
+ .pack-target {
397
+ font-size: 11px; color: var(--text-muted); margin-top: 4px;
398
+ }
399
+
400
+ /* ─── FOOTER ─── */
401
+ .footer {
402
+ border-top: 1px solid var(--border); padding: 32px 24px;
403
+ max-width: var(--max-width); margin: 60px auto 0;
404
+ }
405
+ .footer-inner {
406
+ display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px;
407
+ }
408
+ .footer-inner p { font-size: 14px; color: var(--text-secondary); }
409
+ .footer-links { display: flex; gap: 24px; }
410
+ .footer-links a { font-size: 13px; color: var(--text-muted); }
411
+ .footer-links a:hover { color: var(--text-primary); }
412
+
413
+ /* ─── FADE-IN ANIMATION ─── */
414
+ @media (prefers-reduced-motion: no-preference) {
415
+ .skill-card {
416
+ opacity: 0; transform: translateY(12px);
417
+ animation: fadeUp 0.35s ease forwards;
418
+ }
419
+ .pack-card {
420
+ opacity: 0; transform: translateY(12px);
421
+ animation: fadeUp 0.35s ease forwards;
422
+ }
423
+ }
424
+ @keyframes fadeUp {
425
+ to { opacity: 1; transform: translateY(0); }
426
+ }
427
+
428
+ /* ─── RESPONSIVE ─── */
429
+ @media (max-width: 768px) {
430
+ .stats-bar { flex-direction: column; gap: 0; }
431
+ .stat-item { border-right: none; border-bottom: 1px solid var(--border); }
432
+ .stat-item:last-child { border-bottom: none; }
433
+ .skills-grid { grid-template-columns: 1fr; }
434
+ .packs-grid { grid-template-columns: 1fr; }
435
+ .nav-links a:not(.btn):not([data-keep]) { display: none; }
436
+ .page-header { padding-top: calc(var(--nav-h) + 36px); }
437
+ }
438
+ @media (max-width: 480px) {
439
+ .filter-tabs { gap: 4px; }
440
+ .filter-tab { padding: 5px 10px; font-size: 12px; }
441
+ .search-wrap { max-width: 100%; }
442
+ }
443
+
444
+ /* Highlight for search matches */
445
+ mark {
446
+ background: rgba(16, 185, 129, 0.25); color: inherit;
447
+ border-radius: 2px; padding: 0 1px;
448
+ }
449
+ </style>
450
+ </head>
451
+ <body>
452
+
453
+ <!-- NAV -->
454
+ <nav class="nav">
455
+ <div class="nav-inner">
456
+ <a href="../index.html" class="nav-logo">
457
+ <span class="logo-rune">&#9670;</span> Rune
458
+ </a>
459
+ <div class="nav-links">
460
+ <a href="../index.html">Home</a>
461
+ <a href="../guides/">Guides</a>
462
+ <a href="index.html" class="active" data-keep>Skills</a>
463
+ <div class="nav-controls">
464
+ <button class="theme-toggle" id="theme-toggle" aria-label="Toggle light/dark mode" title="Toggle theme">&#9790;</button>
465
+ </div>
466
+ <a href="https://github.com/rune-kit/rune" class="btn" target="_blank" rel="noopener">GitHub</a>
467
+ </div>
468
+ </div>
469
+ </nav>
470
+
471
+ <!-- PAGE HEADER -->
472
+ <div class="page-header">
473
+ <p class="page-header-badge">v2.3.0 — Skills Reference</p>
474
+ <h1>Rune <span>Skills</span> Catalog</h1>
475
+ <p>59 skills &middot; 5 layers &middot; 200+ mesh connections. Every skill has a clear responsibility and knows exactly which skills to call.</p>
476
+ </div>
477
+
478
+ <!-- STATS BAR -->
479
+ <div class="stats-bar">
480
+ <div class="stat-item">
481
+ <span class="stat-value">58</span>
482
+ <span class="stat-label">Total Skills</span>
483
+ </div>
484
+ <div class="stat-item">
485
+ <span class="stat-value" style="color: var(--l1-color)">1+5</span>
486
+ <span class="stat-label">L0 Router + L1 Orchestrators</span>
487
+ </div>
488
+ <div class="stat-item">
489
+ <span class="stat-value" style="color: var(--l2-color)">27</span>
490
+ <span class="stat-label">L2 Workflow Hubs</span>
491
+ </div>
492
+ <div class="stat-item">
493
+ <span class="stat-value">25</span>
494
+ <span class="stat-label">L3 Utilities</span>
495
+ </div>
496
+ <div class="stat-item">
497
+ <span class="stat-value" style="color: var(--l4-color)">14</span>
498
+ <span class="stat-label">L4 Extension Packs</span>
499
+ </div>
500
+ <div class="stat-item">
501
+ <span class="stat-value">200+</span>
502
+ <span class="stat-label">Mesh Connections</span>
503
+ </div>
504
+ </div>
505
+
506
+ <!-- CONTROLS -->
507
+ <div class="catalog-controls">
508
+ <div class="filter-row">
509
+ <span class="filter-label">Layer</span>
510
+ <div class="filter-tabs" id="layer-tabs">
511
+ <button class="filter-tab active" data-layer="all">All</button>
512
+ <button class="filter-tab" data-layer="L0">L0 Router</button>
513
+ <button class="filter-tab" data-layer="L1">L1 Orchestrators</button>
514
+ <button class="filter-tab" data-layer="L2">L2 Workflows</button>
515
+ <button class="filter-tab" data-layer="L3">L3 Utilities</button>
516
+ </div>
517
+ </div>
518
+ <div class="search-row">
519
+ <div class="search-wrap">
520
+ <span class="search-icon">&#128269;</span>
521
+ <input type="search" class="search-input" id="skill-search" placeholder="Search skills by name or description..." aria-label="Search skills">
522
+ </div>
523
+ <div class="sort-wrap">
524
+ <span class="sort-label">Sort</span>
525
+ <select class="sort-select" id="sort-select" aria-label="Sort skills">
526
+ <option value="layer">By layer</option>
527
+ <option value="name">By name</option>
528
+ <option value="model">By model</option>
529
+ <option value="tools">By tools count</option>
530
+ </select>
531
+ </div>
532
+ <span class="results-count" id="results-count">57 skills</span>
533
+ </div>
534
+ </div>
535
+
536
+ <!-- SKILLS CATALOG -->
537
+ <div class="catalog-section" id="catalog-section">
538
+ <!-- populated by JS -->
539
+ </div>
540
+
541
+ <div class="empty-state" id="empty-state">
542
+ <div class="empty-state-icon">&#128269;</div>
543
+ <p>No skills match your search. Try a different query.</p>
544
+ </div>
545
+
546
+ <!-- L4 EXTENSION PACKS -->
547
+ <div class="packs-section" id="packs-section">
548
+ <div class="packs-header">
549
+ <h2>L4 Extension Packs</h2>
550
+ <p>Domain-specific skill bundles. Install only what you need. Free packs are MIT-licensed — Pro packs require Rune Pro.</p>
551
+ </div>
552
+ <div class="packs-grid" id="packs-grid">
553
+ <!-- populated by JS -->
554
+ </div>
555
+ </div>
556
+
557
+ <!-- FOOTER -->
558
+ <div class="footer">
559
+ <div class="footer-inner">
560
+ <p>Rune &mdash; Less skills. Deeper connections. &copy; 2025 rune-kit</p>
561
+ <div class="footer-links">
562
+ <a href="https://github.com/rune-kit/rune" target="_blank" rel="noopener">GitHub</a>
563
+ <a href="../index.html">Home</a>
564
+ <a href="../guides/">Guides</a>
565
+ </div>
566
+ </div>
567
+ </div>
568
+
569
+ <script>
570
+ // ─── THEME TOGGLE ───
571
+ const themeToggle = document.getElementById('theme-toggle');
572
+ const saved = localStorage.getItem('rune-theme') || 'dark';
573
+ document.documentElement.setAttribute('data-theme', saved);
574
+ themeToggle.textContent = saved === 'dark' ? '☀' : '☾';
575
+ themeToggle.addEventListener('click', () => {
576
+ const cur = document.documentElement.getAttribute('data-theme');
577
+ const next = cur === 'dark' ? 'light' : 'dark';
578
+ document.documentElement.setAttribute('data-theme', next);
579
+ localStorage.setItem('rune-theme', next);
580
+ themeToggle.textContent = next === 'dark' ? '☀' : '☾';
581
+ });
582
+
583
+ // ─── SKILL DATA ───
584
+ const SKILLS = [
585
+ // L0
586
+ { name: 'skill-router', layer: 'L0', model: 'haiku', tools: 3, desc: 'Meta-enforcement layer that routes every agent action through the correct skill. Prevents hallucinated commands by mapping intent to the canonical skill.' },
587
+ // L1
588
+ { name: 'cook', layer: 'L1', model: 'sonnet', tools: 6, desc: 'Feature implementation orchestrator. Use for ANY code change — the primary entry point for development work with phase-aware execution.' },
589
+ { name: 'team', layer: 'L1', model: 'opus', tools: 6, desc: 'Multi-agent meta-orchestrator for tasks spanning 5+ files or 3+ modules. Spawns and coordinates parallel specialist agents.' },
590
+ { name: 'launch', layer: 'L1', model: 'sonnet', tools: 6, desc: 'Deploy + marketing orchestrator. Handles production deployment plus announcement strategy in one coordinated workflow.' },
591
+ { name: 'rescue', layer: 'L1', model: 'sonnet', tools: 6, desc: 'Legacy refactoring orchestrator. Assesses technical debt, creates safety nets, and incrementally modernizes codebases.' },
592
+ { name: 'scaffold', layer: 'L1', model: 'sonnet', tools: 8, desc: 'Autonomous project bootstrapper. Generates a complete project from a description — structure, code, tests, and CI/CD config.' },
593
+ // L2
594
+ { name: 'adversary', layer: 'L2', model: 'opus', tools: 3, desc: 'Pre-implementation red-team analysis. Stress-tests plans across edge cases, security, scalability, and integration risk before code is written.' },
595
+ { name: 'audit', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Comprehensive project audit across security, dependencies, code quality, architecture, and performance.' },
596
+ { name: 'autopsy', layer: 'L2', model: 'opus', tools: 4, desc: 'Full codebase health assessment. Analyzes complexity, dependencies, dead code, tech debt, and git history hotspots.' },
597
+ { name: 'ba', layer: 'L2', model: 'opus', tools: 3, desc: 'Business Analyst agent. Elicits and validates requirements before planning begins — uncovers hidden constraints and ambiguities.' },
598
+ { name: 'brainstorm', layer: 'L2', model: 'opus', tools: 3, desc: 'Creative ideation and solution exploration. Generates multiple approaches with trade-offs; includes Rescue Mode for divergent alternatives.' },
599
+ { name: 'db', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Database workflow specialist. Generates migration files with rollback scripts, detects breaking schema changes, and audits query performance.' },
600
+ { name: 'debug', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Root cause analysis for bugs and unexpected behavior. Traces symptoms to origin and classifies whether escalation is needed.' },
601
+ { name: 'deploy', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Deploy application to target platform. Handles Vercel, Railway, Fly.io, AWS, and Docker with environment validation.' },
602
+ { name: 'design', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Design system reasoning. Maps product domain to style, palette, typography, and platform-specific patterns.' },
603
+ { name: 'docs', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Auto-generate and maintain project documentation — README, API docs, architecture docs, and changelogs.' },
604
+ { name: 'fix', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Apply code changes and bug fixes. Writes implementation code and verifies changes with targeted tests.' },
605
+ { name: 'incident', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Structured incident response. Triage, mitigation, root cause analysis, and post-mortem generation.' },
606
+ { name: 'logic-guardian', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Protects complex business logic from accidental deletion or overwrite during refactoring.' },
607
+ { name: 'marketing', layer: 'L2', model: 'sonnet', tools: 7, desc: 'Create marketing assets and execute launch strategy — copy, social posts, landing pages, and SEO audits.' },
608
+ { name: 'mcp-builder', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Build Model Context Protocol servers from specifications in TypeScript or Python with tests and docs.' },
609
+ { name: 'onboard', layer: 'L2', model: 'sonnet', tools: 5, desc: 'Auto-generate project context for AI sessions — scans codebase, creates CLAUDE.md and architecture summaries.' },
610
+ { name: 'perf', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Performance regression gate. Detects N+1 queries, sync-in-async patterns, missing indexes, and memory leaks.' },
611
+ { name: 'plan', layer: 'L2', model: 'opus', tools: 5, desc: 'Create structured implementation plans from requirements. Supports Feature Spec Mode and Roadmap Mode with ICE scoring.' },
612
+ { name: 'preflight', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Pre-commit quality gate. Catches "almost right" code — type errors, missing tests, lint failures, and broken imports.' },
613
+ { name: 'review', layer: 'L2', model: 'sonnet', tools: 3, desc: 'Code quality review covering patterns, security vulnerabilities, performance, and correctness.' },
614
+ { name: 'review-intake', layer: 'L2', model: 'sonnet', tools: 3, desc: 'Process external code review feedback or PR comments before implementing changes.' },
615
+ { name: 'safeguard', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Build safety nets before refactoring — characterization tests, boundary markers, and config freeze snapshots.' },
616
+ { name: 'scout', layer: 'L2', model: 'haiku', tools: 3, desc: 'Fast codebase scanner. Provides architecture context to other skills without loading full file contents.' },
617
+ { name: 'sentinel', layer: 'L2', model: 'sonnet', tools: 4, desc: 'Automated security gatekeeper. Blocks unsafe code — secret scanning, OWASP top 10, dependency vulnerabilities.' },
618
+ { name: 'sentinel-env', layer: 'L3', model: 'haiku', tools: 4, desc: 'Environment-aware pre-flight check. Validates OS, runtime versions, installed tools, and port availability.' },
619
+ { name: 'skill-forge', layer: 'L2', model: 'opus', tools: 6, desc: 'Create new Rune skills, edit existing ones, and verify skill quality before deployment.' },
620
+ { name: 'surgeon', layer: 'L2', model: 'sonnet', tools: 6, desc: 'Incremental refactorer. Refactors one module per session using Strangler Fig, Branch by Abstraction, and Parallel Run patterns.' },
621
+ { name: 'test', layer: 'L2', model: 'sonnet', tools: 6, desc: 'TDD test writer. Writes failing tests first (red), verifies they pass after implementation (green), enforces 80% coverage.' },
622
+ // L3
623
+ { name: 'asset-creator', layer: 'L3', model: 'sonnet', tools: 5, desc: 'Creates code-based visual assets — SVG icons, OG image HTML templates, social banners, and icon sets.' },
624
+ { name: 'browser-pilot', layer: 'L3', model: 'sonnet', tools: 4, desc: 'Playwright browser automation. Navigates URLs, takes screenshots, checks accessibility, and interacts with web UIs.' },
625
+ { name: 'completion-gate', layer: 'L3', model: 'haiku', tools: 4, desc: 'Validates agent claims against evidence trail. Catches "done" without proof and "tests pass" without output.' },
626
+ { name: 'constraint-check', layer: 'L3', model: 'haiku', tools: 3, desc: 'Meta-validator for HARD-GATEs. Verifies that skills\' mandatory constraints were followed during a workflow step.' },
627
+ { name: 'context-engine', layer: 'L3', model: 'haiku', tools: 3, desc: 'Context window management. Auto-triggered when context is filling up — summarizes and compacts without losing state.' },
628
+ { name: 'dependency-doctor', layer: 'L3', model: 'haiku', tools: 4, desc: 'Dependency health management. Detects outdated packages and vulnerabilities across npm, pip, cargo, and go modules.' },
629
+ { name: 'doc-processor', layer: 'L3', model: 'sonnet', tools: 6, desc: 'Generate and parse office documents — PDF, DOCX, XLSX, PPTX, and CSV files.' },
630
+ { name: 'docs-seeker', layer: 'L3', model: 'haiku', tools: 5, desc: 'Find documentation for APIs, libraries, and error messages via web search and curated sources.' },
631
+ { name: 'git', layer: 'L3', model: 'haiku', tools: 4, desc: 'Specialized git operations — semantic commits, PR descriptions, branch management, and conflict resolution.' },
632
+ { name: 'hallucination-guard', layer: 'L3', model: 'haiku', tools: 4, desc: 'Verify AI-generated imports, API calls, and packages actually exist before code is written.' },
633
+ { name: 'integrity-check', layer: 'L3', model: 'haiku', tools: 3, desc: 'Verify integrity of persisted state, skill outputs, and context bus data across workflow steps.' },
634
+ { name: 'journal', layer: 'L3', model: 'haiku', tools: 5, desc: 'Persistent state tracking and Architecture Decision Records. Maintains project history across sessions.' },
635
+ { name: 'neural-memory', layer: 'L3', model: 'haiku', tools: 1, desc: 'Cross-session cognitive persistence via Neural Memory MCP. Stores and recalls decisions, patterns, and context.' },
636
+ { name: 'problem-solver', layer: 'L3', model: 'sonnet', tools: 3, desc: 'Structured reasoning frameworks for complex multi-variable problems. Applies first-principles and systems thinking.' },
637
+ { name: 'research', layer: 'L3', model: 'haiku', tools: 5, desc: 'Web search and external knowledge lookup. Gathers data on technologies, libraries, and best practices.' },
638
+ { name: 'sast', layer: 'L3', model: 'haiku', tools: 4, desc: 'Static analysis tool runner. Wraps ESLint, Semgrep, Bandit, Clippy, and language-specific analyzers.' },
639
+ { name: 'scope-guard', layer: 'L3', model: 'haiku', tools: 3, desc: 'Detects scope creep. Auto-triggered by L1 orchestrators when files exceed the original plan.' },
640
+ { name: 'sequential-thinking', layer: 'L3', model: 'sonnet', tools: 3, desc: 'Step-by-step complex reasoning for multi-variable problems with explicit dependency tracking.' },
641
+ { name: 'session-bridge', layer: 'L3', model: 'haiku', tools: 5, desc: 'Universal context persistence across sessions. Auto-saves decisions, conventions, and progress to project files.' },
642
+ { name: 'trend-scout', layer: 'L3', model: 'haiku', tools: 5, desc: 'Scan market trends, competitor activity, and emerging technology patterns.' },
643
+ { name: 'verification', layer: 'L3', model: 'haiku', tools: 4, desc: 'Universal verification runner. Runs lint, type-check, tests, and build in one coordinated pass.' },
644
+ { name: 'video-creator', layer: 'L3', model: 'sonnet', tools: 5, desc: 'Video content planning. Writes narration scripts, storyboards, shot lists, and asset checklists.' },
645
+ { name: 'watchdog', layer: 'L3', model: 'sonnet', tools: 4, desc: 'Post-deploy monitoring. Checks HTTP status, response times, error rates, and service health after deployment.' },
646
+ { name: 'worktree', layer: 'L3', model: 'haiku', tools: 4, desc: 'Git worktree lifecycle management. Creates isolated workspaces for parallel feature development.' },
647
+ ];
648
+
649
+ const PACKS = [
650
+ { name: '@rune/ui', desc: 'Frontend UI patterns — design systems, color palettes, typography, component architecture, landing page sections, accessibility audits, and animation patterns.', target: 'Frontend developers', price: 'free', skills: 8 },
651
+ { name: '@rune/backend', desc: 'Backend patterns — API design, authentication, database patterns, middleware architecture, caching strategies, and background job processing.', target: 'Backend developers', price: 'free', skills: 7 },
652
+ { name: '@rune/devops', desc: 'DevOps patterns — Docker, CI/CD pipelines, monitoring setup, server configuration, and SSL/domain management.', target: 'DevOps engineers', price: '$12', skills: 7 },
653
+ { name: '@rune/mobile', desc: 'Mobile development patterns — React Native, Flutter, app store preparation, and native bridge integration.', target: 'Mobile developers', price: '$15', skills: 5 },
654
+ { name: '@rune/security', desc: 'Deep security analysis — OWASP audit, penetration testing patterns, secret management, compliance checking, and API hardening.', target: 'Security engineers', price: '$15', skills: 6 },
655
+ { name: '@rune/trading', desc: 'Fintech and trading patterns — real-time data, financial dashboards, technical indicators, WebSocket architecture, and strategy development.', target: 'Fintech developers', price: '$15', skills: 7 },
656
+ { name: '@rune/saas', desc: 'SaaS patterns — multi-tenancy, billing integration, subscription management, feature flags, team permissions, and user onboarding flows.', target: 'SaaS builders', price: '$12', skills: 6 },
657
+ { name: '@rune/ecommerce', desc: 'E-commerce patterns — Shopify development, payment integration, shopping cart state machines, and inventory management.', target: 'E-commerce developers', price: '$12', skills: 5 },
658
+ { name: '@rune/ai-ml', desc: 'AI/ML integration patterns — LLM integration, RAG pipelines, embeddings, fine-tuning workflows, and prompt engineering.', target: 'AI engineers', price: '$15', skills: 6 },
659
+ { name: '@rune/gamedev', desc: 'Game development patterns — Three.js, WebGL, game loops, physics engines, and asset pipelines.', target: 'Game developers', price: '$12', skills: 5 },
660
+ { name: '@rune/content', desc: 'Content platform patterns — blog systems, CMS integration, MDX authoring, internationalization, and SEO.', target: 'Content creators', price: '$9', skills: 5 },
661
+ { name: '@rune/analytics', desc: 'Analytics patterns — tracking setup, A/B testing, funnel analysis, statistical significance, and dashboard design.', target: 'Growth engineers', price: '$12', skills: 6 },
662
+ { name: '@rune/chrome-ext', desc: 'Chrome extension development — Manifest V3 scaffolding, service worker lifecycle, message passing, CWS compliance, and Chrome AI integration.', target: 'Chrome extension developers', price: 'free', skills: 6 },
663
+ { name: '@rune/zalo', desc: 'Zalo platform integration — Official Account API (OAuth2, messaging, webhooks, MCP server) and personal account automation (zca-js). Dual-track with risk gating.', target: 'Vietnamese developers', price: 'free', skills: 7 },
664
+ ];
665
+
666
+ // ─── LAYER ORDER ───
667
+ const LAYER_ORDER = { L0: 0, L1: 1, L2: 2, L3: 3, L4: 4 };
668
+ const LAYER_LABELS = {
669
+ L0: 'L0 — Router',
670
+ L1: 'L1 — Orchestrators',
671
+ L2: 'L2 — Workflow Hubs',
672
+ L3: 'L3 — Utilities',
673
+ };
674
+
675
+ // ─── STATE ───
676
+ let activeLayer = 'all';
677
+ let searchQuery = '';
678
+ let sortBy = 'layer';
679
+
680
+ // ─── RENDER ───
681
+ function escapeHTML(str) {
682
+ return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
683
+ }
684
+
685
+ function highlight(text, query) {
686
+ if (!query) return escapeHTML(text);
687
+ const escaped = escapeHTML(text);
688
+ const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
689
+ return escaped.replace(new RegExp(escapedQuery, 'gi'), match => `<mark>${match}</mark>`);
690
+ }
691
+
692
+ function renderSkillCard(skill, query, delay) {
693
+ const layerClass = `badge-${skill.layer.toLowerCase()}`;
694
+ const modelClass = `badge-${skill.model}`;
695
+ return `
696
+ <div class="skill-card" data-layer="${skill.layer}" style="animation-delay:${delay}ms">
697
+ <div class="skill-card-top">
698
+ <span class="skill-name">${highlight(skill.name, query)}</span>
699
+ <div class="skill-badges">
700
+ <span class="badge ${layerClass}">${skill.layer}</span>
701
+ <span class="badge ${modelClass}">${skill.model}</span>
702
+ </div>
703
+ </div>
704
+ <p class="skill-desc">${highlight(skill.desc, query)}</p>
705
+ <div class="skill-footer">
706
+ <span class="skill-tools">
707
+ <span class="skill-tools-dot"></span>
708
+ ${skill.tools} tool${skill.tools !== 1 ? 's' : ''}
709
+ </span>
710
+ </div>
711
+ </div>`;
712
+ }
713
+
714
+ function getFilteredSorted() {
715
+ let filtered = SKILLS.filter(s => {
716
+ if (activeLayer !== 'all' && s.layer !== activeLayer) return false;
717
+ if (searchQuery) {
718
+ const q = searchQuery.toLowerCase();
719
+ return s.name.includes(q) || s.desc.toLowerCase().includes(q);
720
+ }
721
+ return true;
722
+ });
723
+
724
+ filtered.sort((a, b) => {
725
+ if (sortBy === 'name') return a.name.localeCompare(b.name);
726
+ if (sortBy === 'model') {
727
+ const modelOrder = { haiku: 0, sonnet: 1, opus: 2 };
728
+ return modelOrder[a.model] - modelOrder[b.model] || a.name.localeCompare(b.name);
729
+ }
730
+ if (sortBy === 'tools') return b.tools - a.tools;
731
+ // default: layer
732
+ return (LAYER_ORDER[a.layer] - LAYER_ORDER[b.layer]) || a.name.localeCompare(b.name);
733
+ });
734
+
735
+ return filtered;
736
+ }
737
+
738
+ function render() {
739
+ const filtered = getFilteredSorted();
740
+ const section = document.getElementById('catalog-section');
741
+ const emptyState = document.getElementById('empty-state');
742
+ const countEl = document.getElementById('results-count');
743
+ const packsSection = document.getElementById('packs-section');
744
+
745
+ countEl.textContent = `${filtered.length} skill${filtered.length !== 1 ? 's' : ''}`;
746
+
747
+ if (filtered.length === 0) {
748
+ section.innerHTML = '';
749
+ emptyState.classList.add('visible');
750
+ return;
751
+ }
752
+ emptyState.classList.remove('visible');
753
+
754
+ // Group by layer when sort=layer and no specific layer filter
755
+ const groupByLayer = sortBy === 'layer' && activeLayer === 'all';
756
+
757
+ if (groupByLayer) {
758
+ const groups = {};
759
+ for (const skill of filtered) {
760
+ if (!groups[skill.layer]) groups[skill.layer] = [];
761
+ groups[skill.layer].push(skill);
762
+ }
763
+ let html = '';
764
+ let globalDelay = 0;
765
+ for (const layer of ['L0', 'L1', 'L2', 'L3']) {
766
+ if (!groups[layer]) continue;
767
+ html += `
768
+ <div class="section-divider">
769
+ <span class="section-divider-label">${LAYER_LABELS[layer]}</span>
770
+ <div class="section-divider-line"></div>
771
+ <span class="section-divider-count">${groups[layer].length} skill${groups[layer].length !== 1 ? 's' : ''}</span>
772
+ </div>
773
+ <div class="skills-grid">
774
+ ${groups[layer].map((s, i) => renderSkillCard(s, searchQuery, (globalDelay + i) * 30)).join('')}
775
+ </div>`;
776
+ globalDelay += groups[layer].length;
777
+ }
778
+ section.innerHTML = html;
779
+ } else {
780
+ section.innerHTML = `
781
+ <div class="skills-grid">
782
+ ${filtered.map((s, i) => renderSkillCard(s, searchQuery, i * 25)).join('')}
783
+ </div>`;
784
+ }
785
+
786
+ // Show/hide L4 packs section based on filter
787
+ packsSection.style.display = (activeLayer === 'all' || activeLayer === 'L4') ? '' : 'none';
788
+ }
789
+
790
+ function renderPacks() {
791
+ const grid = document.getElementById('packs-grid');
792
+ grid.innerHTML = PACKS.map((p, i) => {
793
+ const isFree = p.price === 'free';
794
+ return `
795
+ <div class="pack-card" style="animation-delay:${i * 40}ms">
796
+ <span class="pack-name">${escapeHTML(p.name)}</span>
797
+ <div class="pack-meta">
798
+ <span class="pack-badge ${isFree ? 'pack-badge-free' : 'pack-badge-paid'}">${isFree ? 'FREE' : p.price}</span>
799
+ <span class="pack-skills-count">${p.skills} skills</span>
800
+ </div>
801
+ <p class="pack-desc">${escapeHTML(p.desc)}</p>
802
+ <span class="pack-target">&#128100; ${escapeHTML(p.target)}</span>
803
+ </div>`;
804
+ }).join('');
805
+ }
806
+
807
+ // ─── EVENT LISTENERS ───
808
+ document.getElementById('layer-tabs').addEventListener('click', e => {
809
+ const tab = e.target.closest('.filter-tab');
810
+ if (!tab) return;
811
+ document.querySelectorAll('.filter-tab').forEach(t => t.classList.remove('active'));
812
+ tab.classList.add('active');
813
+ activeLayer = tab.dataset.layer;
814
+ render();
815
+ });
816
+
817
+ document.getElementById('skill-search').addEventListener('input', e => {
818
+ searchQuery = e.target.value.trim();
819
+ render();
820
+ });
821
+
822
+ document.getElementById('sort-select').addEventListener('change', e => {
823
+ sortBy = e.target.value;
824
+ render();
825
+ });
826
+
827
+ // ─── INIT ───
828
+ render();
829
+ renderPacks();
830
+ </script>
831
+ </body>
832
+ </html>