create-kywi-app 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,6 +64,22 @@ next.config.mjs the FULL config to consume core (transpilePac
64
64
  .env.example DATABASE_URL + AUTH_SECRET
65
65
  .gitignore
66
66
  README.md project-specific quick start (createdb, migrate, seed, dev, /admin, admin.features)
67
+ AGENTS.md guidance for AI agents working on this site: an app-specific
68
+ header (where things live, the /admin URL, the CLI, the three
69
+ scaffolded Claude Code skills) + Kywi's official building
70
+ patterns (docs/agents/AGENT-PATTERNS.md, verbatim)
71
+ CLAUDE.md thin pointer that sends a Claude Code session to AGENTS.md first,
72
+ plus a nudge toward the kywi-content-model skill (and the other
73
+ two scaffolded skills)
74
+ .claude/skills/kywi-content-model/SKILL.md Claude Code project skill: design a CONTENT-MODEL.md and get
75
+ owner sign-off before creating types or entering content — loaded
76
+ automatically by Claude Code, a pre-build checklist for other agents
77
+ .claude/skills/kywi-collections/SKILL.md Claude Code project skill: build any collection (testimonials,
78
+ logos, posts, products, …) as folder + feed + Feed Display — loaded
79
+ automatically when adding a collection
80
+ .claude/skills/kywi-personalization/SKILL.md Claude Code project skill: confirm the use case, then
81
+ build audiences, page variants, or A/B experiments — loaded
82
+ automatically when the owner wants personalization or A/B testing
67
83
  lib/kywi.ts server runtime: DB, API handler, content scope (memoised)
68
84
  lib/config.ts re-export of kywi.config
69
85
  middleware.ts auth gate + session refresh + cookie→bearer bridge (over core/host)
@@ -0,0 +1,294 @@
1
+ # Kywi Patterns — building sites end users can maintain
2
+
3
+ Guidance for agents building Kywi sites. These are opinions, not rules — a
4
+ developer can overrule any of them — but they encode what Kywi is *for*: sites
5
+ that get handed to non-developers who keep them alive without you. Follow them
6
+ unless the project has a stated reason not to.
7
+
8
+ ## The core principle
9
+
10
+ **If a non-developer might ever want to change it, model it in the CMS — don't
11
+ hardcode it.** Every hardcoded testimonial array, pasted-in logo grid, or
12
+ copy-edited JSX headline is a future support request. The test before writing
13
+ any markup: *"When the site owner wants this different next month, do they
14
+ edit content in the admin, or do they need a developer?"* If the answer is
15
+ "developer," reach for one of the patterns below instead.
16
+
17
+ Static code is for structure and brand chrome. Content — anything with words,
18
+ images, prices, names, dates — belongs in Kywi.
19
+
20
+ ## Before you build: design the content model and get sign-off
21
+
22
+ The most expensive failure in a CMS build is a poorly planned content model —
23
+ types bolted on mid-build, collections discovered after their items were
24
+ entered as free-form pages, taxonomy retrofitted onto a hundred nodes. Content
25
+ entry is the costly part; planning is cheap. So **start every new site (or any
26
+ significant new content area) by proposing the content model and getting the
27
+ owner's sign-off before creating types or entering content.**
28
+
29
+ Produce a short `CONTENT-MODEL.md` at the project root covering:
30
+
31
+ - **Content inventory → types.** Every repeated shape, with its proposed type
32
+ (built-in page vs. custom), fields (name, field type, required), and
33
+ relationships. One table per type.
34
+ - **Site tree sketch.** Top-level pages and a folder per collection (§1).
35
+ - **Feeds.** Name, source type, taxonomy filter, sort/limit, and which pages
36
+ display each one.
37
+ - **Taxonomy.** The category sets and expected tags (§3).
38
+ - **Reusable components.** The sections that will appear on multiple pages (§4).
39
+ - **Maintenance notes.** Who edits what; whether review workflow (§9), i18n
40
+ (§11), or scheduling (§10) apply.
41
+ - **Open questions** for the owner.
42
+
43
+ Present it, iterate, and get explicit approval — then build in playbook order.
44
+ Keep the file updated as the model evolves; it's the site's living map.
45
+
46
+ Proportionality applies (this is guidance, not law): a single-page brochure
47
+ site needs a few sentences of confirmation, not a document. But anything
48
+ involving custom types, collections, or more than a handful of pages deserves
49
+ the full proposal — ten minutes of sign-off beats re-entering fifty nodes.
50
+
51
+ ## Quick decisions
52
+
53
+ | You're about to… | Do this instead |
54
+ |---|---|
55
+ | Hardcode a list (testimonials, logos, posts, products, team) | Folder of nodes + a Feed + the Feed Display module (§1) |
56
+ | Create one-off pages with identical field shapes | A custom content type + one node per item (§2) |
57
+ | Filter/group content by topic, industry, audience | Categories & tags, queried by feeds (§3) |
58
+ | Copy-paste a CTA/banner/snippet across pages | Save it as a reusable component (§4) |
59
+ | Write page sections as JSX | Build them as layout sections/modules the owner can edit (§5) |
60
+ | Hand-code a `<form>` | Build it in the Forms admin, place the Form module (§6) |
61
+ | Drop images into `/public` | Upload to the Media library (§7) |
62
+ | Build segment-specific or experimental UI unprompted | Ask the owner first — offer personalization/A-B with examples (§8) |
63
+ | Let everyone publish straight to the live site | Configure groups + review workflow (§9) |
64
+ | "Launch this next Tuesday" by deploying on Tuesday | Scheduled publishing / a changeset (§10) |
65
+
66
+ ---
67
+
68
+ ## 1. Collections: folders + feeds + Feed Display
69
+
70
+ **The workhorse pattern.** Any repeating set of items that appears somewhere on
71
+ the site — customer logos, testimonials, recent blog posts, products, portfolio
72
+ pieces, team members, FAQs, press mentions — is a collection, and collections
73
+ are never hardcoded.
74
+
75
+ - **Structure:** a folder in the Site Tree per collection (`/testimonials`,
76
+ `/customers`, `/work`), one content node per item. Items that need their own
77
+ public page (blog posts, portfolio pieces) live at real paths; items that are
78
+ only ever displayed in aggregate (logos, quotes) still get nodes — their pages
79
+ just aren't linked.
80
+ - **Query:** create a Feed (admin → Feeds): pick the content type, filter by
81
+ taxonomy, sort, and limit ("6 most recent posts", "testimonials tagged
82
+ `homepage`").
83
+ - **Display:** place the **Feed Display** module wherever the collection should
84
+ render — home page hero strip, interior sidebars, a `/blog` index. One feed
85
+ can feed many pages.
86
+ - **Why it wins:** the owner adds a testimonial by creating one node. Every
87
+ page showing that feed updates. No layout edits, no deploys.
88
+
89
+ Advanced: product catalogs and portfolios are the same pattern with a richer
90
+ custom type (§2) and taxonomy-driven feeds per category (§3). Multiple feeds
91
+ over one collection give different slices (featured vs. all, per-category).
92
+
93
+ ## 2. Structured data: custom content types
94
+
95
+ When several nodes share the same shape — recipes (ingredients, steps, prep
96
+ time), team profiles (role, bio, headshot, links), products (price, SKU,
97
+ gallery), case studies (client, industry, outcome) — define a **custom content
98
+ type** so the shape is enforced and the data is queryable, instead of burying
99
+ it in free-form body text.
100
+
101
+ **Default: create types in the admin (Type Designer), not in kywi.config.ts.**
102
+ Admin-created types are runtime-managed: the site owner can add a field next
103
+ year ("dietary tags on recipes") without a developer or a migration. Reserve
104
+ config-defined types (`contentTypes` in `kywi.config.ts`) for schema the
105
+ *developer* must own — shapes that code depends on, reviewed in git, migrated
106
+ by `kywi migrate`. When in doubt, admin-created.
107
+
108
+ - Fields available include text, textarea, rich text, date, image, URL, email,
109
+ JSON, and relationship (with a target type — e.g. a recipe's `author` →
110
+ profile). Title can be made optional per type.
111
+ - **Combine with §1:** the type gives you structure; a feed over the type gives
112
+ you the listing (`/recipes` index, an intranet's people directory); each
113
+ node's own page is the detail view. Listing + detail with zero custom code.
114
+ - Don't shadow built-in fields (slug, body) with custom ones — Kywi will block
115
+ it; use the built-ins.
116
+
117
+ ## 3. Taxonomy: categories & tags
118
+
119
+ Categories and tags are the cross-cutting organization layer over §1 and §2.
120
+ Use **categories** for a site's stable sections (Recipes: Breakfast / Dinner /
121
+ Dessert) and **tags** for freeform, evolving labels (gluten-free, quick,
122
+ featured).
123
+
124
+ - Feeds filter by taxonomy, so "show featured testimonials on the home page"
125
+ is: tag the nodes `featured`, point the feed at the tag. The owner curates
126
+ the home page by tagging — never by editing the page.
127
+ - Prefer taxonomy-driven feeds over separate folders when items belong to
128
+ multiple groupings (a recipe is both `quick` and `vegetarian`; a folder can
129
+ only hold it once).
130
+
131
+ ## 4. Reusable components: edit once, propagate everywhere
132
+
133
+ Anything designed once and used on multiple pages — CTA bands, promo banners,
134
+ newsletter signup blocks, campaign snippets, "as seen in" strips — should be a
135
+ **reusable component**, not copy-pasted sections.
136
+
137
+ - In the layout editor, select the module and **Save as Component**; insert it
138
+ elsewhere from the component picker. Placed instances are **live
139
+ references**: editing the source component updates every page that uses it.
140
+ - **Detach** an instance when a page genuinely needs a one-off variant — it
141
+ becomes an independent copy from that point on.
142
+ - Rule of thumb: the second time you paste the same section, stop and make it
143
+ a component. The owner should be able to update the sitewide CTA in one
144
+ place, ten minutes before their webinar.
145
+
146
+ ## 5. Page layouts & modules — not hardcoded JSX
147
+
148
+ Pages the owner should be able to restructure — landing pages, the home page,
149
+ campaign pages — should be built **in the layout editor** (sections, columns,
150
+ modules), rendered through Kywi's layout renderer. A scaffolded app already
151
+ renders saved layouts on every content page; keep it that way.
152
+
153
+ - The built-in module palette (hero, cards, CTA, testimonial, feed display,
154
+ pricing, FAQ, forms, comments, media…) covers most marketing-site needs.
155
+ - Brand-specific blocks the palette lacks: build a **custom module** once
156
+ (`defineModule` in `kywi.config.ts` + a React component registered in the
157
+ host's module map — the scaffold's `lib/modules.tsx` shows the shape). The
158
+ developer owns the component; the owner places and configures instances.
159
+ - Create **saved layouts** (Layouts admin) as page templates — "Landing page",
160
+ "Case study" — so new pages start from a consistent skeleton instead of a
161
+ blank canvas.
162
+ - Hand-written JSX pages are fine for genuinely fixed chrome (a bespoke 404,
163
+ legal boilerplate shells) — but if marketing will ever want to swap a
164
+ headline, it's a layout page.
165
+
166
+ ## 6. Forms: always the Forms builder
167
+
168
+ Never hand-code a `<form>`. Build forms in the Forms admin (fields, multi-step,
169
+ success message, notification emails, optional reCAPTCHA via Settings →
170
+ Security) and place them with the **Form / Form Embed** modules, which submit
171
+ through Kywi's pipeline into admin → Submissions.
172
+
173
+ - The owner edits fields, recipients, and the thank-you message without code.
174
+ - Submissions are stored, browsable, and exportable — a hand-rolled form that
175
+ emails someone is data loss with extra steps.
176
+
177
+ ## 7. Media: the library, not /public
178
+
179
+ All owner-managed imagery — logos, hero images, headshots, product shots —
180
+ goes through the Media library (upload, alt text, automatic variants,
181
+ on-demand resize). Reserve `/public` for build-time brand assets (favicon,
182
+ font files) that only change when the code does.
183
+
184
+ The difference matters at handover: the owner can swap a hero image in the
185
+ library; they cannot ship a new `/public` file.
186
+
187
+ ## 8. Personalization & A/B testing — offer it, don't default to it
188
+
189
+ Kywi ships a full personalization stack: **audiences** (rule-based, plus an
190
+ optional self-identification widget), **page variants** targeted per audience,
191
+ and **A/B experiments** with stable per-visitor assignment and recorded
192
+ exposures. Most sites don't need it on day one — and unrequested
193
+ personalization is complexity the owner didn't ask to maintain.
194
+
195
+ **The agent's job is to surface the capability, not to assume it.** When
196
+ scoping a build, ask the owner whether any of these fit, with examples:
197
+
198
+ - *"Should returning visitors see a different home-page hero than first-timers
199
+ (e.g. 'Welcome back — pick up where you left off')?"*
200
+ - *"Do you serve distinct segments (agencies vs. freelancers, industries,
201
+ regions) that should get tailored messaging on key pages?"*
202
+ - *"Would you like visitors to self-identify (e.g. 'I'm a developer / I'm a
203
+ marketer') and see content ordered for them?"*
204
+ - *"Is there a headline, CTA, or pricing presentation you'd like to A/B test
205
+ before committing?"*
206
+
207
+ If yes, principles: personalize **sections and modules**, not whole sites; the
208
+ default variant must stand alone (personalization is progressive enhancement);
209
+ one experiment per conversion goal, and let it conclude before layering more.
210
+ If no, skip it — the machinery is there when they grow into it.
211
+
212
+ ## 9. Editorial workflow: drafts, review, versions
213
+
214
+ For any site with more than one author — or an owner who wants a safety net —
215
+ configure the workflow rather than letting everything publish directly:
216
+
217
+ - **Groups & permissions:** editors write (`draft → Submit for Review`),
218
+ a smaller group approves and publishes. Content-level permissions can gate
219
+ specific sections (e.g. only Legal edits `/legal/*`).
220
+ - Published pages accept **pending revisions** — edits go through review while
221
+ the live page keeps serving — so review doesn't mean taking pages down.
222
+ - Every save records a **version** with restore; approvers see field-level
223
+ diffs. Mention this at handover: "you can always roll back."
224
+
225
+ Solo-owner sites can publish directly — but still enable it before the team
226
+ grows past one.
227
+
228
+ ## 10. Scheduling & changesets
229
+
230
+ - Content has schedule fields: publish at a future time, unpublish/expire
231
+ automatically. The background scheduler handles both — "post this Monday 9am"
232
+ is a field, not a calendar reminder.
233
+ - **Changesets** batch related edits (a product launch touching six pages) and
234
+ publish them together, optionally on a schedule. Use one whenever a launch
235
+ spans multiple nodes — partial launches are worse than late ones.
236
+
237
+ ## 11. Multilingual sites
238
+
239
+ If the owner needs more than one language, declare `locales` in
240
+ `kywi.config.ts` up front — the scaffold routes locale prefixes (`/es/...`),
241
+ falls back to the default locale, and emits hreflang alternates. Translations
242
+ are per-node in the admin. Retrofitting i18n is far costlier than declaring it
243
+ early, so ask at scoping time.
244
+
245
+ ## 12. SEO & the Agent Experience layer
246
+
247
+ - Fill the **SEO tab** on every page that matters (meta title/description,
248
+ og:image); the scaffold maps it into the public head and emits JSON-LD.
249
+ - The AX layer serves `llms.txt`, `llms-full.txt`, `sitemap.xml`, and
250
+ `robots.txt` at the site root out of the box — a Kywi site is legible to
251
+ agents and crawlers by default. Don't remove these routes; they're part of
252
+ the product's value.
253
+
254
+ ## Other capabilities worth knowing
255
+
256
+ - **Comments:** a moderated Comments module (submissions land pending) for
257
+ blogs/community pages — ask the owner if discussion fits.
258
+ - **Webhooks** (Web Services → Webhooks): notify external systems on content
259
+ events (rebuild a static mirror, ping Slack, sync a CRM) — signed, retried.
260
+ - **API keys** with enforced scopes: hand a read-only key to a partner or a
261
+ frontend without exposing write access.
262
+
263
+ ## Build-order playbook (new site)
264
+
265
+ 0. **Propose the content model and get sign-off** ("Before you build", above)
266
+ — nothing else starts until the owner approves it.
267
+ 1. **Model first:** custom content types for every repeated shape (§2).
268
+ 2. **Structure:** site tree — pages, folders per collection (§1).
269
+ 3. **Taxonomy:** categories/tags the feeds will need (§3).
270
+ 4. **Media:** upload the brand's assets to the library (§7).
271
+ 5. **Templates:** saved layouts + reusable components for the recurring
272
+ sections (§4, §5); custom modules only where the palette falls short.
273
+ 6. **Wire collections:** feeds + Feed Display placements (§1).
274
+ 7. **Forms:** contact/newsletter/etc. in the builder (§6).
275
+ 8. **Workflow:** groups, permissions, review path (§9).
276
+ 9. **Ask about** personalization/A-B (§8), comments, webhooks, i18n (§11).
277
+ 10. **SEO pass** (§12), then hand over: show the owner where *their* edits
278
+ live — content nodes, feeds, components — and confirm nothing they'll want
279
+ to change requires you.
280
+
281
+ ## Anti-patterns (smells)
282
+
283
+ - Creating types and entering content before the owner signed off on a
284
+ content model.
285
+ - A hardcoded array of testimonials/logos/posts in a page component.
286
+ - The same CTA JSX pasted on four pages.
287
+ - A `<form>` that POSTs to a hand-rolled route (or nowhere).
288
+ - Marketing imagery in `/public`.
289
+ - A "blog" that is a folder of `.mdx` files the owner can't edit.
290
+ - One-off content types created in config for shapes the owner will evolve.
291
+ - Personalization built speculatively, with no owner request behind it.
292
+ - Direct-publish-only workflow on a multi-author site.
293
+
294
+ Every one of these has a section above. If you catch yourself mid-smell, refactor to the pattern before handover — it's minutes now, migrations later.
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: kywi-collections
3
+ description: Use when adding ANY collection to this Kywi site — testimonials, customer logos, blog posts, products, portfolio pieces, team members, FAQs, press mentions — or whenever you catch yourself about to hardcode a repeating list of items in a page. Walks the folder + feed + Feed Display pattern end to end.
4
+ ---
5
+
6
+ # Build a collection: folder + feed + Feed Display
7
+
8
+ Collections are never hardcoded. A collection is any set of similar items that
9
+ grows over time and is displayed somewhere — the owner must be able to add an
10
+ item in the admin and see every relevant page update, with no code changes.
11
+
12
+ ## Workflow
13
+
14
+ ### 1. Recognize the collection
15
+
16
+ About to write an array of testimonials, a grid of logos, a list of cards in
17
+ JSX? Stop — that's a collection. Confirm the shape: what fields does one item
18
+ have, and where on the site do items appear (one page? several? aggregate-only
19
+ or does each item need its own page)?
20
+
21
+ ### 2. Model the item type
22
+
23
+ - Items with structure beyond title/body/image (products, recipes, profiles)
24
+ → create a custom content type first (admin → Content Types; admin-created
25
+ by default so the owner can evolve it). Match the approved `CONTENT-MODEL.md`
26
+ — if this collection isn't in it, update the model and confirm the addition
27
+ with the owner before building.
28
+ - Simple items (a quote + attribution, a logo + link) can use an existing or
29
+ minimal type — don't over-model.
30
+
31
+ ### 3. Structure: a folder in the Site Tree
32
+
33
+ Create a folder for the collection (`/testimonials`, `/work`, `/customers`)
34
+ and add the initial nodes inside it. Two gotchas:
35
+
36
+ - **Nodes must be `published`** to appear in public feeds — drafts won't show.
37
+ - Aggregate-only items (logos, quotes) still get nodes; their individual pages
38
+ simply go unlinked. Items needing detail pages (posts, portfolio pieces)
39
+ live at real, linkable paths.
40
+
41
+ ### 4. Query: create the Feed
42
+
43
+ Admin → Feeds → new feed: pick the content type, add taxonomy filters if the
44
+ display is a slice ("testimonials tagged `homepage`"), set sort and limit
45
+ ("6 most recent"). **Check the feed preview shows the expected items before
46
+ moving on** — an empty preview means a wrong type, unpublished nodes, or a
47
+ taxonomy term that isn't applied to anything yet.
48
+
49
+ Multiple slices of one collection = multiple feeds over the same type
50
+ (featured vs. all; per-category).
51
+
52
+ ### 5. Display: place the Feed Display module
53
+
54
+ In the layout editor on each target page, add the **Feed Display** module and
55
+ select the feed. If the same feed placement (with surrounding design) recurs
56
+ on several pages, save the section as a reusable component instead of
57
+ rebuilding it per page.
58
+
59
+ Scaffolded apps hydrate feeds server-side automatically. A hand-built host
60
+ must call `hydrateLayoutFeeds` before rendering `KywiLayout` — if the module
61
+ renders an empty list publicly but previews fine in the admin, missing host
62
+ hydration is the usual cause.
63
+
64
+ ### 6. Verify end to end
65
+
66
+ Publish the page, load it publicly, confirm items render. Then the real test:
67
+ **add one more node to the folder and reload — it must appear without touching
68
+ the page.** If it doesn't, the loop isn't closed; fix before moving on.
69
+
70
+ ### 7. Handover note
71
+
72
+ Record in `CONTENT-MODEL.md` (feeds table) and tell the owner: "to add a
73
+ <item>, create it in <folder>; it appears on <pages> automatically."
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: kywi-content-model
3
+ description: Use BEFORE building out this Kywi site or adding any significant new content area (custom types, collections, structured sections) — designs the content model and gets the owner's sign-off first. The most expensive CMS failure is a poorly planned content model; content entry is costly, planning is cheap.
4
+ ---
5
+
6
+ # Design the content model, then get sign-off
7
+
8
+ Do not create content types, enter content, or build page layouts for a new
9
+ site or content area until the owner has approved a content model. This is
10
+ guidance, not law — a single-page brochure site needs a sentence of
11
+ confirmation, not a document — but anything with custom types, collections, or
12
+ more than a handful of pages gets the full treatment.
13
+
14
+ ## Workflow
15
+
16
+ ### 1. Discovery — ask before proposing
17
+
18
+ Understand the site before modeling it. Ask the owner (batch the questions):
19
+
20
+ - What does the site need to communicate, and to whom?
21
+ - What kinds of content exist or are planned? (posts, products, recipes,
22
+ people, case studies, events, FAQs, testimonials, locations…)
23
+ - Which of those are *collections* — sets of similar items that grow over time?
24
+ - Who maintains the site after handover, and how technical are they?
25
+ - Multiple authors (→ review workflow)? Multiple languages? Timed launches?
26
+ - Any personalization/A-B interest? (Offer examples; don't assume — see
27
+ AGENTS.md §8.)
28
+
29
+ ### 2. Draft `CONTENT-MODEL.md` at the project root
30
+
31
+ Use this template:
32
+
33
+ ```markdown
34
+ # Content model — <site>
35
+
36
+ ## Types
37
+ | Type | Built-in/custom | Purpose | Maintained by |
38
+ |---|---|---|---|
39
+
40
+ ### <each custom type>
41
+ | Field | Type | Required | Notes |
42
+ |---|---|---|---|
43
+
44
+ ## Site tree
45
+ <top-level pages and a folder per collection, as an indented list>
46
+
47
+ ## Feeds
48
+ | Feed | Source type | Filter | Sort/limit | Displayed on |
49
+ |---|---|---|---|---|
50
+
51
+ ## Taxonomy
52
+ Categories: <stable sets> · Tags: <expected freeform labels>
53
+
54
+ ## Reusable components
55
+ | Component | Used on |
56
+ |---|---|
57
+
58
+ ## Maintenance
59
+ Editors/approvers, workflow needs, i18n locales, scheduling needs.
60
+
61
+ ## Open questions
62
+ ```
63
+
64
+ Modeling rules of thumb (full rationale in AGENTS.md):
65
+
66
+ - Repeated shape → custom type, **admin-created by default** (owners can add
67
+ fields later without a developer); config-defined only for developer-owned
68
+ schema.
69
+ - Anything displayed as a list somewhere → collection: folder + feed + Feed
70
+ Display module. Never a hardcoded array.
71
+ - Cross-cutting groupings → categories (stable) and tags (evolving), queried
72
+ by feeds.
73
+ - Sections used on 2+ pages → reusable components.
74
+ - Don't shadow built-in fields (title, slug, body); don't invent a type per
75
+ page — types are for repeated shapes.
76
+
77
+ ### 3. Get explicit sign-off
78
+
79
+ Present the proposal and iterate. Ask directly: *"Does this content model
80
+ match how you think about your content, and who will maintain each part?"*
81
+ Get an explicit yes before proceeding. If scope is trivial, a one-paragraph
82
+ summary and a "confirm?" suffices.
83
+
84
+ ### 4. Build — only after approval
85
+
86
+ Follow the build-order playbook in AGENTS.md: types → tree/folders → taxonomy
87
+ → media → layouts/components → feeds → forms → workflow → (ask about
88
+ personalization, comments, i18n) → SEO pass → handover.
89
+
90
+ ### 5. Keep the model current
91
+
92
+ `CONTENT-MODEL.md` is a living document. When the model changes later (new
93
+ type, new collection), update the file and re-confirm significant changes with
94
+ the owner. It doubles as the handover map of "where your content lives."
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: kywi-personalization
3
+ description: Use when the site owner wants personalization or A/B testing on this Kywi site — audience-targeted content, segment-specific pages, self-identification, or experiments — or before YOU propose any of those. Multi-step - audiences, variants/experiments, preview-token verification, conclusion.
4
+ ---
5
+
6
+ # Personalization & A/B testing — confirmed use case first, then build
7
+
8
+ **Never build personalization speculatively.** If the owner hasn't asked,
9
+ offer it with concrete examples and let them decide (returning-visitor hero,
10
+ segment-specific messaging, self-identified content ordering, a CTA test).
11
+ Only proceed with a confirmed use case and a stated success measure.
12
+
13
+ ## Workflow
14
+
15
+ ### 1. Confirm the use case and what "working" means
16
+
17
+ With the owner, pin down: which segment or hypothesis, which page(s) and
18
+ section(s), and what outcome defines success (a conversion, a click-through, a
19
+ qualitative "the right people see the right message"). Write it down — it
20
+ decides the mechanism and when you're done.
21
+
22
+ ### 2. Choose the mechanism
23
+
24
+ - **Known segment, deterministic content** → audience + **page variant**
25
+ ("agencies see the agency hero"). No measurement involved.
26
+ - **Hypothesis to measure** → **A/B experiment** ("does the short headline
27
+ convert better?"). Variants + recorded exposures.
28
+ - **Visitor-declared identity** → **self-ID widget** ("I'm a developer / I'm
29
+ a marketer") feeding an audience.
30
+
31
+ ### 3. Build the audience (variant and self-ID paths)
32
+
33
+ Admin → Audiences: define the rules, or configure the self-ID widget and its
34
+ fields. Use the audience test tool to confirm the rules match the intended
35
+ visitors before wiring anything to it.
36
+
37
+ ### 4a. Page-variant path
38
+
39
+ In the layout editor on the target page: **+ Page Variant** → select the
40
+ audience → edit the variant. Change **only the sections that segment needs** —
41
+ the default variant must remain complete and self-sufficient (personalization
42
+ is progressive enhancement; anonymous visitors get the default).
43
+
44
+ ### 4b. Experiment path
45
+
46
+ Admin → Experiments: create the experiment. In the layout, add an A/B
47
+ container with an arm per treatment. Assignment is deterministic per visitor
48
+ (the scaffold's middleware issues a persistent `kywi_visitor` cookie), so a
49
+ visitor sees the same arm on every visit — no client runtime needed.
50
+
51
+ ### 5. Verify before calling it done
52
+
53
+ - **Preview tokens** (Audiences → Preview Tokens): view the page *as each
54
+ audience* and confirm the right variant serves.
55
+ - Experiments: two fresh browser profiles should get (possibly) different
56
+ arms, and each profile must get the **same** arm on reload.
57
+ - Confirm exposures are being recorded in the Experiments admin.
58
+ - Load the page as a plain anonymous visitor: the default must be complete.
59
+
60
+ ### 6. Run, conclude, promote
61
+
62
+ One experiment per conversion goal; let it reach a conclusion before layering
63
+ another on the same page. When it concludes, **promote the winner into the
64
+ base layout and remove the experiment container** — a site accreting stale
65
+ experiments is a maintenance smell.
66
+
67
+ ### 7. Document for handover
68
+
69
+ Record what is personalized where (and why) in `CONTENT-MODEL.md`'s
70
+ maintenance notes. The owner must know a page has variants before they edit
71
+ it — editing only the default of a heavily-personalized page is a classic
72
+ post-handover surprise.
package/lib/templates.mjs CHANGED
@@ -32,6 +32,10 @@
32
32
  * ships session fixes without the app hand-maintaining crypto.
33
33
  */
34
34
 
35
+ import { readFileSync } from 'node:fs'
36
+ import { dirname, join } from 'node:path'
37
+ import { fileURLToPath } from 'node:url'
38
+
35
39
  /** @typedef {{ projectName: string, dbProvider: 'postgresql'|'mysql', authProviders: string[], mode: 'coupled'|'headless'|'decoupled', kywiVersion: string }} Answers */
36
40
 
37
41
  const CORE_RANGE = (v) => `^${v}`
@@ -1903,6 +1907,11 @@ ${themingBlock}
1903
1907
 
1904
1908
  \`\`\`
1905
1909
  kywi.config.ts your config: sites, themes, content types, auth, mode, admin.features
1910
+ AGENTS.md guidance for AI agents working on this site (Kywi's building patterns)
1911
+ CLAUDE.md points AI agents to AGENTS.md
1912
+ .claude/skills/kywi-content-model/SKILL.md content-model skill (loaded automatically)
1913
+ .claude/skills/kywi-collections/SKILL.md collections skill (loaded automatically)
1914
+ .claude/skills/kywi-personalization/SKILL.md personalization skill (loaded automatically)
1906
1915
  middleware.ts auth gate + session refresh + cookie→bearer bridge
1907
1916
  next.config.mjs required Next config to consume @kywi-software/core
1908
1917
  lib/kywi.ts server runtime (DB, API handler, content scope)
@@ -1929,6 +1938,150 @@ function escapeJsxText(value) {
1929
1938
  return String(value).replace(/[{}<>]/g, (ch) => `{'${ch}'}`)
1930
1939
  }
1931
1940
 
1941
+ // ── Agent guidance (AGENTS.md / CLAUDE.md) ──────────────────────────────────────
1942
+
1943
+ /**
1944
+ * Kywi's canonical building-patterns doc, shipped as a package asset and embedded
1945
+ * verbatim into every generated app's AGENTS.md. Resolved relative to THIS module
1946
+ * (import.meta.url), NOT process.cwd(), so it loads from an installed
1947
+ * create-kywi-app the same as from the monorepo. Kept byte-identical to
1948
+ * docs/agents/AGENT-PATTERNS.md by scripts/sync-agent-patterns.mjs (a drift test
1949
+ * guards it). Cached so repeated buildFileSet() calls don't re-read the file.
1950
+ * @returns {string}
1951
+ */
1952
+ let _agentPatternsDoc
1953
+ function agentPatternsDoc() {
1954
+ if (_agentPatternsDoc === undefined) {
1955
+ const assetPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'assets', 'agent-patterns.md')
1956
+ _agentPatternsDoc = readFileSync(assetPath, 'utf8')
1957
+ }
1958
+ return _agentPatternsDoc
1959
+ }
1960
+
1961
+ /**
1962
+ * Kywi's Claude Code project skills — each ships as a package asset and is
1963
+ * embedded verbatim into every generated app's .claude/skills/<slug>/SKILL.md
1964
+ * (all modes, in this order). Exported so tests can iterate the same table
1965
+ * instead of duplicating it.
1966
+ * @type {Array<{ slug: string, asset: string }>}
1967
+ */
1968
+ export const SKILLS = [
1969
+ // Before building anything: design the content model, get owner sign-off.
1970
+ { slug: 'kywi-content-model', asset: 'kywi-content-model-skill.md' },
1971
+ // When adding any collection: folder + feed + Feed Display, end to end.
1972
+ { slug: 'kywi-collections', asset: 'kywi-collections-skill.md' },
1973
+ // When the owner wants personalization/A-B: confirm the use case, then build.
1974
+ { slug: 'kywi-personalization', asset: 'kywi-personalization-skill.md' },
1975
+ ]
1976
+
1977
+ /**
1978
+ * Cache of asset filename → file content for scaffolded Claude Code skills,
1979
+ * populated lazily by {@link skillDoc}.
1980
+ * @type {Map<string, string>}
1981
+ */
1982
+ const _skillDocCache = new Map()
1983
+
1984
+ /**
1985
+ * Read (and cache) one Claude Code skill's SKILL.md asset by filename.
1986
+ * Resolved relative to THIS module (import.meta.url), same mechanism as
1987
+ * {@link agentPatternsDoc}, so it loads from an installed create-kywi-app the
1988
+ * same as from the monorepo. Cached so repeated buildFileSet() calls don't
1989
+ * re-read the file.
1990
+ * @param {string} asset
1991
+ * @returns {string}
1992
+ */
1993
+ function skillDoc(asset) {
1994
+ if (!_skillDocCache.has(asset)) {
1995
+ const assetPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'assets', asset)
1996
+ _skillDocCache.set(asset, readFileSync(assetPath, 'utf8'))
1997
+ }
1998
+ return _skillDocCache.get(asset)
1999
+ }
2000
+
2001
+ /**
2002
+ * AGENTS.md — a short, app-specific header that orients an agent in THIS generated
2003
+ * app, followed by Kywi's canonical patterns doc verbatim. Mode-aware: only
2004
+ * coupled apps have public-render wiring (lib/site.ts), so the header reflects it.
2005
+ * @param {Answers} a
2006
+ */
2007
+ function agentsMd(a) {
2008
+ const coupled = a.mode === 'coupled'
2009
+ const modeSentence = coupled
2010
+ ? 'This app renders the public site AND serves the admin + API.'
2011
+ : a.mode === 'headless'
2012
+ ? 'This app serves the admin + API only (`GET /` returns 404); there is no public rendering here.'
2013
+ : 'This app serves the admin + API only; a separate frontend consumes the API via `@kywi-software/sdk`. There is no public rendering here.'
2014
+ const surfaceLine = coupled
2015
+ ? `- \`lib/site.ts\` — public-render helpers (path/locale resolution, feeds,
2016
+ components, personalization) used by \`app/(site)/[[...slug]]/page.tsx\`, which
2017
+ renders every published page at its slug.`
2018
+ : a.mode === 'headless'
2019
+ ? `- No public rendering in this mode — \`app/page.tsx\` returns 404. Content is
2020
+ served over the REST API at \`/api/v1\`.`
2021
+ : `- No public rendering in this mode — \`app/page.tsx\` returns 404. Build a
2022
+ separate frontend against the REST API at \`/api/v1\` with \`@kywi-software/sdk\`.`
2023
+ const header = `# Agent guide — ${a.projectName}
2024
+
2025
+ This is a **Kywi CMS** project scaffolded by \`create-kywi-app\` (\`${a.mode}\` mode).
2026
+ ${modeSentence}
2027
+
2028
+ ## Where things live
2029
+
2030
+ - \`kywi.config.ts\` — project config: sites, themes, content types, auth
2031
+ providers, deployment mode, and \`admin.features\`. Edit it, then re-run
2032
+ \`pnpm migrate\`.
2033
+ - \`app/admin/[[...admin]]/page.tsx\` — mounts Kywi's **full admin**
2034
+ (\`KywiAdminApp\`) at **\`/admin\`**. Every surface — content, media, feeds,
2035
+ forms, audiences, settings, … — is already there; never hand-build admin pages.
2036
+ - \`lib/modules.tsx\` — the custom-module map (\`defineModule\` renderers), shared
2037
+ by the admin editor and the public site.
2038
+ ${surfaceLine}
2039
+ - \`middleware.ts\` — auth gate + session refresh, thin wiring over
2040
+ \`@kywi-software/core/host\`.
2041
+ - \`.claude/skills/kywi-content-model/SKILL.md\` — content-model planning
2042
+ skill, loaded automatically before building anything.
2043
+ - \`.claude/skills/kywi-collections/SKILL.md\` — collections skill, loaded
2044
+ automatically when adding any collection.
2045
+ - \`.claude/skills/kywi-personalization/SKILL.md\` — personalization skill,
2046
+ loaded automatically when the owner wants personalization or A/B testing.
2047
+
2048
+ ## Running it
2049
+
2050
+ \`\`\`bash
2051
+ pnpm migrate # apply the schema
2052
+ pnpm seed # create the default site + a superadmin (prints credentials)
2053
+ pnpm dev # http://localhost:3000 (admin at /admin)
2054
+ \`\`\`
2055
+
2056
+ ## Kywi's building patterns
2057
+
2058
+ Everything below is **Kywi's official guidance** for building sites end users can
2059
+ maintain — read it before adding content, pages, forms, or modules. The rule that
2060
+ matters most: **if a non-developer might ever want to change it, model it in the
2061
+ CMS instead of hardcoding it.**
2062
+
2063
+ ---
2064
+
2065
+ `
2066
+ return header + agentPatternsDoc()
2067
+ }
2068
+
2069
+ /**
2070
+ * CLAUDE.md — a thin pointer so a Claude Code session reads AGENTS.md first.
2071
+ * @param {Answers} a
2072
+ */
2073
+ function claudeMd(a) {
2074
+ return `# ${a.projectName}
2075
+
2076
+ This is a **Kywi CMS** project. **Read \`AGENTS.md\` before building anything** — it
2077
+ holds Kywi's official building patterns and a map of where things live in this app.
2078
+
2079
+ Core principle: **model content in the CMS instead of hardcoding it** — see \`AGENTS.md\`.
2080
+
2081
+ Before building out a new content area, use the \`kywi-content-model\` skill (design the model, get sign-off) — skills in \`.claude/skills/\` also cover collections and personalization.
2082
+ `
2083
+ }
2084
+
1932
2085
  /**
1933
2086
  * Build the complete map of relative-path → file-content for a project.
1934
2087
  * @param {Answers} answers
@@ -1945,6 +2098,10 @@ export function buildFileSet(answers) {
1945
2098
  '.env.example': envExample(),
1946
2099
  '.gitignore': gitignore(),
1947
2100
  'README.md': readme(answers),
2101
+ // agent guidance (every mode): app-specific header + Kywi's canonical patterns
2102
+ // doc verbatim. CLAUDE.md is a thin pointer into AGENTS.md.
2103
+ 'AGENTS.md': agentsMd(answers),
2104
+ 'CLAUDE.md': claudeMd(answers),
1948
2105
  // server runtime + config
1949
2106
  'lib/kywi.ts': libKywi(),
1950
2107
  'lib/config.ts': libConfig(),
@@ -1969,6 +2126,15 @@ export function buildFileSet(answers) {
1969
2126
  'app/robots.txt/route.ts': axRootRoute('robots.txt'),
1970
2127
  }
1971
2128
 
2129
+ // Claude Code project skills (every mode): content-model-first discipline,
2130
+ // collections, and personalization — Claude Code loads each automatically at
2131
+ // the moment it's relevant (see SKILLS above for when-to-use). Verbatim
2132
+ // assets, same readFileSync-from-import.meta.url + cache mechanism as
2133
+ // AGENTS.md above.
2134
+ for (const { slug, asset } of SKILLS) {
2135
+ files[`.claude/skills/${slug}/SKILL.md`] = skillDoc(asset)
2136
+ }
2137
+
1972
2138
  if (answers.mode === 'coupled') {
1973
2139
  // Public site: an optional catch-all renders "/" (home) and every published
1974
2140
  // page at its slug. More-specific /admin and /api routes take precedence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kywi-app",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Scaffold a new Kywi CMS project — npx create-kywi-app my-site",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/Kywi-Software/kywi-cms#readme",
@@ -15,6 +15,7 @@
15
15
  "files": [
16
16
  "bin",
17
17
  "lib",
18
+ "assets",
18
19
  "README.md",
19
20
  "LICENSE"
20
21
  ],