Self-managed website kit for Next.js: /admin to edit pages with blocks, media, menus, settings, forms and users
Install
genpm add @core/kit-cmsWhat you get
- Source in src/app/(cms)/, 0 files.
- AI rules in src/app/(cms)/AGENTS.md, plus IDE rule files.
- Resolves @core/admin, @core/blocks, @core/content, @core/forms, @core/media, @core/rich-text, @core/search, @core/seo, @core/site for you.
README
This package has no README.
This is exactly what your AI reads when it works in src/app/(cms). Nothing else is added to its context.
@core/kit-cms — rules for AI agents
Purpose
Turns a Next.js (App Router) site into a self-managed one: /admin where non-technical people edit pages built with
blocks, images, menus, site settings, forms, redirects and users, with drafts, signed previews, scheduled publishing
and roles. Glue only: all logic lives in @core/admin, content, media, rich-text, blocks, site, seo, forms, search,
auth and rbac (the last two arrive through @core/admin). Public routes: pages created in the admin (/[...slug]), /search, /sitemap.xml, /robots.txt,
/llms.txt. Base for kit-blog, kit-landing and kit-store.
Map
admin/[[...path]]/page.tsx— panel (server checks session +admin:access).admin/login/page.tsx— OAuth sign-in.api/admin/[...path]— admin API.api/media/uploads…— uploads.api/preview,api/exit-preview— draft mode.api/forms/[key]— form posts.api/cron— @core/jobs.api/search— JSON search.[...slug]/page.tsx— pages of thepagescollection (redirects before the 404).search/page.tsx— results._lib/page.tsx—CmsPage,pageMetadata._lib/home.tsx— home page from thehomeentry._lib/session.ts._components/— admin client app and themedia/richTextfield editors._templates/genpm/*.ts— the project's registration files._templates/middleware.ts— redirects middleware._templates/layout.tsx— root layout for a new site._templates/env.example— every variable the kit and its packages read._lib/maintenance.ts— dailycms.maintenancejob (prunes rate limits and old jobs), loaded byapi/cron._scripts/create-owner.ts— gives the first signed-in user theownerrole (and creates the default roles)._scripts/setup-schedules.ts— schedules the daily jobs of the installed kits (cms.maintenance,search.reindex,store.maintenance,experiments.prune) and queues the first reindex.
Integration
- Check Next ≥ 15.5 with App Router,
src/appand the@/*→src/*alias. Withoutsrc/: install with--dest app/(cms)and adapt@/imports. GenPM packages import each other with.tsextensions:tsconfig.jsonneeds"allowImportingTsExtensions": true(Next projects already have"noEmit": true). Works with webpack and Turbopack. Innext.configsethtmlLimitedBots: /.*/: metadata comes from the database, and without it Next 15 streams<title>, description and canonical into<body>for every client except a few bots (Lighthouse, link previews and some crawlers miss them). - Env: copy
_templates/env.exampleto the project's.env.example(merge if it exists; Next's default.gitignorehas.env*, so add a line!.env.example) and the real values to.env.local. Required:DATABASE_URL,AUTH_SECRET, one OAuth provider (GITHUB_CLIENT_ID/SECRETorGOOGLE_…),STORAGE_*,EMAIL_FROM+RESEND_API_KEY,SITE_URL,CONTENT_PREVIEW_SECRET(≥ 32 chars),ANTISPAM_SECRET,CRON_SECRET. WithSTORAGE_DRIVER=localalso mount the storage route (src/lib/storage/AGENTS.md, Integration 1). - Mount the @core/auth routes (
app/auth/login/[provider],app/auth/callback/[provider],app/auth/logout) as its AGENTS.md says. - Copy
_templates/genpm/*.tstosrc/genpm/(skip files that exist and merge by hand). These files belong to the project: later kits add lines to them. Copy_templates/middleware.tstosrc/middleware.ts(or merge into the existing one). - Migrations: GenPM does not install dev tools — run
src/lib/db/AGENTS.mdsteps 3–5 (drizzle-kit+tsx, thedb:generate/db:migratescripts), generate one migration for all packages and apply it where a database exists. - Schedule
GET /api/cronevery minute withAuthorization: Bearer $CRON_SECRET(e.g.vercel.jsoncrons). Once the database is migrated, the person runsnpx tsx "src/app/(cms)/_scripts/setup-schedules.ts"(idempotent; it schedules the daily jobs of every installed kit, so run it again after adding another kit). - The person signs in once at
/admin/login, then runsnpx tsx "src/app/(cms)/_scripts/create-owner.ts" <their email>. Never run it for them with an email they did not give you. - New site: create
src/app/page.tsxwithexport { default, generateMetadata } from './(cms)/_lib/home';and create thehomepage in the admin (blocks: hero, features, cta…). Existing site: follow "Retrofit" below. - Root layout: new site → copy
_templates/layout.tsxtosrc/app/layout.tsx. Existing layout → import@/lib/ui/ui.cssand@/lib/blocks/blocks.css, render menus withgetMenu('header')/getMenu('footer')and the site name withgetSiteSettings()(@core/site), and addexport const dynamic = 'force-dynamic'(it reads the database; without itnext buildtries to prerender and fails withoutDATABASE_URL). - Verify:
npx tsc --noEmitandnpx next buildpass without a database. Then, with one: sign in at/admin, edit a page, open "Preview", publish, and see the change on the public URL. - Delete every
src/lib/*/adapters/hono.ts(rm src/lib/*/adapters/hono.ts): they importhono, which a Next project does not install, andtscchecks every file. Keepadapters/next.ts.
Retrofit (make an existing site editable)
Follow the @core/content procedure page by page, one commit per page: inventory visible literals → a singleton per
page (or a collection when repeated) defined in src/genpm/content.ts → seed with the current values (seedEntry) →
replace literals with typed reads → compare the rendered HTML before/after (must be identical). Keep markup and design;
only the data source changes: skip className="ui-root" on <body> and don't add buildMetadata tags the site did not
have (keep its titles and descriptions, now read from the CMS). Local images go to @core/media with their alt text;
SVG (which @core/media rejects) stays in public/ with its path in a text field. Keep the root layout's existing
header/footer markup and feed it from getSiteSettings() / getMenu(). Existing routes keep priority over
[...slug]. Non-Next sites (static HTML, Astro, Vite): out of scope for 1.0 — say so.
Conventions
- Page sections are blocks registered in
src/genpm/blocks.ts; add new blocks there, never per-page components. - Admin resources are registered in
src/genpm/admin.ts; never write a custom admin page for a resource. - The
pagesentry with slughomeis/; other slugs map to/<slug>. - Default roles:
editorpublishes pages but cannot see users or settings beyond content;authoredits only own entries.
Don't
- Don't invent legal texts (privacy, terms, imprint): create the pages empty with a "pending review" note for the owner.
- Don't expose
/adminor/apiin the sitemap, and don't removerobots: noindexfrom admin pages. - Don't bypass
CRON_SECRETor callrunDue()from a public route. - Don't grant roles or create owners from code paths reachable by visitors.
# @core/kit-cms — rules for AI agents
## Purpose
Turns a Next.js (App Router) site into a self-managed one: `/admin` where non-technical people edit pages built with
blocks, images, menus, site settings, forms, redirects and users, with drafts, signed previews, scheduled publishing
and roles. Glue only: all logic lives in @core/admin, content, media, rich-text, blocks, site, seo, forms, search,
auth and rbac (the last two arrive through @core/admin). Public routes: pages created in the admin (`/[...slug]`), `/search`, `/sitemap.xml`, `/robots.txt`,
`/llms.txt`. Base for kit-blog, kit-landing and kit-store.
## Map
- `admin/[[...path]]/page.tsx` — panel (server checks session + `admin:access`). `admin/login/page.tsx` — OAuth sign-in.
- `api/admin/[...path]` — admin API. `api/media/uploads…` — uploads. `api/preview`, `api/exit-preview` — draft mode.
`api/forms/[key]` — form posts. `api/cron` — @core/jobs. `api/search` — JSON search.
- `[...slug]/page.tsx` — pages of the `pages` collection (redirects before the 404). `search/page.tsx` — results.
- `_lib/page.tsx` — `CmsPage`, `pageMetadata`. `_lib/home.tsx` — home page from the `home` entry. `_lib/session.ts`.
- `_components/` — admin client app and the `media` / `richText` field editors.
- `_templates/genpm/*.ts` — the project's registration files. `_templates/middleware.ts` — redirects middleware.
`_templates/layout.tsx` — root layout for a new site. `_templates/env.example` — every variable the kit and its packages read.
- `_lib/maintenance.ts` — daily `cms.maintenance` job (prunes rate limits and old jobs), loaded by `api/cron`.
- `_scripts/create-owner.ts` — gives the first signed-in user the `owner` role (and creates the default roles).
- `_scripts/setup-schedules.ts` — schedules the daily jobs of the installed kits (`cms.maintenance`, `search.reindex`,
`store.maintenance`, `experiments.prune`) and queues the first reindex.
## Integration
1. Check Next ≥ 15.5 with App Router, `src/app` and the `@/*` → `src/*` alias. Without `src/`: install with `--dest app/(cms)` and adapt `@/` imports.
GenPM packages import each other with `.ts` extensions: `tsconfig.json` needs `"allowImportingTsExtensions": true`
(Next projects already have `"noEmit": true`). Works with webpack and Turbopack. In `next.config` set
`htmlLimitedBots: /.*/`: metadata comes from the database, and without it Next 15 streams `<title>`, description and
canonical into `<body>` for every client except a few bots (Lighthouse, link previews and some crawlers miss them).
2. Env: copy `_templates/env.example` to the project's `.env.example` (merge if it exists; Next's default `.gitignore`
has `.env*`, so add a line `!.env.example`) and the real values to `.env.local`. Required: `DATABASE_URL`, `AUTH_SECRET`, one OAuth provider
(`GITHUB_CLIENT_ID/SECRET` or `GOOGLE_…`), `STORAGE_*`, `EMAIL_FROM` + `RESEND_API_KEY`, `SITE_URL`,
`CONTENT_PREVIEW_SECRET` (≥ 32 chars), `ANTISPAM_SECRET`, `CRON_SECRET`. With `STORAGE_DRIVER=local` also mount the
storage route (`src/lib/storage/AGENTS.md`, Integration 1).
3. Mount the @core/auth routes (`app/auth/login/[provider]`, `app/auth/callback/[provider]`, `app/auth/logout`) as its AGENTS.md says.
4. Copy `_templates/genpm/*.ts` to `src/genpm/` (skip files that exist and merge by hand). These files belong to the project:
later kits add lines to them. Copy `_templates/middleware.ts` to `src/middleware.ts` (or merge into the existing one).
5. Migrations: GenPM does not install dev tools — run `src/lib/db/AGENTS.md` steps 3–5 (`drizzle-kit` + `tsx`, the
`db:generate` / `db:migrate` scripts), generate one migration for all packages and apply it where a database exists.
6. Schedule `GET /api/cron` every minute with `Authorization: Bearer $CRON_SECRET` (e.g. `vercel.json` crons). Once the
database is migrated, the person runs `npx tsx "src/app/(cms)/_scripts/setup-schedules.ts"` (idempotent; it schedules
the daily jobs of every installed kit, so run it again after adding another kit).
7. The person signs in once at `/admin/login`, then runs `npx tsx "src/app/(cms)/_scripts/create-owner.ts" <their email>`.
Never run it for them with an email they did not give you.
8. New site: create `src/app/page.tsx` with `export { default, generateMetadata } from './(cms)/_lib/home';` and create
the `home` page in the admin (blocks: hero, features, cta…). Existing site: follow "Retrofit" below.
9. Root layout: new site → copy `_templates/layout.tsx` to `src/app/layout.tsx`. Existing layout → import `@/lib/ui/ui.css`
and `@/lib/blocks/blocks.css`, render menus with `getMenu('header')` / `getMenu('footer')` and the site name with
`getSiteSettings()` (@core/site), and add `export const dynamic = 'force-dynamic'` (it reads the database; without it
`next build` tries to prerender and fails without `DATABASE_URL`).
10. Verify: `npx tsc --noEmit` and `npx next build` pass without a database. Then, with one: sign in at `/admin`, edit a
page, open "Preview", publish, and see the change on the public URL.
11. Delete every `src/lib/*/adapters/hono.ts` (`rm src/lib/*/adapters/hono.ts`): they import `hono`, which a Next
project does not install, and `tsc` checks every file. Keep `adapters/next.ts`.
## Retrofit (make an existing site editable)
Follow the @core/content procedure page by page, one commit per page: inventory visible literals → a singleton per
page (or a collection when repeated) defined in `src/genpm/content.ts` → seed with the current values (`seedEntry`) →
replace literals with typed reads → compare the rendered HTML before/after (must be identical). Keep markup and design;
only the data source changes: skip `className="ui-root"` on `<body>` and don't add `buildMetadata` tags the site did not
have (keep its titles and descriptions, now read from the CMS). Local images go to @core/media with their alt text;
SVG (which @core/media rejects) stays in `public/` with its path in a text field. Keep the root layout's existing
header/footer markup and feed it from `getSiteSettings()` / `getMenu()`. Existing routes keep priority over
`[...slug]`. Non-Next sites (static HTML, Astro, Vite): out of scope for 1.0 — say so.
## Conventions
- Page sections are blocks registered in `src/genpm/blocks.ts`; add new blocks there, never per-page components.
- Admin resources are registered in `src/genpm/admin.ts`; never write a custom admin page for a resource.
- The `pages` entry with slug `home` is `/`; other slugs map to `/<slug>`.
- Default roles: `editor` publishes pages but cannot see users or settings beyond content; `author` edits only own entries.
## Don't
- Don't invent legal texts (privacy, terms, imprint): create the pages empty with a "pending review" note for the owner.
- Don't expose `/admin` or `/api` in the sitemap, and don't remove `robots: noindex` from admin pages.
- Don't bypass `CRON_SECRET` or call `runDue()` from a public route.
- Don't grant roles or create owners from code paths reachable by visitors.
The exact tree that will be injected, after .genpmignore. Pinned to
Source temporarily unavailable. Metadata is still accurate.
This package declares no MCP servers.
| Version | Commit | Published | Scan |
|---|---|---|---|
| 1.0.1 | bb5d8e0 | 1 hour ago | scan passed |
- genpm
- @core/admin ^1.0.0@core/blocks ^1.0.0@core/content ^1.1.0@core/forms ^1.0.0@core/media ^1.0.0@core/rich-text ^1.0.0@core/search ^1.0.0@core/seo ^1.0.0@core/site ^1.0.0
- proposed
- GenPM proposes the npm command and runs it only if you say yes.
- scan
- scan passed · 0 findings
- commit
- v1.0.1 → bb5d8e0aed9a7a877e1234a71887c1c33af7acea · verified after fetch
- scripts
- None. GenPM never runs package code.
- license
- MIT
- Quality
- 100/100
- Recognized licensepassed
- AGENTS.md explains its purposepassed
- AGENTS.md has integration stepspassed
- AGENTS.md lists conventions or don'tspassed
- Includes testspassed
- Security scan passedpassed
- Released in the last 6 monthspassed
- Verified publisherpassed
- Summary and keywordspassed
- report
- See something wrong?