- Update `@types/node`, `semver`, TanStack Router/Start, `posthog-node`, `shadcn`, `fumadocs-mdx`, and a handful of other minor dep bumps across the monorepo
- Upgrade pnpm to 11.5.2
- Replace deprecated `.inputValidator(…)` calls with `.validator(…)` on all server functions
- Set `disableThanks`, `prettier: false`, `privatePackages: { version: false, tag: false }`, and `updateInternalDependents: "always"` in `.changeset/config.json`; simplify the release workflow by dropping `NPM_TOKEN` and `NPM_CONFIG_PROVENANCE` from the CI env and switching to `vp run version`
- Add `stanza-cli` as a second bin entry in `publishConfig` alongside `stanza` so the package is callable by either name; reset the CLI package version to `0.0.0` so changesets drive versioning from a clean baseline
- Update docs throughout to reflect the `doctor` command surface (CLI reference, agents guide, telemetry list), the main-file registry URL convention (`registries` entries now point at the full `index.json` URL, not a directory), and the `STANZA_REGISTRY` env var description
- Replace `npm create stanza@latest` → `npm init stanza` and `bun create stanza@latest` → `bun create stanza` in docs, SKILL.md, and `buildCommand` to match how these package managers actually resolve the initializer
- Update the agents.mdx rule to reference `npm init` instead of `npm create`
- Derive `init` vs `create` sub-command in `buildCommand` based on the selected package manager so generated install snippets are correct for npm
- Added @number-flow/react as a dependency to improve number display.
- Replaced the custom formatCount function with NumberFlow for projectsScaffolded and modulesInstalled stats, ensuring better formatting and handling of loading states.
- Updated LastRefreshed component's styling for improved readability.
- Extract a new `ModuleMetadata` type (rename of `ModuleSummary`) containing only the display/resolution fields needed client-side; the full `Module` graph (templates, codemods, file lists, etc.) stays server-side
- Replace the dual `modules: Record<string, Module>` + `summaries: ModuleSummary[]` props on `Builder` and `ModuleCards` with a single `metadata: ModuleMetadata[]`; category bucketing and `${cat}:${id}` keyed lookup are derived once via `useMemo` inside `ModuleCards`
- Teach `resolveAdapter` (and its call sites in `module-cards.tsx`, `selection.ts`, `resolver.ts`) to accept `ModuleMetadata` directly so adapter-compatibility checks no longer require a full `Module` lookup
- Update `builder-state.functions.ts` and the index route to project only metadata before the server function returns, removing the `state.modules` record from the serialized payload
- Replace hand-rolled `JSON.parse`/`JSON.stringify` round-trips in `json.ts` with `jsonc-parser` `modify` + `applyEdits` so comments, trailing commas, indentation, and key ordering are preserved across all `setJsonPath`, `unsetJsonPath`, `mergeJson`, and `addPackageDependency` calls
- Add `setJsonPathSegments` / `unsetJsonPathSegments` for callers whose keys contain `.` (tsconfig `paths` aliases like `"@acme/ui.foo/*"` would be mis-split by the dot-path API); export both from `packages/codemods/src/index.ts`
- Refactor `set-tsconfig-paths` to use the new segment-aware helpers and drop its local `parseJsonc` + `applyEdits` usage — the builtin no longer performs raw `fs.readFileSync`/`writeFileSync` itself
- Add `jsonc-parser` to `apps/cli/package.json` (was already a transitive dep; now explicit)
- Add `"format preservation (JSONC)"` test suite covering comment/trailing-comma survival, key-ordering stability under `addPackageDependency`, dotted-key segment addressing, and no-op behavior when a parent key is absent
- Refuse to sweep `packages/<dir>/` in two new cases: (1) stray non-Stanza files exist under the dir (user-authored secrets, scratch scripts, etc.), and (2) a still-installed module lists the dir in `consumesPackages` — both append to `manualCleanup` with targeted hints instead of silently destroying user work; `findStrayFiles` and `collectConsumesPackagesProtectors` (parallel `Promise.all` over all installed records) implement the checks
- Snapshot `codemods` onto each `StanzaModuleRecord` at install time; `remove` drives `revertCodemods` from that snapshot so reversal is correct even when the upstream registry has renamed the adapter or reshuffled codemod ids since install — falls back to the live registry entry only when no snapshot is present
- Fix cardinality scope in `add` for package-home and repo-home categories: pass `undefined` (not `pickedAppId`) to `selectedAll` so the per-project uniqueness check covers all apps; previously a second install targeting a different app could slip past the check only to fail on the next read
- Use composite owner keys (`<id>@<app>`) in `regionsOwnedBy` for app-home installs so two apps installing the same module don't share a region owner and accidentally release each other's claims during remove; bare `<id>` fallback covers pre-snapshot manifests and non-app homes
- Refactor `applyCodemods` / `revertCodemods` to accept `codemods`, `adapterKey`, and `consumesPackages` directly rather than full `Module` + adapter objects; add a comprehensive `codemod-runner.test.ts` covering apply, revert, dry-run, idempotency, and error paths for all built-in codemod types
- Add `packages/registry/src/safe-path.ts` with `safePath` / `isSafePath` guards and tests; use in the codemod runner to reject path-traversal write targets before any disk touch
- Add `set-tsconfig-paths` builtin codemod with tests; harden `add-array-entry-in-call`, `add-jsx-child`, `wrap-root-layout`, and `replace-import` for idempotency and edge cases
- Validate unknown `--app` flags early in `remove` with a targeted error before any registry fetch; replace the `posthog-provider` React component and `posthog.server.ts` in the web app with a unified `analytics.ts` / `api.events.ts` pattern
- Register a new `ai` category in `CATEGORIES` (cardinality `one`, home `packages/ai/`) so both modules land under the same single-choice slot
- Add `ai-vercel-ai-sdk`: installs `ai` + `@ai-sdk/openai`, emits a `model.ts` configured for `gpt-4o-mini`, a shared package entry-point, and a streaming `/api/chat` route for Next.js (`StreamingTextResponse`) and TanStack Start (`EventStream`); peers on `framework`
- Add `ai-tanstack-ai`: installs `@tanstack/ai` + `@tanstack/ai-openai`, emits an `adapter.ts` wrapping `openaiText("gpt-4o-mini")`, a shared entry-point, and a streaming `/api/chat` route for both frameworks via `toServerSentEventsResponse`; peers on `framework`
- Both modules declare `OPENAI_API_KEY` in `env` and ship provider-swap instructions in `readme.md`
- Update `registry.mdx` AI table: mark both modules 🟢 Available with short capability notes
- Drop `IconHome` anchor from `ModuleCard` header; only the `IconInfoCircle` details link remains (opacity toned down to `text-foreground/40`)
- Reword error messages (`describeError`), empty-state strings, route-boundary messages, and stats page copy for clarity and consistency
- Replace `rel="noreferrer"` with `rel="noopener noreferrer"` across header, footer, detail page, and stats
- Add external-link icon to footer GitHub anchor; add `hover:text-foreground` to footer nav links
- Fix `PreviewBlock` top spacing (`mt-4` moved to `pt-4` on the wrapper so the border-top sits flush)
- Add "audit the code" link to stats telemetry opt-out blurb pointing at the telemetry source file
- Add `recharts` and `motion` deps; register `@evilcharts` registry in `components.json`
- Vendor evilcharts primitives (`AreaChart`, `Chart`, `Dot`, `EvilBrush`, `Legend`, `Tooltip`, `Background`) under `components/evilcharts/`
- Add `ActivityChart` wrapper in `components/stats/` that uses the evilcharts `AreaChart` for the daily-run sparkline on `/stats`; replaces the hand-rolled inline-SVG `SparkAreaChart` (deleted)
- Update `stats.tsx` to render `ActivityChart` instead of the removed component
- Tighten `.gitignore`: add `.vercel`, collapse `.env.local` / `.env.*.local` to `.env*` with a `!.env.example` carve-out
- Replace `file-preview`'s `useEffect`-based tree→URL sync with an `onSelectionChange` callback so the URL hash is the sole source of truth for `activePath`; drop `useFileTreeSelector` and the last-file latch ref in favor of a direct `hash && filePaths.includes(hash)` guard
- Simplify `ProjectSetup` to a nullable `draft` (null until first keystroke) + `useDebouncedCallback` so no sync effect or `lastSyncedRef` is needed to avoid echo-back from the URL-backed prop
- Remove `@tanstack/react-router-ssr-query`, `@tanstack/router-plugin`, `handlebars`, and `web-vitals` from `apps/web` deps — none were imported
- Delete the re-exported `summaryFor` helper from `adapter-switcher` (callers use `findModule` from `@stanza/registry` directly)
- Fix `PreviewBlock` code padding (`[&_pre]:p-4` → `[&_pre]:p-0`, add `mt-4` top margin)
- Fix intra-doc anchor in `getting-started.mdx` to use a relative `#` fragment instead of an absolute `/docs/getting-started#` path
- Add `svgo` as a dev dep and run each logo through `optimize` with `multipass: true` + `preset-default` at build time
- Apply the `prefixIds` plugin scoped to the module slug (e.g. `email-resend-light`) so gradient / clip-path `id` collisions between logos are impossible when multiple modules are rendered on the same page
- Add `email-resend`: `resend` SDK + `react-email` template in `packages/email/`; ships a shared `resend.ts` client, a sample `WelcomeEmail` component, and framework-native `/api/webhook/resend` Svix-verifier route handlers for Next.js and TanStack Start; light/dark logos inlined from svgl
- `RESEND_API_KEY` is required; `RESEND_WEBHOOK_SECRET` is optional and gates the webhook route
- Update `registry.mdx` resend row from 🟨 Planned to 🟢 Available with a feature summary
- Rename `styling` → `ui` across the entire codebase (CATEGORIES array, docs, AGENTS.md, CLI tests, resolver, template tests, codemod runner); `ui` is `home: package` so shadcn installs into `packages/ui/` as a workspace dep rather than wiring directly into the app shell
- Add `ui-shadcn-base` (Tailwind v4 / CSS-first, no Radix peer) and `ui-shadcn-radix` (Tailwind v4 + Radix primitives) with full per-framework template sets for Next.js and TanStack Start: `components.json`, `globals.css`, `postcss.config.mjs`, `theme-provider.tsx`, shared `Button` + `utils.ts`; light/dark logos inlined from svgl
- Rename `styling-tailwind` → `ui-tailwind` to match the new category id; update its module definition to `category: "ui"` and `home: package`
- Add four new codemod builtins with tests: `add-array-entry-in-call` (inserts an element into a named array argument of any `CallExpression`), `replace-import` (rewrites a specifier in an existing import declaration), `set-html-attributes` (sets attributes on an HTML/JSX element by tag name or selector), `set-tsconfig-paths` (merges `compilerOptions` entries into a `tsconfig.json` AST node); register all four in the builtins index and export from `@stanza/codemods`
- Add `wrap-root-layout` builtin tests
- Update registry.mdx, concepts.mdx, cli.mdx, getting-started.mdx, agents.mdx, index.mdx, AGENTS.md, README.md, TODO.md, and SKILL.md to reflect the category rename and new modules
- Add `@tanstack/react-pacer` and swap both hand-rolled `setTimeout`-based debounce patterns (project-name commit in `project-setup.tsx`, module search fetch in `site-search.tsx`) for `useDebouncedValue`; the search effect now runs directly against the debounced value with an abort controller rather than nesting the fetch inside the timer
- Extract `useGracePeriod` from `file-preview.tsx` into `src/hooks/use-grace-period.ts` so it can be reused independently
- Hoist `fileSelection`, `lastFileRef`, `activePath`, and `preview` above the reseed effect in `file-preview.tsx` so the effect can reference `defaultPath` and `lastFileRef` without stale-closure issues; expand ancestor directories when a hash-driven selection lands so the highlighted row is visible in the tree
- Add `payments-stripe`: `stripe` SDK + framework-native Checkout Session / webhook route handlers for Next.js and TanStack Start; Better Auth bridge variant ships `@better-auth/stripe` and wires the plugin into `auth.ts` when `auth: better-auth` is selected; light logo inlined from svgl
- Sync the web builder's open file to the URL hash so refreshes and shared links restore the same selection; latch the last file path so folder-expand clicks don't wipe the preview; back/forward navigation reflects hash changes back into the tree; default-file selections don't pollute the hash (keeps shareable URLs clean)
- Trim over-explained JSDoc comments in `add-package-dep.ts` and `add-plugin-to-call.ts` to the terse one-liner style; condense the `init.ts` `fullPending` comment to match
- Update `registry.mdx` stripe row from 🟨 Planned to 🟢 Available with a brief feature summary
- Replace per-framework template files (`eslint.config.next.mjs`, `eslint.config.tanstack.mjs`) with a single `eslint.config.mjs` that branches via Handlebars `{{#if peers.framework}}` conditionals; add `handlebars` as a direct dependency to `@stanza/registry`, `apps/cli`, and `apps/web`
- Extend the template render context with a `peers: Partial<Record<CategoryId, string>>` map so any template can inspect the active framework, auth provider, etc.; thread it through `buildRenderContext`, `synthesizeTemplates`, the CLI `codemod-runner`, and the web `getBuilderState` server function
- Extract and export `activePeerIds(manifest, appId?)` from `@stanza/registry` to derive the peer map from a manifest (app-scoped for `home: app` categories); add `selectedPeerIds(selections)` in `apps/web/src/lib/selection.ts` as the URL-params-derived equivalent
- Add integration tests verifying framework-conditional eslint-prettier output for `default`, `next`, and `tanstack-start` adapters; expand resolver tests for `activePeerIds` app-scoping and for modules that declare both a `default` adapter and a framework-specific override
- 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
- Extract `getAllModules()` into `registry-modules.server.ts` as a singleton promise that loads the full module catalog once per server process; `getBuilderState` now pays the I/O cost exactly once per deployment rather than on every request
- Wrap `renderPreviewImpl` with an LRU cache (max 500 entries, keyed by `filePath + source`) so repeated Shiki highlight calls for unchanged files are returned from memory
- Add `lru-cache` as a direct dependency
- Replace the single `index` root-loader prop with `{ registry, docs }` and split `SiteSearch` to accept both; empty state renders the docs page list and all modules from the root payload, while typed queries fan out to new `/api/search/docs` (Fumadocs Orama) and `/api/search/modules` (server-side Orama) API routes so the client bundle never carries a full search index
- Add `api.search.docs.ts` wired to the Fumadocs search server and `api.search.modules.ts` that builds an in-memory Orama index over the full registry on each request (including richer fields than the client-side summary); add `docs-index.functions.ts` to enumerate docs pages for the root loader's empty-state list
- Unify module and doc results into a `Hit` discriminated union and a `Group` abstraction; `SearchRow` replaces `SearchResult` and dispatches navigation to `/registry/$category/$id` or an arbitrary docs URL; arrow-key selection and `⌘K` hotkey are preserved
- Pre-seed the hotkey label with the macOS format (`{ platform: "mac" }`) on first render to eliminate the null→label layout shift, then upgrade to the platform-correct value in a `useEffect`; add `@orama/orama` as a direct dependency
- Add `@vitejs/plugin-rsc` and flip `components.json` to `rsc: true`; annotate all client-side UI components, builder pieces, docs sidebar/TOC, search, theme toggle, and footer with `"use client"` so the RSC boundary is explicit
- Replace the global `useRouterState({ select: s => s.isLoading })` in `Builder` with a `useMatch`-scoped `isFetching` selector so navigating away doesn't flash the file-preview overlay before the page unmounts; thread the result as an `isReloading` prop into `FilePreview` (removing the `useRouterState` import there entirely)
- Extract `apps/web/src/server/docs-meta.functions.ts` as a dedicated server functions module for the docs route; refactor `docs.$.tsx` to consume it
- Trim verbose inline comments throughout `file-preview.tsx`, `builder/index.tsx`, and `project-setup.tsx` (no logic changes)
- Extend `buildHead` / `HeadOutput` with a `scripts` field for `application/ld+json` injection and a `markdownPath` field for `<link rel="alternate" type="text/markdown">`; add `getWebSiteJsonLd`, `getTechArticleJsonLd`, and `getSoftwareSourceCodeJsonLd` helpers; wire them into the home, docs, and module-detail routes
- Add an `og.docs.$` route that generates per-page OG images for docs pages using the page title/description; rename `og.$slot.$id` → `og.m.$slot.$id` to match the `/m/` route structure; regenerate the route tree
- Replace `@vercel/og` with `@takumi-rs/image-response` in `og-card.server.tsx`; remove `@vercel/analytics` and its `<Analytics />` usage from the root layout
- Add `content/docs/agents.mdx` covering SKILL.md installation, agent guidance rules, common errors, and an end-to-end scaffolding script; link from the docs index and reorder `meta.json` to surface it before `concepts`
- Replace every bare `stanza <verb>` example in `getting-started.mdx` and `cli.mdx` with `npx stanza-cli <verb>` to match the published package name and avoid global-install assumptions
- Update `registry.mdx`, web builder components (`project-setup`, `slot-cards`), `header`, `footer`, `index` route, and `styles.css` with miscellaneous copy and UI refinements; bump `vite.config.ts` and add one new `package.json` dependency
- Add `packages/registry/src/project-name.ts` with a `validateProjectName` helper backed by `validate-npm-package-name`; rejects scoped names (the user supplies only the bare name, stanza synthesizes `@<name>/db` etc.) and returns a typed `ProjectNameValidation` discriminated union; export from `@stanza/registry`
- Replace the inline regex in `apps/cli/src/lib/wizard.ts` (both interactive and `--yes` paths) with `validateProjectName` so error messages are consistent
- Update `apps/web/src/components/builder/project-setup.tsx` to call `validateProjectName` via `useMemo`, gate the debounced `onNameChange` callback to only fire on valid input, and display inline `FieldError` feedback (suppressed while the field is empty so the placeholder fallback isn't alarming); add `maxLength`, `aria-invalid`, and `aria-describedby` for accessibility
- Add `concepts.mdx` covering categories, cardinality, home, vendoring, peers/adapters, manifest, regions, and the open registry model
- Flesh out `getting-started.mdx` with prerequisites, install/run steps, package-manager variants, inspect/remove commands, and a non-interactive `--yes` example
- Rewrite `cli-reference.mdx` with per-verb sections, global flags, environment variables, and dependency-versioning semantics
- Expand `README.md` with a "Why Stanza?" section, quick-start snippet, docs link, and updated module list; swap `pnpm create` example for `npm init`
- Add `concepts` to `meta.json` nav order
- Promote `tooling` from an add-on category to a single-choice **slot** in `KNOWN_SLOTS`/`SLOTS` and remove it from `KNOWN_ADDONS`/`ADDON_CATEGORIES`; the three toolchains are mutually exclusive substitutes so single-choice semantics fit, and nothing peers on `tooling` so it bears no outbound dispatch constraints
- Add `registry/modules/tooling-eslint-prettier` — ESLint flat config + Prettier with per-framework adapters (`next` → `eslint.config.next.mjs`; `tanstack-start` → `eslint.config.tanstack.mjs`) and a shared `prettier.config.mjs`; scripts: `lint: "eslint ."`, `format: "prettier --write ."`
- Add `registry/modules/tooling-biome` — Biome 2.x with a single `default` adapter (framework-agnostic `biome.json`); scripts: `lint`, `format`, `check`
- Add `registry/modules/tooling-oxlint-oxfmt` — Oxlint + oxfmt with a single `default` adapter (`dot_oxlintrc.json`, `dot_oxfmtrc.json`); scripts: `lint`, `format`
- Drop `lint: "next lint"` from `framework-next` — `next lint` was removed in Next 16, so the tooling module now owns `scripts.lint` cleanly
- Add CLI tests covering `init --yes --tooling eslint-prettier`, framework-next shipping no lint script without a tooling pick, `add tooling biome` on an existing project, and slot-already-filled rejection
- Add `src/components/posthog-provider.tsx` — initializes the `posthog-js` browser SDK once (guarded by `VITE_PUBLIC_POSTHOG_KEY` and `posthog.__loaded`) with history-based pageview capture, pageleave tracking, and session replay + autocapture disabled; wraps the app shell in `__root.tsx` via `<PostHogProvider>`
- Add `src/lib/analytics.ts` with a `useAnalytics` hook that returns a stable `capture` callback bound to the PostHog singleton (no-ops when the key is absent or the SDK is uninitialized) and a `selectionProperties` helper that flattens slot/addon selections into a consistent event property shape across events
- Wire `capture` into `Builder` (`builder_module_selected`, `builder_module_deselected`, `builder_addon_toggled`) and `CommandPreview` (`builder_command_copied` with `package_manager`, `command`, `name_customized`, `module_count`, and per-slot properties); add an `onCopied` callback prop to `CopyButton` to fire the capture after a successful clipboard write
- Add `src/server/posthog.server.ts` — a lazy singleton that constructs a `posthog-node` `PostHog` client from `VITE_PUBLIC_POSTHOG_KEY`/`VITE_PUBLIC_POSTHOG_HOST` and returns `null` when unconfigured; refactor `/api/events` to use this client instead of a raw `fetch` to PostHog's `/batch/` endpoint, and flush via `waitUntil` from `@vercel/functions` so the serverless function stays alive past the response without blocking it
- Swap `mri` for `citty` and wrap every command handler with `defineCommand` so citty owns parsing, validation, and `--help` output; remove the hand-rolled `mri` parse block from `bin.ts` and simplify `run()` to accept no arguments
- Extract shared global flags (`--dry-run`, `--dangerously-allow-dirty`, `--no-telemetry`) into a new `commands/_args.ts` as `commonArgs` that is spread into each subcommand's `args` definition; introduce a `CliArgs` open-record type for handler signatures
- Migrate `add`/`remove`/`init`/`list`/`search` command signatures from `({ positional, argv: Argv }) => …` to a flat `(args: CliArgs) => …` shape; declare `slot`, `moduleId`, and `name` as `type: "positional"` in their respective `defineCommand` blocks
- Update the `commands.test.ts` helper from `argv(flags)` to `args(flags)` to match the flat citty-parsed shape (named positionals promoted to top-level keys alongside booleans)
- Remove `cmdk` and the generated `src/components/ui/command.tsx` shadcn primitive; replace `CommandDialog` / `CommandInput` / `CommandList` / `CommandItem` in `SiteSearch` with a plain `Dialog` + `<input>` + `<button>` elements so the component owns its own rendering without the cmdk abstraction layer
- Add `@tanstack/react-hotkeys` and replace the hand-rolled `keydown` listener with `useHotkey(HOTKEY, …)`; use `formatForDisplay` (deferred to mount to avoid SSR hydration mismatch) so the `<Kbd>` badge shows `⌘K` on macOS and `Ctrl+K` on Windows/Linux
- Implement client-side fuzzy filtering via a local `matches` helper (splits query on whitespace, checks every term against a joined haystack of group/id/label/description) and drive the list from derived `filtered` / `groups` / `flat` memos
- Add keyboard navigation — `ArrowDown`/`ArrowUp` cycle `activeIndex` through the flat item list, `Enter` selects; `onPointerMove` on each row syncs hover → `activeIndex`; the active row scrolls into view via a `data-index` attribute + `scrollIntoView`
- Reset `query` and `activeIndex` when the dialog opens; reset `activeIndex` whenever `query` changes
- Pin `fumadocs-ui` to `npm:@fumadocs/base-ui@^16.9.0`
- Add `react-resizable-panels` dependency and a `ResizablePanelGroup` / `ResizablePanel` / `ResizableHandle` shadcn-style UI primitive in `src/components/ui/resizable.tsx`
- Replace the static `grid-cols-[260px_1fr]` layout in `FilePreview` with a `ResizablePanelGroup` (horizontal on `sm`+, vertical on mobile) so the tree and preview panes are user-draggable; default split is 35 / 65
- Add a `useMediaQuery` hook (SSR-safe, defaults to the wide layout to avoid hydration mismatch) to switch the panel group orientation at the `sm` breakpoint
- Fix `PreviewPane` height propagation — both the empty state and the code pane now use `h-full`/`flex-1`/`min-h-0` so the scroll container fills the resizable panel instead of relying on `max-h` clamps
- Install `fumadocs-core`, `fumadocs-mdx`, and `fumadocs-ui`; add `fumadocs-mdx/vite` plugin and wire it first in `vite.config.ts` so MDX files are processed before TanStack Start
- Add `source.config.ts` (single `defineDocs` collection pointing at `content/docs`), `src/lib/source.ts` (Fumadocs loader with `/docs` base URL), and `src/lib/layout.shared.tsx` (disables Fumadocs' own nav, theme switch, and search toggle so the site shell owns those concerns)
- Add `src/components/mdx.tsx` exporting `getMDXComponents`/`useMDXComponents` built on `fumadocs-ui/mdx` defaults; declare global `MDXProvidedComponents` for type-safe component overrides
- Add `src/routes/docs.$.tsx` — splat route that runs a server function to look up the page + serialize the page tree, preloads the client MDX bundle, renders `<DocsLayout>` inside `<RootProvider theme/search disabled>`, and feeds `buildHead` for per-page SEO
- Add `content/docs/index.mdx` (Introduction), `content/docs/getting-started.mdx`, `content/docs/cli-reference.mdx`, and `content/docs/meta.json` controlling sidebar order
- Add `collections/*` path alias pointing at `.source/` in `tsconfig.json`; add `.source/` to `.gitignore`
- Import `fumadocs-ui/css/neutral.css` and `fumadocs-ui/css/preset.css` plus the Tailwind source glob in `styles.css`
- Add a "Docs" nav link in `<Header>` pointing to `/docs/$` with an empty splat
## Vercel Web Analytics Installation
Successfully installed and configured Vercel Web Analytics for the stanza-web project following the official Vercel documentation.
### Changes Made
#### 1. Package Installation
- Installed `@vercel/analytics` version 2.0.1 using pnpm
- Package added to `apps/web/package.json` dependencies
#### 2. Analytics Component Integration
Modified `apps/web/src/routes/__root.tsx`:
- Added import: `import { Analytics } from "@vercel/analytics/react";`
- Added `<Analytics />` component to the RootComponent's JSX, placed just before the `<Scripts />` component
- This ensures analytics tracking is enabled across all pages of the application
#### 3. Framework-Specific Implementation
The project uses TanStack Start (React-based framework) with Nitro as the server. According to the Vercel documentation, the correct integration approach for React applications is:
- Import `Analytics` from `@vercel/analytics/react`
- Add the `<Analytics />` component to the root layout component
- This integration automatically handles page view tracking for client-side route transitions
### Files Modified
- `apps/web/package.json` - Added @vercel/analytics dependency
- `apps/web/src/routes/__root.tsx` - Added Analytics component import and usage
- `package.json` - Added tsx as a dev dependency for build tooling
- `pnpm-lock.yaml` - Updated with new dependencies
### Verification
✅ Type checking passed (tsc --noEmit)
✅ Linting passed (oxlint) - 0 warnings, 0 errors
✅ Tests passed (vitest) - All 10 tests passing
✅ No breaking changes introduced
✅ Existing code structure preserved
### Next Steps for Activation
To enable analytics tracking in production:
1. Deploy the application to Vercel
2. Enable Web Analytics in the Vercel dashboard (Analytics section)
3. Analytics data will start appearing after user visits
### Notes
- The Analytics component is lightweight and only tracks page views by default
- Custom event tracking is available with Pro/Enterprise plans
- The component automatically detects the environment (development vs production)
- Integration follows React best practices by placing the component at the root level
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
- Add `/m/$slot/$id` route (`apps/web/src/routes/m.$slot.$id.tsx`) with `module-detail.ts` server function — full adapter switcher (peer chip rows), deps/devDeps/scripts table, env-var table, templates list with click-to-expand Shiki preview, and a "Try it" copy-command bar; new detail components land under `apps/web/src/components/detail/`
- Add ⌘K search popover (`apps/web/src/components/search/site-search.tsx`) backed by `apps/web/src/lib/module-search.ts` — fuzzy-matches id/label/description/slot exactly as the CLI does; wired into the header
- Add dynamic OG image generation via `@vercel/og` + Satori: `server/routes/og/[slot]/[id].ts` renders a per-module card, `server/routes/og.ts` renders the default fallback; both served as Nitro route handlers with appropriate CDN cache headers; OG React components in `apps/web/src/server/og-card.tsx`
- Add `server/routes/sitemap.xml.ts` (enumerates `/` + all `/m/$slot/$id` URLs from registry index) and `public/robots.txt` pointing at it
- Add `apps/web/src/lib/seo.ts` — `buildHead()` helper that emits `title`, `og:*`, `twitter:*`, and `canonical` tags; consumed by `__root.tsx`, `index.tsx`, and `m.$slot.$id.tsx`
- Extract `ModuleLogo` into `apps/web/src/components/module-logo.tsx` — shared between slot-cards and the new detail page; remove the duplicate from `slot-cards.tsx`
- Add new shadcn-style UI primitives: `command.tsx` (cmdk wrapper), `dialog.tsx`, `input-group.tsx`, `kbd.tsx`, `popover.tsx`, `textarea.tsx`
- Add `apps/web/src/server/registry-base.ts` (filesystem reads to avoid SSR loopback-fetch deadlock), `registry-index.ts`, and `module-detail.ts` as the server-side data layer
- Add `@vercel/og` and `cmdk` to `apps/web` dependencies; add `vitest run` as `pnpm test` in web package
- Mark all corresponding TODO items done
- Initialize Changesets: `.changeset/config.json` with `@changesets/changelog-github` for PR-linked changelog entries, `access: "public"`, `baseBranch: "main"`, `updateInternalDependencies: "patch"`
- Add `.github/workflows/release.yml` — on push to main, `changesets/action@v1` opens a "Version Packages" PR when changesets are queued; merging that PR re-runs the workflow and calls `pnpm release` (build CLI + create-stanza, then `changeset publish`); provenance attestations enabled via `id-token: write` + `NPM_CONFIG_PROVENANCE=true`; requires `NPM_TOKEN` in repo secrets
- Add root `pnpm changeset` and `pnpm release` scripts; add `@changesets/cli` + `@changesets/changelog-github` to root devDependencies
- `@stanza/codemods` and `@stanza/registry` marked `private: true` — they are inlined into the CLI bundle by tsdown and never published independently; drop the now-redundant `files` field from codemods `package.json`
- `apps/cli/package.json` and `packages/create-stanza/package.json` gain `keywords`, `homepage`, `bugs`, `author`, `repository`, and `publishConfig.access: "public"` — npm metadata required for a clean first publish
- Fix `add-vite-plugin` multiline-array indent regression — when inserting into a `plugins: [...]` array that already spans multiple lines, ts-morph was reformatting the inserted and following elements to 8-space indent while leaving the first element at 4; fix preserves the existing element indent; add a dedicated test case covering the regression
- Drop stale bun shebang comment from CI workflow; remove `packages/internal` from README; mark npm publish TODO done; update CLAUDE.md with Changesets workflow, `private: true` invariant, and `pnpm release` entry
- `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)
- Replace Bun runtime with Node.js + `tsx` for the CLI dev path (`#!/usr/bin/env node` shebang, `tsx watch ./src/bin.ts` for `pnpm dev`) and `tsdown` for publishing — emits `dist/bin.mjs` as plain ESM; `create-stanza` gets the same treatment with its own `tsdown.config.ts`
- Add `.github/workflows/ci.yml` running lint, fmt-check, registry build, web build, typecheck, vitest, CLI bundle build, and a smoke-test (`node dist/bin.mjs --version` / `--help`) on push/PR with concurrency cancel-in-progress
- Add `apps/cli/src/commands/commands.test.ts` (196 lines, vitest) with unit tests for `add`, `init`, `list`, `remove`, and `search` command handlers; add `apps/cli/vitest.config.ts`
- Replace `apps/web/scripts/copy-registry.ts` with a pure-bash `copy-registry.sh` — no Node/tsx prerequisite on the deploy target; drop the TS version and its `predev` auto-build hook (`pnpm registry:build` must be run before the first web build)
- Add root `pnpm test` and `pnpm check-types` scripts that fan out to all workspaces via turbo; CI uses these as canonical entry points
- Update `tsconfig.json` files for `packages/registry`, `packages/codemods`, and `packages/create-stanza` to stay source-only (`noEmit: true`); add `publishConfig` pointing at `dist/` in publishable `package.json` files so workspace dev resolution uses `./src/` while published tarballs resolve `./dist/`
- Update CLAUDE.md toolchain invariants: document Node-only runtime, tsdown build pipeline, and per-workspace `dist/` paths; remove stale `bun apps/cli/src/bin.ts` dev command and all "Bun runs the CLI directly" references
- 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
- **Module logos**: `registry-build.ts` now auto-detects `logo.svg` / `logo-light.svg` + `logo-dark.svg` in each module directory and inlines them as `mod.logo` (string or `{ light, dark }`) — no `module.ts` declarations needed; first-party SVGs added for all current modules
- **Codemod bundling**: `registry-build.ts` bundles each module's `codemods/index.ts` via `Bun.build` (ESM, externals: `@stanza/codemods`, `ts-morph`, `node:*`) and embeds the output as `mod.codemodBundle`; the CLI runner materializes it to `node_modules/.cache/stanza-codemods/` so externals resolve normally, replacing the old "first-party only" approach
- **Web builder UI**: split monolithic `builder.tsx` into `builder/{index,slot-cards,command-bar,file-preview}.tsx`; add `Header`, `Footer`, `ModeToggle`, `ThemeProvider`; scaffold shadcn `Badge`, `Card`, `DropdownMenu`, `Input`, `Separator`, `Sonner`, `Tabs`, `Tooltip` components
- **Server-side Shiki**: `apps/web/src/server/highlighter.ts` keeps a warm Shiki instance; `builder-state.ts` (`createServerFn`) computes selections from URL params and pre-renders `{ light, dark }` HTML per file — Shiki is never imported client-side
- **Selection logic**: new `apps/web/src/lib/selection.ts` with full unit tests
- **copy-registry script**: `predev`/`prebuild` hook copies `dist/registry/` → `apps/web/public/registry/` so the same-domain registry path resolves without a separate CDN
- Add `shiki`, `sonner`, `@pierre/trees`, `@fontsource-variable/geist-mono` to `apps/web`; update oxfmt ignore patterns and swap `react-hooks` for `react-perf` + `promise` in oxlint web config
- configure Tailwind v4 via `@tailwindcss/vite` with a full CSS variable theme (light/dark) and Geist Variable font
- add shadcn `components.json` (base-lyra style, tabler icons) and scaffold `Button` component using `@base-ui/react`
- wire in `TanStackDevtools` + `TanStackRouterDevtoolsPanel` in the root layout
- switch path alias from `~/` to `@/` across tsconfig, routes, and components
- add `clsx`/`tailwind-merge` `cn` utility, `vitest`, `prettier-plugin-tailwindcss`, and other supporting deps
- rename `packages/registry-build` → `packages/internal` to serve as a single home for internal tooling
- add `module-new.ts`: interactive CLI (powered by `@clack/prompts`) that scaffolds a new first-party module under `registry/modules/<slot>-<id>/` with `module.ts`, `package.json`, `tsconfig.json`, `templates/.gitkeep`, and an optional `codemods/index.ts` stub
- expose `pnpm module:new [slot] [id]` at the repo root and update `registry:build` turbo filter to `@stanza/internal`
- split better-auth drizzle adapters into separate postgres/sqlite variants with a dedicated sqlite schema template
- simplify auth-client templates to use `createAuthClient()` without an explicit baseURL
- export `Node` type and `SyntaxKind` enum from the codemods package
- add codemod for the auth-clerk module