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). --versiony--helpse atienden en cualquier posición y ganan a lo demás:stxt validate --helpimprime 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
2sin 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:
formatsolo escribe con--write, einstallsolo 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
sí 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 devalidate --format json. - No hay ayuda por comando:
stxt --helpes 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.