Commit Graph
122 Commits
Author SHA1 Message Date
github-actions[bot]andjake 06bd24838c chore: release create-stanza@0.1.0 stanza-cli@0.1.0 2026-05-30 14:05:31 -04:00
jake ea7fe48d00 chore: tighten changeset config, add stanza-cli bin alias, and sync docs
- 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
2026-05-30 13:59:46 -04:00
jake e89fb63c31 feat: unify registry loading behind a main file, add apply rollback, and add stanza doctor
- **Registry main file.** The CLI now addresses any registry (first-party, third-party, or `STANZA_REGISTRY` override) by the full URL/path to its JSON index file. The `modules/<category>-<id>.json` convention, the in-repo source-tree loader, the directory-based auto-detect, and the inline-template disk fallback are all removed — one loader, no heuristics. `STANZA_REGISTRY` must be the full path/URL to the main file (not a directory); `registries` entries now carry `url` pointing at the main file instead of an `indexUrl`+template. **Breaking** (pre-release, clean break): registry index bumped to `schemaVersion: 2`; every module entry carries a `path` field resolved relative to the main file.
- **Auto-rollback.** `stanza add` (and each module in `stanza init`) now wraps its file writes in a transaction; a mid-apply failure restores touched files and `stanza.json` to their pre-apply state. `reportApplyFailure` updated to say "The change was rolled back" and demotes the `stanza remove` / `git restore` steps to a backstop note.
- **`stanza doctor`.** New read-only command that checks `stanza.json` against the filesystem (tracked files, deps, scripts, env vars, internal packages) and reports drift, exiting non-zero when found.
- Update integration tests to build the first-party registry once into a temp dir and point `STANZA_REGISTRY` at its main file; update the third-party HTTP fixture server to serve `/index.json` as the main file alongside per-module paths.
- Update `AGENTS.md` E2E smoke instructions and `apps/cli/README.md` to reflect the new loader and `doctor` command.
2026-05-30 12:49:11 -04:00
jake 7349b53b82 feat: add api category, improve stanza add failure reporting, and ship npm READMEs
- Add `api` to the category taxonomy (`cardinality: "one"`, `home: package` → `packages/api/`) for a future typed RPC layer; no first-party modules yet — wizard skips it and the web builder hides it while empty
- Replace the raw `throw err` in `cmdAdd` with `reportApplyFailure`: a `RegionConflictError` states plainly that nothing was written; a mid-apply failure points at `stanza remove …` to sweep tracked files and (when a clean baseline was enforced) `git restore . && git clean -fd` to reset the worktree
- Add npm READMEs to `apps/cli` and `packages/create-stanza` covering quick-start, commands, safety flags, registries, and telemetry
- Unwrap hard-wrapped lines in `category-taxonomy.md` changeset and update AGENTS.md to reference the `compile-registry` vp task instead of the old `prebuild` script
2026-05-30 11:35:14 -04:00
jake 4fbb060226 feat: add /registry root index page, sort detail tables, and rename OG routes to .webp URLs
- Add `registry.index.tsx` — a root registry landing page at `/registry` listing all modules; emit `/registry` in `prerender.ts` so it is prerendered alongside category and module paths
- Sort deps, scripts, env vars, and templates alphabetically (env vars: required-first then alpha; templates: by scope rank then dest) for consistent display order
- Rename OG image routes to include the `.webp` extension in the URL (`og.index.ts` → `og[.]webp.ts`, `og.docs.$.ts` → `og.docs.{$}[.]webp.ts`, `og.registry.$category.$id.ts` → `og.registry.$category.{$id}[.]webp.ts`) and update the default `ogImage` fallback in `seo.ts` to `/og.webp`
- Swap version/icon layout in `DepsTable` so version is hidden on hover and the external-link icon appears in its place
2026-05-29 18:06:42 -04:00
jake 9237dd7f46 chore: update deps 2026-05-29 08:58:53 -04:00
jake 1201b238b1 fix: add touch-pan-y to search listbox to enable scrolling on iOS 2026-05-28 20:59:12 -04:00
jake 5143a0ee76 fix: tighten stats page card spacing and drop unused chart props
- Switch top-level stat cards to `size="sm"` with `!`-overrides on gap/padding to match the per-category card density
- Remove `pb-1` overrides from `CardHeader` in favour of the card's own `gap-2.5!` so spacing is driven consistently by the card gap
- Drop `curveType="monotone"` and `animationType="left-to-right"` from `ActivityChart` as they are unused/unsupported props
2026-05-28 19:38:52 -04:00
jake bbf7e416b8 docs: add early-alpha warning callout to docs index page 2026-05-28 13:59:08 -04:00
jake 981b1a88d6 fix: rename auth-clerk's app-scoped middleware.tsproxy.ts for Next 16
- Rename the template file and update `src`/`dest` in the adapter's file list to match the new `proxy.ts` convention required by Next 16
- Update the example filename in `AGENTS.md` and `authoring.mdx` (`middleware.ts` → `proxy.ts`) so the scope guidance stays accurate
- Refactor `BarList` bar fill to animate `width` instead of `transform: scaleX` so the gradient and rounded cap don't distort; replace diagonal hatch overlay with a solid gradient + inset ring and tighten leading gap to `gap-1`
2026-05-28 13:01:50 -04:00
jake dda6cb2e44 fix: accessibility pass and miscellaneous UI polish across components
- Add `aria-hidden="true"`, `aria-label`, `aria-labelledby`, `aria-expanded`, `aria-controls`, and `role="region"` attributes throughout builder, detail, docs, and search components
- Fix `describeError` for `incompatible-peer` to show the module's human-readable label instead of the raw peer ID string
- Replace `IconLoader2` spinner ad-hoc pattern with a shared `Spinner` component; delete unused `popover.tsx`
- Add `IconExternalLink` indicator to deps table rows and make version column `tabular-nums`
- Fix scripts table to use `break-words whitespace-pre-wrap` on the command column so long commands wrap instead of overflowing
- Add `truncate overflow-hidden` to the file preview path header bar
- Capitalize "Environment Variables", "Required", "Optional" badge labels; add trailing period to empty-state copy
- Refactor `use-pointer-capability` and `use-time-ago` hooks for clarity; update chart legend, tooltip, and activity chart components
2026-05-28 12:27:41 -04:00
jake 1e4e0269ba fix: miscellaneous UI polish and doc link corrections
- Fix internal doc links to use absolute paths (`#anchor` → `/docs/page#anchor`) in `authoring.mdx` and `registry.mdx` so they resolve correctly from any page
- Clarify `consumesPackages` description wording in the module field reference table
- Switch category badge on module detail page from `outline` to `default` variant for better visual hierarchy
- Nudge `IconCheck` margin, `Label` leading, and adapter-switcher category span font size for tighter alignment
- Normalize `CardTitle` color from `text-foreground/85` → `text-foreground/80` and `text-muted-foreground` across stats cards for consistency; add `size="sm"` to per-category cards and tighten card spacing
- Add `text-sm` to the "Learn more…" docs link on the home page so it inherits the correct size
- Lighten `LastRefreshed` text from `/70` → `/85` opacity for legibility
2026-05-28 11:18:28 -04:00
jake 703a1f76cf feat: add category index pages and consolidate detail table components
- Merge `deps-table.tsx` and `env-table.tsx` into a single `tables.tsx` that also exports a new `ScriptsTable`; split was unnecessary and the unified file is easier to maintain
- Add `count` prop to `Section` so all detail sections (templates, deps, env vars, scripts) show an item-count badge in the heading
- Link each dependency row in `DepsTable` to `npmx.dev` so users can inspect package versions directly
- Introduce `registry.$category.index.tsx` — a new category landing page at `/registry/<cat>` that lists all modules in that category
- Update `prerender.ts` to emit both `/registry/<cat>` (category index) and `/registry/<cat>/<id>` (module detail) paths so all public registry URLs are prerendered
2026-05-27 20:18:28 -04:00
jake 440d1d38da fix: correct npm init / bun create invocations and drop @latest tags from create-style commands
- 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
2026-05-27 19:44:45 -04:00
jake 1e45429ea3 chore: trim AGENTS.md, expand stanza-cli skill docs, and tidy stats page
- Condense `AGENTS.md` by moving CLI-specific guidance into `skills/stanza-cli/SKILL.md` where it is co-located with the skill it describes
- Expand `SKILL.md` with the relocated context so agent runs against the CLI skill have the full picture without loading root-level agent instructions
- Minor cleanup to `stats.tsx` layout/typography
2026-05-27 19:13:03 -04:00
jake 77de7bcbc9 chore: replace prepare-registry.sh pre-hooks with a Vite run task
- Delete `scripts/prepare-registry.sh` and its `prebuild`/`predev` npm lifecycle hooks
- Define a `compile-registry` task in `vite.config.ts` using the `run.tasks` API with explicit `input`/`output` globs so Vite can cache and skip the build step when sources are unchanged
2026-05-27 18:56:04 -04:00
jake 38cc24362c fix: serve docs pages in sidebar order and polish UI typography
- Add `getOrderedPages()` to `source.ts` that walks `source.getPageTree()` recursively so pages are yielded in the order declared by `meta.json` (sidebar order) rather than arbitrary filesystem order; replace `source.getPages()` in `llms-full.txt` and `docs-index.functions.ts`
- Enhance `getLLMText` to prepend the page description as a blockquote when present, and `trimStart()` the processed body so the output is clean
- Fix `useTimeAgo` to return `"just now"` for sub-minute diffs instead of the awkward `Intl.RelativeTimeFormat` "0 seconds ago" / "in 0 seconds" output
- Tighten `LastRefreshed` markup so the tooltip wraps only the relative-time string, not the surrounding prose
- Switch heading `font-semibold` → `font-medium` across stats and home pages; update category card title color from `text-muted-foreground` to `text-foreground/85` for better contrast
2026-05-27 18:45:08 -04:00
jake 190a078593 feat: add DocsActions toolbar to docs pages with copy-markdown and AI chat links
- New `DocsActions` component renders a `ButtonGroup` with a "Copy Page" button (fetches the `.md` alternate and writes it to the clipboard) and a chevron dropdown linking to View as Markdown, ChatGPT, Claude, and Perplexity — each AI link pre-fills a prompt with the page URL
- Wire `DocsActions` into the docs route's page header alongside the title/description, computing `markdownPath` and `pageUrl` from `page.url` and the exported `SITE_URL`
2026-05-27 18:22:11 -04:00
jake f59cc4c195 feat: integrate @number-flow/react for enhanced number formatting in stats page
- 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.
2026-05-27 17:49:07 -04:00
jake 2de4b24fc9 fix: stop shipping full Module registry to the frontend — serve ModuleMetadata only
- 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
2026-05-27 16:07:49 -04:00
jake 7554ae92eb fix: run build before lint/test in CI so typechecking doesn't fail on unbuilt generated files 2026-05-27 15:12:08 -04:00
jake f6fa1aebaf chore: add Renovate config 2026-05-27 14:48:24 -04:00
jake e3f5b977ba fix: migrate JSON helpers to jsonc-parser for format-preserving in-place edits
- 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
2026-05-26 22:34:18 -04:00
jake cf233f8275 fix: harden remove sweep safety, snapshot codemods on records, and fix package-home cardinality scope
- 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
2026-05-26 22:25:10 -04:00
jake 1fc133c7dc fix: harden module-id validation, parallelize registry loading, and redact third-party ids in telemetry
- Add `isLikelyNamespaceTypo` and `isValidModuleId` to `@stanza/registry` and gate both `add` and `remove` on them before any registry fetch or disk write — bare `@ns` specs and path-traversal-style ids now fail fast with a targeted hint instead of an opaque 404
- Add `captureModule` to the telemetry client: mirrors `capture("cli_module", ...)` but redacts the module id when the namespace is not `@stanza` so proprietary ids never leave the user's machine; replace all `capture("cli_module", ...)` call sites in `add`, `remove`, and `init`
- Parallelize `loadRegistries` via `Promise.all` so custom-namespace index fetches happen concurrently; split `tryFetchIndex` into three discrete try/catch blocks that differentiate URL-build errors (surfaced as warnings), network failures (silent), 404s (legitimate no-index), and schema-invalid 200s (warned)
- Re-stamp `version` to `CURRENT_MANIFEST_VERSION` in `readManifest` so older manifests are transparently upgraded in memory and the next `writeManifest` persists the new version; export `CURRENT_MANIFEST_VERSION` and `SUPPORTED_MANIFEST_VERSIONS` from `@stanza/registry`
- Move codemod-catalog id validation before `ensureSlotPackage` in `applyModule` so an unknown codemod id can't bootstrap a slot package and leave it orphaned on a mid-flight throw; validation still runs on `--dry-run`
- Add a dedicated empty-state branch in `search` for namespaces that are configured but expose no browsable index, directing users to `stanza add <category> <ns>/<id>` instead of a generic "No modules found"
2026-05-26 21:03:53 -04:00
jake f195505a48 feat: add third-party registry support with @namespace/id module addressing
- Add `packages/registry/src/registry-config.ts` with a Zod schema for the `registries` field in `stanza.json` (string URL prefix or `{ url, indexUrl?, headers?, params? }` object), plus `parseModuleSpec` to split `@ns/id` specs and `expandEnv` to substitute `${VAR}` tokens in header values (dropping the header silently when the env var is unset)
- Replace `loadRegistry` with `loadRegistries(manifest)` in `registry-loader.ts`; the loader reads `registries` from `stanza.json`, builds a per-namespace fetch client with auth headers, and routes module loads to the correct HTTP endpoint — `@stanza` remains the implicit default namespace backed by the existing registry URL
- Update `add`, `remove`, `search`, and `list` commands to accept `[@<namespace>/]<id>` syntax; `namespace` is recorded alongside each module entry in `stanza.json` at install time so `remove` knows which registry to refetch from
- Enforce that codemods are catalog-only: third-party modules may reference first-party codemod IDs but cannot introduce new ones; catalog ID validation runs before any writes so unknown IDs are caught on `--dry-run` too
- Add a full third-party registry test suite in `commands.test.ts` using a stub `node:http` server: covers install + manifest recording, unknown-namespace rejection, remove round-trip (asserts correct namespace URL is hit), `${ENV_VAR}` header expansion, and codemod-catalog enforcement
- Export `DEFAULT_NAMESPACE` and `parseModuleSpec` from `@stanza/registry`; telemetry `cli_module` events now include a `namespace` field so first- vs. third-party installs can be distinguished in PostHog
- Add `apps/web/content/docs/authoring.mdx` covering the full module-authoring reference (schema fields, templates, adapters, codemods, third-party registry config)
2026-05-26 20:12:01 -04:00
jake 659f441343 fix: harden applyModule ordering, partial-init diagnostics, and consumesPackages context
- Add `consumesPackages` field to `Module` and thread it into `buildRenderContext` / `TemplateContext` so templates can reference sibling package names; update `manifest.ts`, `package-json.ts`, `template.ts`, and their tests
- Queue all file/package.json/env writes behind a `deferredWrites` array so `writeManifest` always persists before any disk mutation — a mid-flush crash leaves the manifest as ground truth with no orphan files it can't account for
- Move codemod-catalog ID validation before any writes so `--dry-run` catches unknown codemod IDs without touching the filesystem; codemods now run in a second phase after deferred writes flush, with a follow-up `writeManifest` after claim gathering
- Wrap `init`'s module-apply loop in try/catch; on failure, log which modules were already applied and leave the partial project on disk for inspection rather than silently exiting
2026-05-26 19:20:01 -04:00
jake 42da7f238b feat: add ai category with vercel-ai-sdk and tanstack-ai modules
- 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
2026-05-26 18:44:52 -04:00
jake 3028165da4 fix: polish copy, remove homepage icon from module cards, and tighten footer/link styles
- 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
2026-05-26 18:03:03 -04:00
jake 2bde308229 feat: replace hand-rolled SparkAreaChart with evilcharts area chart on stats page
- 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
2026-05-26 17:34:35 -04:00
jake 187375ee01 feat: add homepage and details icon links to module cards
- Convert `<button>` to `<div role="button">` with an `onKeyDown` handler (Space/Enter) so nested `<a>` and `<Link>` elements can be interactive without violating the no-interactive-in-button HTML rule
- Add an `IconHome` anchor next to the module label that opens `m.homepage` in a new tab when present; `e.stopPropagation()` prevents the click from toggling the card
- Add an `IconInfoCircle` TanStack Router `<Link>` that navigates to `/registry/$category/$id` for the full module details page; same stopPropagation guard applied
2026-05-26 16:32:39 -04:00
jake fc2cf8375d fix: simplify file-preview sync, project-setup state, and remove unused deps
- 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
2026-05-26 14:00:02 -04:00
jake aa7c2bfe29 fix: upgrade stats cache from fixed TTL to stale-while-revalidate via waitUntil
- Replace the simple 1-hour hard TTL with a soft/hard split: cache hits younger than 1h are returned immediately with no work; hits between 1h and 24h are returned stale while a background `refreshAndCache` call (via `waitUntil`) updates the cache for the next visitor; only a true cold miss (first request per region or age ≥ 24h) blocks on the PostHog round-trip
- Extract `refreshAndCache` helper so the "fetch → conditionally write" logic is shared between the background and cold-miss paths; failed fetches still never pollute the cache
- Move the "Last refreshed" timestamp from the bottom of the telemetry section to directly under the page header so it's visible without scrolling
- Inline `HeroStat` and `TelemetrySection` subcomponents into `StatsPage`; they were single-use and the indirection added no value
2026-05-26 12:27:26 -04:00
jake c1df786210 fix: fix spark chart dot distortion and add leading-slot + module logos to stats BarList
- Wrap `SparkAreaChart` SVG in a `div` and move the last-point dot from an inline `<circle>` to an absolutely-positioned HTML `<span>` so it stays a perfect circle regardless of `preserveAspectRatio="none"` scaling
- Add `leading?: ReactNode` slot to `BarListEntry` so any icon, logo, or swatch can be rendered before the row name; wrap name + leading in a flex container so truncation still works
- Wire `ModuleLogo` into the stats page `BarList` as the `leading` element for each module row; refactor `moduleLabel` to `findModule` so the full `ModuleSummary` is available for both the label and the logo
- Add `/stats` to the prerender list so the page is statically generated at build time
2026-05-25 21:19:04 -04:00
jake 2103e3607e fix: optimize and namespace SVG logo IDs at build time with SVGO
- 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
2026-05-25 21:07:54 -04:00
jake c7ba9bec3c feat: add public /stats page with PostHog-backed CLI telemetry aggregates
- Add `/stats` route: server function fetches PostHog event aggregates (total runs, daily sparkline, top modules by installs, top modules by removes, command breakdown) and renders them with a `SparkAreaChart` + `BarList` layout; data is cached server-side with a 5-minute TTL via TanStack Start server functions
- Add `BarList` UI component: proportional horizontal-bar leaderboard, optional trailing label + href per row, empty-state fallback; no charting deps
- Add `SparkAreaChart` UI component: inline SVG area + stroke chart with native `<title>` tooltip targets, `--color-chart-1/2` for automatic dark-mode theming; no charting deps
- Add `posthog-query.server.ts`: typed PostHog HogQL query helper that hits the `/query` REST endpoint; separate query builders for the daily run series, per-module install/remove counts, and per-command totals
- Add Stats link to header nav and footer; minor footer font-size tweak (`text-xs` → `text-[13px]`)
- Document telemetry in `cli.mdx`: what's sent, what's never sent, opt-out options, and `STANZA_TELEMETRY_URL` env var for self-hosted ingest
2026-05-25 21:05:27 -04:00
jake 17c0026f49 feat: add {{env}} render context, {{json}} helper, and declaredEnvNames to wire turbo.json globalEnv automatically
- Add `declaredEnvNames(manifest)` to `@stanza/registry`: extracts a sorted, deduped list of env var names from `manifest.regions[".env.example"]` — the same keys the CLI records as modules apply; exported from the package index
- Add `env: string[]` to `TemplateContext` and an `envNames` option to `buildRenderContext`: sorts + dedupes the input so rendered output is stable regardless of module-apply order; defaults to `[]` when omitted
- Register a `{{json}}` Handlebars helper that `JSON.stringify`s any value (primary use: `"globalEnv": {{json env}}` in `turbo.json`, but generic enough for any template embedding a JS value into a JSON literal)
- Thread `envNames` through `codemod-runner`'s `renderContextFor` (derived from `declaredEnvNames(manifest)` so late-running repo-home modules like `monorepo-turbo` see all prior env claims) and through `synthesizeTemplates` + `synthesizeReadme` (aggregated from every resolved entry's merged install fields, matching what the CLI derives post-apply so CLI and web preview render identically)
- Update `monorepo-turbo/templates/turbo.json` to emit `"globalEnv": {{json env}}` so Turborepo task hashes track every project-wide env var; update `readme.md` to document the auto-population behavior
- Add `declaredEnvNames` tests (empty manifest, multi-key `.env.example` regions, non-env file regions ignored), `buildRenderContext` tests (sort+dedupe, omitted default), `{{json}}` helper tests (env array, empty array, missing key), and a `synthesizeTemplates` integration test verifying the aggregated env list flows into a rendered `turbo.json`
2026-05-25 20:33:46 -04:00
jake 2e392508b9 feat: add monorepo-turbo module, pm-aware workspace fan-out scripts, and monorepo cardinality fix
- Add `monorepo-turbo`: `turbo ^2.9.0` dev dep + a Handlebars-templated `turbo.json` at the repo root; module scripts (`dev`/`build`/`test`/`lint`) overwrite the pm-native fan-out defaults seeded by `rootPackageJson`; an `append-to-file` codemod adds `.turbo/` to `.gitignore` idempotently so both fresh `init` and `stanza add` projects are covered; light/dark logos inlined from svgl
- Add `pmRecursive(pm, script)` to `@stanza/registry` (and export it): produces the pm-native workspace-wide invocation (`pnpm -r run <s>`, `bun run --filter '*' <s>`, `npm run <s> --workspaces --if-present`); `rootPackageJson` now seeds all four root scripts via `pmRecursive` instead of hard-coding the pnpm form, and gains a `lint` entry that was previously missing
- Change `applyFields` script merging from guard-if-undefined to unconditional overwrite so a `monorepo` module's declared scripts can replace the seeded defaults; the same fix is applied to the `app`-overlay script path
- Fix `monorepo` category cardinality from `"many"` to `"one"` (only one task orchestrator makes sense per repo) and update its description
- Remove `.turbo/` from the hardcoded `.gitignore` written by `bootstrapShell` in `init.ts` — the module codemod now owns that entry
- Add `package-json.test.ts` coverage for `rootPackageJson` default scripts per pm and for monorepo module script overrides
2026-05-25 20:17:26 -04:00
jake 6b8a46ac71 fix: apply jsx: "react-jsx" to all slot packages, not just ui 2026-05-25 16:57:54 -04:00
jake bcf1d20f22 feat: add email-resend module with React Email templates and Svix-verified webhook
- 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
2026-05-25 16:37:09 -04:00
jake dd806479d8 feat: add auto-generated project README with checksum-based edit detection and app install-fields overlay
- Add `synthesizeReadme(resolved, ctx)` to `@stanza/registry`: generates a Markdown README from the assembled module selection using each module's `readme` / `description` copy; exported alongside new `Resolved` / `ResolvedEntry` types that capture the module + adapter pair after resolution
- Add `apps/cli/src/lib/readme.ts` with `writeFreshReadme` (used by `init` into a guaranteed-empty dir), `regenerateReadmeIfUnmodified` (used by `add`/`remove` to skip user-edited files), and `readmeChecksum` / `userModifiedReadme` helpers that compare SHA-256 against a new `readmeChecksum` field on `StanzaManifest`; a missing stored checksum with a present file is treated as user-owned (conservative, covers legacy projects)
- Wire README generation into all three commands: `init` calls `writeFreshReadme` after all modules apply and records the checksum; `add` and `remove` call `regenerateReadmeIfUnmodified` post-mutation and warn when the file has been edited; both update `stanza.json` with the new checksum on write
- Add `app` install-fields overlay in `codemod-runner.ts`: a per-module `installFields.app` bucket routes `dependencies`, `devDependencies`, and `scripts` into each consuming app's `package.json` rather than the workspace root or `packages/ui`; use case is package-home modules whose app-scoped shims import npm packages that belong in the app (e.g. `next-themes` for `ui-shadcn-*`); claims are recorded under `app.dependencies.<name>` region keys so removal can reverse them
- Fix `remove.ts` region handling: strip the `app.` prefix before dispatching `dependencies.*` / `devDependencies.*` / `scripts.*` removals so `app`-overlay claims are reversed correctly
- Add `pmRun(pm, script)` helper to `@stanza/registry` and use it in the `init` outro so the suggested dev command respects the project's package manager (e.g. `npm run dev` vs `pnpm dev`)
- Update `ui-shadcn-base` and `ui-shadcn-radix` module definitions to use the new `app` install-fields overlay for app-scoped peer deps; expand `package-json.test.ts`, `synthesize.test.ts`, and `template.test.ts` with coverage for the new paths
2026-05-25 16:23:00 -04:00
jake 460e888b7e feat: pin packageManager field to the latest matching release at init time
- Add `resolveExactVersion(name, version)` to `npm-version.ts`: wraps `resolveRange` with an implied `^${version}` range and strips the modifier from the result so callers get a bare pinned version suitable for Corepack; falls back to the input version on any lookup failure
- Rename the private `PM_VERSION` constant to `PM_FLOOR_VERSION` (exported from `@stanza/registry`) and store bare versions (no `${pm}@` prefix) so they can be fed directly into the resolver; the `rootPackageJson` helper now accepts an optional `packageManagerVersion` override and interpolates it into the `packageManager` field, falling back to the floor when absent
- Make `bootstrapShell` async in `init.ts` and call `resolveExactVersion` before writing `package.json` so newly-generated projects are pinned to the latest compatible release rather than a hardcoded floor; the web preview's synth path continues to use the floor as-is to avoid per-render npm lookups
- Add `resolveExactVersion` tests covering the happy path, `STANZA_NO_NPM_LOOKUP` bypass, network rejection fallback, and unsatisfied range fallback
2026-05-25 15:50:22 -04:00
jake 678b5bc1af feat: add ui-shadcn-base, ui-shadcn-radix, and ui-tailwind modules; rename styling category to ui
- 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
2026-05-25 15:26:08 -04:00
jake 6767362641 refactor: replace hand-rolled debounce with @tanstack/react-pacer and extract useGracePeriod
- 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
2026-05-25 14:23:33 -04:00
jake 16295952a8 feat: add payments-stripe module and sync file-preview selection to URL hash
- 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
2026-05-25 14:06:07 -04:00
jake e28eba2702 feat: add Handlebars template engine with peer-aware render context and consolidate eslint-prettier into a single adaptive template
- 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
2026-05-25 13:35:27 -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 9953570814 perf: add process-level module catalog cache and LRU preview cache
- 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
2026-05-23 23:08:35 -04:00
jake 9e1f0076a2 fix: set infinite stale time for web builder data loading 2026-05-23 22:45:00 -04:00
jake be001b0037 feat: add multi-app support with per-app module targeting and app-aware manifest schema
- Introduce `AppSpec` (`{ id, dir, kind }`) and `AppKind = "web" | "native"` types; `stanza.json` now declares `apps: AppSpec[]` alongside modules, and `stanza init` scaffolds a default web app via `defaultWebApp()`
- Add optional `apps?: string[]` to `StanzaModuleRecord` to encode which apps a record targets; `home: app` modules require it, `home: package` modules use it to scope workspace deps (omitting means all apps), `home: repo` modules forbid it
- Make cardinality enforcement app-scoped for `home: app` categories: `selectedOne(m, cat, appId?)` and `selectedAll(m, cat, appId?)` now filter by app, and `add`/`remove` thread a `targetApp` through the runner so per-app single-choice rejection works correctly
- Extend the template render context with an `app: { id, dir, kind }` binding (plus the legacy `project.appDir` alias) re-bound per target app; the codemod runner fans out template/codemod/dep application once per consuming app in a loop
- Update `synthesizePackageJsons`, `synthesizeTemplates`, `buildRenderContext`, and related web-preview helpers to accept an `apps` array and produce per-app output
- Update `package-json.ts` to strip workspace deps from all apps' `package.json` on remove and to inject them into every consuming app on install
- Expand `manifest.test.ts` and `template.test.ts` with multi-app fixture coverage; add `"use client"` directive to codemod test files to satisfy the RSC boundary
- Refresh `AGENTS.md`, `SKILL.md`, and all affected docs (`concepts.mdx`, `registry.mdx`, `cli.mdx`, `getting-started.mdx`, `agents.mdx`) to document the new apps schema and routing semantics
2026-05-23 22:26:09 -04:00