diff --git a/AGENTS.md b/AGENTS.md index ed71db2..daae8e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,109 +1,67 @@ # Stanza -Shadcn-style CLI for assembling modular full-stack TS monorepos. Currently ships `init`, `add`, `remove`, `list`, `search` against these **categories**: `framework`, `ui`, `db`, `orm`, `auth`, `tooling`, `testing`. `swap` + `update` verbs and more categories are planned — the manifest already reserves the fields they'll need (`modules[category][].version`, `regions`). See the [module registry](apps/web/content/docs/registry.mdx) for the module roadmap. - -Three things differentiate Stanza from other scaffolders: - -1. Post-init `stanza add` works on existing projects (manifest-driven, peer-aware). -2. Generated code is vendored verbatim — no `@stanza/runtime` dep. -3. Open registry spec — third parties can host their own static JSON. +Shadcn-style CLI that assembles modular full-stack TS monorepos. Two unusual properties: (1) generated code is vendored verbatim — no `@stanza/runtime` dep — and (2) `stanza add` works on existing projects, not just at init. Verbs today: `init`, `add`, `remove`, `list`, `search` (`swap` + `update` planned — manifest already reserves `modules[category][].version` and `regions`). The canonical category list lives in [packages/registry/src/module.ts](packages/registry/src/module.ts) (`CATEGORIES`); module roadmap in [registry.mdx](apps/web/content/docs/registry.mdx). ## Layout -- `apps/cli/` — `stanza-cli`, node entrypoint at `src/bin.ts` (run via tsx in dev, built to `dist/bin.mjs` for publish via `vp pack`) -- `apps/web/` — `@stanza/web`, TanStack Start visual builder (Vite-native, no Vinxi) -- `packages/registry/` — shared schema, category/peer resolver, Zod manifest validator -- `packages/codemods/` — ts-morph helpers (idempotent + reversible) -- `packages/create-stanza/` — `pnpm create stanza` shim -- `registry/modules/-/` — first-party modules: `module.ts` + `templates/` (modules don't ship codemod code; see Architecture rules) +- `apps/cli/` — `stanza-cli`, entry `src/bin.ts` (publishable, ESM via tsdown) +- `apps/web/` — `@stanza/web`, TanStack Start visual builder (private, deployed to Vercel) +- `packages/registry/` — schema, resolver, manifest validator (private; inlined into CLI bundle) +- `packages/codemods/` — ts-morph helpers, idempotent + reversible (private; inlined) +- `packages/create-stanza/` — `pnpm create stanza` shim (publishable) +- `registry/modules/-/` — first-party modules: `module.ts` + `templates/` (private; data, not code) +- `skills/stanza-cli/SKILL.md` — agent skill for the published CLI. Update whenever the public CLI contract changes; agents using this skill won't have the source repo -A generated project has one or more **apps** declared in `stanza.json` as `apps: [{ id, dir, kind }]` (e.g. `{ id: "web", dir: "apps/web", kind: "web" }`). `AppKind = "web" | "native"` is a closed enum, and framework modules declare an `appKind` so the runner can validate that the framework matches its target app. `stanza init` scaffolds a single web app today (`defaultWebApp()`); multi-app init is a planned follow-up, but schema and runtime are already multi-app-shaped — `applyModule` threads a `targetApps: AppSpec[]` per call, and the web builder's `synthesize*` functions all take an `apps` array. +## Commands -In a **generated project**, a module's output lands per its category's `home` (in the canonical [`CATEGORIES`](packages/registry/src/module.ts) array): `auth`/`db`/`orm`/`ui` (`home: package`) install into an internal workspace package at `packages//` (named `@/`, consumed via `workspace:*` by every consuming app; `db` + `orm` share `packages/db/`); `framework`/`testing` (`home: app`) wire the chosen app's shell; `tooling` (`home: repo`) writes config at the repo root + scripts in the root `package.json`. The app a record targets is encoded as `apps: ["", …]` on `StanzaModuleRecord` — **required** for `home: app`, **optional** for `home: package` (omitted means "ship app-scoped shims into every app"; an explicit list restricts), and **forbidden** for `home: repo`. The app `id` doubles as the workspace package suffix: `@/` (so `id: "web"` → `@acme/web`, regardless of `dir`). `categoryHome(id)`, `PACKAGE_DIRS`, and `categoryLabel(id)` are all derived from the `CATEGORIES` array. +The repo runs on the **Vite+ toolchain** (`vp`). All config lives in the root [`vite.config.ts`](vite.config.ts) — there is no `turbo.json`, `.oxlintrc.json`, `.oxfmtrc.json`, or per-package `vitest.config.ts`. `vite`/`vitest` are catalog-aliased to `@voidzero-dev/vite-plus-core` / `@voidzero-dev/vite-plus-test` in `pnpm-workspace.yaml` — **never add standalone `vite`/`vitest`**. Source imports use `vite-plus` / `vite-plus/test`; template files under `registry/modules/*/templates/` stay on stock `vite`/`vitest` (they target user projects). -The repo runs on the **Vite+ toolchain** (`vp`) — one CLI for dev/build/test/lint/fmt/check/pack, all configured by the root [`vite.config.ts`](vite.config.ts) (`test`/`lint`/`fmt`/`staged` blocks). There is **no** `turbo.json`, `.oxlintrc.json`, `.oxfmtrc.json`, or per-package `vitest.config.ts` — that config now lives in `vite.config.ts`. `vp run