Ask AI

Proyectos: estructura, apps nuevas y publicación

Qué guarda todo proyecto de Remy en el mismo lugar, cómo empezar una app nueva, la publicación de paquetes, las traducciones, Cloudflare y los planes. Todos los comandos están en Comandos.

La herramienta es remy (y remy-check), los bins de @joeblew999/remy-cli (packages/remy-cli/src, un módulo de TypeScript por comando), publicados junto con @joeblew999/remy-ui bajo la misma versión. Una app obtiene ambos con vp install, en la versión de su catálogo, ejecuta el comando como pnpm remy … y lo nombra en los scripts de su package.json y en las tareas de su vite.config.ts, los mismos en todas las apps ("ci": "vp run ready && vp run test:browser && vp run test:audits"). Cada valor vive en el archivo de la herramienta que lo lee, nunca en uno nuestro: dónde se despliegan la app y su documentación es el .env de Vite (la línea de comandos lo lee con el loadEnv de Vite), los ajustes de cada Worker su wrangler.jsonc, y el entorno de cada despliegue los vars de Wrangler (producción, la sección previews, un .dev.vars local):

# .env
VITE_DEPLOY_ORIGIN=https://your-app.your-subdomain.workers.dev
VITE_DOCS_ORIGIN=https://your-app-docs.your-subdomain.workers.dev

remy.config.ts es solo para un repositorio que publica paquetes: su release (el nombre de remy-auth es @joeblew999/remy-ui), tipado por @joeblew999/remy-cli/project.

La estructura

Toda app de Remy 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 la mantiene. Una app es una carpeta: en su propio repositorio, la raíz del repositorio. remy-auth, que además es dueño de los paquetes, es un workspace con las mismas piezas lado a lado: los archivos de la app que se listan abajo están en apps/remy-auth/, la documentación en apps/remy-auth-docs/ (no docs/), los paquetes en packages/* (remy-ui, remy-cli, el contrato y el showcase), cada carpeta con el nombre de su paquete, y la raíz contiene los scripts de package.json, pnpm-workspace.yaml (los paquetes y el catálogo), remy.config.ts, el vite.config.ts raíz (el estilo compartido y las comprobaciones del repositorio, ready) y los archivos del repositorio (.plans/, .github/, template/), tal como vp create vite:monorepo organiza un workspace. Los comandos encuentran la app por su wrangler.jsonc y la documentación por su docs.config.ts, así que se ejecutan desde la raíz en ambos casos.

RutaLeído porContiene
package.json's scripts, .node-version (el de remy-auth)Vite Plus (vp run)los scripts que componen las tareas (ci, translate, release, setup; el upgrade de una app); la versión de Node
package.json, pnpm-workspace.yaml, pnpm-lock.yaml, .npmrcpnpm (a través de vp install), remylo que importa cada paquete (catalog:, workspace:*), la versión de pnpm (devEngines); los paquetes del workspace, el catálogo de versiones, los overrides de copia única; GitHub Packages como registro de @joeblew999
.envVite (import.meta.env), remy (loadEnv)dónde se despliegan la app y su documentación (VITE_DEPLOY_ORIGIN, VITE_DOCS_ORIGIN)
remy.config.tsremy packages, remy releaseel paquete de release, en un repositorio que publica paquetes
wrangler.jsoncwrangler, el plugin de Vite de Cloudflare, los builds Git de Cloudflare, remy cloudflareel nombre del Worker, los bindings, los assets y el entorno (vars, previews)
vite.config.ts, tsconfig.json, playwright.config.tsVite Plus (las tareas escritas ahí, vp check), Playwright (vp run test:browser)el build (remyApp()), cada tarea, los tipos y las comprobaciones de navegador
.vite-hooks/ (pre-commit, pre-push)Git, a través de Vite Plus (vp config, el script prepare)los hooks: vp staged antes de un commit, vp run ci antes de un push (VP_GIT_HOOKS=0 los omite); los copia remy upgrade
fnox.toml, skills-lock.jsonfnox, remy skillslos nombres de los secretos; las skills fijadas
src/routes/, src/routeTree.gen.tsTanStack Start (el árbol se genera)las páginas del producto
src/parts.jsonel remyParts() del paquetelas partes compartidas que lista la app (ninguna sin el archivo; la app en blanco lista seo-routes)
src/api/la ruta api.$ de la applos procedimientos del producto, si tiene API
tests/ (smoke.spec.ts por nombre)remy test (remy test live: el smoke contra un despliegue)las comprobaciones compartidas con los ajustes de la app, y las propias
public/Vitefavicon, _headers
dist/ (dist/client/assets)Wrangler (el despliegue, su simulacro), las comprobacionesresultado del build, nunca en commit
docs/ (docs.config.ts con su contract, collections.ts, wrangler.jsonc, vite.config.ts, tsconfig.json, package.json, content/{users,dev,ui}/ con i18n.json y meta.json, content/questions.json; dist/ generado)sus propias tareas, remy docs, remy i18nla documentación de la app: su identidad y sus páginas, las colecciones de Fumadocs (en commit), los ajustes del Worker de la documentación; el código del Worker es del paquete
packages/<name>/ con README.mdremy plans, remy packageslos paquetes que publica el repositorio, si los hay
messages/, project.inlang/el preset de la app, la tarea generate de la app, remy i18nlas cadenas propias de la app, si las hay (compiladas en src/paraglide/)
.plans/ (now.md, done/, parked/)remy planslos planes del repositorio
AGENTS.md, .github/workflows/agentes; GitHubel puntero de los agentes a la documentación; CI
packages/remy-cli/src/, template/, fixtures/consumer/el paquete; giget; vp run new-appsolo 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

La receta única. Una app nueva arranca desde la app en blanco, 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: el vp de la máquina en modo gestionado (herramientas para desarrollo), Node 26, gh auth login con un token que tenga read:packages, y Google Chrome (lo usan las comprobaciones).
  2. vpx 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; vp 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 .env, docs/wrangler.jsonc, 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. El token de GitHub Packages en la configuración de pnpm de esta máquina, una sola vez (pnpm no lee ningún token del .npmrc de un repositorio, que solo nombra el registro): pnpm config set --location user //npm.pkg.github.com/:_authToken "$(gh auth token)". Luego vp install una vez para escribir pnpm-lock.yaml, y después vp run setup (la instalación desde el lockfile, las skills fijadas y la skill remy, el bloque de reglas de AGENTS.md, el registro MCP, las comprobaciones de la instalación y, por último, vp run ready).
  5. Haz commit de todo, pnpm-lock.yaml, 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), ejecuta vp run translate, que escribe los demás idiomas (nunca hace commit: haz commit de ellos junto con el resto); a partir de entonces se ejecuta antes de cada commit que cambie texto.
  6. Desplegar es un push: conecta el repositorio a los builds Git de Cloudflare, uno por Worker (la app en la raíz, la documentación en docs/), con los ajustes de build de remy-auth (en Cloudflare; remy upgrade imprime el comando de build). Crear las bases de datos y los secretos que nombra un binding corresponde a quien es dueño (pnpm remy cloudflare provisioned indica qué falta). A partir de ahí el trabajo pasa por el flujo: cada cambio terminado se sube a main, y cada push a main despliega.
  7. CI: .github/workflows/ci.yml llama al flujo de trabajo compartido de remy-auth en la misma tag. Da al repositorio nuevo acceso de lectura en la configuración de los paquetes @joeblew999/remy-ui y @joeblew999/remy-cli ("Manage Actions access"), o su instalación 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 vp run upgrade <version>: @joeblew999/remy-ui y @joeblew999/remy-cli en esa versión (vp add), la tag del flujo de trabajo de CI, los scripts, los archivos de .vite-hooks/ y vite.config.ts de la app en blanco (uno antiguo se conserva como vite.config.ts.before-upgrade, para volver a mover a él sus propios plugins y opciones), pnpm-workspace.yaml, el collections.ts y el wrangler.jsonc de la documentación donde la app no los tenga, los orígenes antiguos de remy.config.ts movidos a .env, y luego las skills, las reglas y las comprobaciones de la instalación; imprime el comando de build de Cloudflare, que vive en Cloudflare. Una app en la 0.15.1 instala primero a mano los dos paquetes en la versión nueva, porque el upgrade de la 0.15.1 no puede. 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 vp run …).

Un repositorio que publica paquetes

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 los del workspace (packages/<name>/, listados en pnpm-workspace.yaml), y cada uno que no es private se publica mediante remy packages (el pack y el publish de pnpm, que escriben cada versión de catalog: y workspace:), cuyos subcomandos están en Comandos.

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 herramientas de traducción encuentran el proyecto inlang del repositorio a través de git. Las comprobaciones que el repositorio añade a un release son pasos de su propio script release, después de vp run ci (remy-auth: vp run template:check, vp run ui:verify). remy-auth fija la versión de un release con vp 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

El vp run release de remy-auth comprueba y etiqueta el commit como vX.Y.Z; después el job de release de GitHub publica @joeblew999/remy-ui X.Y.Z y, a su lado, la línea de comandos, @joeblew999/remy-cli (los comandos remy y remy-check), siempre en la misma versión, junto con el showcase y el contrato cuando se subió su versión. Todavía no se publica ninguno de los dos de esta forma, y publicar es cosa del job de release, nunca de una sesión en la nube. Quien consume fija ambos en su catálogo (pnpm-workspace.yaml, y la tag de su flujo de trabajo de CI), y vp run upgrade X.Y.Z los mueve, junto con los scripts, los hooks y la configuración de la app en blanco en esa tag. Para probar el checkout propio de remy-auth en una app, haz lo que hace la prueba de app nueva: pnpm pack de los paquetes en remy-auth y apunta el catálogo y los overrides de la app a los tarballs (file:./vendor/remy-ui.tgz).

Cada paquete lista lo que importa, en la versión del catálogo (catalog:): el package.json de una app nombra los dos paquetes de la plataforma, los paquetes del framework que importan sus propios archivos, las herramientas que llaman sus tareas (Wrangler, Playwright, Vite Plus), sus propios paquetes y lo que añada su producto. Las herramientas que ejecutan las tareas compartidas son dependencias de remy-cli. Un paquete que se rompe si se instala por duplicado (React, TanStack Router y Query, el stringifier de MDX: remy.singleCopy en el package.json de remy-ui) tiene una sola copia mediante los overrides de pnpm-workspace.yaml, en la versión del catálogo; la parte de copias únicas de la comprobación (check:single-copies de vp run ready) falla cuando el store de pnpm contiene más de una versión de cualquiera de ellos. La preparación propia de una app es una tarea en su propio vite.config.ts: una tarea local (el estado local que necesita antes de ejecutarse, que sus dev y build nombran primero; remy-auth escribe ahí su .dev.vars y migra su D1 local) y su generate (su propio catálogo, project.inlang, compilado con los tipos del Worker). Sus propias comprobaciones de release son pasos de su script release.

El flujo son los scripts y las tareas de Vite Plus; cada paso y quién lo ejecuta: cómo trabajamos; todos los comandos: Comandos.

vp run ci ejecuta todas las comprobaciones, las de navegador, las auditorías de Google y, en remy-auth, la prueba de app nueva (vp run new-app, fixtures/consumer/run.sh): los demás repositorios a los que este sirve, probados tal como los reciben. Copia template/ a una carpeta temporal y le superpone fixtures/consumer/files (archivos terminados: un paquete propio de la app, su API y una parte del showcase; una comprobación unitaria los contrasta con la app en blanco), empaqueta remy-ui, remy-cli y el showcase en su vendor/ con pnpm pack (cada uno con una versión que ningún registro tiene), y luego instala y ejecuta las skills (instaladas una vez por lista fijada y luego reutilizadas), los invariantes de las herramientas, el vp run ready de la app, las comprobaciones rápidas de navegador y las de la documentación (unos 93 s); Vite Plus lo guarda en caché según de qué está hecha una app nueva, así que un cambio en la app propia de remy-auth o en la documentación de su producto ya no lo ejecuta. 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/). Las auditorías de Google son vp run test:audits (Lighthouse, local; parte de vp run ci, así que de cada push) y Core Web Vitals es pnpm remy test cwv (en un despliegue: producción, o el Preview de una rama); 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: un idioma por cada forma en que la app dibuja el texto por defecto, todos los idiomas con CHECK_LOCALES=all (el release), quick para el idioma base y el primero de derecha a izquierda, o una lista separada por comas; las propias comprobaciones determinan los idiomas, así que ningún comando los nombra (cuáles y por qué). Cada paso indica cuánto tardó.

Traducciones

Una misma estructura en cada app, para que los mismos comandos remy i18n funcionen en todas partes (la regla sobre quién traduce es en el cambio):

QuéInglésTraducción
Docsdocs/content/<site>/<page>.md o .mdx (estructura de Fumadocs)a su lado: <page>.<locale>.md o .mdx
Catálogos de UIel 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 a partir del registro de cada traducción sobre lo que se tradujo, traducir con el Claude Code de la máquina (headless, sin herramientas) y escribir las traducciones y sus registros (el commit del cambio los incluye). Ambos son TypeScript en el paquete (packages/remy-cli/src/i18n.ts, translate.ts: remy i18n check, remy i18n translate); la tarea check:translations y el script translate los ejecutan.

  • Mensajes de UI (Paraglide): los catálogos, los idiomas y el idioma base vienen del propio settings.json de inlang (el único proyecto que git conoce). Se detectan (packages/remy-cli/src/i18n.ts): catálogos y claves que faltan, {placeholders} perdidos o inventados, claves cuyo inglés difiere del registro del catálogo (translated-from/<locale>.json a su lado), y las categorías de plural que necesita el idioma (Intl.PluralRules: ninguna herramienta externa las comprueba).
  • Docs (Fumadocs): la carpeta es el content/ del paquete de documentación (docs/content, o apps/remy-auth-docs/content en remy-auth); 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, desactualizadas (su frontmatter translatedFrom: una huella por sección en inglés), 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; de una página desactualizada vuelven las secciones que cambiaron y se insertan en su sitio (una página que ya no encaja con su registro, o una que falta, vuelve completa), y solo se escribe cuando es una página (page.ts). Los prompts están en packages/remy-cli/src/prompts.ts. Una llamada por idioma (mensajes) o por página (docs), ocho a la vez (--jobs).
  • Las comprobaciones no necesitan clave, ni red, ni historial de git (un clon superficial da la misma respuesta). 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.

Los dos comandos, pnpm remy i18n check y pnpm remy i18n translate, con sus opciones: Comandos.

Cloudflare

Todos los comandos pnpm remy cloudflare …, y pnpm remy test remote, pnpm remy observe y pnpm remy upgrade, con lo que hace y lo que rechaza cada uno: Comandos. Cada uno indica en su comentario de cabecera si es local o remoto (y de producción).

Las credenciales vienen del login de Wrangler, o del llavero a través de fnox (herramientas).

Planes

La regla de los planes (cómo trabajamos) como comandos (pnpm remy 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.

Los comandos, con lo que hace cada uno, están en Comandos.

close y park cambian archivos y preparan el movimiento, pero nunca hacen commit: lee git diff y luego vp run ready.

On this page