@rune-kit/rune 2.2.1 → 2.2.2
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 +39 -13
- package/compiler/bin/rune.js +26 -4
- package/docs/ANTIGRAVITY-GAP-ANALYSIS.md +369 -0
- package/docs/ARCHITECTURE.md +332 -0
- package/docs/COMMUNITY-PACKS.md +109 -0
- package/docs/CONTRIBUTING-L4.md +215 -0
- package/docs/CROSS-IDE-ANALYSIS.md +164 -0
- package/docs/EXTENSION-TEMPLATE.md +108 -0
- package/docs/MESH-RULES.md +34 -0
- package/docs/MULTI-PLATFORM.md +804 -0
- package/docs/SKILL-DEPTH-AUDIT.md +191 -0
- package/docs/SKILL-TEMPLATE.md +72 -0
- package/docs/TRADE-MATRIX.md +327 -0
- package/docs/VERSIONING.md +91 -0
- package/docs/VISION.md +263 -0
- package/docs/assets/demo-subtitles.srt +215 -0
- package/docs/assets/end-card.html +276 -0
- package/docs/assets/mesh-diagram.html +654 -0
- package/docs/assets/thumbnail.html +295 -0
- package/docs/guides/cli.md +403 -0
- package/docs/guides/index.html +1346 -0
- package/docs/index.html +674 -0
- package/docs/references/claudekit-analysis.md +414 -0
- package/docs/references/voltagent-analysis.md +189 -0
- package/docs/script.js +277 -0
- package/docs/skills/index.html +832 -0
- package/docs/style.css +583 -0
- package/docs/video-demo-plan.md +172 -0
- package/extensions/ui/PACK.md +2 -0
- package/extensions/ui/skills/design-decision.md +10 -0
- package/extensions/ui/skills/palette-picker.md +11 -0
- package/hooks/.gitkeep +0 -0
- package/hooks/auto-format/index.cjs +48 -0
- package/hooks/context-watch/index.cjs +68 -0
- package/hooks/hooks.json +99 -0
- package/hooks/metrics-collector/index.cjs +42 -0
- package/hooks/post-session-reflect/index.cjs +153 -0
- package/hooks/pre-compact/index.cjs +95 -0
- package/hooks/pre-tool-guard/index.cjs +68 -0
- package/hooks/run-hook +17 -0
- package/hooks/run-hook.cjs +16 -0
- package/hooks/run-hook.cmd +1 -0
- package/hooks/secrets-scan/index.cjs +100 -0
- package/hooks/session-start/index.cjs +65 -0
- package/hooks/typecheck/index.cjs +65 -0
- package/package.json +9 -4
- package/references/ui-pro-max-data/LICENSE-UI-PRO-MAX +21 -0
- package/references/ui-pro-max-data/charts.csv +26 -0
- package/references/ui-pro-max-data/colors.csv +162 -0
- package/references/ui-pro-max-data/styles.csv +85 -0
- package/references/ui-pro-max-data/typography.csv +74 -0
- package/references/ui-pro-max-data/ui-reasoning.csv +162 -0
- package/references/ui-pro-max-data/ux-guidelines.csv +100 -0
- package/skills/ba/SKILL.md +10 -0
- package/skills/completion-gate/SKILL.md +34 -1
- package/skills/context-engine/SKILL.md +13 -0
- package/skills/cook/SKILL.md +71 -0
- package/skills/debug/SKILL.md +56 -1
- package/skills/design/SKILL.md +11 -0
- package/skills/fix/SKILL.md +26 -1
- package/skills/plan/SKILL.md +23 -6
- package/skills/review/SKILL.md +2 -0
- package/skills/skill-forge/SKILL.md +38 -3
- package/skills/test/SKILL.md +10 -1
- package/skills/verification/SKILL.md +60 -2
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Rune Demo Video — Production Plan
|
|
2
|
+
|
|
3
|
+
**Topic**: Rune plugin full demo — install to debug to ship
|
|
4
|
+
**Audience**: Developers using AI coding assistants (Claude Code, Cursor, Windsurf)
|
|
5
|
+
**Duration**: 180 seconds (3 minutes)
|
|
6
|
+
**Platform**: YouTube (primary), Twitter/X (60s cut), GitHub README (embedded)
|
|
7
|
+
**Style**: Screen recording + narration overlay, dark terminal theme
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Script (with timing marks)
|
|
12
|
+
|
|
13
|
+
### HOOK (0:00–0:08)
|
|
14
|
+
|
|
15
|
+
> "You're debugging for the 4th time. Claude keeps suggesting the same fix. Same loop. Same frustration."
|
|
16
|
+
> [Screen: terminal showing repeated failed attempts]
|
|
17
|
+
> "What if your AI assistant had a structured workflow that actually worked?"
|
|
18
|
+
|
|
19
|
+
### INTRO (0:08–0:20)
|
|
20
|
+
|
|
21
|
+
> "This is Rune — a 57-skill mesh that turns your AI coding assistant from a chatbot into an engineering team."
|
|
22
|
+
> [Screen: Rune GitHub page, star count, "57 skills | 200+ connections"]
|
|
23
|
+
> "It works on Claude Code, Cursor, Windsurf, and Antigravity. Let me show you."
|
|
24
|
+
|
|
25
|
+
### SCENE 1: INSTALL (0:20–0:40)
|
|
26
|
+
|
|
27
|
+
> "Install takes 10 seconds."
|
|
28
|
+
> [Screen: terminal]
|
|
29
|
+
> ```
|
|
30
|
+
> claude plugin install rune-kit/rune
|
|
31
|
+
> ```
|
|
32
|
+
> [Plugin installs, shows "57 skills loaded"]
|
|
33
|
+
> "For Cursor or Windsurf, one command compiles all skills into your IDE's format."
|
|
34
|
+
> [Screen: terminal]
|
|
35
|
+
> ```
|
|
36
|
+
> npx --yes @rune-kit/rune init --platform cursor
|
|
37
|
+
> ```
|
|
38
|
+
> [Shows files being generated]
|
|
39
|
+
|
|
40
|
+
### SCENE 2: ONBOARD (0:40–1:00)
|
|
41
|
+
|
|
42
|
+
> "First thing — let Rune understand your project."
|
|
43
|
+
> [Screen: terminal in a real project directory]
|
|
44
|
+
> ```
|
|
45
|
+
> /rune onboard
|
|
46
|
+
> ```
|
|
47
|
+
> [Shows scout scanning files, detecting Next.js + Prisma + TypeScript]
|
|
48
|
+
> [Shows .rune/ directory being created with decisions.md, conventions.md]
|
|
49
|
+
> "Rune scans your codebase, detects your stack, and creates context files that persist across sessions. No more re-explaining your project every time."
|
|
50
|
+
|
|
51
|
+
### SCENE 3: DEBUG (1:00–1:30)
|
|
52
|
+
|
|
53
|
+
> "Now let's debug. Users report login returns 401 for valid credentials."
|
|
54
|
+
> [Screen: terminal]
|
|
55
|
+
> ```
|
|
56
|
+
> /rune debug "login returns 401 for valid credentials"
|
|
57
|
+
> ```
|
|
58
|
+
> [Shows debug skill activating]
|
|
59
|
+
> [Shows scout finding auth files → debug reading them → identifying root cause]
|
|
60
|
+
> "Rune's debug skill traces the code path, finds the root cause — an expired JWT secret rotation — and hands it to fix."
|
|
61
|
+
> [Shows fix skill writing the code change]
|
|
62
|
+
> [Shows verification running: lint pass, typecheck pass, tests pass]
|
|
63
|
+
> "Fixed. Verified. One command."
|
|
64
|
+
|
|
65
|
+
### SCENE 4: BUILD A FEATURE (1:30–2:15)
|
|
66
|
+
|
|
67
|
+
> "Now let's build something. Add Stripe billing to our SaaS app."
|
|
68
|
+
> [Screen: terminal]
|
|
69
|
+
> ```
|
|
70
|
+
> /rune cook "add Stripe subscription billing with plan management"
|
|
71
|
+
> ```
|
|
72
|
+
> [Shows cook Phase 1: scout scanning → detecting @rune/saas pack]
|
|
73
|
+
> [Shows cook Phase 2: plan creating implementation steps]
|
|
74
|
+
> [Shows adversary challenging the plan: "Missing webhook signature verification — CRITICAL"]
|
|
75
|
+
> "See that? Before writing a single line of code, Rune's adversary skill red-teams the plan. Found a missing webhook security check."
|
|
76
|
+
> [Shows plan updated → user approves]
|
|
77
|
+
> [Shows cook Phase 3: test writing failing tests]
|
|
78
|
+
> [Shows cook Phase 4: fix implementing code]
|
|
79
|
+
> [Shows cook Phase 5: preflight + sentinel + review running in parallel]
|
|
80
|
+
> [Shows cook Phase 6: all green]
|
|
81
|
+
> "8 phases. TDD. Security scan. Code review. All orchestrated automatically."
|
|
82
|
+
|
|
83
|
+
### SCENE 5: THE MESH (2:15–2:40)
|
|
84
|
+
|
|
85
|
+
> "This isn't a list of independent tools. It's a mesh."
|
|
86
|
+
> [Screen: animated diagram showing skill connections]
|
|
87
|
+
> "When debug can't find the cause, it escalates to problem-solver. When cook hits a wall 3 times, it pivots to brainstorm. When sentinel finds a vulnerability, it blocks the commit."
|
|
88
|
+
> [Shows mesh diagram with glowing connections]
|
|
89
|
+
> "57 skills. 200+ connections. They talk to each other so you don't have to."
|
|
90
|
+
|
|
91
|
+
### CTA (2:40–3:00)
|
|
92
|
+
|
|
93
|
+
> "Rune is free and open source. MIT license."
|
|
94
|
+
> [Screen: GitHub repo]
|
|
95
|
+
> ```
|
|
96
|
+
> claude plugin install rune-kit/rune
|
|
97
|
+
> ```
|
|
98
|
+
> "Or for any IDE:"
|
|
99
|
+
> ```
|
|
100
|
+
> npx --yes @rune-kit/rune init
|
|
101
|
+
> ```
|
|
102
|
+
> [Screen: landing page with GitHub stars]
|
|
103
|
+
> "Less skills. Deeper connections. Link in description."
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Storyboard
|
|
108
|
+
|
|
109
|
+
| Time | Visual | Audio | Transition |
|
|
110
|
+
|------|--------|-------|------------|
|
|
111
|
+
| 0:00 | Dark terminal, red error messages repeating | Narration: frustration hook | Fade in |
|
|
112
|
+
| 0:08 | Rune GitHub page, hero section | Narration: intro | Cut |
|
|
113
|
+
| 0:20 | Terminal: plugin install command | Narration: install | Cut |
|
|
114
|
+
| 0:30 | Terminal: npx init for Cursor | Narration: multi-platform | Cut |
|
|
115
|
+
| 0:40 | Terminal: /rune onboard in real project | Narration: onboard | Cut |
|
|
116
|
+
| 0:50 | .rune/ directory tree appearing | Narration: context persistence | Slide in |
|
|
117
|
+
| 1:00 | Terminal: /rune debug command | Narration: debug scenario | Cut |
|
|
118
|
+
| 1:10 | Code files being read, root cause highlighted | Narration: tracing | Zoom |
|
|
119
|
+
| 1:20 | Fix being written, tests passing | Narration: fixed + verified | Cut |
|
|
120
|
+
| 1:30 | Terminal: /rune cook command | Narration: build feature | Cut |
|
|
121
|
+
| 1:45 | Plan appearing, adversary challenge in red | Narration: red-team | Highlight |
|
|
122
|
+
| 2:00 | TDD cycle: red → green → refactor | Narration: phases | Split screen |
|
|
123
|
+
| 2:10 | Quality gates all green | Narration: automatic orchestration | Cut |
|
|
124
|
+
| 2:15 | Mesh diagram (animated) | Narration: mesh explanation | Fade |
|
|
125
|
+
| 2:30 | Connections glowing between skills | Narration: resilience | Pulse animation |
|
|
126
|
+
| 2:40 | GitHub repo + install command | Narration: CTA | Cut |
|
|
127
|
+
| 2:50 | Landing page | Narration: tagline | Fade out |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Shot List
|
|
132
|
+
|
|
133
|
+
| # | Type | Content | Duration | Notes |
|
|
134
|
+
|---|------|---------|----------|-------|
|
|
135
|
+
| 1 | Screen rec | Terminal with repeated errors | 8s | Use real project, dark theme |
|
|
136
|
+
| 2 | Screen rec | Rune GitHub page scroll | 5s | Show stars, description |
|
|
137
|
+
| 3 | Screen rec | `claude plugin install` | 8s | Real-time, no speedup |
|
|
138
|
+
| 4 | Screen rec | `npx @rune-kit/rune init` | 8s | Show file generation |
|
|
139
|
+
| 5 | Screen rec | `/rune onboard` full flow | 15s | Speed up 2x, show key moments |
|
|
140
|
+
| 6 | Screen rec | `/rune debug` full flow | 25s | Speed up 2x, pause on root cause |
|
|
141
|
+
| 7 | Screen rec | `/rune cook` full flow | 40s | Speed up 3x, pause on adversary + quality |
|
|
142
|
+
| 8 | Graphic | Mesh diagram animation | 20s | Create with Motion Canvas or After Effects |
|
|
143
|
+
| 9 | Screen rec | GitHub + install commands | 15s | Clean, centered text |
|
|
144
|
+
| 10 | Graphic | Landing page + tagline | 10s | End card with links |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Asset Checklist
|
|
149
|
+
|
|
150
|
+
- [ ] Real project for demo (Next.js + Prisma SaaS app recommended)
|
|
151
|
+
- [ ] Rune plugin installed and working
|
|
152
|
+
- [ ] Terminal theme: dark, high contrast, large font (18-20pt)
|
|
153
|
+
- [ ] Screen recording tool: OBS or Loom
|
|
154
|
+
- [ ] Mesh diagram: create SVG or use Motion Canvas
|
|
155
|
+
- [ ] Background music: lo-fi or minimal electronic (royalty-free)
|
|
156
|
+
- [ ] Microphone for narration (or use ElevenLabs/AI voice)
|
|
157
|
+
- [ ] Thumbnail: terminal screenshot + "57 Skills" text overlay
|
|
158
|
+
- [ ] Captions/subtitles file (.srt) for accessibility
|
|
159
|
+
|
|
160
|
+
## 60-Second Twitter/X Cut
|
|
161
|
+
|
|
162
|
+
Use shots: 1 (3s) → 3 (5s) → 6 (15s) → 7 (20s) → 8 (10s) → 9 (7s)
|
|
163
|
+
Skip: onboard detail, mesh explanation
|
|
164
|
+
Hook: "Your AI assistant keeps failing? Try giving it a brain." + demo
|
|
165
|
+
|
|
166
|
+
## Distribution
|
|
167
|
+
|
|
168
|
+
- YouTube: full 3-min version with chapters
|
|
169
|
+
- Twitter/X: 60s cut, captions baked in, no music
|
|
170
|
+
- GitHub README: YouTube embed at top
|
|
171
|
+
- Landing page: YouTube embed in hero section
|
|
172
|
+
- Reddit r/ClaudeAI, r/cursor, r/webdev: link + text summary
|
package/extensions/ui/PACK.md
CHANGED
|
@@ -12,6 +12,8 @@ metadata:
|
|
|
12
12
|
|
|
13
13
|
# @rune/ui
|
|
14
14
|
|
|
15
|
+
> Design intelligence data: [UI/UX Pro Max](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) (MIT) — 161 palettes, 84 styles, 73 font pairings, 99 UX guidelines. Located at `references/ui-pro-max-data/`.
|
|
16
|
+
|
|
15
17
|
## Purpose
|
|
16
18
|
|
|
17
19
|
Frontend development accumulates invisible debt: ad-hoc color variables, mismatched font pairings, prop-drilled components, untested accessibility, janky animations, React anti-patterns, and slow page loads — all before you even decide what the product should *look* like. This pack addresses all layers systematically. Ten skills cover the full UI lifecycle: React codebase health scoring, Core Web Vitals performance auditing, token consistency, color palette selection, typography pairing, component composability, landing page structure, design-domain mapping, WCAG compliance, and motion polish. Run any skill independently or chain all ten as a comprehensive UI health check + design foundation generator.
|
|
@@ -23,6 +23,8 @@ Emit a `design-system.md` file in the project root (or `.rune/`) with: color tok
|
|
|
23
23
|
|
|
24
24
|
#### Domain → Style Matrix
|
|
25
25
|
|
|
26
|
+
The matrix below provides default mappings. When `references/ui-pro-max-data/styles.csv` is available, query it for **84 additional styles** with industry-specific parameters — filter by domain column for expanded recommendations beyond these 10 defaults.
|
|
27
|
+
|
|
26
28
|
```
|
|
27
29
|
Domain Style Palette Typography Component Aesthetic
|
|
28
30
|
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
@@ -40,6 +42,14 @@ DevTools Terminal + Crisp terminal-dark JetBrains Mono+Inter C
|
|
|
40
42
|
* Palette not shown in palette-picker example block — generate with same CSS custom props pattern.
|
|
41
43
|
```
|
|
42
44
|
|
|
45
|
+
#### Extended Data (UI/UX Pro Max)
|
|
46
|
+
|
|
47
|
+
When `references/ui-pro-max-data/` exists:
|
|
48
|
+
- `styles.csv` — 84 styles with color params, animation, WCAG levels, mobile flags
|
|
49
|
+
- `typography.csv` — 73 font pairings with Google Fonts URLs, Tailwind config, mood keywords
|
|
50
|
+
- `ui-reasoning.csv` — 161 industry-specific reasoning rules (filter by domain)
|
|
51
|
+
- Query: filter CSV by domain/category column → get expanded recommendations
|
|
52
|
+
|
|
43
53
|
#### Style Characteristic Reference
|
|
44
54
|
|
|
45
55
|
```
|
|
@@ -26,6 +26,8 @@ Run contrast checks: primary text on background (≥ 4.5:1), large headings (≥
|
|
|
26
26
|
|
|
27
27
|
#### Decision Tree
|
|
28
28
|
|
|
29
|
+
The tree below provides 10 default palettes. When `references/ui-pro-max-data/colors.csv` is available, query it for **161 industry-specific palettes** with full dark/light variants, semantic tokens, and design psychology notes. Filter by domain column for expanded options.
|
|
30
|
+
|
|
29
31
|
```
|
|
30
32
|
Product Type → Palette Recommendation
|
|
31
33
|
─────────────────────────────────────────────────
|
|
@@ -41,6 +43,15 @@ productivity / tools → Calm Focus (gray-50 + indigo-700, minimal noise)
|
|
|
41
43
|
developer tools → Terminal Dark (zinc-950 + emerald-400 mono)
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
#### Extended Palette DB (UI/UX Pro Max)
|
|
47
|
+
|
|
48
|
+
When `references/ui-pro-max-data/colors.csv` exists:
|
|
49
|
+
- 161 palettes with Primary, Secondary, Accent, Background, Foreground (dark+light)
|
|
50
|
+
- Semantic tokens: Card, Muted, Border, Destructive, Ring variants
|
|
51
|
+
- Design psychology notes per palette
|
|
52
|
+
- Query: `grep -i "<domain>" references/ui-pro-max-data/colors.csv` → get domain-matched palettes
|
|
53
|
+
- Anti-AI check: if selected palette uses #6366f1 (indigo) or #8b5cf6 (violet) as primary → flag and suggest alternatives from DB
|
|
54
|
+
|
|
44
55
|
#### Palette Reference
|
|
45
56
|
|
|
46
57
|
```css
|
package/hooks/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Rune Auto-Format Hook
|
|
2
|
+
// PostToolUse hook on Edit/Write — runs Prettier on JS/TS files after modification
|
|
3
|
+
//
|
|
4
|
+
// Only runs if Prettier is available in the project.
|
|
5
|
+
// Silent pass-through if not applicable.
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const input = JSON.parse(process.env.CLAUDE_TOOL_INPUT || '{}');
|
|
11
|
+
const filePath = input.file_path || input.filePath || '';
|
|
12
|
+
|
|
13
|
+
// Only format JS/TS/JSON/CSS files
|
|
14
|
+
if (!/\.(js|jsx|ts|tsx|json|css|scss|md|html|yaml|yml)$/i.test(filePath)) {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check if file exists and is within a project with Prettier
|
|
19
|
+
const dir = path.dirname(filePath);
|
|
20
|
+
|
|
21
|
+
// Try to find prettier in the project
|
|
22
|
+
let hasPrettier = false;
|
|
23
|
+
try {
|
|
24
|
+
execSync('npx prettier --version', { cwd: dir, encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
|
|
25
|
+
hasPrettier = true;
|
|
26
|
+
} catch {
|
|
27
|
+
// No Prettier available — skip silently
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!hasPrettier) {
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Run Prettier on the file
|
|
35
|
+
try {
|
|
36
|
+
execSync(`npx prettier --write "${filePath}"`, {
|
|
37
|
+
cwd: dir,
|
|
38
|
+
encoding: 'utf-8',
|
|
39
|
+
stdio: 'pipe',
|
|
40
|
+
timeout: 10000,
|
|
41
|
+
});
|
|
42
|
+
// Silent success — formatted files are seamless
|
|
43
|
+
} catch (e) {
|
|
44
|
+
// Prettier failed — non-critical, just warn
|
|
45
|
+
console.log(`[Rune auto-format] Prettier failed on ${path.basename(filePath)}: ${e.message.split('\n')[0]}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
process.exit(0);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Rune Context Watch Hook
|
|
2
|
+
// Lightweight tool call counter — detects context pressure and suggests rune:context-engine
|
|
3
|
+
// Runs as PreToolUse hook on Edit/Write (high-cost operations)
|
|
4
|
+
//
|
|
5
|
+
// H3 Intelligence: also tracks tool type distribution and session start timestamp
|
|
6
|
+
// for metrics aggregation at session end.
|
|
7
|
+
//
|
|
8
|
+
// Uses a temp file counter (survives across hook invocations within same session).
|
|
9
|
+
// Zero overhead: just reads/increments a number. No token cost.
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
|
|
15
|
+
// Counter file scoped to current working directory (hash of cwd)
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
const hash = Buffer.from(cwd).toString('base64url').slice(0, 16);
|
|
18
|
+
const counterFile = path.join(os.tmpdir(), `rune-context-watch-${hash}.json`);
|
|
19
|
+
|
|
20
|
+
// Thresholds
|
|
21
|
+
const FIRST_WARNING = 40;
|
|
22
|
+
const REPEAT_INTERVAL = 20;
|
|
23
|
+
const CRITICAL_THRESHOLD = 80;
|
|
24
|
+
|
|
25
|
+
// Detect tool type from environment
|
|
26
|
+
const toolName = process.env.CLAUDE_TOOL_NAME || 'unknown';
|
|
27
|
+
|
|
28
|
+
// Read current state
|
|
29
|
+
let state = { count: 0, lastWarning: 0, sessionStart: null, toolCounts: {} };
|
|
30
|
+
try {
|
|
31
|
+
const raw = fs.readFileSync(counterFile, 'utf-8');
|
|
32
|
+
state = JSON.parse(raw);
|
|
33
|
+
// Ensure toolCounts exists (upgrade from old format)
|
|
34
|
+
if (!state.toolCounts) state.toolCounts = {};
|
|
35
|
+
if (!state.sessionStart) state.sessionStart = new Date().toISOString();
|
|
36
|
+
} catch {
|
|
37
|
+
// First run or corrupted — start fresh
|
|
38
|
+
state.sessionStart = new Date().toISOString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Increment total and per-tool counters
|
|
42
|
+
state.count += 1;
|
|
43
|
+
state.toolCounts[toolName] = (state.toolCounts[toolName] || 0) + 1;
|
|
44
|
+
|
|
45
|
+
// Check thresholds
|
|
46
|
+
const count = state.count;
|
|
47
|
+
const sinceLast = count - state.lastWarning;
|
|
48
|
+
|
|
49
|
+
if (count >= CRITICAL_THRESHOLD && sinceLast >= REPEAT_INTERVAL) {
|
|
50
|
+
console.log(`\n🔴 [Rune context-watch] ${count} tool calls — context likely RED (>85%).`);
|
|
51
|
+
console.log(' RECOMMENDED: Invoke rune:context-engine for state save + /compact.');
|
|
52
|
+
console.log(' Risk: auto-compaction may lose critical decisions without state save.\n');
|
|
53
|
+
state.lastWarning = count;
|
|
54
|
+
} else if (count >= FIRST_WARNING && sinceLast >= REPEAT_INTERVAL) {
|
|
55
|
+
console.log(`\n🟡 [Rune context-watch] ${count} tool calls — context filling up.`);
|
|
56
|
+
console.log(' Consider invoking rune:context-engine at the next logical boundary.');
|
|
57
|
+
console.log(' Or run /compact manually if at a good stopping point.\n');
|
|
58
|
+
state.lastWarning = count;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Persist
|
|
62
|
+
try {
|
|
63
|
+
fs.writeFileSync(counterFile, JSON.stringify(state));
|
|
64
|
+
} catch {
|
|
65
|
+
// Non-critical — counter resets next run
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
process.exit(0);
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup|resume|clear|compact",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" session-start",
|
|
10
|
+
"async": false
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PreToolUse": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Read|Write|Edit",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" pre-tool-guard",
|
|
22
|
+
"async": false
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"matcher": "Edit|Write",
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" context-watch",
|
|
32
|
+
"async": true
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"matcher": "Bash",
|
|
38
|
+
"hooks": [
|
|
39
|
+
{
|
|
40
|
+
"type": "command",
|
|
41
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" secrets-scan",
|
|
42
|
+
"async": false
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"PostToolUse": [
|
|
48
|
+
{
|
|
49
|
+
"matcher": "Edit|Write",
|
|
50
|
+
"hooks": [
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" auto-format",
|
|
54
|
+
"async": true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "command",
|
|
58
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" typecheck",
|
|
59
|
+
"async": true
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"matcher": "Skill",
|
|
65
|
+
"hooks": [
|
|
66
|
+
{
|
|
67
|
+
"type": "command",
|
|
68
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" metrics-collector",
|
|
69
|
+
"async": true
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"PreCompact": [
|
|
75
|
+
{
|
|
76
|
+
"matcher": ".*",
|
|
77
|
+
"hooks": [
|
|
78
|
+
{
|
|
79
|
+
"type": "command",
|
|
80
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" pre-compact",
|
|
81
|
+
"async": false
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"Stop": [
|
|
87
|
+
{
|
|
88
|
+
"matcher": ".*",
|
|
89
|
+
"hooks": [
|
|
90
|
+
{
|
|
91
|
+
"type": "command",
|
|
92
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" post-session-reflect",
|
|
93
|
+
"async": false
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Rune Metrics Collector Hook
|
|
2
|
+
// PostToolUse on Skill — captures skill invocations for H3 mesh analytics
|
|
3
|
+
// Append-only JSONL to tmpdir. Flushed to .rune/metrics/ at session end.
|
|
4
|
+
// Async: true — never blocks skill execution.
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const hash = Buffer.from(cwd).toString('base64url').slice(0, 16);
|
|
12
|
+
const metricsFile = path.join(os.tmpdir(), `rune-metrics-${hash}.jsonl`);
|
|
13
|
+
|
|
14
|
+
// Extract skill name from tool input
|
|
15
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
16
|
+
let skillName = 'unknown';
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(toolInput);
|
|
19
|
+
// Skill tool input has { skill: "rune:cook" } or { skill: "cook" }
|
|
20
|
+
const raw = parsed.skill || parsed.name || '';
|
|
21
|
+
skillName = raw.replace(/^rune:/, '');
|
|
22
|
+
} catch {
|
|
23
|
+
// If not JSON, try raw string match
|
|
24
|
+
const match = toolInput.match(/(?:rune:)?([a-z][\w-]*)/i);
|
|
25
|
+
if (match) skillName = match[1];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (skillName && skillName !== 'unknown') {
|
|
29
|
+
const entry = JSON.stringify({
|
|
30
|
+
ts: new Date().toISOString(),
|
|
31
|
+
skill: skillName,
|
|
32
|
+
event: 'invoke'
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
fs.appendFileSync(metricsFile, entry + '\n');
|
|
37
|
+
} catch {
|
|
38
|
+
// Non-critical — metrics are best-effort
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
process.exit(0);
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Rune Post-Session Reflect Hook
|
|
2
|
+
// 1. Flushes session metrics from tmpdir to .rune/metrics/ (H3 Intelligence)
|
|
3
|
+
// 2. Displays structured self-review checklist at session end (Stop event)
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
const hash = Buffer.from(cwd).toString('base64url').slice(0, 16);
|
|
11
|
+
|
|
12
|
+
// === H3: Flush Session Metrics ===
|
|
13
|
+
|
|
14
|
+
const metricsJsonl = path.join(os.tmpdir(), `rune-metrics-${hash}.jsonl`);
|
|
15
|
+
const counterFile = path.join(os.tmpdir(), `rune-context-watch-${hash}.json`);
|
|
16
|
+
const runeMetricsDir = path.join(cwd, '.rune', 'metrics');
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
flushMetrics();
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// Metrics flush is best-effort — never block session end
|
|
22
|
+
// Silently ignore errors
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function flushMetrics() {
|
|
26
|
+
// Read session skill invocations from tmpdir JSONL
|
|
27
|
+
let skillEvents = [];
|
|
28
|
+
if (fs.existsSync(metricsJsonl)) {
|
|
29
|
+
const lines = fs.readFileSync(metricsJsonl, 'utf-8').trim().split('\n').filter(Boolean);
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
try { skillEvents.push(JSON.parse(line)); } catch { /* skip malformed */ }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Read context-watch state for tool counts and session timing
|
|
36
|
+
let watchState = { count: 0, sessionStart: null, toolCounts: {} };
|
|
37
|
+
if (fs.existsSync(counterFile)) {
|
|
38
|
+
try {
|
|
39
|
+
watchState = JSON.parse(fs.readFileSync(counterFile, 'utf-8'));
|
|
40
|
+
} catch { /* use defaults */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Nothing to flush if no data
|
|
44
|
+
if (skillEvents.length === 0 && watchState.count === 0) return;
|
|
45
|
+
|
|
46
|
+
// Ensure .rune/metrics/ exists
|
|
47
|
+
fs.mkdirSync(runeMetricsDir, { recursive: true });
|
|
48
|
+
|
|
49
|
+
const now = new Date().toISOString();
|
|
50
|
+
const sessionStart = watchState.sessionStart || now;
|
|
51
|
+
const durationMin = Math.round((new Date(now) - new Date(sessionStart)) / 60000);
|
|
52
|
+
|
|
53
|
+
// Build skill usage map
|
|
54
|
+
const skillCounts = {};
|
|
55
|
+
const skillChain = [];
|
|
56
|
+
for (const evt of skillEvents) {
|
|
57
|
+
skillCounts[evt.skill] = (skillCounts[evt.skill] || 0) + 1;
|
|
58
|
+
skillChain.push(evt.skill);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Determine primary skill (most invoked)
|
|
62
|
+
const primarySkill = Object.entries(skillCounts)
|
|
63
|
+
.sort((a, b) => b[1] - a[1])[0]?.[0] || 'none';
|
|
64
|
+
|
|
65
|
+
// Generate session ID
|
|
66
|
+
const sessionId = `s-${now.slice(0, 10).replace(/-/g, '')}-${now.slice(11, 19).replace(/:/g, '')}`;
|
|
67
|
+
|
|
68
|
+
// 1. Append to sessions.jsonl
|
|
69
|
+
const sessionEntry = {
|
|
70
|
+
id: sessionId,
|
|
71
|
+
date: now.slice(0, 10),
|
|
72
|
+
duration_min: durationMin,
|
|
73
|
+
tool_calls: watchState.count,
|
|
74
|
+
tool_distribution: watchState.toolCounts,
|
|
75
|
+
skill_invocations: skillEvents.length,
|
|
76
|
+
skills_used: Object.keys(skillCounts),
|
|
77
|
+
primary_skill: primarySkill
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const sessionsFile = path.join(runeMetricsDir, 'sessions.jsonl');
|
|
81
|
+
fs.appendFileSync(sessionsFile, JSON.stringify(sessionEntry) + '\n');
|
|
82
|
+
|
|
83
|
+
// Cap at 100 sessions (rotate oldest)
|
|
84
|
+
try {
|
|
85
|
+
const allLines = fs.readFileSync(sessionsFile, 'utf-8').trim().split('\n').filter(Boolean);
|
|
86
|
+
if (allLines.length > 100) {
|
|
87
|
+
fs.writeFileSync(sessionsFile, allLines.slice(-100).join('\n') + '\n');
|
|
88
|
+
}
|
|
89
|
+
} catch { /* cap is best-effort */ }
|
|
90
|
+
|
|
91
|
+
// 2. Merge into skills.json (running totals)
|
|
92
|
+
const skillsFile = path.join(runeMetricsDir, 'skills.json');
|
|
93
|
+
let skillsData = { version: 1, updated: now, skills: {} };
|
|
94
|
+
if (fs.existsSync(skillsFile)) {
|
|
95
|
+
try {
|
|
96
|
+
skillsData = JSON.parse(fs.readFileSync(skillsFile, 'utf-8'));
|
|
97
|
+
} catch { /* start fresh */ }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const [skill, count] of Object.entries(skillCounts)) {
|
|
101
|
+
if (!skillsData.skills[skill]) {
|
|
102
|
+
skillsData.skills[skill] = { total_invocations: 0, last_used: now.slice(0, 10) };
|
|
103
|
+
}
|
|
104
|
+
skillsData.skills[skill].total_invocations += count;
|
|
105
|
+
skillsData.skills[skill].last_used = now.slice(0, 10);
|
|
106
|
+
}
|
|
107
|
+
skillsData.updated = now;
|
|
108
|
+
|
|
109
|
+
fs.writeFileSync(skillsFile, JSON.stringify(skillsData, null, 2) + '\n');
|
|
110
|
+
|
|
111
|
+
// 3. Append to chains.jsonl
|
|
112
|
+
if (skillChain.length > 0) {
|
|
113
|
+
const chainsFile = path.join(runeMetricsDir, 'chains.jsonl');
|
|
114
|
+
const chainEntry = {
|
|
115
|
+
session: sessionId,
|
|
116
|
+
chain: skillChain,
|
|
117
|
+
depth: skillChain.length
|
|
118
|
+
};
|
|
119
|
+
fs.appendFileSync(chainsFile, JSON.stringify(chainEntry) + '\n');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 4. Cleanup tmpdir files
|
|
123
|
+
try { fs.unlinkSync(metricsJsonl); } catch { /* already gone */ }
|
|
124
|
+
// Note: counterFile is cleaned by session-start hook on next session
|
|
125
|
+
|
|
126
|
+
// Report metrics flush
|
|
127
|
+
const skillList = Object.entries(skillCounts)
|
|
128
|
+
.sort((a, b) => b[1] - a[1])
|
|
129
|
+
.map(([s, c]) => `${s}(${c})`)
|
|
130
|
+
.join(', ');
|
|
131
|
+
|
|
132
|
+
console.log(`\n📊 [Rune metrics] Session ${sessionId} — ${durationMin}min, ${watchState.count} tool calls, ${skillEvents.length} skill invocations`);
|
|
133
|
+
if (skillList) console.log(` Skills: ${skillList}`);
|
|
134
|
+
console.log(` Saved to .rune/metrics/\n`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// === Original: Verification Checklist ===
|
|
138
|
+
|
|
139
|
+
console.log(`
|
|
140
|
+
┌─────────────────────────────────────────────────────┐
|
|
141
|
+
│ Rune Session End — Verification Checklist │
|
|
142
|
+
├─────────────────────────────────────────────────────┤
|
|
143
|
+
│ Before closing this session, confirm: │
|
|
144
|
+
│ │
|
|
145
|
+
│ □ All TodoWrite tasks marked complete? │
|
|
146
|
+
│ □ Tests ran and passing? │
|
|
147
|
+
│ □ No hardcoded secrets introduced? │
|
|
148
|
+
│ □ If schema changed: migration + rollback exist? │
|
|
149
|
+
│ □ Verification ran (lint + types + build)? │
|
|
150
|
+
│ │
|
|
151
|
+
│ If any item is unclear → address it now. │
|
|
152
|
+
└─────────────────────────────────────────────────────┘
|
|
153
|
+
`);
|