The working environment

The tutorial describes the language; this page describes the working environment: organising a project, placing its grammar where the tools resolve it, and validating from the editor, from the command line and in continuous integration. It follows the same example as the tutorial, the record of a book.

The stxt command line is required (npm install -g @stxt-lang/cli, or npx @stxt-lang/cli without installing) and, for section 4, Visual Studio Code with the stxt-lang.stxt extension. Section 7 repeats the walkthrough in the playground, with no installation. Everything used here is described in Tools.

1. The project and the .stxt/ directory

An STXT project is any directory with .stxt documents in it. The only thing STXT adds is a directory called exactly .stxt/ that contains the project's grammars — schemas and templates. When a tool validates a document it looks for that directory in the document's directory and in all of its ancestor directories, then in ~/.stxt and in /etc/stxt. That list is the document's resolution chain, and it is the same for the editor, the command line and any other tool: that is why a document validates the same everywhere.

For the example, a books project with the grammar in .stxt/ and the documents in docs/:

books/
├── .stxt/            the project's grammars
└── docs/
    └── book.stxt     the documents
mkdir -p books/.stxt books/docs
cd books

Inside .stxt/ every .stxt file is loaded, recursively, and neither file names nor subdirectories have any meaning: they are just organisation. Each file must be a definition (@stxt.schema or @stxt.template); any other content inside .stxt/ is a resolution error. See STXT-DISCOVERY-SPEC §3 and §4.

2. Writing the grammar

The book template is the one from the tutorial (section 8). It can be written straight into .stxt/ under any name, or the CLI can place it: stxt install parses it, checks that it validates against its meta-schema and only then writes it, in canonical form, as .stxt/@stxt.template/<namespace>.stxt. That naming is a CLI convention, not part of the language.

Template (@stxt.template): com.acme.book
	Structure >>
		Book:
			Title: (1)
			Authors: (1)
				Author: (+)
			ISBN: (1)
			Publisher: (?)
			Published: (?) DATE
			Summary: (?) TEXT
			Chapter: (+)
				Content: (?) TEXT
	Description >>
		Book: Template for publishing book records
stxt install book-template.stxt

Installed com.acme.book (@stxt.template) to /home/ana/books/.stxt/@stxt.template/com.acme.book.stxt

stxt schemas shows the resolution chain of a directory, which definition is active for each namespace and where it comes from:

stxt schemas docs

Resolution chain for /home/ana/books/docs:
    /home/ana/books/.stxt

Namespaces:
    com.acme.book <- /home/ana/books/.stxt/@stxt.template/com.acme.book.stxt

If the project lives inside another one that also has a .stxt/ (a monorepo), the chain includes both directories: both take part, and for each namespace the one closest to the document wins.

3. Writing and validating the document

The document, in docs/book.stxt, with the template's namespace:

Book (com.acme.book):
	Title: Modern software architecture
	Authors:
		Author: María Pérez
		Author: Juan García
	ISBN: 978-84-123456-7-8
	Published: 2025-10-01
	Summary: A practical introduction to the architecture of modern systems.
	Chapter: Introduction
		Content >>
			Basic concepts and goals of the book.
stxt validate docs/book.stxt

It prints nothing and exits with code 0: there is no output when everything passes. To observe the findings, two typical mistakes: a date that is not YYYY-MM-DD and a required node that is missing. With Published changed to free text and without the ISBN line:

Book (com.acme.book):
	Title: Modern software architecture
	Authors:
		Author: María Pérez
		Author: Juan García
	Published: October 1st, 2025
	Summary: A practical introduction to the architecture of modern systems.
	Chapter: Introduction
		Content >>
			Basic concepts and goals of the book.
# ERROR: the date is not YYYY-MM-DD and ISBN, which is mandatory, is missing
stxt validate docs/book.stxt

/home/ana/books/docs/book.stxt:6: [INVALID_VALUE] Published: Invalid date (October 1st, 2025) (error)
/home/ana/books/docs/book.stxt:1: [TOO_FEW_CHILDREN] 0 nodes of 'com.acme.book:isbn' and min is 1 (error)
2 error(s), 0 warning(s)

Each finding is file:line: [CODE] message (severity). The code is stable and identical in the CLI, the extension and the three libraries (INVALID_VALUE, TOO_FEW_CHILDREN, CHILD_NOT_DECLARED, INDENTATION_MIXED…), so it can be looked up in the documentation or filtered in a script. The failed cardinality is reported on the parent's line (Book, line 1), because it is the parent that has zero ISBN.

The exit code tells the documents have errors (1) apart from the command was used wrongly (2). Three options change what counts as a failure:

  • --warn-schema: grammar errors are reported as warnings and do not fail; syntax errors still do.
  • --no-schema: syntax only, without looking for or applying any grammar.
  • --format json: the same findings as a JSON array, for machines.

With --recursive (-r) whole directories are validated; any .stxt/ found on the way is omitted, because it is the resolution chain, not documents to check.

4. The same from VS Code

With the books/ folder open in Visual Studio Code and the extension installed (code --install-extension stxt-lang.stxt) there is nothing to configure: the extension follows the same resolution chain as the CLI — the .stxt/ of the document and of its ancestors, even above the workspace root, then ~/.stxt and /etc/stxt — so it resolves the same template and emits the same codes. With docs/book.stxt open:

  • Syntax errors are underlined in red, and grammar errors show as warnings, with the same code and message as on the command line.
  • Completion (Ctrl+Space): inside Book it proposes only the children the template allows; in an ENUM, its values.
  • Hover over a node name: its canonical name, its type, the allowed values if it is an ENUM, and the description given by the template.
  • Go to definition (F12) on Published opens .stxt/@stxt.template/com.acme.book.stxt at the line Published: (?) DATE.
  • Format document, from the command palette or the editor's shortcut: it reindents and normalises without losing comments or blank lines.

When the template is edited, the extension resolves again and revalidates the open documents without reloading anything. If a document has a namespace but there is no grammar for it anywhere, no warning is shown: schemas are optional, and a document without a grammar is not wrong, it just cannot be validated.

5. More than one level: user and system

The project's .stxt/ is the first level of the chain; there are two more:

Level Where stxt install … What for
Project .stxt/ of the document and its ancestors --local The project's grammars, versioned with it
User ~/.stxt (%USERPROFILE%\.stxt) --user Personal definitions, shared by all the user's projects
System /etc/stxt (%ProgramData%\stxt) --system Definitions an organisation distributes to a whole machine

Precedence is per namespace, not per directory: for each namespace the closest level that defines it wins, and the other levels keep contributing the namespaces that one does not define. So a project can carry its com.acme.book while the user keeps an org.ana.notes template for personal notes in ~/.stxt, and both apply in the same validation.

Two definitions of the same namespace at the same level are not allowed. If the book template is copied to .stxt/other/copy.stxt, stxt schemas reports it and the namespace is left without an active definition until one of the two is removed:

stxt schemas docs

Resolution chain for /home/ana/books/docs:
    /home/ana/books/.stxt

No namespaces resolved.

Errors:
    DISCOVERY_DUPLICATE_NAMESPACE /home/ana/books/.stxt/other/copy.stxt: Duplicate definition for namespace 'com.acme.book' at level /home/ana/books/.stxt: already defined in /home/ana/books/.stxt/@stxt.template/com.acme.book.stxt

stxt validate reports that same error (line 0, naming the offending file) and fails: the tool never silently picks one of the two. See STXT-DISCOVERY-SPEC §5 and §8.

6. Continuous integration and STXT_PATH

In a CI job the result should not depend on the contents of ~/.stxt or /etc/stxt on the machine running it. The STXT_PATH environment variable replaces the whole resolution chain with a list of directories (separated by :, or ; on Windows), in order of precedence; the entries do not have to be called .stxt. With it, two steps make the job fail if any document does not validate or is not well formatted:

STXT_PATH=./.stxt npx @stxt-lang/cli validate --recursive docs/
npx @stxt-lang/cli format --check --recursive docs/

format --check writes nothing: it only lists which files would change and fails if there is any, like gofmt -l or prettier --check. Reformatting requires --write; no command rewrites files without an explicit request.

A STXT_PATH that is defined but empty leaves the chain empty: no document with a namespace finds a grammar and validate fails with SCHEMA_NOT_FOUND; checking the syntax alone must be requested explicitly with --no-schema. An entry that does not exist contributes nothing and is not an error. See STXT-DISCOVERY-SPEC §6.

7. Without installing anything: the playground

In the playground there is no file system, so there is no .stxt/ and no directory chain: documents and grammars are associated by namespace within the workspace. Every template or schema present in the document list feeds validation, and each document is validated against the definition whose namespace matches. Two grammars with the same namespace in the workspace are an error, exactly as in section 5.

The initial seed includes book.stxt and the com.acme.book grammar: when the date is altered or the ISBN removed from the book, the problems panel at the bottom shows the same codes as the CLI. Completion, hover and the tabs/spaces switch work as in the extension, and Share copies a URL that contains the whole workspace, to share it with no installation.

8. Summary

Goal How
Start a project A .stxt/ directory next to the documents
Put a grammar in its place Write it into .stxt/, or stxt install grammar.stxt
See which grammar applies and where it is from stxt schemas [directory]
Validate from the terminal stxt validate --recursive docs/
Validate while writing VS Code with the extension, or the playground
Share a grammar across projects stxt install --user grammar.stxt (~/.stxt)
Keep CI independent of the machine STXT_PATH=./.stxt in front of the command
Check formatting in CI stxt format --check --recursive docs/