Parsers for the NVS (Novel Visual Studio) project file format — available in TypeScript and Python — plus the tooling to convert transcript-like sources into NVS and measure whether the conversion is any good.
NVS stores dialogue-driven fiction as plain Markdown files with YAML frontmatter. See spec/nvs-format.md for the canonical format reference.
New here? Read docs/getting-started.md. Checking a conversion's quality? docs/benchmarks.md:
PYTHONPATH=packages/py/src python3 -m nvs_parser.quality <project-or-library-root>nvs-parser/
docs/
getting-started.md ← start here: what this repo is, converting your first transcript
building-a-cleaner.md ← the refine loop for supporting a new source format
benchmarks.md ← the quality oracle: running, reading, and extending it
spec/
nvs-format.md ← canonical format reference
reference/
content/ ← golden NVS examples (real files from the Hamlet demo, public domain)
packages/
ts/ ← TypeScript parser (@nvs/parser)
py/ ← Python parser (nvs-parser) + cleaning library + quality oracle
(Maintainers keep a local internal/ workspace — conversion backlog, corpus scripts, raw fixtures —
that is deliberately not part of the public repo.)
cd packages/ts
npm install
npm run buildimport { parseProject, parseScene, parseDialogue } from '@nvs/parser'
// Parse an entire NVS project
const project = parseProject('/path/to/my-novel')
console.log(project.chapters[0].scenes[0].dialogue)
// Parse a single scene file
const scene = parseScene('/path/to/content/story/book/001-act-i/s001-opening.md')
// Parse dialogue from a raw string
const beats = parseDialogue("HAMLET\nTo be or not to be.")npm testnpm run typecheckcd packages/py
pip install -e ".[dev]"Or with uv:
uv pip install -e .from nvs_parser import parse_project, parse_scene, parse_dialogue
# Parse an entire NVS project
project = parse_project('/path/to/my-novel')
for chapter in project.chapters:
for scene in chapter.scenes:
for beat in scene.dialogue:
print(f"{beat.speaker}: {beat.text}")
# Parse a single scene file
scene = parse_scene('/path/to/content/story/book/001-act-i/s001-opening.md')
# Parse dialogue from a raw string
beats = parse_dialogue("BARNARDO\nWho's there?")pip install pytest
pytestAn NVS project is a directory with this layout:
<project>/
content/
story/
<group>/ ← e.g. "book"
NNN-<chapter>/ ← e.g. "001-act-i"
sNNN-<slug>.md ← scene files
world/
characters/<id>.md
lore/<id>.md
items/<id>.md
See spec/nvs-format.md for full details on frontmatter fields, dialogue beat syntax, speaker detection, and file naming conventions.