Rune Inspector — Transformation Pipeline Debugger for VS Code
@refrakt-md/vscode, @refrakt-md/language-server
@refrakt-md/vscode, @refrakt-md/language-server
When authoring runes, there's no way to see what the rune actually produces at each pipeline stage. Authors write Markdown inside {% hero %} and hope it transforms correctly. A "Rune Inspector" tree view in VS Code would show the transformation output at every stage, updating live as the cursor moves between runes.
This requires a prerequisite: relocating serialize() from @refrakt-md/svelte to @refrakt-md/runes so the language server can use it without depending on a framework-specific package. The identity transform uses createTransform() from @refrakt-md/transform with theme config discovered dynamically from refrakt.config.json.
@refrakt-md/svelte to @refrakt-md/runes, re-export for backward compatibilityrefrakt.config.json and dynamically imports ${theme}/transform — works with any theme, not just Luminaserialize() to @refrakt-md/runesThe function (24 lines, only depends on @markdoc/markdoc) currently lives in packages/svelte/src/serialize.ts. It has nothing Svelte-specific.
Files to change:
| File | Action |
|---|---|
packages/runes/src/serialize.ts | Create — copy serialize() and serializeTree() from svelte package |
packages/runes/src/index.ts | Export serialize, serializeTree |
packages/svelte/src/serialize.ts | Change to re-export from @refrakt-md/runes |
packages/svelte/src/index.ts | No change — still exports serialize (now re-exported) |
Consumers (site/src/routes/[...slug]/+page.server.ts, create-refrakt/template/...) continue importing from @refrakt-md/svelte unchanged.
Add to packages/language-server/package.json:
"@refrakt-md/transform": "0.4.0"
Note: @refrakt-md/runes is already a dependency (provides serialize() after relocation). Theme config is dynamically imported at runtime — no hard dependency on any specific theme.
Create packages/language-server/src/providers/inspector.ts
Core function: given a document URI and cursor position, run the full pipeline on the rune at the cursor and return structured debug output for all 4 stages.
interface InspectorResult { runeName: string; stages: { ast: object; // Markdoc AST node for the rune transform: object; // Markdoc.transform() output (typeof markers, meta, refs) serialized: object; // serialize() output (plain JSON) identity: object; // createTransform(config)() output (BEM classes, structure) }; }
Pipeline execution:
Markdoc.parse(document.getText()) — get full ASTMarkdoc.transform(ast, { tags, nodes }) — run rune schemas (tags/nodes from registry)serialize() — convert Tag instances to plain objectsrefrakt.config.json from workspace root, dynamically import ${theme}/transformcreateTransform(themeConfig)() — apply identity transformTheme discovery (mirrors packages/sveltekit/src/plugin.ts pattern):
const configPath = path.join(workspaceRoot, 'refrakt.config.json'); const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const themeTransform = await import(`${config.theme}/transform`); const themeConfig = themeTransform.luminaConfig ?? themeTransform.default; const transform = createTransform(themeConfig);
If theme config can't be loaded (no config file, theme not installed), stage 4 is skipped gracefully with a message.
Edit packages/language-server/src/server.ts
Register a custom request handler:
connection.onRequest('refrakt/inspectRune', async (params: { uri: string; position: Position }) => { return inspectRuneAtPosition(documents, params.uri, params.position); });
Edit packages/vscode/package.json — add contributions:
"views": { "explorer": [{ "id": "refraktRuneInspector", "name": "Rune Inspector", "when": "resourceLangId == markdown" }] }, "commands": [{ "command": "refrakt.inspectRune", "title": "Inspect Rune at Cursor", "category": "Refrakt" }]
Create packages/vscode/src/inspector.ts — TreeDataProvider implementation:
refraktRuneInspector viewonDidChangeTextEditorSelection and onDidChangeActiveTextEditorrefrakt/inspectRune request to language serverInspectorResult:RUNE INSPECTOR (hero) ├── AST │ └── tag: hero [align="center"] │ ├── heading: "Hello World" │ └── paragraph: "Welcome to..." ├── Transform │ └── typeof: Hero │ ├── properties: headline, blurb, image │ ├── refs: actions │ └── meta: align → "center" ├── Serialized │ └── article [typeof="Hero"] │ ├── meta [property="align"] │ ├── header │ └── div [data-name="actions"] └── Identity Transform └── article.rf-hero.rf-hero--center ├── div.rf-hero__header │ ├── h1.rf-hero__headline │ └── p.rf-hero__blurb └── div.rf-hero__actions
Tree items have:
Edit packages/vscode/src/extension.ts — register the tree view provider and wire up cursor tracking.
| File | Action |
|---|---|
packages/runes/src/serialize.ts | Create — relocate serialize/serializeTree |
packages/runes/src/index.ts | Add serialize exports |
packages/svelte/src/serialize.ts | Re-export from @refrakt-md/runes |
packages/language-server/package.json | Add @refrakt-md/transform dependency |
packages/language-server/src/providers/inspector.ts | Create — pipeline execution + InspectorResult |
packages/language-server/src/server.ts | Add refrakt/inspectRune custom request handler |
packages/vscode/package.json | Add tree view contribution + command |
packages/vscode/src/inspector.ts | Create — TreeDataProvider for Rune Inspector |
packages/vscode/src/extension.ts | Register tree view, cursor tracking |
npm run build — all packages compile (especially runes, svelte, language-server, vscode)npm test — existing serialize tests still pass.md file with runes in VS Code with the extension loaded{% hero %} blockrefrakt.config.json — Stage 4 shows "Theme not found" gracefullySPEC-041 (Agent Rune Reference) builds a content-model renderer (renderContentModel) on top of the same serializeContentModel infrastructure this inspector relies on. Once both ship, the Rune Inspector tree view could expose a fifth sibling node — "Syntax Reference" — that displays the rune's input syntax (attributes, content structure, example) alongside the four pipeline stages already shown. Same serializer, different presentation. No work in this spec; flagged here so the integration point is recorded.