@rune-kit/rune 2.11.0 → 2.12.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.
Files changed (60) hide show
  1. package/README.md +58 -1
  2. package/compiler/__tests__/detect-invariants.test.js +136 -0
  3. package/compiler/__tests__/doctor-mesh.test.js +229 -0
  4. package/compiler/__tests__/hook-dispatch.test.js +91 -0
  5. package/compiler/__tests__/hooks-antigravity.test.js +120 -0
  6. package/compiler/__tests__/hooks-cursor.test.js +141 -0
  7. package/compiler/__tests__/hooks-install.test.js +307 -0
  8. package/compiler/__tests__/hooks-merge.test.js +204 -0
  9. package/compiler/__tests__/hooks-tiers.test.js +620 -0
  10. package/compiler/__tests__/hooks-windsurf.test.js +117 -0
  11. package/compiler/__tests__/inject-claude-md.test.js +152 -0
  12. package/compiler/__tests__/load-invariants.test.js +408 -0
  13. package/compiler/__tests__/onboard-invariants.test.js +240 -0
  14. package/compiler/adapters/hooks/antigravity.js +140 -0
  15. package/compiler/adapters/hooks/claude.js +166 -0
  16. package/compiler/adapters/hooks/cursor.js +191 -0
  17. package/compiler/adapters/hooks/index.js +82 -0
  18. package/compiler/adapters/hooks/tier-emitter.js +182 -0
  19. package/compiler/adapters/hooks/windsurf.js +202 -0
  20. package/compiler/bin/rune.js +196 -6
  21. package/compiler/commands/hook-dispatch.js +87 -0
  22. package/compiler/commands/hooks/install.js +120 -0
  23. package/compiler/commands/hooks/merge.js +211 -0
  24. package/compiler/commands/hooks/presets.js +116 -0
  25. package/compiler/commands/hooks/status.js +112 -0
  26. package/compiler/commands/hooks/tiers.js +323 -0
  27. package/compiler/commands/hooks/uninstall.js +94 -0
  28. package/compiler/doctor.js +236 -0
  29. package/package.json +2 -2
  30. package/skills/ba/SKILL.md +85 -1
  31. package/skills/brainstorm/SKILL.md +39 -1
  32. package/skills/browser-pilot/SKILL.md +1 -0
  33. package/skills/context-engine/SKILL.md +6 -2
  34. package/skills/design/SKILL.md +1 -0
  35. package/skills/docs-seeker/SKILL.md +1 -0
  36. package/skills/fix/SKILL.md +4 -2
  37. package/skills/hallucination-guard/SKILL.md +1 -0
  38. package/skills/journal/SKILL.md +1 -0
  39. package/skills/logic-guardian/SKILL.md +22 -4
  40. package/skills/marketing/SKILL.md +62 -1
  41. package/skills/neural-memory/SKILL.md +13 -16
  42. package/skills/onboard/SKILL.md +30 -2
  43. package/skills/onboard/references/invariants-template.md +76 -0
  44. package/skills/onboard/scripts/detect-invariants.js +439 -0
  45. package/skills/onboard/scripts/inject-claude-md.js +150 -0
  46. package/skills/onboard/scripts/onboard-invariants.js +196 -0
  47. package/skills/perf/SKILL.md +1 -0
  48. package/skills/plan/SKILL.md +2 -0
  49. package/skills/preflight/SKILL.md +1 -1
  50. package/skills/research/SKILL.md +4 -0
  51. package/skills/review/SKILL.md +4 -2
  52. package/skills/scope-guard/SKILL.md +4 -1
  53. package/skills/scout/SKILL.md +6 -0
  54. package/skills/sentinel/SKILL.md +2 -0
  55. package/skills/session-bridge/SKILL.md +53 -1
  56. package/skills/session-bridge/scripts/load-invariants.js +397 -0
  57. package/skills/slides/SKILL.md +19 -0
  58. package/skills/team/SKILL.md +2 -1
  59. package/skills/test/SKILL.md +6 -0
  60. package/skills/verification/SKILL.md +8 -0
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * onboard-invariants.js — Orchestrate invariant detection, INVARIANTS.md
5
+ * generation (merge-preserving), and CLAUDE.md pointer injection.
6
+ *
7
+ * Writes/updates:
8
+ * - .rune/INVARIANTS.md (seeded from template, append-only on re-run)
9
+ * - CLAUDE.md (pointer block between markers)
10
+ *
11
+ * Safe re-runs:
12
+ * - Existing user sections above the auto-detected block are preserved.
13
+ * - New detections land under `## Auto-detected (new)` — never overwrite.
14
+ *
15
+ * Usage as CLI:
16
+ * node onboard-invariants.js --root <project-root> [--dry]
17
+ */
18
+
19
+ import { existsSync } from 'node:fs';
20
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
21
+ import path from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+ import { parseArgs } from 'node:util';
24
+ import { detectInvariants, renderInvariants } from './detect-invariants.js';
25
+ import { applyInvariantsPointer } from './inject-claude-md.js';
26
+
27
+ const AUTO_HEADER = '## Auto-detected (new)';
28
+ const TEMPLATE_REL_PATH = '../references/invariants-template.md';
29
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
30
+
31
+ /**
32
+ * Locate the next `## `-level section boundary in `text`, ignoring any `##`
33
+ * lines that appear inside fenced code blocks (``` or ~~~).
34
+ * Returns the index of the start of the boundary line, or -1 if none.
35
+ */
36
+ function findNextSectionOffset(text) {
37
+ let offset = 0;
38
+ let inFence = false;
39
+ let fenceDelim = null;
40
+ while (offset < text.length) {
41
+ const eol = text.indexOf('\n', offset);
42
+ const lineEnd = eol === -1 ? text.length : eol;
43
+ const line = text.slice(offset, lineEnd);
44
+ const trimmed = line.trimStart();
45
+ if (inFence) {
46
+ if (trimmed.startsWith(fenceDelim)) {
47
+ inFence = false;
48
+ fenceDelim = null;
49
+ }
50
+ } else {
51
+ if (trimmed.startsWith('```') || trimmed.startsWith('~~~')) {
52
+ inFence = true;
53
+ fenceDelim = trimmed.startsWith('```') ? '```' : '~~~';
54
+ } else if (line.startsWith('## ') && !line.startsWith(AUTO_HEADER)) {
55
+ return offset;
56
+ }
57
+ }
58
+ if (eol === -1) break;
59
+ offset = eol + 1;
60
+ }
61
+ return -1;
62
+ }
63
+
64
+ export async function loadTemplate(templatePath) {
65
+ const resolved = templatePath ?? path.resolve(__dirname, TEMPLATE_REL_PATH);
66
+ return readFile(resolved, 'utf8');
67
+ }
68
+
69
+ export function mergeInvariantsContent({ existing, autoDetected, template }) {
70
+ const freshBody = renderAutoDetectedBlock(autoDetected);
71
+
72
+ if (!existing || existing.trim().length === 0) {
73
+ return replaceAutoBlock(template, freshBody, { seeded: true });
74
+ }
75
+
76
+ if (!existing.includes(AUTO_HEADER)) {
77
+ const trailing = existing.endsWith('\n') ? '' : '\n';
78
+ const appended = `${existing}${trailing}\n---\n\n${AUTO_HEADER}\n\n${freshBody}\n`;
79
+ return { content: appended, seeded: false, replaced: false, appended: true };
80
+ }
81
+
82
+ return replaceAutoBlock(existing, freshBody, { seeded: false });
83
+ }
84
+
85
+ function replaceAutoBlock(source, freshBody, { seeded }) {
86
+ const headerIdx = source.indexOf(AUTO_HEADER);
87
+ if (headerIdx === -1) {
88
+ const trailing = source.endsWith('\n') ? '' : '\n';
89
+ return {
90
+ content: `${source}${trailing}\n${AUTO_HEADER}\n\n${freshBody}\n`,
91
+ seeded,
92
+ replaced: false,
93
+ appended: true,
94
+ };
95
+ }
96
+
97
+ const afterHeader = source.slice(headerIdx + AUTO_HEADER.length);
98
+ const nextOffsetRaw = findNextSectionOffset(afterHeader);
99
+ const nextOffset = nextOffsetRaw === -1 ? afterHeader.length : nextOffsetRaw;
100
+
101
+ const before = source.slice(0, headerIdx);
102
+ const after = afterHeader.slice(nextOffset);
103
+ const replaced = `${before}${AUTO_HEADER}\n\n${freshBody}\n\n${after.replace(/^\n+/, '')}`;
104
+
105
+ return { content: replaced, seeded, replaced: true, appended: false };
106
+ }
107
+
108
+ function renderAutoDetectedBlock(result) {
109
+ const total =
110
+ (result?.danger?.length ?? 0) +
111
+ (result?.critical?.length ?? 0) +
112
+ (result?.state?.length ?? 0) +
113
+ (result?.cross?.length ?? 0);
114
+ if (total === 0) return '_No new detections on this run._';
115
+ return renderInvariants(result).trimEnd();
116
+ }
117
+
118
+ export function collectPointerGlobs(result, limit = 8) {
119
+ const danger = (result?.danger ?? []).map((e) => firstWhere(e)).filter(Boolean);
120
+ const critical = (result?.critical ?? []).map((e) => firstWhere(e)).filter(Boolean);
121
+ return Array.from(new Set([...danger, ...critical])).slice(0, limit);
122
+ }
123
+
124
+ function firstWhere(entry) {
125
+ if (!entry) return null;
126
+ if (Array.isArray(entry.where)) return entry.where[0] ?? null;
127
+ return entry.where ?? null;
128
+ }
129
+
130
+ export async function runOnboardInvariants({ root, dryRun = false, templatePath } = {}) {
131
+ if (!root) throw new Error('root is required');
132
+
133
+ const runeDir = path.join(root, '.rune');
134
+ const invariantsPath = path.join(runeDir, 'INVARIANTS.md');
135
+ const claudeMdPath = path.join(root, 'CLAUDE.md');
136
+
137
+ const result = await detectInvariants({ root });
138
+ const rendered = renderInvariants(result);
139
+ const template = await loadTemplate(templatePath);
140
+
141
+ const existing = existsSync(invariantsPath) ? await readFile(invariantsPath, 'utf8') : '';
142
+ const merged = mergeInvariantsContent({ existing, autoDetected: result, template });
143
+
144
+ if (!dryRun) {
145
+ await mkdir(runeDir, { recursive: true });
146
+ await writeFile(invariantsPath, merged.content, 'utf8');
147
+ }
148
+
149
+ const globs = collectPointerGlobs(result);
150
+ const pointer = await applyInvariantsPointer({
151
+ claudeMdPath,
152
+ globs,
153
+ invariantsPath: path.relative(root, invariantsPath).replaceAll('\\', '/'),
154
+ dryRun,
155
+ });
156
+
157
+ return {
158
+ invariants: {
159
+ path: invariantsPath,
160
+ action: merged.seeded ? 'seeded' : merged.replaced ? 'replaced' : merged.appended ? 'appended' : 'noop',
161
+ stats: result.stats ?? null,
162
+ detected: {
163
+ danger: result.danger?.length ?? 0,
164
+ critical: result.critical?.length ?? 0,
165
+ state: result.state?.length ?? 0,
166
+ cross: result.cross?.length ?? 0,
167
+ },
168
+ },
169
+ claudeMd: {
170
+ path: pointer.path,
171
+ action: pointer.action,
172
+ reason: pointer.reason ?? null,
173
+ },
174
+ rendered,
175
+ };
176
+ }
177
+
178
+ async function main() {
179
+ const { values } = parseArgs({
180
+ options: {
181
+ root: { type: 'string' },
182
+ dry: { type: 'boolean', default: false },
183
+ },
184
+ });
185
+
186
+ const root = values.root ?? process.cwd();
187
+ const result = await runOnboardInvariants({ root, dryRun: values.dry });
188
+ console.log(JSON.stringify(result, null, 2));
189
+ }
190
+
191
+ if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith('onboard-invariants.js')) {
192
+ main().catch((err) => {
193
+ console.error(err.message);
194
+ process.exit(1);
195
+ });
196
+ }
@@ -37,6 +37,7 @@ Performance regression gate. Analyzes code changes for patterns that cause measu
37
37
  - `audit` (L2): performance dimension delegation
38
38
  - `review` (L2): performance patterns detected in diff
39
39
  - `deploy` (L2): pre-deploy perf regression check
40
+ - `adversary` (L2): scalability stress test when bottleneck patterns detected in plan
40
41
 
41
42
  ## References
42
43
 
@@ -97,6 +97,8 @@ High-level multi-feature planning — organize features into milestones.
97
97
  - `scaffold` (L1): Phase 3 architecture planning
98
98
  - `skill-forge` (L2): plan structure for new skill
99
99
  - User: `/rune plan` direct invocation
100
+ - `debug` (L2): when root cause requires architectural changes
101
+ - `retro` (L2): reference past plans during retrospective analysis
100
102
 
101
103
  ## Data Flow
102
104
 
@@ -3,7 +3,7 @@ name: preflight
3
3
  description: Pre-commit quality gate that catches "almost right" code. Goes beyond linting — checks logic correctness, error handling, regressions, and completeness.
4
4
  metadata:
5
5
  author: runedev
6
- version: "0.8.0"
6
+ version: "1.0.0"
7
7
  layer: L2
8
8
  model: sonnet
9
9
  group: quality
@@ -27,6 +27,10 @@ None — pure L3 utility using `WebSearch` and `WebFetch` tools directly.
27
27
  - `marketing` (L2): competitor analysis, SEO data
28
28
  - `hallucination-guard` (L3): verify package existence on npm/pypi
29
29
  - `autopsy` (L2): research best practices for legacy patterns
30
+ - `ba` (L2): research similar products and integrations
31
+ - `graft` (L2): research source repo patterns before grafting
32
+ - `mcp-builder` (L2): research MCP standards and existing implementations
33
+ - `scaffold` (L1): research project templates and best practices
30
34
 
31
35
  ## Execution
32
36
 
@@ -3,7 +3,7 @@ name: review
3
3
  description: Code quality review — patterns, security, performance, correctness. Finds bugs, suggests improvements, triggers fix for issues found. Escalates to opus for security-critical code.
4
4
  metadata:
5
5
  author: runedev
6
- version: "0.8.0"
6
+ version: "1.0.0"
7
7
  layer: L2
8
8
  model: sonnet
9
9
  group: development
@@ -52,6 +52,8 @@ Every review MUST cite at least one specific concern, suggestion, or explicit ap
52
52
  - User: `/rune review` direct invocation
53
53
  - `surgeon` (L2): review refactored code quality
54
54
  - `rescue` (L1): review refactored code quality
55
+ - `design` (L2): review UI/design implementation quality
56
+ - `graft` (L2): review grafted code integration
55
57
 
56
58
  ## Cross-Hub Connections
57
59
 
@@ -564,7 +566,7 @@ Append to Code Review Report when invoked standalone. Suppress when called as su
564
566
  ```yaml
565
567
  chain_metadata:
566
568
  skill: "rune:review"
567
- version: "0.8.0"
569
+ version: "1.0.0"
568
570
  status: "[DONE | DONE_WITH_CONCERNS]"
569
571
  domain: "[area reviewed]"
570
572
  files_changed: [] # review doesn't change files
@@ -19,7 +19,10 @@ Passive scope monitor. Reads the original task plan, inspects current git diff t
19
19
 
20
20
  ## Called By (inbound)
21
21
 
22
- - Auto-triggered by L1 orchestrators when files changed exceed plan expectations
22
+ - `cook` (L1): Phase 6.6 scope drift detection when files touched > planned
23
+ - `team` (L1): after each parallel workstream completes, before merge
24
+ - `rescue` (L1): during safeguard phase to detect unplanned changes
25
+ - `plan` (L2): optional scope validation after plan acceptance
23
26
 
24
27
  ## Calls (outbound)
25
28
 
@@ -198,6 +198,12 @@ None — pure scanner using Glob, Grep, Read, and Bash tools directly. Does not
198
198
  - `perf` (L2): find hotpath files and performance-critical code
199
199
  - `review-intake` (L2): scan codebase for review context
200
200
  - `skill-forge` (L2): scan existing skills for patterns when creating new skills
201
+ - `ba` (L2): scan existing codebase for context before requirements elicitation
202
+ - `retro` (L2): scan commit history and codebase for retrospective analysis
203
+ - `graft` (L2): scan target codebase before grafting code from external repo
204
+ - `docs` (L2): scan codebase structure for documentation generation
205
+ - `logic-guardian` (L2): scan business logic modules for protection mapping
206
+ - `adversary` (L2): scan codebase before red-team analysis
201
207
 
202
208
  ## Output Format
203
209
 
@@ -47,6 +47,8 @@ If status is BLOCK, output the report and STOP. Do not hand off to commit. The c
47
47
  - `audit` (L2): Phase 2 full security audit
48
48
  - `incident` (L2): security dimension check during incident response
49
49
  - `review-intake` (L2): security scan on code submitted for structured review
50
+ - `adversary` (L2): deep security analysis when attack vectors identified in plan
51
+ - `scaffold` (L1): security baseline for new projects
50
52
 
51
53
  ## Severity Levels
52
54
 
@@ -3,12 +3,13 @@ name: session-bridge
3
3
  description: Universal context persistence across sessions. Auto-saves decisions, conventions, and progress to .rune/ files. Loads state at session start. Use when any skill makes architectural decisions or establishes patterns that must survive session boundaries.
4
4
  metadata:
5
5
  author: runedev
6
- version: "0.6.0"
6
+ version: "0.7.0"
7
7
  layer: L3
8
8
  model: haiku
9
9
  group: state
10
10
  tools: "Read, Write, Edit, Glob, Grep"
11
11
  listen: phase.complete, checkpoint.request
12
+ emit: invariants.loaded
12
13
  ---
13
14
 
14
15
  # session-bridge
@@ -37,6 +38,8 @@ Solve the #1 developer complaint: context loss across sessions. Session-bridge a
37
38
  - `cook` (L1): auto-save decisions during feature implementation
38
39
  - `rescue` (L1): state management throughout refactoring
39
40
  - `context-engine` (L3): save state before compaction
41
+ - `context-pack` (L3): coordinate state for sub-agent handoff
42
+ - `neural-memory` (L3): sync key decisions back to `.rune/` files after Capture Mode
40
43
 
41
44
  ## State Files Managed
42
45
 
@@ -354,6 +357,54 @@ Handle results:
354
357
  - `SUSPICIOUS` → present warning to user with specific findings. Ask: "Suspicious patterns detected in .rune/ files. Load anyway?" If user approves → proceed. If not → exit load mode.
355
358
  - `TAINTED` → **BLOCK load**. Report: ".rune/ integrity check FAILED — possible poisoning detected. Run `/rune integrity` for details."
356
359
 
360
+ #### Step 1.7 — Load invariants (auto-discipline)
361
+
362
+ Before loading the usual state files, run the invariants loader so the agent sees active discipline rules without being told to look:
363
+
364
+ ```
365
+ Execute: node skills/session-bridge/scripts/load-invariants.js --root <project-root> --json
366
+ ```
367
+
368
+ The loader:
369
+ - Reads `.rune/INVARIANTS.md` (silent no-op if missing)
370
+ - Strips the `## Archived` section (retired rules don't re-activate)
371
+ - Parses active rules into `{ section, title, what, where, why }`
372
+ - Returns a token-budgeted preview (≤ 500 tokens by default)
373
+ - Flags staleness when mtime > 30 days
374
+
375
+ **Emit signal**: `invariants.loaded` with payload `{ loaded, count, rules, stats, stale, overflow, path }` where:
376
+ - `loaded` (boolean) — whether any active rules were parsed
377
+ - `count` (number) — total active rules (convenience alias for `stats.total`)
378
+ - `rules` (array) — full rule objects `[{ section, title, what, where: string[], why }]` — consumers cache these for glob matching
379
+ - `stats` — `{ danger, critical, state, cross, total, archivedSkipped }`
380
+ - `stale` (boolean) — mtime > 30 days
381
+ - `overflow` (number) — rules present but not shown in preview (budget overflow)
382
+ - `path` (string) — absolute path to `.rune/INVARIANTS.md`
383
+
384
+ Downstream listeners (`logic-guardian`, Pro `autopilot`) consume `rules[]` directly — no second file read needed.
385
+
386
+ **Present to agent** (injected verbatim into the Load Mode summary):
387
+
388
+ ```
389
+ 📎 Active Invariants (.rune/INVARIANTS.md)
390
+ ⚠ skills/skill-router/** — L0 router, never bypass
391
+ 🔒 compiler/parser.js — IR schema is the adapter contract
392
+ 🔁 compiler/hooks/dispatch.js — phase order is pre → run → post
393
+ 🔗 .claude-plugin/marketplace.json — mirrors plugin.json
394
+ …+2 more rules in .rune/INVARIANTS.md
395
+ ```
396
+
397
+ **Staleness warning** (emit ONCE per session, not per tool call):
398
+
399
+ ```
400
+ ⚠ Invariants file is stale (> 30 days since last onboard). Consider `rune onboard --refresh`.
401
+ ```
402
+
403
+ **Failure modes**:
404
+ - Missing file → silent no-op (no preview, no error). Don't nag fresh repos.
405
+ - Malformed file → `loaded: false, rules: []`. Log a single-line warning, continue.
406
+ - File fails `integrity-check` in Step 1.5 → this step is skipped entirely (load already blocked).
407
+
357
408
  #### Step 2 — Load files
358
409
 
359
410
  Use `Read` on all four state files in parallel:
@@ -474,6 +525,7 @@ At Load Mode Step 1, after checking `.rune/*.md` existence, also check for `.run
474
525
  ## Session Bridge — Loaded
475
526
  - **Last session**: [date and summary]
476
527
  - **Checkpoint**: [detected — resume point] | [none]
528
+ - **Invariants**: [N loaded from .rune/INVARIANTS.md] | [none] | [stale — run rune onboard --refresh]
477
529
  - **Decisions on file**: [count]
478
530
  - **Conventions on file**: [count]
479
531
  - **Learnings on file**: [count] (top 5 surfaced if 10+)