@agility/create-next-app 1.0.0-beta.2

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.
Files changed (213) hide show
  1. package/.claude/settings.json +7 -0
  2. package/.claude/settings.local.json +24 -0
  3. package/FEATURE_ROADMAP.md +343 -0
  4. package/README.md +205 -0
  5. package/TESTING.md +131 -0
  6. package/bin/create-agility-app.js +48 -0
  7. package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
  8. package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
  9. package/dist/agility/api-keys/generateApiKeys.js +99 -0
  10. package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
  11. package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
  12. package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
  13. package/dist/agility/api-keys/getApiKeys.js +14 -0
  14. package/dist/agility/api-keys/getApiKeys.js.map +1 -0
  15. package/dist/agility/index.d.ts +3 -0
  16. package/dist/agility/index.d.ts.map +1 -0
  17. package/dist/agility/index.js +8 -0
  18. package/dist/agility/index.js.map +1 -0
  19. package/dist/agility/instance/createNewInstance.d.ts +8 -0
  20. package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
  21. package/dist/agility/instance/createNewInstance.js +65 -0
  22. package/dist/agility/instance/createNewInstance.js.map +1 -0
  23. package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
  24. package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
  25. package/dist/agility/instance/getAvailableInstances.js +43 -0
  26. package/dist/agility/instance/getAvailableInstances.js.map +1 -0
  27. package/dist/agility/instance/manageInstance.d.ts +9 -0
  28. package/dist/agility/instance/manageInstance.d.ts.map +1 -0
  29. package/dist/agility/instance/manageInstance.js +82 -0
  30. package/dist/agility/instance/manageInstance.js.map +1 -0
  31. package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
  32. package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
  33. package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
  34. package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
  35. package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
  36. package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
  37. package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
  38. package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
  39. package/dist/auth/index.d.ts +3 -0
  40. package/dist/auth/index.d.ts.map +1 -0
  41. package/dist/auth/index.js +8 -0
  42. package/dist/auth/index.js.map +1 -0
  43. package/dist/auth/oauth/authenticate.d.ts +6 -0
  44. package/dist/auth/oauth/authenticate.d.ts.map +1 -0
  45. package/dist/auth/oauth/authenticate.js +162 -0
  46. package/dist/auth/oauth/authenticate.js.map +1 -0
  47. package/dist/auth/oauth/constants.d.ts +5 -0
  48. package/dist/auth/oauth/constants.d.ts.map +1 -0
  49. package/dist/auth/oauth/constants.js +9 -0
  50. package/dist/auth/oauth/constants.js.map +1 -0
  51. package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
  52. package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
  53. package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
  54. package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
  55. package/dist/cli/index.d.ts +3 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +290 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/promptForMissingOptions.d.ts +8 -0
  60. package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
  61. package/dist/cli/promptForMissingOptions.js +92 -0
  62. package/dist/cli/promptForMissingOptions.js.map +1 -0
  63. package/dist/config/env/createEnvFile.d.ts +6 -0
  64. package/dist/config/env/createEnvFile.d.ts.map +1 -0
  65. package/dist/config/env/createEnvFile.js +31 -0
  66. package/dist/config/env/createEnvFile.js.map +1 -0
  67. package/dist/config/index.d.ts +2 -0
  68. package/dist/config/index.d.ts.map +1 -0
  69. package/dist/config/index.js +6 -0
  70. package/dist/config/index.js.map +1 -0
  71. package/dist/config/mcp/createMcpConfig.d.ts +5 -0
  72. package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
  73. package/dist/config/mcp/createMcpConfig.js +32 -0
  74. package/dist/config/mcp/createMcpConfig.js.map +1 -0
  75. package/dist/config/packages/installAgilityPackages.d.ts +6 -0
  76. package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
  77. package/dist/config/packages/installAgilityPackages.js +61 -0
  78. package/dist/config/packages/installAgilityPackages.js.map +1 -0
  79. package/dist/config/setupProject.d.ts +8 -0
  80. package/dist/config/setupProject.d.ts.map +1 -0
  81. package/dist/config/setupProject.js +32 -0
  82. package/dist/config/setupProject.js.map +1 -0
  83. package/dist/create-next-app/createNextApp.d.ts +9 -0
  84. package/dist/create-next-app/createNextApp.d.ts.map +1 -0
  85. package/dist/create-next-app/createNextApp.js +83 -0
  86. package/dist/create-next-app/createNextApp.js.map +1 -0
  87. package/dist/create-next-app/index.d.ts +3 -0
  88. package/dist/create-next-app/index.d.ts.map +1 -0
  89. package/dist/create-next-app/index.js +8 -0
  90. package/dist/create-next-app/index.js.map +1 -0
  91. package/dist/scaffold/components/createPageComponents.d.ts +6 -0
  92. package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
  93. package/dist/scaffold/components/createPageComponents.js +62 -0
  94. package/dist/scaffold/components/createPageComponents.js.map +1 -0
  95. package/dist/scaffold/containers/createContainers.d.ts +6 -0
  96. package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
  97. package/dist/scaffold/containers/createContainers.js +48 -0
  98. package/dist/scaffold/containers/createContainers.js.map +1 -0
  99. package/dist/scaffold/index.d.ts +2 -0
  100. package/dist/scaffold/index.d.ts.map +1 -0
  101. package/dist/scaffold/index.js +6 -0
  102. package/dist/scaffold/index.js.map +1 -0
  103. package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
  104. package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
  105. package/dist/scaffold/instance/createBlankInstance.js +51 -0
  106. package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
  107. package/dist/scaffold/models/createContentModels.d.ts +6 -0
  108. package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
  109. package/dist/scaffold/models/createContentModels.js +70 -0
  110. package/dist/scaffold/models/createContentModels.js.map +1 -0
  111. package/dist/templates/copyDirectory.d.ts +5 -0
  112. package/dist/templates/copyDirectory.d.ts.map +1 -0
  113. package/dist/templates/copyDirectory.js +28 -0
  114. package/dist/templates/copyDirectory.js.map +1 -0
  115. package/dist/templates/copyTemplates.d.ts +8 -0
  116. package/dist/templates/copyTemplates.d.ts.map +1 -0
  117. package/dist/templates/copyTemplates.js +58 -0
  118. package/dist/templates/copyTemplates.js.map +1 -0
  119. package/dist/templates/index.d.ts +2 -0
  120. package/dist/templates/index.d.ts.map +1 -0
  121. package/dist/templates/index.js +6 -0
  122. package/dist/templates/index.js.map +1 -0
  123. package/dist/types/index.d.ts +50 -0
  124. package/dist/types/index.d.ts.map +1 -0
  125. package/dist/types/index.js +3 -0
  126. package/dist/types/index.js.map +1 -0
  127. package/dist/utils/git.d.ts +9 -0
  128. package/dist/utils/git.d.ts.map +1 -0
  129. package/dist/utils/git.js +71 -0
  130. package/dist/utils/git.js.map +1 -0
  131. package/dist/utils/validation.d.ts +45 -0
  132. package/dist/utils/validation.d.ts.map +1 -0
  133. package/dist/utils/validation.js +180 -0
  134. package/dist/utils/validation.js.map +1 -0
  135. package/package.json +45 -0
  136. package/src/agility/api-keys/generateApiKeys.ts +100 -0
  137. package/src/agility/api-keys/getApiKeys.ts +13 -0
  138. package/src/agility/index.ts +3 -0
  139. package/src/agility/instance/createNewInstance.ts +67 -0
  140. package/src/agility/instance/getAvailableInstances.ts +49 -0
  141. package/src/agility/instance/manageInstance.ts +90 -0
  142. package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
  143. package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
  144. package/src/auth/index.ts +3 -0
  145. package/src/auth/oauth/authenticate.ts +165 -0
  146. package/src/auth/oauth/constants.ts +6 -0
  147. package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
  148. package/src/cli/index.ts +281 -0
  149. package/src/cli/promptForMissingOptions.ts +104 -0
  150. package/src/config/env/createEnvFile.ts +30 -0
  151. package/src/config/index.ts +2 -0
  152. package/src/config/mcp/createMcpConfig.ts +30 -0
  153. package/src/config/packages/installAgilityPackages.ts +63 -0
  154. package/src/config/setupProject.ts +31 -0
  155. package/src/create-next-app/createNextApp.ts +75 -0
  156. package/src/create-next-app/index.ts +3 -0
  157. package/src/scaffold/components/createPageComponents.ts +74 -0
  158. package/src/scaffold/containers/createContainers.ts +55 -0
  159. package/src/scaffold/index.ts +2 -0
  160. package/src/scaffold/instance/createBlankInstance.ts +55 -0
  161. package/src/scaffold/models/createContentModels.ts +83 -0
  162. package/src/templates/copyDirectory.ts +24 -0
  163. package/src/templates/copyTemplates.ts +57 -0
  164. package/src/templates/index.ts +2 -0
  165. package/src/types/index.ts +55 -0
  166. package/src/utils/git.ts +74 -0
  167. package/src/utils/validation.ts +184 -0
  168. package/templates/.claude/QUICK-START.md +230 -0
  169. package/templates/.claude/README.md +32 -0
  170. package/templates/.claude/settings.json +8 -0
  171. package/templates/BLANK-INSTANCE-SETUP.md +375 -0
  172. package/templates/DEVELOPMENT.md +160 -0
  173. package/templates/EXAMPLE-PROMPTS.md +643 -0
  174. package/templates/PROMPTS.md +410 -0
  175. package/templates/README.md +281 -0
  176. package/templates/agents.md +429 -0
  177. package/templates/app/[locale]/[...slug]/error.tsx +17 -0
  178. package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
  179. package/templates/app/[locale]/[...slug]/page.tsx +102 -0
  180. package/templates/app/[locale]/layout.tsx +22 -0
  181. package/templates/app/[locale]/page.tsx +12 -0
  182. package/templates/app/api/dynamic-redirect/route.ts +24 -0
  183. package/templates/app/api/preview/exit/route.ts +34 -0
  184. package/templates/app/api/preview/route.ts +63 -0
  185. package/templates/app/api/revalidate/route.ts +118 -0
  186. package/templates/components/agility-components/RichTextArea.tsx +66 -0
  187. package/templates/components/agility-components/index.ts +30 -0
  188. package/templates/components/agility-pages/MainTemplate.tsx +36 -0
  189. package/templates/components/agility-pages/index.ts +11 -0
  190. package/templates/docs/01-agility-cms-overview.md +139 -0
  191. package/templates/docs/02-page-routing.md +251 -0
  192. package/templates/docs/03-creating-components.md +462 -0
  193. package/templates/docs/04-data-fetching.md +484 -0
  194. package/templates/docs/05-containers-and-lists.md +596 -0
  195. package/templates/docs/06-localization.md +561 -0
  196. package/templates/docs/07-caching-strategies.md +410 -0
  197. package/templates/docs/08-common-components.md +756 -0
  198. package/templates/docs/09-whats-included.md +279 -0
  199. package/templates/docs/10-mcp-server-setup.md +153 -0
  200. package/templates/docs/11-linked-nested-content.md +611 -0
  201. package/templates/docs/README.md +164 -0
  202. package/templates/lib/cms/getAgilityContext.ts +28 -0
  203. package/templates/lib/cms/getAgilityPage.ts +51 -0
  204. package/templates/lib/cms/getAgilitySDK.ts +22 -0
  205. package/templates/lib/cms/getContentItem.ts +20 -0
  206. package/templates/lib/cms/getContentList.ts +19 -0
  207. package/templates/lib/cms/getRedirections.ts +85 -0
  208. package/templates/lib/cms/getSitemapFlat.ts +19 -0
  209. package/templates/lib/cms/getSitemapNested.ts +19 -0
  210. package/templates/lib/env.ts +99 -0
  211. package/templates/lib/i18n/config.ts +28 -0
  212. package/templates/proxy.ts +101 -0
  213. package/tsconfig.json +21 -0
@@ -0,0 +1,429 @@
1
+ # AI Assistant Guide for Agility CMS Projects
2
+
3
+ This guide helps AI coding assistants (Claude Code, Cursor, GitHub Copilot, Google AI Studio, etc.) understand and work with this Agility CMS + Next.js project.
4
+
5
+ ## Project Overview
6
+
7
+ This is a Next.js 15 project with Agility CMS headless CMS integration. It uses:
8
+ - **Next.js 15** with App Router
9
+ - **React 19** Server Components
10
+ - **TypeScript** for type safety
11
+ - **Tailwind CSS** for styling
12
+ - **Agility CMS** for content management
13
+
14
+ ## Key Architecture Patterns
15
+
16
+ ### 1. Headless CMS Flow
17
+ ```
18
+ Agility CMS (Content) → API Fetch → Next.js (Server Component) → React Component → Browser
19
+ ```
20
+
21
+ ### 2. File Structure
22
+ ```
23
+ src/
24
+ ├── app/[locale]/[...slug]/page.tsx ← All pages route here
25
+ ├── components/
26
+ │ ├── agility-components/ ← CMS modules (Hero, Blog, etc.)
27
+ │ └── agility-pages/ ← Page templates
28
+ └── lib/
29
+ └── cms/ ← CMS helper functions
30
+ ```
31
+
32
+ ### 3. Module Pattern
33
+ Agility CMS "modules" are React components that editors add to pages:
34
+ - Module defined in Agility CMS → React component in `agility-components/`
35
+ - Registered in `agility-components/index.ts`
36
+ - Renders via `<ContentZone>` in page template
37
+
38
+ ## 🎯 Understanding User Prompts
39
+
40
+ Users may have varying levels of detail in their prompts. Your job is to:
41
+ 1. **Assess prompt quality** - Is it specific enough?
42
+ 2. **Ask clarifying questions** - If details are missing
43
+ 3. **Suggest best practices** - Guide them to better prompts
44
+ 4. **Fill in reasonable defaults** - When appropriate
45
+
46
+ ### Prompt Quality Levels
47
+
48
+ **⭐ Poor Prompt** (Too Vague):
49
+ ```
50
+ User: "Add a blog"
51
+ ```
52
+ **Your Response**: Ask clarifying questions:
53
+ - "I can help you add a blog! To create the best solution, I need some details:
54
+ 1. Do you want a blog listing page, or are you building the single post page?
55
+ 2. What fields should blog posts have in Agility CMS? (e.g., title, content, featured image, author, date, categories)
56
+ 3. How many posts per page?
57
+ 4. Any specific design requirements?
58
+ 5. Have you already created the 'blogPosts' content model in Agility CMS?"
59
+
60
+ **⭐⭐⭐ Good Prompt** (Specific):
61
+ ```
62
+ User: "Add a blog listing component with pagination that shows 9 posts per page"
63
+ ```
64
+ **Your Response**: Proceed with reasonable defaults and confirm:
65
+ - "I'll create a blog listing component. I'll assume standard blog fields (title, slug, excerpt, featuredImage, date). Let me know if your Agility CMS blog posts have different field names."
66
+
67
+ **⭐⭐⭐⭐ Excellent Prompt** (Complete):
68
+ ```
69
+ User: "Create a blog listing component that fetches from 'blogPosts' content list with fields: title, slug, excerpt, featuredImage, date, author. Show 9 posts in a 3-column grid with pagination."
70
+ ```
71
+ **Your Response**: Build it exactly as specified!
72
+
73
+ ### When Prompts Lack Detail
74
+
75
+ If a prompt is missing critical information:
76
+
77
+ 1. **Check existing files** - Look for similar components to understand patterns
78
+ 2. **Check documentation** - Reference PROMPTS.md or EXAMPLE-PROMPTS.md if available
79
+ 3. **Ask specific questions** - Don't ask "what do you want?" but rather "Do you want A or B?"
80
+ 4. **Suggest the better approach** - Guide them based on best practices
81
+
82
+ Example:
83
+ ```
84
+ User: "Add testimonials"
85
+
86
+ You: "I can create a testimonials component! I have a few questions:
87
+
88
+ 1. Data source: Should this fetch from a 'testimonials' content list, or will you provide static testimonials in the module fields?
89
+ - Recommended: Content list (more flexible for editors)
90
+
91
+ 2. Display style:
92
+ - Grid showing all testimonials
93
+ - Carousel showing one at a time (recommended for homepage)
94
+
95
+ 3. What fields does each testimonial have? (typical: quote, author, title, company, photo)
96
+
97
+ Let me know and I'll build it!"
98
+ ```
99
+
100
+ ## How to Help Users
101
+
102
+ ### When User Wants a New Feature
103
+
104
+ 1. **Assess prompt completeness** - Do you have enough information?
105
+ 2. **Ask questions if needed** - Get field names, layout requirements, data source
106
+ 3. **Identify Type**: Is it a module, page template, or standalone component?
107
+ 4. **Create Files**: Generate TypeScript component files
108
+ 5. **Register**: Add to appropriate index.ts
109
+ 6. **Fetch Data**: Use `getContentList()` or `getContentItem()` for CMS data
110
+ 7. **Add Types**: Define TypeScript interfaces
111
+ 8. **Provide CMS setup instructions** - Tell them what to create in Agility CMS
112
+
113
+ ### When User Wants to Fix a Bug
114
+
115
+ 1. **Read Files**: Use Read tool on relevant files
116
+ 2. **Understand Pattern**: Check similar working components
117
+ 3. **Apply Fix**: Use Edit tool to fix issues
118
+ 4. **Verify**: Explain the fix and ask user to test
119
+
120
+ ### When User Has a Blank Agility Instance
121
+
122
+ Users may be starting from scratch! Help them plan:
123
+
124
+ 1. **Understand their goals** - What type of website?
125
+ 2. **Suggest content structure** - What content models to create
126
+ 3. **Provide creation order** - Build simple components first
127
+ 4. **Reference guides** - Point them to BLANK-INSTANCE-SETUP.md
128
+ 5. **Walk through setup** - Guide them step-by-step
129
+
130
+ ### When User Wants a Blog/List Feature
131
+
132
+ 1. **Content List Module**: Create in `agility-components/`
133
+ 2. **Fetch Data**: Use `getContentList({ referenceName: "posts", locale })`
134
+ 3. **Display**: Map over items and render
135
+ 4. **Pagination**: Use searchParams for page numbers
136
+
137
+ ### When User Wants Multi-Language
138
+
139
+ 1. **Check Config**: Read `lib/i18n/config.ts`
140
+ 2. **Use Locale**: Always pass `locale` prop to CMS functions
141
+ 3. **URL Structure**: Default locale has no prefix, others do (/fr/, /es/)
142
+
143
+ ## Common Tasks
144
+
145
+ ### Task: Create a New Module Component
146
+
147
+ ```tsx
148
+ // 1. Create file: src/components/agility-components/MyModule.tsx
149
+ interface MyModuleProps {
150
+ module: {
151
+ fields: {
152
+ title: string;
153
+ // ... other fields
154
+ };
155
+ };
156
+ locale: string;
157
+ }
158
+
159
+ export default function MyModule({ module, locale }: MyModuleProps) {
160
+ const { title } = module.fields;
161
+ return <div>{title}</div>;
162
+ }
163
+
164
+ // 2. Register in src/components/agility-components/index.ts
165
+ import MyModule from "./MyModule";
166
+
167
+ export const getModule = (moduleName: string) => {
168
+ switch (moduleName) {
169
+ case "MyModule": // Must match Agility CMS reference name
170
+ return MyModule;
171
+ // ... other cases
172
+ }
173
+ };
174
+ ```
175
+
176
+ ### Task: Fetch and Display Content List
177
+
178
+ ```tsx
179
+ import { getContentList } from "@/lib/cms/getContentList";
180
+
181
+ export default async function PostListing({ locale }: any) {
182
+ const posts = await getContentList({
183
+ referenceName: "posts",
184
+ locale,
185
+ take: 10,
186
+ sort: "fields.date desc",
187
+ });
188
+
189
+ return (
190
+ <div>
191
+ {posts.map((post) => (
192
+ <article key={post.contentID}>
193
+ <h3>{post.fields.title}</h3>
194
+ <p>{post.fields.excerpt}</p>
195
+ </article>
196
+ ))}
197
+ </div>
198
+ );
199
+ }
200
+ ```
201
+
202
+ ### Task: Add Interactive Client Component
203
+
204
+ ```tsx
205
+ // Server component (data fetching)
206
+ import MyComponentClient from "./MyComponent.client";
207
+
208
+ export default async function MyComponent({ module, locale }: any) {
209
+ const data = await fetchData(locale);
210
+ return <MyComponentClient data={data} />;
211
+ }
212
+
213
+ // Client component (interactivity)
214
+ "use client";
215
+ import { useState } from "react";
216
+
217
+ export default function MyComponentClient({ data }: any) {
218
+ const [state, setState] = useState(0);
219
+ return <button onClick={() => setState(state + 1)}>{state}</button>;
220
+ }
221
+ ```
222
+
223
+ ### Task: Add a New Page Template
224
+
225
+ ```tsx
226
+ // 1. Create file: src/components/agility-pages/TwoColumn.tsx
227
+ import { ContentZone } from "@agility/nextjs";
228
+ import { getModule } from "../agility-components";
229
+
230
+ export default function TwoColumn(props: any) {
231
+ return (
232
+ <div className="grid md:grid-cols-2 gap-8">
233
+ <ContentZone name="left-zone" {...props} getModule={getModule} />
234
+ <ContentZone name="right-zone" {...props} getModule={getModule} />
235
+ </div>
236
+ );
237
+ }
238
+
239
+ // 2. Register in src/components/agility-pages/index.ts
240
+ import TwoColumn from "./TwoColumn";
241
+
242
+ export const getPageTemplate = (templateName: string) => {
243
+ switch (templateName) {
244
+ case "TwoColumn":
245
+ return TwoColumn;
246
+ // ... other cases
247
+ }
248
+ };
249
+ ```
250
+
251
+ ## Important Patterns to Remember
252
+
253
+ ### 1. Always Use AgilityPic for Images
254
+
255
+ **CRITICAL**: All images from Agility CMS MUST be rendered using the `<AgilityPic>` component from `@agility/nextjs`. Never use Next.js `<Image>` or plain `<img>` tags for Agility CMS images.
256
+
257
+ ```tsx
258
+ import { AgilityPic } from "@agility/nextjs";
259
+ import type { ImageField } from "@agility/nextjs";
260
+
261
+ // GOOD - Use AgilityPic for Agility CMS images
262
+ <AgilityPic
263
+ image={imageField}
264
+ fallbackWidth={600}
265
+ className="w-full h-auto rounded-2xl"
266
+ data-agility-field="image"
267
+ />
268
+
269
+ // BAD - Never use Next.js Image or img for Agility images
270
+ <Image src={imageField.url} alt="..." /> // ❌ WRONG
271
+ <img src={imageField.url} alt="..." /> // ❌ WRONG
272
+ ```
273
+
274
+ **Key AgilityPic Props**:
275
+ - `image` (required): The ImageField object from Agility CMS
276
+ - `fallbackWidth`: Width in pixels for fallback (e.g., 600)
277
+ - `alt`: Optional alt text (uses CMS alt by default)
278
+ - `className`: CSS classes for styling
279
+ - `priority`: Set true for above-the-fold images
280
+ - `sources`: Array of responsive sources with media queries
281
+ - `data-agility-field`: Field name for inline editing
282
+
283
+ **Example with Responsive Images**:
284
+ ```tsx
285
+ <AgilityPic
286
+ image={post.image}
287
+ fallbackWidth={400}
288
+ className="w-full h-full object-cover rounded-2xl"
289
+ sources={[
290
+ { media: "(max-width: 639px)", width: 640 },
291
+ { media: "(max-width: 767px)", width: 800 },
292
+ { media: "(max-width: 1023px)", width: 1200 },
293
+ ]}
294
+ data-agility-field="image"
295
+ />
296
+ ```
297
+
298
+ ### 2. Always Use Locale
299
+ ```tsx
300
+ // GOOD
301
+ const data = await getContentList({ referenceName: "posts", locale });
302
+
303
+ // BAD - missing locale
304
+ const data = await getContentList({ referenceName: "posts" });
305
+ ```
306
+
307
+ ### 3. Reference Name for Lists
308
+ ```tsx
309
+ // GOOD - use referenceName from fields
310
+ const items = await getContentList({
311
+ referenceName: cards.referenceName,
312
+ locale,
313
+ });
314
+
315
+ // BAD - hardcoded string
316
+ const items = await getContentList({
317
+ referenceName: "testimonials",
318
+ locale,
319
+ });
320
+ ```
321
+
322
+ ### 4. Server vs Client Components
323
+ ```tsx
324
+ // Server Component (default) - for data fetching
325
+ export default async function MyComponent() {
326
+ const data = await fetchData();
327
+ return <div>{data}</div>;
328
+ }
329
+
330
+ // Client Component - for interactivity
331
+ "use client";
332
+ export default function MyComponent() {
333
+ const [count, setCount] = useState(0);
334
+ return <button onClick={() => setCount(count + 1)}>{count}</button>;
335
+ }
336
+ ```
337
+
338
+ ### 5. Type Safety
339
+ ```tsx
340
+ import type { ImageField } from "@agility/nextjs";
341
+
342
+ // GOOD - define types with ImageField for images
343
+ interface Post {
344
+ contentID: number;
345
+ fields: {
346
+ title: string;
347
+ slug: string;
348
+ image: ImageField; // Always use ImageField for Agility images
349
+ };
350
+ }
351
+
352
+ const posts: Post[] = await getContentList({ ... });
353
+
354
+ // BAD - using any
355
+ const posts: any = await getContentList({ ... });
356
+ ```
357
+
358
+ ## File Naming Conventions
359
+
360
+ - `.tsx` - React component (auto-detected as server or client)
361
+ - `.server.tsx` - Explicitly server component (optional)
362
+ - `.client.tsx` - Explicitly client component (must have "use client")
363
+ - `index.ts` - Registry/barrel file for exports
364
+
365
+ ## Common Gotchas
366
+
367
+ 1. **Image Components**: ALWAYS use `<AgilityPic>` for Agility CMS images, NEVER use `<Image>` or `<img>`
368
+ 2. **Module Reference Names**: Must match EXACTLY between Agility CMS and code
369
+ 3. **Locale Param**: Always pass locale to CMS functions
370
+ 4. **ContentID vs ReferenceName**: Use referenceName for lists, contentID for single items
371
+ 5. **Preview Mode**: Uses different API key (PREVIEW vs FETCH)
372
+ 6. **Cache Tags**: Automatically added, no manual setup needed
373
+ 7. **Search Params**: Encoded with ~~~ in URLs for static generation
374
+
375
+ ## CMS Helper Functions Reference
376
+
377
+ | Function | Purpose | Example |
378
+ |----------|---------|---------|
379
+ | `getAgilityPage()` | Full page data | `await getAgilityPage({ params })` |
380
+ | `getContentItem()` | Single content | `await getContentItem({ contentID: 123, locale })` |
381
+ | `getContentList()` | Content collection | `await getContentList({ referenceName: "posts", locale })` |
382
+ | `getSitemapFlat()` | Flat sitemap | `await getSitemapFlat({ locale })` |
383
+ | `getSitemapNested()` | Nested sitemap | `await getSitemapNested({ locale })` |
384
+
385
+ ## When in Doubt
386
+
387
+ 1. **Read existing code**: Check similar components in `agility-components/`
388
+ 2. **Check documentation**: Read the relevant `.md` file in `.claude/docs/`
389
+ 3. **Ask clarifying questions**: If requirements are unclear, ask the user
390
+ 4. **Keep it simple**: Don't over-engineer, follow existing patterns
391
+ 5. **Type everything**: Use TypeScript interfaces for all props
392
+
393
+ ## Documentation Index
394
+
395
+ - [01-agility-cms-overview.md](./01-agility-cms-overview.md) - CMS concepts
396
+ - [02-page-routing.md](./02-page-routing.md) - Routing and URL structure
397
+ - [03-creating-components.md](./03-creating-components.md) - Module components
398
+ - [04-data-fetching.md](./04-data-fetching.md) - Fetching CMS data
399
+ - [05-containers-and-lists.md](./05-containers-and-lists.md) - Content lists
400
+ - [06-localization.md](./06-localization.md) - Multi-language
401
+ - [07-caching-strategies.md](./07-caching-strategies.md) - Caching patterns
402
+ - [08-common-components.md](./08-common-components.md) - Component examples
403
+ - [11-linked-nested-content.md](./11-linked-nested-content.md) - Linked/nested content patterns
404
+
405
+ ## Goal: Enable Vibe Coding
406
+
407
+ This documentation enables users to build complete websites by:
408
+ 1. **Describing what they want** ("Add a blog with categories and pagination")
409
+ 2. **AI generates code** using patterns from docs
410
+ 3. **User configures in Agility CMS** (creates modules, adds content)
411
+ 4. **Site works immediately** following established patterns
412
+
413
+ The key is that AI assistants should be able to:
414
+ - Generate new components following the patterns
415
+ - Fetch data correctly using helper functions
416
+ - Handle locales, caching, and routing automatically
417
+ - Create type-safe, production-ready code
418
+
419
+ ## Success Criteria
420
+
421
+ You're helping effectively when:
422
+ 1. ✅ Generated code follows existing patterns
423
+ 2. ✅ All CMS functions include locale parameter
424
+ 3. ✅ TypeScript types are defined
425
+ 4. ✅ Server/client split is correct
426
+ 5. ✅ Module names match Agility CMS reference names
427
+ 6. ✅ Code is production-ready and maintainable
428
+
429
+ Good luck building amazing websites! 🚀
@@ -0,0 +1,17 @@
1
+ 'use client'
2
+
3
+ export default function Error({
4
+ error,
5
+ reset,
6
+ }: {
7
+ error: Error & { digest?: string }
8
+ reset: () => void
9
+ }) {
10
+ return (
11
+ <div>
12
+ <h2>Something went wrong!</h2>
13
+ <button onClick={() => reset()}>Try again</button>
14
+ </div>
15
+ )
16
+ }
17
+
@@ -0,0 +1,9 @@
1
+ export default function NotFound() {
2
+ return (
3
+ <div>
4
+ <h2>Page Not Found</h2>
5
+ <p>Could not find the requested page.</p>
6
+ </div>
7
+ )
8
+ }
9
+
@@ -0,0 +1,102 @@
1
+ import { getPageTemplate } from "@/components/agility-pages"
2
+ import { type PageProps, getAgilityPage } from "@/lib/cms/getAgilityPage"
3
+ import { getAgilityContext } from "@/lib/cms/getAgilityContext"
4
+ import agilitySDK from "@agility/content-fetch"
5
+
6
+ import type { Metadata, ResolvingMetadata } from "next"
7
+
8
+ import { notFound } from "next/navigation"
9
+ import { locales } from "@/lib/i18n/config"
10
+
11
+ export const revalidate = 60
12
+ export const runtime = "nodejs"
13
+
14
+ /**
15
+ * Generate the list of pages that we want to generate at build time.
16
+ */
17
+ export async function generateStaticParams() {
18
+ const isDevelopmentMode = process.env.NODE_ENV === "development";
19
+ const isPreview = isDevelopmentMode;
20
+ const apiKey = isPreview ? process.env.AGILITY_API_PREVIEW_KEY : process.env.AGILITY_API_FETCH_KEY;
21
+ const agilityClient = agilitySDK.getApi({
22
+ guid: process.env.AGILITY_GUID,
23
+ apiKey,
24
+ isPreview,
25
+ });
26
+
27
+ const allPaths: { locale: string; slug: string[] }[] = [];
28
+
29
+ // Generate paths for each locale
30
+ for (const locale of locales) {
31
+ agilityClient.config.fetchConfig = {
32
+ next: {
33
+ tags: [`agility-sitemap-flat-${locale}`],
34
+ revalidate: 60,
35
+ },
36
+ };
37
+
38
+ // Get the flat sitemap for this locale
39
+ const sitemap: { [path: string]: any } = await agilityClient.getSitemapFlat({
40
+ channelName: process.env.AGILITY_SITEMAP || "website",
41
+ languageCode: locale,
42
+ });
43
+
44
+ const localePaths = Object.values(sitemap)
45
+ .filter((node: any) => {
46
+ if (node.redirect !== null || node.isFolder === true) return false;
47
+ return true;
48
+ })
49
+ .map((node: any) => {
50
+ return {
51
+ locale,
52
+ slug: node.path.split("/").slice(1),
53
+ };
54
+ });
55
+
56
+ allPaths.push(...localePaths);
57
+ }
58
+
59
+ console.log("Pre-rendering", allPaths.length, "static paths across", locales.length, "locales.");
60
+ return allPaths;
61
+ }
62
+
63
+ /**
64
+ * Generate metadata for this page
65
+ */
66
+ export async function generateMetadata(
67
+ props: PageProps,
68
+ parent: ResolvingMetadata
69
+ ): Promise<Metadata> {
70
+ const { params } = props;
71
+ const awaitedParams = await params;
72
+
73
+ const agilityData = await getAgilityPage({ params });
74
+ if (!agilityData.page) return {};
75
+
76
+ // Basic metadata - can be enhanced with SEO fields from Agility
77
+ return {
78
+ title: agilityData.page?.seo?.metaTitle || 'Page',
79
+ description: agilityData.page?.seo?.metaDescription || '',
80
+ };
81
+ }
82
+
83
+ export default async function Page({ params }: PageProps) {
84
+ const agilityData = await getAgilityPage({ params });
85
+ if (!agilityData.page) notFound();
86
+
87
+ const AgilityPageTemplate = getPageTemplate(agilityData.pageTemplateName || "MainTemplate");
88
+
89
+ //get the search params from global data (since they are added in getAgilityPage)
90
+ const globalSearchParams = agilityData.globalData?.["searchParams"] || {};
91
+
92
+ return (
93
+ <div data-agility-page={agilityData.page?.pageID} data-agility-dynamic-content={agilityData.sitemapNode.contentID}>
94
+ {AgilityPageTemplate ? (
95
+ <AgilityPageTemplate {...agilityData} searchParams={globalSearchParams} />
96
+ ) : (
97
+ <div>No template found for page template name: {agilityData.pageTemplateName}</div>
98
+ )}
99
+ </div>
100
+ );
101
+ }
102
+
@@ -0,0 +1,22 @@
1
+ import type React from 'react'
2
+ import { getAgilityContext } from '@/lib/cms/getAgilityContext'
3
+
4
+ interface LayoutProps {
5
+ children: React.ReactNode
6
+ params: Promise<{ locale: string }>
7
+ }
8
+
9
+ export default async function LocaleLayout({
10
+ children,
11
+ params,
12
+ }: LayoutProps) {
13
+ const { locale } = await params
14
+ const { isDevelopmentMode, isPreview } = await getAgilityContext(locale)
15
+
16
+ return (
17
+ <>
18
+ {children}
19
+ </>
20
+ )
21
+ }
22
+
@@ -0,0 +1,12 @@
1
+ import { redirect } from 'next/navigation'
2
+ import { defaultLocale } from '@/lib/i18n/config'
3
+
4
+ export default function LocaleHomePage({
5
+ params,
6
+ }: {
7
+ params: Promise<{ locale: string }>
8
+ }) {
9
+ // Redirect to home page - this will be handled by the [...slug] route
10
+ redirect('/')
11
+ }
12
+
@@ -0,0 +1,24 @@
1
+ import { getDynamicPageURL } from "@agility/nextjs/node";
2
+ import { NextRequest, NextResponse } from "next/server";
3
+ import { draftMode } from "next/headers"
4
+
5
+ export async function GET(req: NextRequest) {
6
+
7
+ const searchParams = req.nextUrl.searchParams
8
+ const contentIDStr = searchParams.get("ContentID") as string
9
+ const contentID = parseInt(contentIDStr)
10
+ const { isEnabled: preview } = await draftMode()
11
+
12
+ if (!isNaN(contentID) && contentID > 0) {
13
+ //*** this is a dynamic page request ***
14
+ //get the slug for this page based on the sitemap and redirect there
15
+ const redirectUrl = await getDynamicPageURL({ contentID, preview, slug: "" })
16
+ if (redirectUrl) {
17
+ return NextResponse.redirect(redirectUrl, { status: 307, headers: { "Location": redirectUrl } })
18
+ }
19
+ }
20
+
21
+ //if we get here, it's a 404
22
+ return NextResponse.json({ message: "Not Found" }, { status: 404 })
23
+
24
+ }
@@ -0,0 +1,34 @@
1
+ "use server";
2
+ import { getDynamicPageURL } from '@agility/nextjs/node';
3
+ import { draftMode } from 'next/headers'
4
+ import { NextRequest, NextResponse } from "next/server";
5
+
6
+ export async function GET(request: NextRequest) {
7
+
8
+ const searchParams = request.nextUrl.searchParams
9
+
10
+ const slug = searchParams.get('slug');
11
+ const ContentID = searchParams.get('ContentID');
12
+
13
+ //disable draft/preview mode
14
+ (await draftMode()).disable()
15
+
16
+ let url = new URL(slug || '', request.nextUrl.origin).toString();
17
+
18
+ if (ContentID) {
19
+ const dynamicPath = await getDynamicPageURL({ contentID: Number(ContentID), preview: false, slug: slug || undefined });
20
+ if (dynamicPath) {
21
+ url = dynamicPath;
22
+ }
23
+ }
24
+
25
+ // Remove the preview URL param if it exists
26
+ const urlObj = new URL(url);
27
+ urlObj.searchParams.delete('preview');
28
+ url = urlObj.toString();
29
+
30
+
31
+ // Redirect to the url
32
+ return NextResponse.redirect(url, { status: 307, headers: { "Location": url } })
33
+
34
+ }