Features
A detailed look at what Knot does today, verified against the codebase. Only SugarCube 2 has full language features; Harlowe, Chapbook, and Snowman are placeholder implementations with the build pipeline working. The current release is v2.1.0 — anything marked Unreleased is in the repository build but not yet in a Marketplace release.
Version-aware SugarCube 2 support
SugarCube has shipped breaking changes across its 2.x life — macros added, deprecated, and removed (2.37.0 alone removed six). Knot's macro catalog is a versioned union catalog: every macro that ever existed in SugarCube 2.x carries its lifecycle (added_in / deprecated_in / removed_in) plus per-era descriptions and argument signatures, sourced from the official docs, upgrade guides, release notes, and the 2.37.3 engine source.
Every user-facing feature resolves through the story's pinned format-version from StoryData:
- Completions only offer macros that exist at your version —
<<type>>and friends are hidden for stories pinned below their introduction, and macros removed in 2.37.0 are offered again for older stories. - Lifecycle diagnostics —
sc-macro-removed(Error: removal version, replacement, downgrade guidance) andsc-macro-not-in-version(Warning: the version the macro was added in). - Hover and signature help resolve era-appropriate documentation, with tombstone notes for macros that don't exist at your version.
- Special passages like
StoryDisplayTitle(2.31.0) and the[init]tag (2.36.0) are only special on versions that have them. - Editing
format-versiontriggers a full reindex — everything updates immediately.
Stories without a parseable version fail open to the latest known SugarCube — unusual metadata degrades to the full current surface, never to bogus diagnostics.
1:: Hub
2Where to next?
3<<click "Open the cellar door">>
4 The door groans open.
5<</click>>
- Macro
<<click>>was removed in SugarCube 2.37.0 — use<<link>>instead line 3
Structural understanding, not just regex
Unlike tooling that bolts regular expressions onto a text editor, Knot actually parses your project into passages and the links between them. This structural model lets it answer questions that pattern matching gets wrong:
- "Where is this variable set?" — Knot tracks definitions across passages, not just mentions.
- "Is this passage reachable from Start?" — a graph reachability check, not a string search. Static analysis can't see through variable-aliased navigation (
<<goto $next>>), so you can mark entry passages with"reachable": trueheader metadata — the warning even offers a quickfix that writes it for you Unreleased. - "Which passages link here?" — reverse navigation through the link graph.
- "Are there any orphan passages?" — passages nothing links to, flagged automatically.
This is the core design principle behind Knot: model the project the way Twine itself models it, and the editor can give you real help instead of approximate help.
Variable tracker
The Variable Tracking panel shows where every variable is set, read, and how it flows across passages. For SugarCube, variable tracking is JavaScript-aware via the oxc parser, so it understands:
<<set $x to 1>>— SugarCube macro syntax<<run $x++>>— inline expressions- JavaScript inside
<<script>>blocks - Variables passed through
<<include>>and similar macros
This means Knot can warn you about uninitialized variables, unused variables, and variables that are read but never set — all in real time as you type. When JavaScript inside a passage has a syntax error, the parser recovers chunk-by-chunk so one broken block doesn't blind the rest of the file.
Passage diagnostics
A dedicated panel inspects any passage's links, variables, macros, and complexity metrics. Real-time diagnostics are all configurable — each of the 11 checks below can be set to error, warning, info, hint, or off in VS Code settings:
| Diagnostic | Default | What it flags |
|---|---|---|
knot.diagnostics.broken-link | warning | Links pointing to passages that don't exist yet |
knot.diagnostics.unreachable-passage | warning | Passages no other passage links to (excluding Start and special passages); manual entry points for dynamic navigation Unreleased |
knot.diagnostics.uninitialized-variable | warning | Variables read before they're set anywhere in the project |
knot.diagnostics.invalid-passage-name | warning | Passage names that break Twine's naming rules |
knot.diagnostics.duplicate-passage-name | error | Two passages with the same name — Twine resolves these unpredictably |
knot.diagnostics.dead-end-passage | info | Passages with no outbound links, where the player has nowhere to go |
knot.diagnostics.complex-passage | hint | Passages whose nesting and branch complexity exceed a threshold |
knot.diagnostics.empty-passage | hint | Passages with no content |
knot.diagnostics.large-passage | hint | Passages that have grown large enough to slow the editor and the reader |
knot.diagnostics.redundant-write | hint | Writing a variable that's never read before it's written again |
knot.diagnostics.unused-variable | hint | Variables that are set but never read |
Alongside the diagnostics, the Passage Diagnostics panel surfaces per-passage complexity metrics: word count, link count, macro count, and variable count.
Embedded CSS linting
Twine projects embed CSS in passages more often than they keep it in separate files — and a single typo can make a browser silently drop an entire rule. Knot runs a real CSS parser over every stylesheet surface and reports:
- Parse errors on
[stylesheet]passages,<style>element bodies, and<<style>>/<<css>>blocks — includingstyle="…"attribute values. - Unknown properties against a curated 500+ property registry (custom and vendor-prefixed names are always allowed) — flags
colr-style typos. - Pseudo-class and pseudo-element typos —
:hvoer,::befor, the typo class browsers punish with total rule silence — plus a placement lint for pseudo-elements that aren't last in their compound selector.
1:: Story Stylesheet [stylesheet]
2.passage {
3 colr: #f8f4ff;
4 line-height: 1.6;
5}
- Unknown property
colr— browsers will silently drop this rule. Did you meancolor? line 3
Embedded HTML validation
Passages and StoryInterface files mix prose with markup, so Knot validates the HTML it finds there with tolerance matching the Twine engine's own:
- Unclosed elements (Error), duplicate attributes and missing attribute values (Warning), and unterminated tags at passage end (Warning).
- Attribute directive support —
@attr="expr"/sc-eval:attr="expr"recognition with completions for the directive families, version-gated to SugarCube 2.21.0+ where they exist. - Deliberate silence on prose
<, stray end tags, and literal regions (<nowiki>,{{{ }}}) — matching what the engine actually tolerates, so you're not spammed with non-errors.
StoryInterface passages get full semantic highlighting — tag names, attributes, entities, and delimiters — via the same token pipeline.
Story Map
An interactive graph visualization of your entire project. Nodes are passages, edges are links. The map is built on @xyflow/react with automatic layout via dagre.
- Click any node to jump to that passage in the editor — including passages in closed files, at the correct line Unreleased.
- The map updates in real time as you edit — no manual refresh.
- Passage metadata (position, group, color) round-trips through the map; manual entry points render with their own green ring, corner marker, and tooltip Unreleased.
- Special passages get distinct node colors; dead-end passages get a yellow double border; unreachable passages are grouped separately.
The Story Map is primarily for visualization and navigation at this time. Deeper structural editing from the map — dragging passages to reorganize, creating links visually — is on the roadmap.
The Knot IDE sidebar
Beside the editor, Knot installs a dedicated activity-bar panel (the Knot icon on the left edge of the window) with three views:
Passage Diagnostics
Live inspection of the passage under the cursor: links, variables, macros, and complexity metrics.
Variable Tracking
Where every variable is set and read, and how values propagate across passages.
Project Info
Workspace statistics: passage and word counts, format and version, IFID, graph health.
Incremental analysis
Knot doesn't re-parse your entire project on every keystroke. Only affected passages are re-parsed, and the graph is updated incrementally. This keeps large projects responsive even as they grow into hundreds of passages and thousands of links.
Integrated build pipeline
Knot's build pipeline is designed to "just work" with zero manual setup:
- Tweego is auto-downloaded on first build into the extension's global storage. You never need to install it yourself.
- Story formats are auto-downloaded based on your project's
StoryDataformat and version. The format is cached per version in<globalStorage>/storyformats/<id>@<version>/. - The workspace is the source directory — put all your
.twee,.js,.css, and asset files directly in the workspace. Story formats live separately in the managed folder, so there's no risk offormat.jsgetting bundled as a passage. - The output filename is derived from
StoryTitle— if your story is called "The Lost City", the build producesThe_Lost_City.html. Falls back toindex.htmlif noStoryTitlepassage exists. - Build stats are logged — every build prints
Passages: N | Words: Nto the Knot Build output channel.
Beyond the defaults, a knot.json file in the workspace root can override the story format and version and declare special passages explicitly.
Watch mode
Toggle auto-rebuild on save. When Watch is on, every save of a .tw, .twee, .js, or .css file triggers a rebuild in the background. This is the recommended workflow for active development: edit, save, refresh the browser.
Supported story formats
| Format | Status | What works | What doesn't (yet) |
|---|---|---|---|
| SugarCube 2 | Full support | Versioned macro catalog (73-entry union, filtered by your story's format-version), JS-aware variable tracking via oxc, CSS & HTML linting, special passages, completion, hover, signature help, diagnostics, Story Map, build pipeline |
— |
| Harlowe 3 | Placeholder | FormatPlugin trait implemented; build pipeline (via Tweego) |
Parser quality, link extraction, Story Map, macro catalog, variable tracking, completion, hover |
| Chapbook 1 | Placeholder | FormatPlugin trait implemented; build pipeline (via Tweego) |
Parser quality, link extraction, Story Map, macro catalog, variable tracking, completion, hover |
| Snowman 2 | Placeholder | FormatPlugin trait implemented; build pipeline (via Tweego) |
Parser quality, link extraction, Story Map, ERB template detection, macro catalog, variable tracking, completion, hover |
Knot auto-detects the format from your StoryData passage. The Language Status indicator (lower-right of the editor, on .twee files) shows the detected format plus passage, broken-link, and unreachable counts — turning warning-colored when links are broken. Bringing Harlowe, Chapbook, and Snowman to full parity is planned — see the roadmap for details.
Want a feature that isn't here? Knot is still early in development and the codebase is small — this is the ideal time to chime in. Join the Discord or open an issue and tell us what you need.