# La línea de comandos (/dev/es/tasks)



La herramienta es `remy`, el bin de `@joeblew999/remy-ui` (`packages/ui/src/cli`, un módulo de TypeScript por
comando); una app la obtiene con el paquete (`npm install`, en la versión del paquete) y la nombra en los
scripts de su `package.json`, los mismos en todas las apps (`"dev:change": "remy change"`). Una app indica una sola vez, en
`remy.config.ts`, dónde está desplegada y cuáles son sus puertos:

```ts
import type { RemyProject } from '@joeblew999/remy-ui/project';

export default {
  origins: { production: 'https://your-app.your-subdomain.workers.dev', docs: 'https://your-app-docs.your-subdomain.workers.dev' },
  ports: { preview: 4173, docsPreview: 4174 },   // the main checkout's; a worktree computes its own from its name
} satisfies RemyProject;
```

### La estructura [#the-layout]

Todo repositorio de Remy, remy-auth incluido, tiene los mismos archivos en los mismos lugares; lo que hay
dentro de los archivos del producto es diferente. La estructura son las rutas que leen los comandos y el
paquete, así que un archivo en cualquier otro lugar simplemente no se
encuentra; ninguna comprobación propia lo mantiene.

| Ruta                                                                                                                                                                                               | Leído por                                                        | Contiene                                                                                                                                                                                     |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `package.json`'s `scripts`, `remy.config.ts`, `.node-version`                                                                                                                                      | npm, `remy`                                                      | la entrada: todos los comandos; los orígenes, los puertos y el paquete de release; la versión de Node                                                                                        |
| `package.json`, `package-lock.json`                                                                                                                                                                | npm, `remy` (workspaces)                                         | las dependencias de la app; `workspaces` cuando es dueña de paquetes                                                                                                                         |
| `wrangler.jsonc`                                                                                                                                                                                   | wrangler, el plugin de Vite de Cloudflare, `remy cloudflare`     | el nombre del Worker, los bindings y los assets                                                                                                                                              |
| `vite.config.ts`, `tsconfig.json`, `playwright.config.ts`                                                                                                                                          | Vite, `remy check types`, `remy test`                            | el build, los tipos y las comprobaciones de navegador                                                                                                                                        |
| `fnox.toml`, `skills-lock.json`                                                                                                                                                                    | fnox, `remy skills`                                              | los nombres de los secretos; las skills fijadas                                                                                                                                              |
| `src/routes/`, `src/routeTree.gen.ts`                                                                                                                                                              | TanStack Start (el árbol se genera)                              | las páginas del producto                                                                                                                                                                     |
| `src/parts.json`                                                                                                                                                                                   | el `remyParts()` del paquete                                     | las partes compartidas que lista la app (ninguna sin el archivo; la app en blanco lista `seo-routes`)                                                                                        |
| `src/api/`                                                                                                                                                                                         | la ruta `api.$` de la app                                        | los procedimientos del producto, si tiene API                                                                                                                                                |
| `tests/` (`smoke.spec.ts` por nombre)                                                                                                                                                              | `remy test`, el smoke en vivo tras un despliegue                 | las comprobaciones compartidas con los ajustes de la app, y las propias                                                                                                                      |
| `public/`                                                                                                                                                                                          | Vite                                                             | favicon, `_headers`                                                                                                                                                                          |
| `dist/` (`dist/client/assets`)                                                                                                                                                                     | los despliegues, las comprobaciones                              | resultado del build, nunca en commit                                                                                                                                                         |
| `docs/` (`docs.config.ts`, `vite.config.ts`, `tsconfig.json`, `package.json`, `content/{users,dev,ui}/` con `i18n.json` y `meta.json`, `content/questions.json`; generados `.remy-docs/`, `dist/`) | `remy docs`, `remy i18n` (`I18N_DOCS_DIR`)                       | la documentación de la app: su identidad y sus páginas; el Worker es del paquete                                                                                                             |
| `packages/<name>/` con `README.md`                                                                                                                                                                 | `remy plans`, `remy packages`                                    | los paquetes que publica el repositorio, si los hay                                                                                                                                          |
| `messages/`, `project.inlang/`                                                                                                                                                                     | el preset de la app, el script `generate` de la app, `remy i18n` | las cadenas propias de la app, si las hay (compiladas en `src/paraglide/`)                                                                                                                   |
| `.plans/` (`now.md`, `done/`, `parked/`)                                                                                                                                                           | `remy plans`                                                     | los planes del repositorio                                                                                                                                                                   |
| `AGENTS.md`, `.github/workflows/`                                                                                                                                                                  | agentes; GitHub                                                  | el puntero de los agentes a la documentación; CI                                                                                                                                             |
| `packages/ui/src/cli/`, `template/`, `fixtures/consumer/`                                                                                                                                          | el paquete; `giget`; `remy test consumers`                       | solo remy-auth: la propia línea de comandos, la app en blanco desde la que arranca un repositorio nuevo, y el fixture de consumidor que prueba ambos tal como los recibiría otro repositorio |

### Un consumidor nuevo [#a-new-consumer]

La receta única. Una app nueva arranca desde la app en blanco, [`template/`](https://github.com/joeblew999/remy-auth/tree/main/template)
en remy-auth en la tag de la versión: la estructura de arriba con solo los archivos propios del repositorio
dentro (sus nombres, una página de inicio en cada idioma, un índice de documentación por sitio, los
archivos de configuración de la estructura con apenas unas líneas cada uno). Todo lo demás es el paquete,
que un solo cambio de versión actualiza.

1. Requisitos: Node según indica `.node-version`, `gh auth login` con un token que tenga `read:packages`,
   Google Chrome (lo usan las comprobaciones) y `wrangler login` antes del primer despliegue.
2. `npx -y giget@3.3.1 gh:joeblew999/remy-auth/template#vX.Y.Z <name>`, luego `cd <name>` y `git init`
   (las herramientas de traducción y de planes leen el historial de git; `npm run setup` lo indica si falta).
3. Nombra la aplicación: sustituye `my-app` y `My app` (`grep -rn -i "my.app" --exclude-dir=node_modules .`):
   `package.json`, `wrangler.jsonc`, `src/service.ts`, los orígenes de `remy.config.ts`, `docs/docs.config.ts` (el
   nombre y la fuente del producto, que también lee el marco de la app) y `messages/en.json`.
4. Node según indica `.node-version`, luego `GITHUB_TOKEN=$(gh auth token) npm install` una vez para
   escribir `package-lock.json`, luego `npm run setup` (npm ci, las skills fijadas y la skill `remy`, el
   bloque de reglas de `AGENTS.md`, el registro MCP, el guardián del flujo, las comprobaciones de la
   instalación).
5. Haz commit de todo, `package-lock.json`, `skills-lock.json` y `src/routeTree.gen.ts` incluidos. Las
   cadenas de la app en blanco ya vienen traducidas; cuando el inglés cambie (`messages/en.json`, la
   documentación), haz commit de ello y traduce esto una vez a mano, `REMY_FLOW=hand npx remy i18n
   translate`, que escribe y hace commit de los demás idiomas; a partir de entonces lo hace
   `dev:land`.
6. El primer despliegue, a mano: `npx remy cloudflare deploy`, y `npx remy cloudflare docs-deploy` para la
   documentación. Una vez que el repositorio está en GitHub (paso 7), el trabajo pasa por [el flujo](./how-we-work.md#the-flow-four-steps-and-a-guard-that-refuses-the-rest):
   `dev:land` para cada cambio terminado, `dev:promote` para producción y la documentación.
7. CI: `.github/workflows/google.yml` llama al flujo de trabajo compartido de remy-auth en la misma tag (todos los
   idiomas, las auditorías de Google en cada push a `main`). Da al repositorio nuevo acceso de lectura en la
   configuración del paquete `@joeblew999/remy-ui` ("Manage Actions access"), o `npm ci` falla allí.

Después, la app crece en sus propios archivos: páginas en `src/routes/` (una página junto a la ruta de un
padre, como `videos.$videoId` al lado de `videos`, es `videos_.$videoId.tsx`: TanStack anida
`videos.$videoId` dentro de `videos`, que entonces necesita un `<Outlet />`), sus enlaces en
`src/remy-app.tsx`, cadenas en `messages/en.json` (`m` desde `src/paraglide/messages.js`), una API como
paquete de contrato y `src/api/` (todo procedimiento que recibe entrada documenta un error, o `apiChecks`
falla: la regla `api/coverage` del paquete), documentación en `docs/content/`. Pasa a una versión nueva con
`npm run upgrade -- <version>` (el paquete y la tag del flujo de trabajo de CI, juntos). Dos ejecuciones de
navegador locales a la vez comparten `PREVIEW_PORT` y la segunda falla al iniciar su servidor: dale a cada
shell el suyo (`PREVIEW_PORT=4232 npm run …`).

### Un repositorio que publica paquetes [#a-repository-that-publishes-packages]

Un repositorio también puede publicar paquetes propios (un contrato, un paquete de UI), como hace
remy-auth; eso no cuesta ningún comando propio. Sus paquetes son workspaces de npm (`packages/<name>/`,
`workspaces` de `package.json`), y cada uno que no es `private` se publica mediante `remy packages`
(`npm run packages -- …`):

| Comando            | Hace                                                                                                                                                                                                                                           |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `check`, `upgrade` | Versiones npm más nuevas de las dependencias propias del repositorio (raíz y workspaces; nunca sus propios paquetes ni `@joeblew999/remy-ui`), previsualizadas (`check`) o aplicadas, y luego las comprobaciones de la instalación (`upgrade`) |
| `pack`             | Un tarball de cada paquete publicado, para que una app de prueba lo instale antes de cualquier release                                                                                                                                         |
| `release`          | Las puertas de control (traducciones en modo estricto, cada paquete modificado con su versión subida, `remy verify`, el script `release-checks` de la app, auditorías de Google, Core Web Vitals), y luego `tag`; `dev:release` lo ejecuta     |
| `tag`              | Sobre un `main` limpio: etiqueta la versión del paquete de release, hace push y luego `publish --release`                                                                                                                                      |
| `publish`          | Publica cada paquete cuya versión es nueva en su registro; con `--release vX.Y.Z` también el release de GitHub a partir de `CHANGELOG.md` (`notes`). El job de CI de la tag ejecuta el mismo comando                                           |

En `remy.config.ts`: `release.package` (el paquete cuya versión da nombre al release) y
`release.title`. El `exports` de un paquete nombra cada subruta que sus usuarios importan
(`"./video": "./src/video.ts"`); cualquier otra cosa se rechaza en tiempo de build y en Playwright, cuyo
mensaje nombra el archivo que importa, no el mapa de exports. Las cadenas propias de un paquete son su
propio proyecto inlang, compilado por su build, cuyo runtime sigue el idioma de la plataforma con una sola
llamada, `followLocale(runtime)` (`locale`); las tareas de i18n lo leen a través de `I18N_INLANG`. Las
comprobaciones que el repositorio añade a un release van en su script `release-checks` (remy-auth:
`template:check`, `ui:verify`). remy-auth fija la versión de un release con `npm run ui:version -- X.Y.Z`,
que la escribe en todos los lugares donde aparece (el paquete y los pines de la app en blanco).

### Elegir la versión: publicada, de desarrollo o local [#choosing-the-version-released-development-or-local]

La herramienta y el paquete son una sola cosa: el `npm run dev:release` de remy-auth publica
`@joeblew999/remy-ui` X.Y.Z, bin incluido, y etiqueta ese mismo commit como `vX.Y.Z`. Quien consume el
paquete fija ese único número en su `package.json` (y en la tag de su flujo de trabajo de CI), y
`npm run upgrade -- X.Y.Z` cambia ambos. Para trabajar contra el checkout propio de remy-auth,
`npm link ../remy-auth/packages/ui` (y `npm unlink` para volver).

La plataforma aporta todos los paquetes npm que necesitan la app y las tareas, en una sola versión: son
dependencias propias de `@joeblew999/remy-ui` (el framework, la cadena de herramientas, las herramientas de
las comprobaciones), así que el `package.json` de una app nombra el paquete, sus propios paquetes y lo que
su producto añada. `the check's single-copies part` (dentro de `remy check`) falla cuando un paquete que se
rompe si se instala por duplicado (React, TanStack Router y Query, el stringifier de MDX; la lista
`remy.singleCopy` del paquete) está presente: quita el pin propio de la app. Los comandos llaman a los
ganchos propios de una app donde los tiene, como scripts de su `package.json`, que se ejecutan si existen:
`generate` (código generado antes de comprobar los tipos; el de la app en blanco compila su propio
catálogo), `local` (el estado local que una app necesita antes de ejecutarse, que `dev`, `build` y
`dev:start` ejecutan primero; remy-auth escribe ahí su `.dev.vars` y migra su D1 local), `release-checks` y
`test:consumers`.

El flujo de desarrollo es código, `packages/ui/src/cli/flow.ts`, con cuatro pasos y un guardián
([cómo trabajamos](./how-we-work.md#the-flow-four-steps-and-a-guard-that-refuses-the-rest)):

| Paso              | Script                                                                                                                                                                                                                                                                                                                                                                           |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Primero           | `dev:status` (main con los veredictos de GitHub, los despliegues, los worktrees por delante y por detrás, las traducciones, las pull requests; un hook `SessionStart` lo ejecuta para los agentes)                                                                                                                                                                               |
| Un trabajo        | `dev:start -- <name>` (un worktree propio desde main, `npm ci`, puertos propios, el guardián); `dev:done` lo elimina cuando el trabajo termina y queda fusionado                                                                                                                                                                                                                 |
| Cada cambio       | `dev:change` (`remy change`, cada parte una función: planes, copias únicas, rutas cuando cambia un archivo de ruta, tipos, las comprobaciones unitarias, las traducciones como aviso, la documentación cuando cambia)                                                                                                                                                            |
| Trabajo terminado | `dev:land -- "<message>"` (la comprobación, commit, main, push, el despliegue en staging, y luego la traducción cuando está desactualizada; GitHub ejecuta entonces `npm run test -- every`, `google` y `consumers` en paralelo, y una ejecución en rojo comenta en el commit. Las mismas tres en local, a propósito, por una persona: `REMY_FLOW=hand npm run test -- <level>`) |
| Producción        | `dev:promote` (se niega ante un commit que GitHub no ha aprobado; luego el despliegue a producción, el despliegue de la documentación, lo que ejecuta cada despliegue)                                                                                                                                                                                                           |
| Un release        | `dev:release` (`remy packages release`, que ejecuta `remy verify`)                                                                                                                                                                                                                                                                                                               |
| Un área           | `npm run test -- only <words>`                                                                                                                                                                                                                                                                                                                                                   |
| El guardián       | `npx remy guard` lo registra (`npm run setup` lo hace); `npx remy guard --check` lo verifica (las comprobaciones de la instalación lo hacen)                                                                                                                                                                                                                                     |

`npm run test -- every` es cada una de nuestras comprobaciones en todos los idiomas (dentro de `remy verify`).
`remy verify` y CI terminan con `remy test consumers`, los demás repositorios a los que este sirve,
probados tal como los reciben: el script `test:consumers` de la app, nada por defecto; en remy-auth, el
fixture de consumidor, que construye la app en
blanco y un paquete propio a partir de la plataforma de este commit (el paquete desde un tarball con
versión propia, las tareas desde una copia fuera de cualquier `node_modules`, el `.npmrc` de la plantilla)
y ejecuta la instalación, las skills (instaladas una vez por lista fijada y luego reutilizadas), los
invariantes de las herramientas, la comprobación, las comprobaciones rápidas de navegador, la documentación y una publicación
de prueba (dry-run) (\~1,5 a 3 min, sobre todo la red). Un cambio que rompería otro repositorio falla ahí, no
en ese repositorio. Las pruebas se comprueban con tipos junto con la app (`tsconfig.json` incluye
`tests/`). El nivel de Google es `npm run test -- google` (auditorías de Lighthouse, local; CI en cada push y
tag) y `npm run test -- cwv` (Core Web Vitals en un Worker de Cloudflare desechable); el release
ejecuta ambas.

Dónde se ejecutan las comprobaciones: el comportamiento propio del paquete compartido se comprueba una vez, en remy-auth, antes de cada
release. Una app construida sobre el paquete ejecuta un conjunto de contrato (sus páginas renderizan, las páginas de sitio y de app se mantienen
separadas, sus propias funcionalidades funcionan) más el nivel de Google en sus propias páginas de sitio, no toda la suite del paquete
de nuevo. Mantén las comprobaciones baratas en lugar de quitarlas (el reloj de Playwright en lugar de esperas
reales, una página de navegador por comprobación, workers en paralelo). Las comprobaciones recorren `checkedLocales` de
`@joeblew999/remy-ui/checks`, que respeta `CHECK_LOCALES`. Cada paso indica cuánto tardó.

### Traducciones [#translations]

Una misma estructura en cada app, para que los mismos comandos `remy i18n` funcionen en todas partes (la regla sobre quién traduce es
[un solo redactor](./how-we-work.md#translations-one-writer)):

| Qué             | Inglés                                                                       | Traducción                                                                                               |
| --------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Docs            | `docs/content/<site>/<page>.md` o `.mdx` (estructura de Fumadocs)            | a su lado: `<page>.<locale>.md` o `.mdx`                                                                 |
| Catálogos de UI | el catálogo del idioma base de cada proyecto inlang (`<dir>/project.inlang`) | el catálogo de cada idioma, según el `pathPattern` propio del proyecto (p. ej. `messages/<locale>.json`) |

Dos flujos, uno para cada tipo de texto, con el mismo patrón en ambos: detectar sin conexión con git, traducir
con el Claude Code de la máquina (headless, sin herramientas) y hacer commit. Ambos son TypeScript en el paquete
(`packages/ui/src/cli/i18n.ts`, `translate.ts`: `remy i18n check`, `remy i18n translate`); las tareas de
abajo les reenvían.

* **Mensajes de UI** (Paraglide): los catálogos, los idiomas y el idioma base vienen del propio
  `settings.json` de inlang (`I18N_INLANG`, o si no, el único proyecto que git conoce). Se detectan: catálogos y claves
  que faltan (`jq`), `{placeholders}` perdidos o inventados (`@lingual/i18n-check`), claves cuyo inglés cambió desde
  el último commit del catálogo (`git`, `jq`), y las categorías de plural que necesita el idioma (`Intl.PluralRules`,
  `tasks/i18n/messages/plurals.mjs`: ninguna herramienta externa las comprueba).
* **Docs** (Fumadocs): la carpeta es `I18N_DOCS_DIR` (por defecto `docs/content`); cada sitio con un
  `i18n.json` traduce cada página en inglés a cada uno de sus idiomas. Se detectan: páginas que faltan y
  desactualizadas (`git`), huérfanas, y el propio texto de Fumadocs UI (`ui/<lang>.json` frente a `ui/en.json`).
* **El traductor** es el Claude Code en modo headless de la máquina (`claude -p`), sin herramientas, sin servidores
  MCP, skills ni ajustes de proyecto, y con salida estructurada: las claves de un catálogo vuelven como JSON y
  se combinan en el orden de claves del inglés; una página vuelve completa, y solo se escribe cuando es una
  página (`page.ts`). Los prompts están en `packages/ui/src/cli/prompts.ts`. Una llamada por idioma
  (mensajes) o por página (docs), `I18N_JOBS` a la vez.
* Las comprobaciones no necesitan clave, ni red, pero sí el historial completo de git (un clon superficial se rechaza).
  Traducir usa el login de Claude de la máquina.
* Una app sin proyecto inlang ni sitios de documentación recibe "nothing to check" y sale con 0.

| Comando                            | Hace                                                                                                                                                  |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `remy i18n check`                  | Ambas comprobaciones; un WARNING y salida 0 mientras se programa (la comprobación la ejecuta), salida 1 con `I18N_STRICT=1` (el release)              |
| `remy i18n check messages`         | Los huecos de los catálogos, y los idiomas que difieren de los de la plataforma (`de unlisted`); `--list` una línea por hueco (`es missing nav_more`) |
| `remy i18n check docs`             | Los huecos de la documentación; `--list` una línea por hueco (`es stale docs/content/dev/gui.es.md`)                                                  |
| `remy i18n translate`              | En main: mensajes y luego documentación, un commit para cada uno; `dev:land` lo ejecuta, al final; a mano, con `REMY_FLOW=hand` delante               |
| `remy i18n translate messages`     | Los huecos de los catálogos, un idioma por llamada al agente                                                                                          |
| `remy i18n translate docs [files]` | Los huecos de la documentación, una página por llamada al agente; los archivos de traducción indicados se rehacen por completo                        |

Ajustes: `I18N_MODEL` (por defecto `sonnet`), `I18N_JOBS` (4), `I18N_COMMIT=0` (deja el cambio
sin commit para revisarlo), `I18N_BRANCH` (`main`).

### Cloudflare [#cloudflare]

`npx remy cloudflare <command>`; cada uno indica en su comentario de cabecera si es local o remoto (y de producción).

| Comando                                                  | Hace                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `deploy`                                                 | Construye con `DEPLOY_ORIGIN`, sube, espera a la nueva versión (`the wait for the new version`) y luego ejecuta su comprobación smoke en vivo. Sin pruebas antes: `dev:promote` lo ejecuta después de las comprobaciones del flujo. Se niega cuando un binding de D1, KV o R2 nombra un recurso que no existe: Wrangler crearía uno durante el despliegue, y crear recursos corresponde al propietario (`preview` se niega del mismo modo) |
| `preview [alias] [--keep]`                               | Despliega este commit como un Worker desechable `<worker>-check-<commit>` (producción intacta), ejecuta el nivel 1 contra él y lo elimina; `--keep` lo conserva                                                                                                                                                                                                                                                                            |
| `preview-delete [name]`                                  | Lista los Workers de comprobación, o elimina uno por nombre; nunca el Worker de producción                                                                                                                                                                                                                                                                                                                                                 |
| `urls [origin]`                                          | Imprime las páginas y `/healthz` del origen de producción (o de uno dado), para informes                                                                                                                                                                                                                                                                                                                                                   |
| `staging`                                                | `deploy` para el entorno de staging: construye con `CLOUDFLARE_ENV=staging` (`env.staging` en la configuración de Wrangler) y sube su propio Worker en `origins.staging`; producción queda intacta. Lo que staging permite más allá de producción está en la [tabla de entornos](./auth.md#environments-one-table) de la app                                                                                                               |
| `versions [origins…]`                                    | Pregunta a cada despliegue (los orígenes de `remy.config.ts`, o los orígenes dados) qué está ejecutando: servicio, entorno, commit, cuán lejos está eso de este checkout, cuándo se desplegó. [Se pregunta, nunca se recuerda](./gui.md#which-version-is-deployed)                                                                                                                                                                         |
| `docs-deploy`                                            | El Worker de la documentación: construye, despliega y publica sus páginas en el índice de Ask AI; `dev:promote` lo ejecuta                                                                                                                                                                                                                                                                                                                 |
| `npm run test -- remote [origin]`                        | El nivel 1 contra un despliegue, por una persona (`REMY_FLOW=hand`): los despliegues del flujo terminan con el smoke en vivo, y GitHub ejecuta la suite                                                                                                                                                                                                                                                                                    |
| `npm run observe -- events\|ai-usage\|ai-check\|gateway` | Workers Logs almacenados, uso de AI Gateway, comprobación de la configuración de IA, los ajustes del gateway ([herramientas](./tooling.md#the-docs-answers-on-cloudflare-ai-search-and-ai-gateway))                                                                                                                                                                                                                                        |
| `npm run upgrade -- <version>`                           | Mueve una app a una misma publicación compartida: la versión del paquete y la tag del flujo de trabajo de CI juntas, y luego las comprobaciones de la instalación                                                                                                                                                                                                                                                                          |

Las credenciales vienen del login de Wrangler, o del llavero a través de fnox
([herramientas](./tooling.md#secrets-fnox)).

### Planes [#plans]

La regla de los planes ([cómo trabajamos](./how-we-work.md#plans-few-short-closed)) como comandos (`npm run plans -- …`), igual en
todos los proyectos: `.plans/now.md` es la única lista ordenada, `.plans/*.md` los pocos archivos de plan abiertos,
`.plans/done/` y `.plans/parked/` el resto, `.plans/stability-log.md` opcional. Un proyecto sin
`.plans/` pasa `plans check` y no tiene nada que listar.

| Comando                         | Hace                                                                                                                                                                                                                                                                                                                  |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                        | Los elementos abiertos de `now.md` por sección, los archivos de plan abiertos con su primera línea `Status:`/`Closed`/`Parked` y su último commit (STALE a los 14 días), los planes aparcados. `--json` para agentes                                                                                                  |
| `check`                         | `now.md` existe, cada enlace relativo a un `.md` bajo `.plans/` resuelve, ningún `.md` fuera de `.plans/`, `done/` y `parked/`, cada archivo de plan abierto enlazado desde `now.md`. Instantánea, sin red; la comprobación la ejecuta primero                                                                        |
| `close <plan> "<lo publicado>"` | Añade `Closed <hoy>: ...` bajo el título, lo mueve a `done/` (desde `.plans/` o `parked/`; `git mv` si está versionado), reapunta los enlaces relativos hacia él en `.plans/`, `docs/`, `packages/*/README.md` y los `*.md` de la raíz, y sus propios enlaces desde la carpeta nueva, y tacha su elemento en `now.md` |
| `park <plan> "<por qué>"`       | Lo mismo hacia `parked/` con `Parked <hoy>: ...`                                                                                                                                                                                                                                                                      |
| `open <plan> "<por qué ahora>"` | Un plan aparcado que se retoma: lo mismo desde `parked/` de vuelta a `.plans/` con `Opened <hoy>: ...`; añade su elemento a `now.md` tú mismo                                                                                                                                                                         |

`close` y `park` cambian archivos y preparan el movimiento, pero nunca hacen commit: lee `git diff` y luego `npm run dev:change`.
