La línea de comandos

stxt es el comando oficial de STXT: valida, formatea y describe documentos desde un terminal, un Makefile o un trabajo de integración continua. Es una interfaz sobre la biblioteca TypeScript @stxt-lang/core —no lleva parser propio—, así que da los mismos errores, con los mismos códigos, que la extensión de VS Code, el playground y las bibliotecas Java y Python. Es además la única línea de comandos del ecosistema: las bibliotecas no traen la suya.

Esta página es la referencia de los comandos. Para verlos en uso sobre un proyecto real, con su directorio .stxt/ y VS Code al lado, está El entorno de trabajo; para el resto de herramientas, Herramientas.

Instalación

Se publica en npm como @stxt-lang/cli y necesita Node 20 o superior:

npm install -g @stxt-lang/cli
stxt --version

stxt 0.7.1 (@stxt-lang/core 0.7.1)

--version enseña dos versiones: la del comando y la del parser que lleva dentro. Sin instalar nada de forma permanente, cualquier comando funciona con npx:

npx @stxt-lang/cli validate --recursive docs/

Para actualizar, npm update -g @stxt-lang/cli; para quitarlo, npm uninstall -g @stxt-lang/cli. Hasta la 1.0, una versión menor puede cambiar la superficie de los comandos; el número de versión y las novedades están en el repositorio.

Sinopsis

stxt [--version | --help]
stxt validate <file|dir>... [--recursive] [--format text|json] [--warn-schema | --no-schema]
stxt format   <file|dir>... [--recursive] [--tabs | --spaces] [--write | --check] [--clean]
stxt describe <file>
stxt schemas  [path]
stxt install  <file> [--local | --user | --system | --root <dir>] [--force] [--ignore-non-definitions]
Comando Qué hace
stxt validate Parsea y valida documentos contra las gramáticas que resuelve; calla si todo pasa
stxt format Reescribe documentos en forma canónica, conservando comentarios
stxt describe Emite el árbol lógico de un documento como JSON (STXT-TREE-SPEC)
stxt schemas Enseña la cadena de resolución y qué gramática aplica a cada namespace
stxt install Valida una gramática y la instala en un nivel de la cadena de resolución

Convenciones de la interfaz, comunes a todos los comandos:

  • Cada opción tiene una sola forma larga, con doble guion (--recursive). Solo cuatro tienen alias corto, y son las convenciones Unix casi universales: -v (--version), -h (--help), -r (--recursive) y -w (--write).
  • --version y --help se atienden en cualquier posición y ganan a lo demás: stxt validate --help imprime la ayuda general (no hay ayuda por comando).
  • Una opción desconocida —también las de un solo guion— o un comando inexistente son errores de uso y terminan con código 2 sin hacer nada.
  • Los resultados (hallazgos, JSON, documentos formateados, listados) van por la salida estándar; los errores de uso y de lectura, por la salida de error.
  • Ningún comando reescribe ficheros sin pedirlo: format solo escribe con --write, e install solo sobrescribe con --force.

Códigos de salida

El contrato es el mismo para todos los comandos y está pensado para scripts: un trabajo de CI tiene que poder distinguir los documentos tienen errores de el comando se ha usado mal.

Código Significado
0 Todo bien: los documentos parsean y validan, no hay nada que reformatear, la gramática se ha instalado…
1 Fallo de los documentos: errores de sintaxis o de gramática, ficheros que --check cambiaría, un fichero ilegible, una definición que no se puede instalar
2 Uso incorrecto: comando u opción desconocidos, falta un argumento, opciones incompatibles, un directorio sin --recursive

Así, stxt validate docs/ && make deploy hace exactamente lo que parece, y un 2 inesperado en CI señala un script roto, no un documento roto.

stxt validate

stxt validate <file|dir>... [--recursive] [--format text|json] [--warn-schema | --no-schema]

Para cada documento: lo parsea, resuelve su cadena de gramáticas —los directorios .stxt/ de su carpeta y de todas sus ancestras, después ~/.stxt y /etc/stxt, o lo que diga STXT_PATH; ver Resolución de gramáticas, más abajo— y lo valida contra la gramática de cada namespace que use. Cuando todo pasa no escribe nada y termina con 0.

Opción Efecto
--recursive, -r Desciende en los directorios y valida todos sus *.stxt, ordenados por nombre; los .stxt/ se saltan
--format text Un hallazgo por línea, más un resumen (por defecto)
--format json Los mismos hallazgos como array JSON, para máquinas
--warn-schema Los errores de gramática se reportan como avisos y no hacen fallar; los de sintaxis, sí
--no-schema Solo sintaxis: no resuelve ni aplica ninguna gramática

Un directorio como argumento exige --recursive; sin él es un error de uso (2), para que nadie valide "todo el proyecto" sin querer. Se pueden mezclar ficheros y directorios en la misma llamada.

Los hallazgos

Con el proyecto de El entorno de trabajo —la plantilla com.acme.book en .stxt/ y docs/book.stxt—, un documento con una fecha mal escrita y sin el ISBN obligatorio produce:

stxt validate docs/book.stxt

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

Cada línea es fichero:línea: [CÓDIGO] mensaje (severidad), con la ruta absoluta del fichero, y al final el recuento. El código es estable, va en mayúsculas y es el mismo en todas las herramientas del ecosistema, así que sirve para filtrar en un script o para buscar en las especificaciones. Los más habituales:

Código De qué habla
MIXED_INDENTATION, INDENTATION_LEVEL_NOT_VALID, INVALID_LINE Sintaxis (STXT-SPEC)
INVALID_VALUE, INVALID_NUMBER, CHILD_NOT_DECLARED, NODE_NOT_EXIST_IN_SCHEMA Gramática (STXT-SCHEMA-SPEC)
SCHEMA_NOT_FOUND El documento usa un namespace que la cadena no define
DISCOVERY_DUPLICATE_NAMESPACE, DISCOVERY_NOT_A_DEFINITION La propia cadena de resolución (STXT-DISCOVERY-SPEC)
FILE_NOT_READABLE El fichero no existe o no se puede leer

Un error de cardinalidad se señala en la línea del padre (Book, línea 1), porque es el padre quien tiene cero ISBN. Los errores de la propia cadena de resolución —una gramática rota en .stxt/, dos definiciones del mismo namespace en el mismo nivel— se reportan como hallazgos en la línea 0 del fichero causante, no del documento, y hacen fallar la validación: la herramienta nunca elige una de las dos en silencio.

stxt validate docs/book.stxt

/home/ana/libros/.stxt/otro/copia.stxt:0: [DISCOVERY_DUPLICATE_NAMESPACE] Duplicate definition for namespace 'com.acme.book' at level /home/ana/libros/.stxt: already defined in /home/ana/libros/.stxt/@stxt.template/com.acme.book.stxt (error)
1 error(s), 0 warning(s)

Sin gramática, o con la gramática en aviso

Las gramáticas son opcionales. Un documento sin namespace, o cuyo namespace no define ninguna gramática de la cadena, no está mal: solo no se puede validar. Por eso SCHEMA_NOT_FOUND únicamente se reporta cuando la cadena del documento tiene gramáticas y ninguna cubre el namespace que usa —el caso de un namespace mal escrito—; si la cadena está vacía, no se dice nada. Un documento que es una definición (@stxt.schema o @stxt.template) se comprueba siempre contra su meta-esquema, aunque no haya ninguna otra gramática.

Dos opciones cambian qué cuenta como fallo. --warn-schema rebaja los errores de gramática a avisos, útil mientras una gramática se está introduciendo en un proyecto con documentos antiguos:

stxt validate --warn-schema docs/book.stxt

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

Termina con 0: hay avisos pero ningún error. Y --no-schema comprueba solo la sintaxis, sin resolver ni aplicar gramáticas: el mismo documento pasa en silencio.

Salida JSON

Con --format json la salida es un array con un objeto por hallazgo —file, line, code, message, severity—, y [] cuando todo pasa. No hay resumen ni texto adicional, así que se puede encadenar directamente con jq o leer desde otro programa. El código de salida es el mismo que en modo texto.

stxt validate --format json docs/book.stxt

[{"file":"/home/ana/libros/docs/book.stxt","line":6,"code":"INVALID_VALUE","message":"Published: Invalid date (1 de octubre de 2025)","severity":"error"},{"file":"/home/ana/libros/docs/book.stxt","line":1,"code":"INVALID_NUMBER","message":"0 nodes of 'com.acme.book:isbn' and min is 1","severity":"error"}]
# Cuántos hallazgos de cada código hay en todo el proyecto
stxt validate --format json --recursive docs/ | jq -r '.[].code' | sort | uniq -c

stxt format

stxt format <file|dir>... [--recursive] [--tabs | --spaces] [--write | --check] [--clean]

Reescribe documentos en su forma canónica: la indentación normalizada a tabuladores (o a cuatro espacios con --spaces), un solo espacio tras los dos puntos, sin espacios al final de línea. Trabaja línea a línea: re-renderiza las líneas que abren un nodo y conserva todo lo que el árbol no describe —comentarios, líneas en blanco y el contenido de los bloques >>, que solo se reindenta—, además del final de línea original (CRLF incluido) y la ausencia de salto final. El namespace se escribe únicamente donde el fuente lo escribió.

Tres modos, mutuamente excluyentes:

Modo Qué hace Sale con 1 si…
(por defecto) Imprime el resultado por la salida estándar; no toca el disco Algún documento no parsea
--check Lista los ficheros que cambiarían (<fichero>: would be reformatted); no escribe nada Alguno cambiaría, o no parsea
--write, -w Reescribe cada fichero in situ, solo si realmente cambia, y lo anuncia (Formatted <fichero>) Algún documento no parsea

Y dos opciones más:

Opción Efecto
--tabs / --spaces Indentar con tabuladores (por defecto) o con cuatro espacios
--recursive, -r Igual que en validate: desciende, ordena por nombre, salta los .stxt/
--clean Cambia de motor: reserializa el árbol lógico, con lo que se pierden comentarios y líneas en blanco

--check es la idea de gofmt -l o prettier --check, y es lo que va en CI. --clean existe para obtener el documento canónico puro; como pierde información, nunca es el comportamiento por defecto y hay que pedirlo. Un documento con errores de sintaxis se reporta y no se reformatea, en ningún modo. format no tiene modo de gramática: reescribir un documento no tiene nada que ver con si valida.

Un documento escrito con espacios, un comentario, una línea en blanco y espacios de más:

# Ficha del libro
Book (com.acme.book):
    Title:   Arquitectura de software moderna

    Authors:
        Author: María Pérez
    ISBN: 978-84-123456-7-8
stxt format docs/book.stxt

# Ficha del libro
Book (com.acme.book):
	Title: Arquitectura de software moderna

	Authors:
		Author: María Pérez
	ISBN: 978-84-123456-7-8

El comentario y la línea en blanco siguen ahí; la indentación es ahora de tabuladores y los espacios sobrantes han desaparecido. Los otros dos modos sobre el mismo fichero:

stxt format --check docs/book.stxt
/home/ana/libros/docs/book.stxt: would be reformatted
# código de salida 1

stxt format --write docs/book.stxt
Formatted /home/ana/libros/docs/book.stxt

stxt format --check docs/book.stxt
# ya no escribe nada: código de salida 0

Combinar --write con --check, o --tabs con --spaces, es un error de uso (2): stxt format: --write and --check cannot be combined.

stxt describe

stxt describe <file>

Parsea un documento y escribe por la salida estándar su árbol lógico en el JSON canónico de STXT-TREE-SPEC: un array con los nodos raíz, y para cada nodo name, canonicalName, el namespace efectivo, la forma ("inline" con value y children, o "block" con lines). No incluye posiciones, comentarios ni nada derivado. Es la forma de llevar un documento STXT a cualquier programa que hable JSON sin escribir un parser.

describe no resuelve gramáticas ni valida: esos resultados no forman parte del árbol lógico. Si el documento tiene errores de sintaxis no emite un árbol parcial: informa los errores por la salida de error y termina con 1.

Book (com.acme.book):
	Title: Arquitectura de software moderna
	Authors:
		Author: María Pérez
	ISBN: 978-84-123456-7-8
	Chapter: Introducción
		Content >>
			Conceptos básicos y objetivos del libro.
stxt describe docs/book.stxt

[
  {
    "name": "Book",
    "canonicalName": "book",
    "namespace": "com.acme.book",
    "form": "inline",
    "value": "",
    "children": [
      {
        "name": "Title",
        "canonicalName": "title",
        "namespace": "com.acme.book",
        "form": "inline",
        "value": "Arquitectura de software moderna",
        "children": []
      },
      {
        "name": "Authors",
        "canonicalName": "authors",
        "namespace": "com.acme.book",
        "form": "inline",
        "value": "",
        "children": [
          {
            "name": "Author",
            "canonicalName": "author",
            "namespace": "com.acme.book",
            "form": "inline",
            "value": "María Pérez",
            "children": []
          }
        ]
      },
      {
        "name": "ISBN",
        "canonicalName": "isbn",
        "namespace": "com.acme.book",
        "form": "inline",
        "value": "978-84-123456-7-8",
        "children": []
      },
      {
        "name": "Chapter",
        "canonicalName": "chapter",
        "namespace": "com.acme.book",
        "form": "inline",
        "value": "Introducción",
        "children": [
          {
            "name": "Content",
            "canonicalName": "content",
            "namespace": "com.acme.book",
            "form": "block",
            "lines": [
              "Conceptos básicos y objetivos del libro."
            ]
          }
        ]
      }
    ]
  }
]
# El título del libro, con jq
stxt describe docs/book.stxt | jq -r '.[0].children[] | select(.canonicalName == "title") | .value'

stxt schemas

stxt schemas [path]

Enseña la cadena de resolución de un documento o de un directorio —por defecto, el directorio actual— y, para cada namespace, qué definición queda activa y de qué fichero sale. Es la manera de responder "¿contra qué se está validando esto?" antes de pelearse con un SCHEMA_NOT_FOUND.

stxt schemas docs

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

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

La cadena lista los niveles en orden de precedencia: los .stxt/ del documento y de sus ancestros, y después el de usuario y el del sistema si existen. Si el proyecto está dentro de otro con su propio .stxt/ (un monorepo), aparecen los dos, y para cada namespace gana el más cercano. Cuando no hay nada:

stxt schemas

Resolution chain for /home/ana/notas:
    (empty — no .stxt directory found)

No namespaces resolved.

Los errores de la cadena se enseñan en un bloque Errors: al final, con su código. El caso típico es una copia de la misma gramática en dos sitios del mismo nivel: el namespace se queda sin definición activa hasta que se quite una, y validate falla con el mismo error.

stxt schemas docs

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

No namespaces resolved.

Errors:
    DISCOVERY_DUPLICATE_NAMESPACE /home/ana/libros/.stxt/otro/copia.stxt: Duplicate definition for namespace 'com.acme.book' at level /home/ana/libros/.stxt: already defined in /home/ana/libros/.stxt/@stxt.template/com.acme.book.stxt

schemas siempre termina con 0, también con errores en la cadena: informa, no juzga; quien falla es validate.

stxt install

stxt install <file> [--local | --user | --system | --root <dir>] [--force] [--ignore-non-definitions]

Instala las definiciones —esquemas y plantillas— de un fichero en un nivel de la cadena de resolución. No es una copia: primero comprueba que el fichero tenga extensión .stxt, que parsee y que cada uno de sus nodos raíz sea una definición que valide contra su meta-esquema (la misma comprobación que hace el resolutor al cargar un nivel), y solo entonces escribe, todo o nada. Cada definición se escribe por separado, en forma canónica, como <nivel>/@stxt.schema/<namespace>.stxt o <nivel>/@stxt.template/<namespace>.stxt, con el namespace que define, no el nombre del fichero de origen; un fichero con varias definiciones se parte en varios.

Esa nomenclatura es una convención de la CLI, no del lenguaje: STXT-DISCOVERY-SPEC no da significado ni a los nombres de fichero ni a los subdirectorios de un nivel, así que a mano se puede colocar la gramática donde se quiera. La convención vale porque hace evidente, con un ls, qué namespaces define cada nivel.

Nivel Opción Dónde escribe
Proyecto --local ./.stxt (el directorio actual; por defecto)
Usuario --user ~/.stxt (%USERPROFILE%\.stxt en Windows)
Sistema --system /etc/stxt (%ProgramData%\stxt en Windows)
Cualquiera --root <dir> El directorio que se indique, exista o no
Opción Efecto
--force Sobrescribe una definición ya instalada para ese namespace (choque de ruta o de namespace)
--ignore-non-definitions Instala las definiciones del fichero y salta los demás nodos raíz, en vez de fallar
stxt install book-template.stxt

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

Lo que hace fallar la instalación (código 1), y qué dice:

stxt install book-template.stxt
stxt install: /home/ana/libros/.stxt/@stxt.template/com.acme.book.stxt already exists (use --force to overwrite)

stxt install docs/book.stxt
stxt install: docs/book.stxt:1: root node 'Book' belongs to 'com.acme.book', not to @stxt.schema or @stxt.template (use --ignore-non-definitions to install only the definitions)

stxt install broken.stxt
stxt install: broken.stxt: invalid @stxt.template definition: Type not valid: NOTATYPE

stxt install notes.txt
stxt install: not an STXT document (.stxt expected): notes.txt

Un fichero que mezcla una plantilla con documentos de ejemplo se instala con --ignore-non-definitions: entra la plantilla, se ignoran los ejemplos. Y para compartir una gramática entre todos los proyectos de una máquina, stxt install --user gramatica.stxt.

Resolución de gramáticas y STXT_PATH

Los tres comandos que aplican gramáticas —validate, schemas e install— usan la misma cadena de resolución que la extensión de VS Code y las bibliotecas, la de STXT-DISCOVERY-SPEC: para un documento, todos los directorios .stxt/ desde su carpeta hacia arriba, después ~/.stxt y por último /etc/stxt. Dentro de cada nivel se cargan todos los ficheros .stxt, recursivamente, y cada uno debe ser una definición. La precedencia es por namespace: para cada uno manda el nivel más cercano que lo defina, y los demás niveles siguen aportando los que aquel no define. Dos definiciones del mismo namespace en el mismo nivel son un error, y ese namespace se queda sin definición.

La variable de entorno STXT_PATH sustituye la cadena entera por una lista de directorios separados por : (; en Windows), en orden de precedencia; las entradas no tienen por qué llamarse .stxt, y una que no exista simplemente no aporta nada. Es la forma de que un trabajo de CI no dependa de lo que haya en el ~/.stxt o el /etc/stxt de la máquina que lo ejecuta:

STXT_PATH=./.stxt stxt validate --recursive docs/

Una STXT_PATH definida pero vacía deja la cadena vacía: los documentos se comprueban solo sintácticamente, y schemas lo enseña como (empty — no .stxt directory found). La CLI no añade ninguna regla propia a esta cadena; toda la política vive en la biblioteca, y por eso es idéntica en todas las herramientas.

Recetas

Integración continua. Dos pasos: los documentos validan y están bien formateados. Falla con 1 si algo no pasa, y con 2 si el propio comando está mal escrito.

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

Un Makefile.

check:
	stxt validate --recursive docs/
	stxt format --check --recursive docs/

fmt:
	stxt format --write --recursive docs/

Formatear un proyecto entero por primera vez, pasándolo a espacios:

stxt format --write --spaces --recursive .

Los .stxt/ se saltan; las gramáticas se formatean aparte si se quiere. Los hallazgos como datos —por ejemplo, solo los errores de un código concreto—:

stxt validate --format json --recursive docs/ | jq '.[] | select(.code == "SCHEMA_NOT_FOUND")'

Un documento como JSON para otro programa:

stxt describe config.stxt > config.json

Límites

Lo que la línea de comandos no hace, para no buscarlo:

  • No lee de la entrada estándar: todos los comandos trabajan sobre ficheros y directorios.
  • No convierte a otros formatos: la única salida estructurada es el JSON de describe (el árbol de STXT-TREE-SPEC) y el de validate --format json.
  • No hay ayuda por comando: stxt --help es toda la ayuda, y esta página, la referencia.
  • No hay parser dentro. Un error de parseo o de validación es de la biblioteca @stxt-lang/core —y, por tanto, común a todas las herramientas—; el sitio para reportarlo es stxt-js. Los de opciones, mensajes o comportamiento del comando, en stxt-cli.