@rune-kit/rune 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +8 -6
  3. package/commands/rune.md +168 -168
  4. package/contexts/dev.md +34 -34
  5. package/contexts/research.md +43 -43
  6. package/contexts/review.md +55 -55
  7. package/extensions/ai-ml/PACK.md +88 -88
  8. package/extensions/ai-ml/skills/ai-agents.md +172 -172
  9. package/extensions/ai-ml/skills/code-sandbox.md +187 -187
  10. package/extensions/ai-ml/skills/deep-research.md +146 -146
  11. package/extensions/ai-ml/skills/embedding-search.md +66 -66
  12. package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -74
  13. package/extensions/ai-ml/skills/llm-architect.md +125 -125
  14. package/extensions/ai-ml/skills/llm-integration.md +64 -64
  15. package/extensions/ai-ml/skills/prompt-patterns.md +72 -72
  16. package/extensions/ai-ml/skills/rag-patterns.md +66 -66
  17. package/extensions/ai-ml/skills/web-extraction.md +114 -114
  18. package/extensions/analytics/PACK.md +92 -92
  19. package/extensions/analytics/skills/ab-testing.md +72 -72
  20. package/extensions/analytics/skills/dashboard-patterns.md +83 -83
  21. package/extensions/analytics/skills/data-validation.md +68 -68
  22. package/extensions/analytics/skills/funnel-analysis.md +81 -81
  23. package/extensions/analytics/skills/sql-patterns.md +57 -57
  24. package/extensions/analytics/skills/statistical-analysis.md +79 -79
  25. package/extensions/analytics/skills/tracking-setup.md +71 -71
  26. package/extensions/backend/PACK.md +104 -104
  27. package/extensions/backend/skills/api-patterns.md +84 -84
  28. package/extensions/backend/skills/async-pipeline.md +193 -193
  29. package/extensions/backend/skills/auth-patterns.md +97 -97
  30. package/extensions/backend/skills/background-jobs.md +133 -133
  31. package/extensions/backend/skills/caching-patterns.md +108 -108
  32. package/extensions/backend/skills/cli-generation.md +133 -133
  33. package/extensions/backend/skills/database-patterns.md +87 -87
  34. package/extensions/backend/skills/middleware-patterns.md +104 -104
  35. package/extensions/chrome-ext/PACK.md +93 -93
  36. package/extensions/chrome-ext/skills/cws-preflight.md +143 -143
  37. package/extensions/chrome-ext/skills/cws-publish.md +104 -104
  38. package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -251
  39. package/extensions/chrome-ext/skills/ext-messaging.md +139 -139
  40. package/extensions/chrome-ext/skills/ext-storage.md +133 -133
  41. package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -164
  42. package/extensions/content/PACK.md +96 -96
  43. package/extensions/content/skills/blog-patterns.md +88 -88
  44. package/extensions/content/skills/cms-integration.md +131 -131
  45. package/extensions/content/skills/content-scoring.md +107 -107
  46. package/extensions/content/skills/i18n.md +83 -83
  47. package/extensions/content/skills/mdx-authoring.md +137 -137
  48. package/extensions/content/skills/reference.md +1014 -1014
  49. package/extensions/content/skills/seo-patterns.md +67 -67
  50. package/extensions/content/skills/video-repurpose.md +153 -153
  51. package/extensions/devops/PACK.md +101 -101
  52. package/extensions/devops/skills/chaos-testing.md +67 -67
  53. package/extensions/devops/skills/ci-cd.md +75 -75
  54. package/extensions/devops/skills/docker.md +58 -58
  55. package/extensions/devops/skills/edge-serverless.md +163 -163
  56. package/extensions/devops/skills/infra-as-code.md +158 -158
  57. package/extensions/devops/skills/kubernetes.md +110 -110
  58. package/extensions/devops/skills/monitoring.md +57 -57
  59. package/extensions/devops/skills/server-setup.md +64 -64
  60. package/extensions/devops/skills/ssl-domain.md +42 -42
  61. package/extensions/ecommerce/PACK.md +116 -116
  62. package/extensions/ecommerce/skills/cart-system.md +79 -79
  63. package/extensions/ecommerce/skills/inventory-mgmt.md +102 -102
  64. package/extensions/ecommerce/skills/order-management.md +126 -126
  65. package/extensions/ecommerce/skills/payment-integration.md +472 -472
  66. package/extensions/ecommerce/skills/shopify-dev.md +69 -69
  67. package/extensions/ecommerce/skills/subscription-billing.md +93 -93
  68. package/extensions/ecommerce/skills/tax-compliance.md +117 -117
  69. package/extensions/gamedev/PACK.md +142 -142
  70. package/extensions/gamedev/skills/asset-pipeline.md +74 -74
  71. package/extensions/gamedev/skills/audio-system.md +129 -129
  72. package/extensions/gamedev/skills/camera-system.md +87 -87
  73. package/extensions/gamedev/skills/ecs.md +98 -98
  74. package/extensions/gamedev/skills/game-loops.md +72 -72
  75. package/extensions/gamedev/skills/input-system.md +199 -199
  76. package/extensions/gamedev/skills/multiplayer.md +180 -180
  77. package/extensions/gamedev/skills/particles.md +105 -105
  78. package/extensions/gamedev/skills/physics-engine.md +89 -89
  79. package/extensions/gamedev/skills/scene-management.md +146 -146
  80. package/extensions/gamedev/skills/threejs-patterns.md +90 -90
  81. package/extensions/gamedev/skills/webgl.md +71 -71
  82. package/extensions/mobile/PACK.md +106 -106
  83. package/extensions/mobile/skills/app-store-connect.md +152 -152
  84. package/extensions/mobile/skills/app-store-prep.md +66 -66
  85. package/extensions/mobile/skills/deep-linking.md +109 -109
  86. package/extensions/mobile/skills/flutter.md +60 -60
  87. package/extensions/mobile/skills/ios-build-pipeline.md +142 -142
  88. package/extensions/mobile/skills/native-bridge.md +66 -66
  89. package/extensions/mobile/skills/ota-updates.md +97 -97
  90. package/extensions/mobile/skills/push-notifications.md +111 -111
  91. package/extensions/mobile/skills/react-native.md +82 -82
  92. package/extensions/saas/PACK.md +116 -116
  93. package/extensions/saas/skills/billing-integration.md +200 -200
  94. package/extensions/saas/skills/feature-flags.md +130 -130
  95. package/extensions/saas/skills/multi-tenant.md +103 -103
  96. package/extensions/saas/skills/onboarding-flow.md +139 -139
  97. package/extensions/saas/skills/subscription-flow.md +95 -95
  98. package/extensions/saas/skills/team-management.md +144 -144
  99. package/extensions/security/PACK.md +99 -99
  100. package/extensions/security/skills/api-security.md +140 -140
  101. package/extensions/security/skills/compliance.md +68 -68
  102. package/extensions/security/skills/owasp-audit.md +64 -64
  103. package/extensions/security/skills/pentest-patterns.md +77 -77
  104. package/extensions/security/skills/secret-mgmt.md +65 -65
  105. package/extensions/security/skills/supply-chain.md +65 -65
  106. package/extensions/trading/PACK.md +80 -80
  107. package/extensions/trading/skills/chart-components.md +55 -55
  108. package/extensions/trading/skills/experiment-loop.md +125 -125
  109. package/extensions/trading/skills/fintech-patterns.md +47 -47
  110. package/extensions/trading/skills/indicator-library.md +58 -58
  111. package/extensions/trading/skills/quant-analysis.md +111 -111
  112. package/extensions/trading/skills/realtime-data.md +58 -58
  113. package/extensions/trading/skills/trade-logic.md +104 -104
  114. package/extensions/ui/PACK.md +130 -130
  115. package/extensions/ui/skills/a11y-audit.md +91 -91
  116. package/extensions/ui/skills/animation-patterns.md +127 -127
  117. package/extensions/ui/skills/component-patterns.md +100 -100
  118. package/extensions/ui/skills/design-decision.md +108 -108
  119. package/extensions/ui/skills/design-system.md +68 -68
  120. package/extensions/ui/skills/landing-patterns.md +155 -155
  121. package/extensions/ui/skills/palette-picker.md +173 -173
  122. package/extensions/ui/skills/react-health.md +90 -90
  123. package/extensions/ui/skills/type-system.md +125 -125
  124. package/extensions/ui/skills/web-vitals.md +153 -153
  125. package/extensions/zalo/PACK.md +145 -145
  126. package/extensions/zalo/skills/zalo-oa-mcp.md +317 -317
  127. package/extensions/zalo/skills/zalo-oa-messaging.md +429 -429
  128. package/extensions/zalo/skills/zalo-oa-setup.md +236 -236
  129. package/extensions/zalo/skills/zalo-oa-webhook.md +189 -189
  130. package/extensions/zalo/skills/zalo-personal-messaging.md +194 -194
  131. package/extensions/zalo/skills/zalo-personal-setup.md +153 -153
  132. package/extensions/zalo/skills/zalo-rate-guard.md +219 -219
  133. package/hooks/auto-format/index.cjs +48 -48
  134. package/hooks/hooks.json +111 -111
  135. package/hooks/post-session-reflect/index.cjs +189 -189
  136. package/hooks/pre-compact/index.cjs +95 -95
  137. package/hooks/run-hook.cmd +1 -1
  138. package/hooks/secrets-scan/index.cjs +100 -100
  139. package/hooks/session-start/index.cjs +71 -71
  140. package/hooks/typecheck/index.cjs +65 -65
  141. package/package.json +63 -63
  142. package/references/ui-pro-max-data/LICENSE-UI-PRO-MAX +21 -21
  143. package/references/ui-pro-max-data/charts.csv +26 -26
  144. package/references/ui-pro-max-data/colors.csv +161 -161
  145. package/references/ui-pro-max-data/styles.csv +68 -68
  146. package/references/ui-pro-max-data/typography.csv +74 -74
  147. package/references/ui-pro-max-data/ui-reasoning.csv +162 -162
  148. package/references/ui-pro-max-data/ux-guidelines.csv +99 -99
  149. package/skills/adversary/SKILL.md +283 -283
  150. package/skills/asset-creator/SKILL.md +157 -157
  151. package/skills/audit/SKILL.md +147 -2
  152. package/skills/autopsy/SKILL.md +335 -335
  153. package/skills/brainstorm/SKILL.md +342 -342
  154. package/skills/browser-pilot/SKILL.md +168 -168
  155. package/skills/constraint-check/SKILL.md +165 -165
  156. package/skills/context-engine/SKILL.md +404 -404
  157. package/skills/cook/SKILL.md +917 -863
  158. package/skills/db/SKILL.md +273 -273
  159. package/skills/debug/SKILL.md +465 -465
  160. package/skills/dependency-doctor/SKILL.md +265 -235
  161. package/skills/deploy/SKILL.md +274 -231
  162. package/skills/design/DESIGN-REFERENCE.md +365 -365
  163. package/skills/design/SKILL.md +589 -589
  164. package/skills/doc-processor/SKILL.md +254 -254
  165. package/skills/docs/SKILL.md +374 -374
  166. package/skills/docs-seeker/SKILL.md +177 -177
  167. package/skills/fix/SKILL.md +330 -330
  168. package/skills/git/SKILL.md +339 -339
  169. package/skills/hallucination-guard/SKILL.md +219 -219
  170. package/skills/incident/SKILL.md +254 -253
  171. package/skills/integrity-check/SKILL.md +169 -169
  172. package/skills/journal/SKILL.md +240 -240
  173. package/skills/launch/SKILL.md +344 -344
  174. package/skills/logic-guardian/SKILL.md +251 -251
  175. package/skills/marketing/SKILL.md +290 -289
  176. package/skills/mcp-builder/SKILL.md +425 -425
  177. package/skills/neural-memory/SKILL.md +362 -362
  178. package/skills/onboard/SKILL.md +404 -403
  179. package/skills/perf/SKILL.md +346 -346
  180. package/skills/plan/SKILL.md +433 -428
  181. package/skills/preflight/SKILL.md +415 -415
  182. package/skills/problem-solver/SKILL.md +380 -284
  183. package/skills/rescue/SKILL.md +474 -474
  184. package/skills/retro/SKILL.md +3 -1
  185. package/skills/review/SKILL.md +612 -588
  186. package/skills/review-intake/SKILL.md +249 -249
  187. package/skills/safeguard/SKILL.md +200 -200
  188. package/skills/sast/SKILL.md +190 -190
  189. package/skills/scaffold/SKILL.md +328 -287
  190. package/skills/scope-guard/SKILL.md +180 -180
  191. package/skills/scout/SKILL.md +263 -263
  192. package/skills/sentinel/SKILL.md +382 -381
  193. package/skills/sentinel-env/SKILL.md +254 -254
  194. package/skills/sequential-thinking/SKILL.md +234 -234
  195. package/skills/session-bridge/SKILL.md +543 -543
  196. package/skills/skill-forge/SKILL.md +581 -581
  197. package/skills/skill-router/SKILL.md +3 -0
  198. package/skills/surgeon/SKILL.md +215 -215
  199. package/skills/team/SKILL.md +556 -537
  200. package/skills/test/SKILL.md +614 -614
  201. package/skills/trend-scout/SKILL.md +145 -145
  202. package/skills/verification/SKILL.md +326 -326
  203. package/skills/video-creator/SKILL.md +201 -201
  204. package/skills/watchdog/SKILL.md +168 -168
  205. package/skills/worktree/SKILL.md +140 -140
package/hooks/hooks.json CHANGED
@@ -1,111 +1,111 @@
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
- "UserPromptSubmit": [
16
- {
17
- "matcher": ".*",
18
- "hooks": [
19
- {
20
- "type": "command",
21
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" intent-router",
22
- "async": true
23
- }
24
- ]
25
- }
26
- ],
27
- "PreToolUse": [
28
- {
29
- "matcher": "Read|Write|Edit",
30
- "hooks": [
31
- {
32
- "type": "command",
33
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" pre-tool-guard",
34
- "async": false
35
- }
36
- ]
37
- },
38
- {
39
- "matcher": "Edit|Write",
40
- "hooks": [
41
- {
42
- "type": "command",
43
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" context-watch",
44
- "async": true
45
- }
46
- ]
47
- },
48
- {
49
- "matcher": "Bash",
50
- "hooks": [
51
- {
52
- "type": "command",
53
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" secrets-scan",
54
- "async": false
55
- }
56
- ]
57
- }
58
- ],
59
- "PostToolUse": [
60
- {
61
- "matcher": "Edit|Write",
62
- "hooks": [
63
- {
64
- "type": "command",
65
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" auto-format",
66
- "async": true
67
- },
68
- {
69
- "type": "command",
70
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" typecheck",
71
- "async": true
72
- }
73
- ]
74
- },
75
- {
76
- "matcher": "Skill",
77
- "hooks": [
78
- {
79
- "type": "command",
80
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" metrics-collector",
81
- "async": true
82
- }
83
- ]
84
- }
85
- ],
86
- "PreCompact": [
87
- {
88
- "matcher": ".*",
89
- "hooks": [
90
- {
91
- "type": "command",
92
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" pre-compact",
93
- "async": false
94
- }
95
- ]
96
- }
97
- ],
98
- "Stop": [
99
- {
100
- "matcher": ".*",
101
- "hooks": [
102
- {
103
- "type": "command",
104
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" post-session-reflect",
105
- "async": false
106
- }
107
- ]
108
- }
109
- ]
110
- }
111
- }
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
+ "UserPromptSubmit": [
16
+ {
17
+ "matcher": ".*",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" intent-router",
22
+ "async": true
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "PreToolUse": [
28
+ {
29
+ "matcher": "Read|Write|Edit",
30
+ "hooks": [
31
+ {
32
+ "type": "command",
33
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" pre-tool-guard",
34
+ "async": false
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "matcher": "Edit|Write",
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" context-watch",
44
+ "async": true
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ "matcher": "Bash",
50
+ "hooks": [
51
+ {
52
+ "type": "command",
53
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" secrets-scan",
54
+ "async": false
55
+ }
56
+ ]
57
+ }
58
+ ],
59
+ "PostToolUse": [
60
+ {
61
+ "matcher": "Edit|Write",
62
+ "hooks": [
63
+ {
64
+ "type": "command",
65
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" auto-format",
66
+ "async": true
67
+ },
68
+ {
69
+ "type": "command",
70
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" typecheck",
71
+ "async": true
72
+ }
73
+ ]
74
+ },
75
+ {
76
+ "matcher": "Skill",
77
+ "hooks": [
78
+ {
79
+ "type": "command",
80
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" metrics-collector",
81
+ "async": true
82
+ }
83
+ ]
84
+ }
85
+ ],
86
+ "PreCompact": [
87
+ {
88
+ "matcher": ".*",
89
+ "hooks": [
90
+ {
91
+ "type": "command",
92
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" pre-compact",
93
+ "async": false
94
+ }
95
+ ]
96
+ }
97
+ ],
98
+ "Stop": [
99
+ {
100
+ "matcher": ".*",
101
+ "hooks": [
102
+ {
103
+ "type": "command",
104
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cjs\" post-session-reflect",
105
+ "async": false
106
+ }
107
+ ]
108
+ }
109
+ ]
110
+ }
111
+ }
@@ -1,189 +1,189 @@
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
- // Resolve skill names → expected model from agent frontmatter
19
- // Reads agents/*.md at flush time — no runtime model detection needed
20
- // Map skill names → models, weighted by invocation count
21
- // skillCounts: { skillName: invocationCount }
22
- function resolveSkillModels(skillCounts) {
23
- const models = {};
24
- const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.join(__dirname, '..', '..');
25
- const agentsDir = path.join(pluginRoot, 'agents');
26
-
27
- if (!fs.existsSync(agentsDir)) return models;
28
-
29
- for (const [skill, count] of Object.entries(skillCounts)) {
30
- try {
31
- const agentFile = path.join(agentsDir, `${skill}.md`);
32
- if (!fs.existsSync(agentFile)) continue;
33
- const content = fs.readFileSync(agentFile, 'utf-8');
34
- const match = content.match(/^model:\s*(\w+)/m);
35
- if (match) {
36
- const model = match[1];
37
- models[model] = (models[model] || 0) + count;
38
- }
39
- } catch { /* best-effort */ }
40
- }
41
- return models;
42
- }
43
-
44
- try {
45
- flushMetrics();
46
- } catch (e) {
47
- // Metrics flush is best-effort — never block session end
48
- // Silently ignore errors
49
- }
50
-
51
- function flushMetrics() {
52
- // Read session skill invocations from tmpdir JSONL
53
- let skillEvents = [];
54
- if (fs.existsSync(metricsJsonl)) {
55
- const lines = fs.readFileSync(metricsJsonl, 'utf-8').trim().split('\n').filter(Boolean);
56
- for (const line of lines) {
57
- try { skillEvents.push(JSON.parse(line)); } catch { /* skip malformed */ }
58
- }
59
- }
60
-
61
- // Read context-watch state for tool counts and session timing
62
- let watchState = { count: 0, sessionStart: null, sessionId: null, toolCounts: {} };
63
- if (fs.existsSync(counterFile)) {
64
- try {
65
- watchState = JSON.parse(fs.readFileSync(counterFile, 'utf-8'));
66
- } catch { /* use defaults */ }
67
- }
68
-
69
- // Nothing to flush if no data
70
- if (skillEvents.length === 0 && watchState.count === 0) return;
71
-
72
- // Ensure .rune/metrics/ exists
73
- fs.mkdirSync(runeMetricsDir, { recursive: true });
74
-
75
- const now = new Date().toISOString();
76
- const sessionStart = watchState.sessionStart || now;
77
- const durationMin = Math.round((new Date(now) - new Date(sessionStart)) / 60000);
78
-
79
- // Build skill usage map and duration aggregation
80
- const skillCounts = {};
81
- const skillDurations = {};
82
- const skillChain = [];
83
- for (const evt of skillEvents) {
84
- skillCounts[evt.skill] = (skillCounts[evt.skill] || 0) + 1;
85
- skillChain.push(evt.skill);
86
- if (evt.duration_ms != null) {
87
- skillDurations[evt.skill] = (skillDurations[evt.skill] || 0) + evt.duration_ms;
88
- }
89
- }
90
-
91
- // Determine primary skill (most invoked)
92
- const primarySkill = Object.entries(skillCounts)
93
- .sort((a, b) => b[1] - a[1])[0]?.[0] || 'none';
94
-
95
- // Map skills to expected models from agent definitions (weighted by invocation count)
96
- const modelsUsed = resolveSkillModels(skillCounts);
97
-
98
- // Use session ID from context-watch (shared) or generate new
99
- const sessionId = watchState.sessionId
100
- || `s-${now.slice(0, 10).replace(/-/g, '')}-${now.slice(11, 19).replace(/:/g, '')}`;
101
-
102
- // 1. Append to sessions.jsonl
103
- const sessionEntry = {
104
- id: sessionId,
105
- date: now.slice(0, 10),
106
- duration_min: durationMin,
107
- tool_calls: watchState.count,
108
- tool_distribution: watchState.toolCounts,
109
- skill_invocations: skillEvents.length,
110
- skills_used: Object.keys(skillCounts),
111
- primary_skill: primarySkill,
112
- models_used: modelsUsed,
113
- skill_durations: Object.keys(skillDurations).length > 0 ? skillDurations : undefined
114
- };
115
-
116
- const sessionsFile = path.join(runeMetricsDir, 'sessions.jsonl');
117
- fs.appendFileSync(sessionsFile, JSON.stringify(sessionEntry) + '\n');
118
-
119
- // Cap at 100 sessions (rotate oldest)
120
- try {
121
- const allLines = fs.readFileSync(sessionsFile, 'utf-8').trim().split('\n').filter(Boolean);
122
- if (allLines.length > 100) {
123
- fs.writeFileSync(sessionsFile, allLines.slice(-100).join('\n') + '\n');
124
- }
125
- } catch { /* cap is best-effort */ }
126
-
127
- // 2. Merge into skills.json (running totals)
128
- const skillsFile = path.join(runeMetricsDir, 'skills.json');
129
- let skillsData = { version: 1, updated: now, skills: {} };
130
- if (fs.existsSync(skillsFile)) {
131
- try {
132
- skillsData = JSON.parse(fs.readFileSync(skillsFile, 'utf-8'));
133
- } catch { /* start fresh */ }
134
- }
135
-
136
- for (const [skill, count] of Object.entries(skillCounts)) {
137
- if (!skillsData.skills[skill]) {
138
- skillsData.skills[skill] = { total_invocations: 0, last_used: now.slice(0, 10) };
139
- }
140
- skillsData.skills[skill].total_invocations += count;
141
- skillsData.skills[skill].last_used = now.slice(0, 10);
142
- }
143
- skillsData.updated = now;
144
-
145
- fs.writeFileSync(skillsFile, JSON.stringify(skillsData, null, 2) + '\n');
146
-
147
- // 3. Append to chains.jsonl
148
- if (skillChain.length > 0) {
149
- const chainsFile = path.join(runeMetricsDir, 'chains.jsonl');
150
- const chainEntry = {
151
- session: sessionId,
152
- chain: skillChain,
153
- depth: skillChain.length
154
- };
155
- fs.appendFileSync(chainsFile, JSON.stringify(chainEntry) + '\n');
156
- }
157
-
158
- // 4. Cleanup tmpdir files
159
- try { fs.unlinkSync(metricsJsonl); } catch { /* already gone */ }
160
- // Note: counterFile is cleaned by session-start hook on next session
161
-
162
- // Report metrics flush
163
- const skillList = Object.entries(skillCounts)
164
- .sort((a, b) => b[1] - a[1])
165
- .map(([s, c]) => `${s}(${c})`)
166
- .join(', ');
167
-
168
- console.log(`\nšŸ“Š [Rune metrics] Session ${sessionId} — ${durationMin}min, ${watchState.count} tool calls, ${skillEvents.length} skill invocations`);
169
- if (skillList) console.log(` Skills: ${skillList}`);
170
- console.log(` Saved to .rune/metrics/\n`);
171
- }
172
-
173
- // === Original: Verification Checklist ===
174
-
175
- console.log(`
176
- ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
177
- │ Rune Session End — Verification Checklist │
178
- ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
179
- │ Before closing this session, confirm: │
180
- │ │
181
- │ ā–” All TodoWrite tasks marked complete? │
182
- │ ā–” Tests ran and passing? │
183
- │ ā–” No hardcoded secrets introduced? │
184
- │ ā–” If schema changed: migration + rollback exist? │
185
- │ ā–” Verification ran (lint + types + build)? │
186
- │ │
187
- │ If any item is unclear → address it now. │
188
- ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
189
- `);
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
+ // Resolve skill names → expected model from agent frontmatter
19
+ // Reads agents/*.md at flush time — no runtime model detection needed
20
+ // Map skill names → models, weighted by invocation count
21
+ // skillCounts: { skillName: invocationCount }
22
+ function resolveSkillModels(skillCounts) {
23
+ const models = {};
24
+ const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.join(__dirname, '..', '..');
25
+ const agentsDir = path.join(pluginRoot, 'agents');
26
+
27
+ if (!fs.existsSync(agentsDir)) return models;
28
+
29
+ for (const [skill, count] of Object.entries(skillCounts)) {
30
+ try {
31
+ const agentFile = path.join(agentsDir, `${skill}.md`);
32
+ if (!fs.existsSync(agentFile)) continue;
33
+ const content = fs.readFileSync(agentFile, 'utf-8');
34
+ const match = content.match(/^model:\s*(\w+)/m);
35
+ if (match) {
36
+ const model = match[1];
37
+ models[model] = (models[model] || 0) + count;
38
+ }
39
+ } catch { /* best-effort */ }
40
+ }
41
+ return models;
42
+ }
43
+
44
+ try {
45
+ flushMetrics();
46
+ } catch (e) {
47
+ // Metrics flush is best-effort — never block session end
48
+ // Silently ignore errors
49
+ }
50
+
51
+ function flushMetrics() {
52
+ // Read session skill invocations from tmpdir JSONL
53
+ let skillEvents = [];
54
+ if (fs.existsSync(metricsJsonl)) {
55
+ const lines = fs.readFileSync(metricsJsonl, 'utf-8').trim().split('\n').filter(Boolean);
56
+ for (const line of lines) {
57
+ try { skillEvents.push(JSON.parse(line)); } catch { /* skip malformed */ }
58
+ }
59
+ }
60
+
61
+ // Read context-watch state for tool counts and session timing
62
+ let watchState = { count: 0, sessionStart: null, sessionId: null, toolCounts: {} };
63
+ if (fs.existsSync(counterFile)) {
64
+ try {
65
+ watchState = JSON.parse(fs.readFileSync(counterFile, 'utf-8'));
66
+ } catch { /* use defaults */ }
67
+ }
68
+
69
+ // Nothing to flush if no data
70
+ if (skillEvents.length === 0 && watchState.count === 0) return;
71
+
72
+ // Ensure .rune/metrics/ exists
73
+ fs.mkdirSync(runeMetricsDir, { recursive: true });
74
+
75
+ const now = new Date().toISOString();
76
+ const sessionStart = watchState.sessionStart || now;
77
+ const durationMin = Math.round((new Date(now) - new Date(sessionStart)) / 60000);
78
+
79
+ // Build skill usage map and duration aggregation
80
+ const skillCounts = {};
81
+ const skillDurations = {};
82
+ const skillChain = [];
83
+ for (const evt of skillEvents) {
84
+ skillCounts[evt.skill] = (skillCounts[evt.skill] || 0) + 1;
85
+ skillChain.push(evt.skill);
86
+ if (evt.duration_ms != null) {
87
+ skillDurations[evt.skill] = (skillDurations[evt.skill] || 0) + evt.duration_ms;
88
+ }
89
+ }
90
+
91
+ // Determine primary skill (most invoked)
92
+ const primarySkill = Object.entries(skillCounts)
93
+ .sort((a, b) => b[1] - a[1])[0]?.[0] || 'none';
94
+
95
+ // Map skills to expected models from agent definitions (weighted by invocation count)
96
+ const modelsUsed = resolveSkillModels(skillCounts);
97
+
98
+ // Use session ID from context-watch (shared) or generate new
99
+ const sessionId = watchState.sessionId
100
+ || `s-${now.slice(0, 10).replace(/-/g, '')}-${now.slice(11, 19).replace(/:/g, '')}`;
101
+
102
+ // 1. Append to sessions.jsonl
103
+ const sessionEntry = {
104
+ id: sessionId,
105
+ date: now.slice(0, 10),
106
+ duration_min: durationMin,
107
+ tool_calls: watchState.count,
108
+ tool_distribution: watchState.toolCounts,
109
+ skill_invocations: skillEvents.length,
110
+ skills_used: Object.keys(skillCounts),
111
+ primary_skill: primarySkill,
112
+ models_used: modelsUsed,
113
+ skill_durations: Object.keys(skillDurations).length > 0 ? skillDurations : undefined
114
+ };
115
+
116
+ const sessionsFile = path.join(runeMetricsDir, 'sessions.jsonl');
117
+ fs.appendFileSync(sessionsFile, JSON.stringify(sessionEntry) + '\n');
118
+
119
+ // Cap at 100 sessions (rotate oldest)
120
+ try {
121
+ const allLines = fs.readFileSync(sessionsFile, 'utf-8').trim().split('\n').filter(Boolean);
122
+ if (allLines.length > 100) {
123
+ fs.writeFileSync(sessionsFile, allLines.slice(-100).join('\n') + '\n');
124
+ }
125
+ } catch { /* cap is best-effort */ }
126
+
127
+ // 2. Merge into skills.json (running totals)
128
+ const skillsFile = path.join(runeMetricsDir, 'skills.json');
129
+ let skillsData = { version: 1, updated: now, skills: {} };
130
+ if (fs.existsSync(skillsFile)) {
131
+ try {
132
+ skillsData = JSON.parse(fs.readFileSync(skillsFile, 'utf-8'));
133
+ } catch { /* start fresh */ }
134
+ }
135
+
136
+ for (const [skill, count] of Object.entries(skillCounts)) {
137
+ if (!skillsData.skills[skill]) {
138
+ skillsData.skills[skill] = { total_invocations: 0, last_used: now.slice(0, 10) };
139
+ }
140
+ skillsData.skills[skill].total_invocations += count;
141
+ skillsData.skills[skill].last_used = now.slice(0, 10);
142
+ }
143
+ skillsData.updated = now;
144
+
145
+ fs.writeFileSync(skillsFile, JSON.stringify(skillsData, null, 2) + '\n');
146
+
147
+ // 3. Append to chains.jsonl
148
+ if (skillChain.length > 0) {
149
+ const chainsFile = path.join(runeMetricsDir, 'chains.jsonl');
150
+ const chainEntry = {
151
+ session: sessionId,
152
+ chain: skillChain,
153
+ depth: skillChain.length
154
+ };
155
+ fs.appendFileSync(chainsFile, JSON.stringify(chainEntry) + '\n');
156
+ }
157
+
158
+ // 4. Cleanup tmpdir files
159
+ try { fs.unlinkSync(metricsJsonl); } catch { /* already gone */ }
160
+ // Note: counterFile is cleaned by session-start hook on next session
161
+
162
+ // Report metrics flush
163
+ const skillList = Object.entries(skillCounts)
164
+ .sort((a, b) => b[1] - a[1])
165
+ .map(([s, c]) => `${s}(${c})`)
166
+ .join(', ');
167
+
168
+ console.log(`\nšŸ“Š [Rune metrics] Session ${sessionId} — ${durationMin}min, ${watchState.count} tool calls, ${skillEvents.length} skill invocations`);
169
+ if (skillList) console.log(` Skills: ${skillList}`);
170
+ console.log(` Saved to .rune/metrics/\n`);
171
+ }
172
+
173
+ // === Original: Verification Checklist ===
174
+
175
+ console.log(`
176
+ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
177
+ │ Rune Session End — Verification Checklist │
178
+ ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
179
+ │ Before closing this session, confirm: │
180
+ │ │
181
+ │ ā–” All TodoWrite tasks marked complete? │
182
+ │ ā–” Tests ran and passing? │
183
+ │ ā–” No hardcoded secrets introduced? │
184
+ │ ā–” If schema changed: migration + rollback exist? │
185
+ │ ā–” Verification ran (lint + types + build)? │
186
+ │ │
187
+ │ If any item is unclear → address it now. │
188
+ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
189
+ `);