12 Commits
Author SHA1 Message Date
jake 6ada5c0a88 fix: correct batch of CLI correctness bugs found in critical-bug sweep
- `stanza init --dry-run` no longer creates the project directory, monorepo shell, or `stanza.json`; the dry-run manifest is built with `emptyManifest` and package.json existence checks are skipped throughout `applyModule`
- Direct-fs codemods (`append-to-file`, `add-package-dep`, `set-tsconfig-paths`) now claim their region before writing so a failed `add` rolls back to true pre-apply bytes and `RegionConflictError` fires before any disk change
- `stanza remove` reads the `consumesPackages` snapshot persisted on each installed record (falling back to a live registry fetch only for legacy records), so package-dir protection holds offline and after upstream renames
- `stanza remove` only applies the legacy bare-id owner fallback when no sibling install of the same module remains, preventing one app's removal from sweeping another app's files on pre-composite-owner manifests
- `stanza doctor` no longer reports false drift for package-home modules installed with an `--app` restriction; `expectedConsumerApps` now intersects targeted apps from the manifest records
- `wrap-root-layout` resolves the framework per dispatched app, fixing multi-app projects with differing frameworks
- The codemod render context now carries `packageManager` so `{{pm}}`/`{{run …}}` templates render bun/npm/pnpm correctly instead of always defaulting to pnpm
2026-06-09 19:04:38 -04:00
jakeandGitHub 04a196e4e9 refactor: serve registry and manifest schema from Vercel Blob as canonical origin (#18) 2026-06-04 15:08:36 -04:00
jakeandGitHub ea2d8c45bd refactor: extract @withstanza/schema and @withstanza/utils packages (#16) 2026-05-30 21:47:16 -04:00
jake 3304abbe98 feat: add payments-polar module, consolidate registry build into packages/registry, and generalize plugin codemods
- Add `payments-polar`: `@polar-sh/sdk` + framework-native checkout/webhook route handlers for Next.js and TanStack Start; Better Auth bridge variant ships `@polar-sh/better-auth` and wires the plugin into `auth.ts` when `auth: better-auth` is selected; light/dark logos inlined from svgl
- Consolidate the registry build: delete `scripts/registry-build.ts` and move the emit logic into `packages/registry/src/build.ts`, which now accepts an optional positional output-base arg (defaults to `dist/`) and writes `{base}/registry/{index,modules/*}.json` + `{base}/schema.json` directly; `prepare-registry.sh` is reduced to a single `jiti packages/registry/src/build.ts apps/web/public` invocation; CI/release workflows drop the Bun setup + `registry:build` steps since the build now runs inline at web-app prebuild time
- Replace `add-vite-plugin` codemod with the more general `add-plugin-to-call` (targets any `CallExpression` by callee + arg position, not just `vite.config` `plugins` arrays); add `add-package-dep` builtin for imperatively injecting deps into a `package.json` AST node; both are fully tested and registered in the builtins index
- Fix `init` adapter resolution order: precompute a `fullPending` map from all peer-category picks before the apply loop so `resolveAdapter` sees the complete peer set regardless of `categoryOrder` traversal sequence; export `PEER_CATEGORIES` from `@stanza/registry` to support this
2026-05-25 13:19:06 -04:00
jake 78f8e44721 chore: migrate to vite+ 2026-05-22 18:37:24 -04:00
jake 71f4c9d89b refactor: unify module taxonomy into a single Category concept with cardinality and home
- Replace the `slot` / `add-on` discriminated-union split with one `Category` shape carrying two orthogonal fields: `cardinality` (`"one"` for single-choice, `"many"` for coexisting) and `home` (`"app"` / `"repo"` / `"package"`) — the old `packageDir` + `repoScoped` pair is dropped
- `Module` is no longer a discriminated union; it carries a single `category` field referencing a `Category` id; `KNOWN_SLOTS`, `KNOWN_ADDONS`, `ADDON_CATEGORIES`, and all related helpers are replaced by a single `CATEGORIES` array and derived `categoryHome` / `categoryLabel` / `PACKAGE_DIRS` lookups
- Manifest schema bumped to version `0.2` — `modules` is now one record keyed by category holding arrays; `cardinality: "one"` categories are enforced to ≤ 1 entry at install time; no migration (stanza is pre-1.0 and unpublished)
- Peer/constraint logic is now emergent from cardinality: the resolver treats only `cardinality: "one"` categories as peers, so `testing` (many) can never accidentally become a peer; install routing is a single `categoryHome` lookup shared by the CLI runner, codemod runner, and web preview
- Update all CLI commands (`add`, `remove`, `init`, `list`, `search`), the wizard, `codemod-runner`, `registry-loader`, all web builder/detail/search components, server functions, OG route, sitemap, analytics, and selection logic to use the new category API
- Update all first-party `module.ts` files, `registry-build.ts`, tests, and docs (`AGENTS.md`, `REGISTRY.md`, `SKILL.md`) to reflect the unified taxonomy
2026-05-21 23:03:02 -04:00
jake b6e9b55c56 feat: generate and publish stanza.json JSON Schema at https://stanza.tools/schema.json
- Add `MANIFEST_SCHEMA_URL` constant and `manifestJsonSchema()` function to `packages/registry/src/manifest.ts`; the schema is derived from `StanzaManifestSchema` via `z.toJSONSchema` and decorated with `$id`, `title`, and `description`
- Add `$schema` field to the `StanzaManifest` type and `StanzaManifestSchema`; `emptyManifest` populates it automatically and `writeManifest` in the CLI spreads `$schema` first so pre-existing manifests gain the pointer on their next write
- Extend `scripts/registry-build.ts` to write `dist/schema.json` alongside the registry output; update `prepare-registry.sh` to copy `dist/schema.json` to `apps/web/public/schema.json` so it is served at the web root
- Gitignore `apps/web/public/schema.json` as a generated artifact
2026-05-21 21:32:58 -04:00
jake 19b5e51075 feat: add-on module kind, testing-vitest + testing-playwright, and multi-choice CLI/web support
- Introduce a `kind: "slot" | "addon"` discriminated union on `Module` — slot modules carry `slot`, add-on modules carry `category` (one of `KNOWN_ADDONS`). `isAddon()` narrows the union; `moduleGroup()` returns the slot-or-category key. Add-on categories (`testing`, `tooling`, `deploy`, `email`, `monorepo`) live in `KNOWN_ADDONS`/`ADDON_CATEGORIES`, deliberately disjoint from `KNOWN_SLOTS` so the resolver never lets add-ons participate in peer dispatch (they can still declare a one-way `peers` + per-framework adapters)
- Ship the first two add-ons: `testing-vitest` (jsdom + RTL, `test`/`test:watch` scripts, per-framework adapters) and `testing-playwright` (per-framework `webServer`, `test:e2e`/`test:e2e:ui` scripts — disjoint from vitest's so region claims don't collide); both under `registry/modules/testing-*/`
- Manifest gains `addons: Partial<Record<AddonCategoryId, StanzaAddonRecord[]>>` (`.default({})` keeps existing `stanza.json` valid); resolver gains `addonOrder` for deterministic codemod sequencing
- CLI: `stanza add <slot|category> <module>` accepts both slots and add-on categories; duplicate-id guard for add-ons instead of the single-fill slot guard; `stanza remove <slot|category> [id]` removes a specific add-on by id (or the whole slot); `stanza list` shows add-ons alongside slots; `stanza init --yes` gains `--testing vitest,playwright` flag; wizard prompts multi-select for add-on categories
- Web builder: add-on slots render as checkbox multi-select cards (`SlotSection` detects `isAddon` and switches to `<CheckboxCard>`), `?testing=vitest,playwright` comma-joined URL param, `builder-state.functions.ts` parses/serializes per-category add-on lists, OG and sitemap routes updated for add-on module paths
- Codemod runner branches on `isAddon` to resolve add-on records from `manifest.addons[category]` rather than `manifest.modules[slot]`; region ownership keys on `module.id` so co-existing add-ons writing the same file at disjoint dot-paths never conflict
- Add `commands.test.ts`, `selection.test.ts`, `manifest.test.ts`, and `resolver.test.ts` coverage for add-on add/remove/list flows and schema validation
2026-05-21 16:34:09 -04:00
jake 743c356b17 refactor: extract CopyButton, memoize callbacks, and add oxc/vitest oxlint plugins
- Extract a shared `CopyButton` component (`apps/web/src/components/copy-button.tsx`) — the clipboard-copy + toast + tooltip pattern was duplicated verbatim in `command-bar.tsx` and `try-it.tsx`; both now import `<CopyButton value={command} />`
- Wrap `setName` and `setSelection` in `useCallback` in `builder/index.tsx`, `onDraftChange` in `command-bar.tsx`, and `ModuleCard`'s `onClick` in `slot-cards.tsx` — these were recreated on every render, causing unnecessary child re-renders
- Move `summaries.filter(m => m.slot === slot)` into `SlotSection` and pass `summaries` + `slot` instead of a pre-filtered `modules` array; return `null` from `SlotSection` when the filtered list is empty — removes an extra allocation in the parent and colocates the null-check with the data
- Memoize `dangerouslySetInnerHTML` objects in `file-preview.tsx` and `templates-list.tsx` as `useMemo(() => ({ __html: ... }))` instead of deriving a plain string and constructing a fresh object inline on every render
- Hoist the GitHub `<a>` element in `header.tsx` to a module-level constant so the Base UI `Button render=` prop receives a stable reference across renders
- Add `oxc`, `eslint`, and `vitest` plugins to `.oxlintrc.json` for both the root config and the `apps/web/**/*.tsx` override
2026-05-21 15:46:56 -04:00
jake d9ccde00e9 fix: wire tanstack-start src/globals.css ownership and drop premature deps
- `framework-tanstack-start` now ships `src/globals.css` as a first-class template (base styles: reset, system font stack, centred `<main>`) and `__root.tsx` imports it directly — CSS was previously written by `styling-tailwind` but never consumed, causing it to be orphaned on disk
- `styling-tailwind`'s tanstack-start adapter drops its `templates` entry and switches to the same `append-to-file { position: "start" }` pattern as the next adapter — prepends `@import "tailwindcss";` before existing base styles, revert is clean, `@import` order is guaranteed; both framework adapters are now structurally identical
- Delete `scripts/module-new.ts` — scaffolding helper no longer used or maintained
- Remove `posthog-node` from `apps/cli` dependencies — telemetry is unimplemented; drop it until the feature is actually wired
- Remove `@clack/prompts`, `kleur`, `mri` from root `package.json` devDependencies — these belong to `apps/cli` and were redundant at the root level
- Mark two TODO items done: tanstack-start globals.css orphan and `auth-better-auth` VITE_BETTER_AUTH_URL (stale — auth-client defaults to current origin, no template references it)
2026-05-20 18:09:37 -04:00
jake 3a30c30d62 refactor: consolidate slot metadata into canonical SLOTS array, hoist module-level install fields, and rename peerPackagesconsumesPackages
- Replace parallel `KNOWN_SLOTS` tuple, `slotOrder` array, and `SLOT_PACKAGE_DIR` map with a single `SLOTS: Slot[]` constant — each entry carries `{ id, label, description, packageDir }`; `KNOWN_SLOTS` is re-derived as `SLOTS.map(s => s.id) as const` for Zod literal inference; `slotLabel` and the package-dir lookup both become simple lookups over the same array; adding a slot is now a two-line edit
- Hoist `dependencies`, `devDependencies`, `env`, and `scripts` to the module level on `ModuleAdapter`'s parent `Module` type — adapter-level values still override per-key; avoids re-declaring the same dep/env block in every adapter (e.g. Better Auth's `better-auth` dep and two env vars are now declared once at module level while per-framework templates and codemods stay in their adapter blocks)
- Rename `peerPackages` → `consumesPackages` and lift it from adapter scope to module scope — cross-package imports are shared infrastructure that doesn't vary per adapter; runner wires `@<project>/<dir>: workspace:*` once per module install rather than once per adapter
- Update all first-party modules (`auth-better-auth`, `orm-drizzle`, `orm-prisma`, `framework-next`, `framework-tanstack-start`, `styling-tailwind`) to the new schema
- Update CLI (`codemod-runner`, `registry-loader`, `wizard`, `add`, `init`) and `scripts/registry-build.ts` to consume the new `SLOTS` array and hoisted module fields
- Update `apps/web/scripts/copy-registry.ts` and docs (`CLAUDE.md`, `REGISTRY.md`, `README.md`, `TODO.md`) to reflect the consolidated model and clarify the slots-vs-add-ons taxonomy
2026-05-20 17:00:56 -04:00
jake 2651daf573 refactor: dissolve packages/internal into scripts/, reorganize CLI into src/lib/, and wire stanza remove codemod revert
- Delete `packages/internal` workspace package — `registry-build.ts` and `module-new.ts` move to top-level `scripts/` (no `package.json`, not shipped); `pnpm registry:build` and `pnpm module:new` scripts updated in root `package.json` and `turbo.json`; CLAUDE.md / TODO.md references updated
- Relocate CLI internals from `apps/cli/src/` → `apps/cli/src/lib/` — `codemod-runner.ts`, `manifest.ts`, `region-tracker.ts`, `registry-loader.ts`, `wizard.ts`; all intra-CLI imports updated to `@/lib/*` path alias via tsconfig `paths`; add `@types/bun` to CLI devDependencies
- `stanza remove` now dispatches `revertCodemods()` as step 1 (before declarative cleanup) — re-loads the module adapter from the registry, calls imperative reverts while framework files are still intact, then sweeps remaining region claims; fixes `wrap-root-layout` / `re-export` regions previously left as "needs manual cleanup" despite shipping working reverts
2026-05-20 16:10:17 -04:00