create-agntcms-app 0.2.1

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 (197) hide show
  1. package/README.md +39 -0
  2. package/dist/index.mjs +297 -0
  3. package/dist/template/.claude/settings.json +6 -0
  4. package/dist/template/.claude/skills/.gitkeep +0 -0
  5. package/dist/template/.claude-plugin/channel/server.mjs +254 -0
  6. package/dist/template/.claude-plugin/channel/server.ts +369 -0
  7. package/dist/template/.claude-plugin/plugin.json +17 -0
  8. package/dist/template/.mcp.json +8 -0
  9. package/dist/template/BRAND.md +49 -0
  10. package/dist/template/CLAUDE.md +157 -0
  11. package/dist/template/agntcms/config.ts +49 -0
  12. package/dist/template/agntcms/sections/ArticleBody/component.tsx +32 -0
  13. package/dist/template/agntcms/sections/ArticleBody/index.ts +10 -0
  14. package/dist/template/agntcms/sections/ArticleBody/schema.ts +5 -0
  15. package/dist/template/agntcms/sections/ArticleHero/component.tsx +87 -0
  16. package/dist/template/agntcms/sections/ArticleHero/index.ts +10 -0
  17. package/dist/template/agntcms/sections/ArticleHero/schema.ts +12 -0
  18. package/dist/template/agntcms/sections/Banner/component.tsx +83 -0
  19. package/dist/template/agntcms/sections/Banner/index.ts +10 -0
  20. package/dist/template/agntcms/sections/Banner/schema.ts +9 -0
  21. package/dist/template/agntcms/sections/BlogIndex/component.tsx +173 -0
  22. package/dist/template/agntcms/sections/BlogIndex/index.ts +10 -0
  23. package/dist/template/agntcms/sections/BlogIndex/schema.ts +33 -0
  24. package/dist/template/agntcms/sections/BlogIndexHeader/component.tsx +44 -0
  25. package/dist/template/agntcms/sections/BlogIndexHeader/index.ts +10 -0
  26. package/dist/template/agntcms/sections/BlogIndexHeader/schema.ts +8 -0
  27. package/dist/template/agntcms/sections/BlogPostBody/component.tsx +50 -0
  28. package/dist/template/agntcms/sections/BlogPostBody/index.ts +10 -0
  29. package/dist/template/agntcms/sections/BlogPostBody/schema.ts +10 -0
  30. package/dist/template/agntcms/sections/BlogPostHero/component.tsx +88 -0
  31. package/dist/template/agntcms/sections/BlogPostHero/index.ts +10 -0
  32. package/dist/template/agntcms/sections/BlogPostHero/schema.ts +35 -0
  33. package/dist/template/agntcms/sections/CaseStudies/component.tsx +92 -0
  34. package/dist/template/agntcms/sections/CaseStudies/index.ts +10 -0
  35. package/dist/template/agntcms/sections/CaseStudies/schema.ts +17 -0
  36. package/dist/template/agntcms/sections/ContactForm/component.tsx +119 -0
  37. package/dist/template/agntcms/sections/ContactForm/index.ts +10 -0
  38. package/dist/template/agntcms/sections/ContactForm/schema.ts +15 -0
  39. package/dist/template/agntcms/sections/DocsArticle/component.tsx +266 -0
  40. package/dist/template/agntcms/sections/DocsArticle/index.ts +10 -0
  41. package/dist/template/agntcms/sections/DocsArticle/schema.ts +33 -0
  42. package/dist/template/agntcms/sections/FAQ/component.tsx +57 -0
  43. package/dist/template/agntcms/sections/FAQ/index.ts +10 -0
  44. package/dist/template/agntcms/sections/FAQ/schema.ts +11 -0
  45. package/dist/template/agntcms/sections/FeatureGrid/component.tsx +117 -0
  46. package/dist/template/agntcms/sections/FeatureGrid/index.ts +10 -0
  47. package/dist/template/agntcms/sections/FeatureGrid/schema.ts +21 -0
  48. package/dist/template/agntcms/sections/FeaturedArticles/component.tsx +99 -0
  49. package/dist/template/agntcms/sections/FeaturedArticles/index.ts +10 -0
  50. package/dist/template/agntcms/sections/FeaturedArticles/schema.ts +17 -0
  51. package/dist/template/agntcms/sections/GettingStarted/component.tsx +116 -0
  52. package/dist/template/agntcms/sections/GettingStarted/index.ts +10 -0
  53. package/dist/template/agntcms/sections/GettingStarted/schema.ts +11 -0
  54. package/dist/template/agntcms/sections/Hero/component.tsx +148 -0
  55. package/dist/template/agntcms/sections/Hero/index.ts +10 -0
  56. package/dist/template/agntcms/sections/Hero/schema.ts +16 -0
  57. package/dist/template/agntcms/sections/HowItWorks/component.tsx +57 -0
  58. package/dist/template/agntcms/sections/HowItWorks/index.ts +10 -0
  59. package/dist/template/agntcms/sections/HowItWorks/schema.ts +11 -0
  60. package/dist/template/agntcms/sections/ImageText/component.tsx +110 -0
  61. package/dist/template/agntcms/sections/ImageText/index.ts +10 -0
  62. package/dist/template/agntcms/sections/ImageText/schema.ts +14 -0
  63. package/dist/template/agntcms/sections/LogoStrip/component.tsx +37 -0
  64. package/dist/template/agntcms/sections/LogoStrip/index.ts +10 -0
  65. package/dist/template/agntcms/sections/LogoStrip/schema.ts +6 -0
  66. package/dist/template/agntcms/sections/Newsletter/component.tsx +48 -0
  67. package/dist/template/agntcms/sections/Newsletter/index.ts +10 -0
  68. package/dist/template/agntcms/sections/Newsletter/schema.ts +8 -0
  69. package/dist/template/agntcms/sections/OpenSource/component.tsx +99 -0
  70. package/dist/template/agntcms/sections/OpenSource/index.ts +10 -0
  71. package/dist/template/agntcms/sections/OpenSource/schema.ts +13 -0
  72. package/dist/template/agntcms/sections/PainAnswer/component.tsx +81 -0
  73. package/dist/template/agntcms/sections/PainAnswer/index.ts +10 -0
  74. package/dist/template/agntcms/sections/PainAnswer/schema.ts +15 -0
  75. package/dist/template/agntcms/sections/PricingPlans/component.tsx +100 -0
  76. package/dist/template/agntcms/sections/PricingPlans/index.ts +10 -0
  77. package/dist/template/agntcms/sections/PricingPlans/schema.ts +13 -0
  78. package/dist/template/agntcms/sections/Problem/component.tsx +49 -0
  79. package/dist/template/agntcms/sections/Problem/index.ts +10 -0
  80. package/dist/template/agntcms/sections/Problem/schema.ts +12 -0
  81. package/dist/template/agntcms/sections/SiteFooter/component.tsx +88 -0
  82. package/dist/template/agntcms/sections/SiteFooter/index.ts +10 -0
  83. package/dist/template/agntcms/sections/SiteFooter/schema.ts +13 -0
  84. package/dist/template/agntcms/sections/SiteHeader/component.tsx +99 -0
  85. package/dist/template/agntcms/sections/SiteHeader/index.ts +10 -0
  86. package/dist/template/agntcms/sections/SiteHeader/schema.ts +14 -0
  87. package/dist/template/agntcms/sections/SiteMeta/component.tsx +26 -0
  88. package/dist/template/agntcms/sections/SiteMeta/index.ts +13 -0
  89. package/dist/template/agntcms/sections/SiteMeta/schema.ts +18 -0
  90. package/dist/template/agntcms/sections/TabbedFeatures/component.tsx +120 -0
  91. package/dist/template/agntcms/sections/TabbedFeatures/index.ts +10 -0
  92. package/dist/template/agntcms/sections/TabbedFeatures/schema.ts +13 -0
  93. package/dist/template/agntcms/sections/TeamGrid/component.tsx +77 -0
  94. package/dist/template/agntcms/sections/TeamGrid/index.ts +10 -0
  95. package/dist/template/agntcms/sections/TeamGrid/schema.ts +14 -0
  96. package/dist/template/agntcms/sections/Testimonials/component.tsx +76 -0
  97. package/dist/template/agntcms/sections/Testimonials/index.ts +10 -0
  98. package/dist/template/agntcms/sections/Testimonials/schema.ts +12 -0
  99. package/dist/template/agntcms/sections/WhatIsBuilt/component.tsx +86 -0
  100. package/dist/template/agntcms/sections/WhatIsBuilt/index.ts +10 -0
  101. package/dist/template/agntcms/sections/WhatIsBuilt/schema.ts +20 -0
  102. package/dist/template/agntcms/site-meta.ts +81 -0
  103. package/dist/template/app/[[...slug]]/page.tsx +123 -0
  104. package/dist/template/app/admin/AdminPageClient.tsx +77 -0
  105. package/dist/template/app/admin/AdminPageDynamic.tsx +24 -0
  106. package/dist/template/app/admin/page.tsx +14 -0
  107. package/dist/template/app/api/agntcms/_shared.ts +80 -0
  108. package/dist/template/app/api/agntcms/assets/route.ts +11 -0
  109. package/dist/template/app/api/agntcms/assets/upload/route.ts +11 -0
  110. package/dist/template/app/api/agntcms/draft/discard/route.ts +12 -0
  111. package/dist/template/app/api/agntcms/draft/list/route.ts +11 -0
  112. package/dist/template/app/api/agntcms/draft/publish/route.ts +11 -0
  113. package/dist/template/app/api/agntcms/draft/reorder/route.ts +10 -0
  114. package/dist/template/app/api/agntcms/draft/save/route.ts +11 -0
  115. package/dist/template/app/api/agntcms/events/route.ts +12 -0
  116. package/dist/template/app/api/agntcms/forms/delete/route.ts +17 -0
  117. package/dist/template/app/api/agntcms/forms/list/route.ts +24 -0
  118. package/dist/template/app/api/agntcms/forms/read/route.ts +23 -0
  119. package/dist/template/app/api/agntcms/forms/submit/route.ts +17 -0
  120. package/dist/template/app/api/agntcms/global/delete/route.ts +13 -0
  121. package/dist/template/app/api/agntcms/global/history/route.ts +10 -0
  122. package/dist/template/app/api/agntcms/global/list/route.ts +14 -0
  123. package/dist/template/app/api/agntcms/global/read/route.ts +11 -0
  124. package/dist/template/app/api/agntcms/global/rollback/route.ts +10 -0
  125. package/dist/template/app/api/agntcms/global/save/route.ts +14 -0
  126. package/dist/template/app/api/agntcms/mcp/route.ts +12 -0
  127. package/dist/template/app/api/agntcms/page/delete/route.ts +10 -0
  128. package/dist/template/app/api/agntcms/page/duplicate/route.ts +11 -0
  129. package/dist/template/app/api/agntcms/page/history/route.ts +10 -0
  130. package/dist/template/app/api/agntcms/page/list/route.ts +10 -0
  131. package/dist/template/app/api/agntcms/page/read/route.ts +11 -0
  132. package/dist/template/app/api/agntcms/page/rename/route.ts +10 -0
  133. package/dist/template/app/api/agntcms/page/rollback/route.ts +10 -0
  134. package/dist/template/app/api/agntcms/page/unpublish/route.ts +11 -0
  135. package/dist/template/app/api/agntcms/preview/enter/route.ts +13 -0
  136. package/dist/template/app/api/agntcms/preview/exit/route.ts +10 -0
  137. package/dist/template/app/api/agntcms/preview/issue/route.ts +12 -0
  138. package/dist/template/app/api/agntcms/template/list/route.ts +15 -0
  139. package/dist/template/app/apple-icon.svg +9 -0
  140. package/dist/template/app/icon.svg +9 -0
  141. package/dist/template/app/layout.tsx +107 -0
  142. package/dist/template/app/not-found.tsx +75 -0
  143. package/dist/template/app/robots.ts +33 -0
  144. package/dist/template/app/sitemap.ts +49 -0
  145. package/dist/template/content/globals/site-footer.json +53 -0
  146. package/dist/template/content/globals/site-header.json +18 -0
  147. package/dist/template/content/globals/site-meta.json +13 -0
  148. package/dist/template/content/pages/404.json +34 -0
  149. package/dist/template/content/pages/about.json +307 -0
  150. package/dist/template/content/pages/article-editor.json +61 -0
  151. package/dist/template/content/pages/article-schemas.json +61 -0
  152. package/dist/template/content/pages/blog.json +162 -0
  153. package/dist/template/content/pages/contact.json +29 -0
  154. package/dist/template/content/pages/home.json +243 -0
  155. package/dist/template/content/pages/pricing.json +219 -0
  156. package/dist/template/content/pages/services.json +177 -0
  157. package/dist/template/fonts/Satoshi-Medium.woff2 +0 -0
  158. package/dist/template/fonts/Satoshi-Regular.woff2 +0 -0
  159. package/dist/template/next.config.ts +6 -0
  160. package/dist/template/package.json +36 -0
  161. package/dist/template/postcss.config.mjs +5 -0
  162. package/dist/template/public/assets/.gitkeep +0 -0
  163. package/dist/template/public/assets/0418d7ed21f57e7b9e0546725c92b8419daeaa355675d9070fab0c2013cf1524.jpg +0 -0
  164. package/dist/template/public/assets/0d0475f21aa96435a8ed3cdb2fddcc6278492e76ae842f569432454f4d33631a.jpg +0 -0
  165. package/dist/template/public/assets/27457a1adee2372030d9876b0d52c44d46be98843999935eaef2526b9b961f12.jpg +0 -0
  166. package/dist/template/public/assets/3855d91192f0c6120b01427b78ef84e52baa9f4b5a17d4271e41c1bfd95a5b0c.jpg +0 -0
  167. package/dist/template/public/assets/3b3b90c5084635b746be673ede92a328f002f5621a42c9a5cb89c5e2435652cb.jpg +0 -0
  168. package/dist/template/public/assets/3e76165a78fd3e7b8ed1e93dee50803ae11110c756c8c1c89229a2dec2bc0abf.jpg +0 -0
  169. package/dist/template/public/assets/4a3e28f85dc850c347ea0fd931696aa936a6bd45f193e7f1c9328b5896fb272c.jpg +0 -0
  170. package/dist/template/public/assets/579f67d5fd4c9106c6cdf2ef29f50df934ad0fc2b7849bac1e1cfb1e3f92303b.jpg +0 -0
  171. package/dist/template/public/assets/5b95209269661bb60fb250f1da682e05b9efa64dd42f350608b299e6bf1f2f35.jpg +0 -0
  172. package/dist/template/public/assets/5e04b46f8317ef95a7ddf85aedfe5c098a755f05056325d0251eccf95ce51172.jpg +0 -0
  173. package/dist/template/public/assets/6167a9164be2cf1183bdfdd4946bf9b908570e79e92a2380c25f0bb702422bbd.jpg +0 -0
  174. package/dist/template/public/assets/75e723ec316de28247924e5dfb73a4b266e10de605e749f150883d280ed8ed16.jpg +0 -0
  175. package/dist/template/public/assets/816a11e6a7245feaf51bbebf09d1bda3f125b334bc24fc3b8f47b5380a7b4294.jpg +0 -0
  176. package/dist/template/public/assets/81eba6f5654b8746a9b0cba1a9521a67f2b4afaaefc7c88d66dfab1461270d8f.jpg +0 -0
  177. package/dist/template/public/assets/82a2ce9e49361098f77a28755779dc5a7c026831cbd135175749c1304e21dacc.jpg +0 -0
  178. package/dist/template/public/assets/8d7b02ba277ba56bdafdbd47b01f7df6d993c714b4dc2305eb65a1307c09647d.jpg +0 -0
  179. package/dist/template/public/assets/b303185b471678e4d62f678a1549ee26022f4745407d08cae44ecb1c25352293.jpg +0 -0
  180. package/dist/template/public/assets/b69b49169c11546100d6dd5280073bc0d84cbbcc6d33fa01ecf6a5866fa42237.jpg +0 -0
  181. package/dist/template/public/assets/c4d2f0d1a310e457ac722a399693652e3c86c55b294243d5ffc679394e12f9d1.jpg +0 -0
  182. package/dist/template/public/assets/cae09f4729f8a348b67267c2f2a550be0f3bfa420689afe1a5cf8b7e2b146238.png +0 -0
  183. package/dist/template/public/assets/cb3acf58b57417a4b26474ba04c096af7103c4320ed2f4f3683f79d7670a055c.jpg +0 -0
  184. package/dist/template/public/assets/d5a0701b2d156284e0ce851cd2534ec632db34f91fbcbee3b8a7784d45ce78d2.jpg +0 -0
  185. package/dist/template/public/assets/d6ef1c3f48b0e488521794fb60701da1fd2c3a1621d6ac5f17ccfd4909d3be60.jpg +0 -0
  186. package/dist/template/public/assets/de249ff9be2539cf0d1ce092de3c57001839b6c3e14fcee3fc31a7b7673ae007.jpg +0 -0
  187. package/dist/template/public/assets/eac45438956be187b010e24b3289757aa00f227c190d49ee99fea510552dd2ba.jpg +0 -0
  188. package/dist/template/public/assets/f8b9200065b5436c6a88361839edc2b89be88d3037c84a80d3ee95c32891510b.jpg +0 -0
  189. package/dist/template/public/assets/placeholder.png +0 -0
  190. package/dist/template/public/brand/mark.svg +6 -0
  191. package/dist/template/public/brand/wordmark-light.svg +6 -0
  192. package/dist/template/public/brand/wordmark.svg +6 -0
  193. package/dist/template/styles/globals.css +69 -0
  194. package/dist/template/styles/theme.css +492 -0
  195. package/dist/template/styles/typography.css +469 -0
  196. package/dist/template/tsconfig.json +30 -0
  197. package/package.json +30 -0
@@ -0,0 +1,369 @@
1
+ // agntcms channel server.
2
+ //
3
+ // Shipped as part of the template's Claude Code plugin
4
+ // (`.claude-plugin/channel/server.ts` + `.claude-plugin/plugin.json`),
5
+ // not as a separate npm package. The wire protocol is identical to the
6
+ // previous standalone `@agntcms/mcp-channel` package — only the
7
+ // packaging changed (ARCHITECTURE.md §7, §11).
8
+ //
9
+ // Build/run model:
10
+ // - `server.ts` is the source of truth.
11
+ // - `server.mjs` (alongside this file) is the pre-built artefact and
12
+ // IS checked in. We pre-build to avoid an extra runtime dependency
13
+ // like `tsx`: the template ships `@modelcontextprotocol/sdk` as a
14
+ // regular dep, so `node .claude-plugin/channel/server.mjs` Just
15
+ // Works without additional installs.
16
+ // - Regenerate `server.mjs` with the build command in this directory.
17
+ //
18
+ // Two transports:
19
+ //
20
+ // 1. MCP over stdio (talking to Claude Code)
21
+ // - Declares `experimental: { 'claude/channel': {} }` so Claude Code
22
+ // delivers channel notifications as user turns in the agent loop.
23
+ // - Exposes `report_task_done` and `report_task_progress` tools the
24
+ // agent calls to report results back.
25
+ //
26
+ // 2. HTTP server on agntcms_CHANNEL_PORT (default 4819)
27
+ // - POST /task — Next.js dispatches tasks here; the server formats them
28
+ // as `notifications/claude/channel` and delivers to Claude Code.
29
+ // - GET /health — liveness check used by the AgentBridge's `start()`.
30
+ //
31
+ // Result flow: tool call → HTTP POST to callback URL → Next.js handler
32
+ // applies the result to the task store.
33
+
34
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js'
35
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
36
+ import {
37
+ ListToolsRequestSchema,
38
+ CallToolRequestSchema,
39
+ } from '@modelcontextprotocol/sdk/types.js'
40
+ import * as http from 'node:http'
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Configuration
44
+ // ---------------------------------------------------------------------------
45
+
46
+ const CHANNEL_PORT = Number(process.env['agntcms_CHANNEL_PORT'] ?? 4819)
47
+ const DEFAULT_CALLBACK_URL =
48
+ process.env['agntcms_CALLBACK_URL'] ?? 'http://localhost:3000/api/agntcms/mcp'
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // State: task_id → callback_url for routing tool results back
52
+ // ---------------------------------------------------------------------------
53
+
54
+ const taskCallbacks = new Map<string, string>()
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Instructions injected into Claude Code context
58
+ // ---------------------------------------------------------------------------
59
+
60
+ const INSTRUCTIONS = [
61
+ 'Tasks from the agntcms runtime arrive as channel messages.',
62
+ 'Each message starts with "task_assigned" followed by task_id, type, and payload fields.',
63
+ '',
64
+ 'For every task_assigned message:',
65
+ '1. Parse the task_id, type, and payload.',
66
+ '2. Check your loaded skills for one that covers this task type',
67
+ ' (e.g. agntcms-section-replace for type=section_replace).',
68
+ ' If a skill matches, follow its workflow.',
69
+ '3. Carry out the work using your file tools.',
70
+ '4. Report completion by calling the report_task_done tool with the',
71
+ ' exact task_id and a short human-readable result.',
72
+ '5. For long-running tasks, call report_task_progress one or more times',
73
+ ' before report_task_done.',
74
+ '',
75
+ 'Always call report_task_done, even on error — use a result starting',
76
+ 'with "Error: " and a short reason. Never leave a task without a',
77
+ 'report_task_done call.',
78
+ '',
79
+ 'IMPORTANT: Never read image files (png, jpg, jpeg, gif, webp, svg, ico).',
80
+ 'Image paths in content JSON are opaque strings — copy them as-is.',
81
+ 'You only edit text: JSON content files, TypeScript config, Markdown.',
82
+ ].join('\n')
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // MCP Server
86
+ // ---------------------------------------------------------------------------
87
+
88
+ const mcp = new Server(
89
+ { name: 'agntcms', version: '0.1.6' },
90
+ {
91
+ capabilities: {
92
+ tools: {},
93
+ experimental: {
94
+ 'claude/channel': {},
95
+ },
96
+ },
97
+ instructions: INSTRUCTIONS,
98
+ },
99
+ )
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // MCP Tools
103
+ // ---------------------------------------------------------------------------
104
+
105
+ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
106
+ tools: [
107
+ {
108
+ name: 'report_task_done',
109
+ description:
110
+ 'Report that an assigned task has been completed. Pass the task_id from the task_assigned message and a short human-readable result.',
111
+ inputSchema: {
112
+ type: 'object' as const,
113
+ properties: {
114
+ task_id: {
115
+ type: 'string',
116
+ description: 'The task_id from the task_assigned message',
117
+ },
118
+ result: {
119
+ type: 'string',
120
+ description: 'Short human-readable description of what was done',
121
+ },
122
+ },
123
+ required: ['task_id', 'result'],
124
+ },
125
+ },
126
+ {
127
+ name: 'report_task_progress',
128
+ description:
129
+ 'Report incremental progress on a long-running task. Optional — only use for tasks that take more than a few seconds.',
130
+ inputSchema: {
131
+ type: 'object' as const,
132
+ properties: {
133
+ task_id: {
134
+ type: 'string',
135
+ description: 'The task_id from the task_assigned message',
136
+ },
137
+ message: {
138
+ type: 'string',
139
+ description: 'Short progress update',
140
+ },
141
+ },
142
+ required: ['task_id', 'message'],
143
+ },
144
+ },
145
+ ],
146
+ }))
147
+
148
+ const postCallback = async (
149
+ callbackUrl: string,
150
+ body: Record<string, unknown>,
151
+ ): Promise<void> => {
152
+ const res = await fetch(callbackUrl, {
153
+ method: 'POST',
154
+ headers: { 'Content-Type': 'application/json' },
155
+ body: JSON.stringify(body),
156
+ })
157
+ if (!res.ok) {
158
+ throw new Error(`callback returned ${String(res.status)}`)
159
+ }
160
+ }
161
+
162
+ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
163
+ const { name } = request.params
164
+ const args = (request.params.arguments ?? {}) as Record<string, unknown>
165
+
166
+ switch (name) {
167
+ case 'report_task_done': {
168
+ const taskId = String(args['task_id'] ?? '')
169
+ const result = String(args['result'] ?? '')
170
+ const callbackUrl = taskCallbacks.get(taskId) ?? DEFAULT_CALLBACK_URL
171
+
172
+ try {
173
+ await postCallback(callbackUrl, {
174
+ action: 'task_callback',
175
+ event: 'completed',
176
+ task_id: taskId,
177
+ result,
178
+ })
179
+ taskCallbacks.delete(taskId)
180
+ return { content: [{ type: 'text' as const, text: `acknowledged: ${taskId}` }] }
181
+ } catch (err: unknown) {
182
+ const msg = err instanceof Error ? err.message : String(err)
183
+ return {
184
+ content: [{ type: 'text' as const, text: `callback failed: ${msg}` }],
185
+ isError: true,
186
+ }
187
+ }
188
+ }
189
+
190
+ case 'report_task_progress': {
191
+ const taskId = String(args['task_id'] ?? '')
192
+ const message = String(args['message'] ?? '')
193
+ const callbackUrl = taskCallbacks.get(taskId) ?? DEFAULT_CALLBACK_URL
194
+
195
+ try {
196
+ await postCallback(callbackUrl, {
197
+ action: 'task_callback',
198
+ event: 'progress',
199
+ task_id: taskId,
200
+ message,
201
+ })
202
+ return { content: [{ type: 'text' as const, text: `progress recorded for ${taskId}` }] }
203
+ } catch (err: unknown) {
204
+ const msg = err instanceof Error ? err.message : String(err)
205
+ return {
206
+ content: [{ type: 'text' as const, text: `callback failed: ${msg}` }],
207
+ isError: true,
208
+ }
209
+ }
210
+ }
211
+
212
+ default:
213
+ return {
214
+ content: [{ type: 'text' as const, text: `unknown tool: ${name}` }],
215
+ isError: true,
216
+ }
217
+ }
218
+ })
219
+
220
+ // ---------------------------------------------------------------------------
221
+ // Task text formatting (mirrors the contract in section-replace SKILL.md)
222
+ // ---------------------------------------------------------------------------
223
+
224
+ const buildTaskAssignedText = (
225
+ taskId: string,
226
+ type: string,
227
+ payload: unknown,
228
+ ): string => {
229
+ const payloadJson = (() => {
230
+ try {
231
+ return JSON.stringify(payload)
232
+ } catch {
233
+ return '"<unserializable payload>"'
234
+ }
235
+ })()
236
+ return [
237
+ 'task_assigned',
238
+ `task_id=${taskId}`,
239
+ `type=${type}`,
240
+ `payload=${payloadJson}`,
241
+ '',
242
+ 'Carry out the task and then call report_task_done with the task_id above.',
243
+ ].join('\n')
244
+ }
245
+
246
+ // ---------------------------------------------------------------------------
247
+ // HTTP Server
248
+ // ---------------------------------------------------------------------------
249
+
250
+ const readBody = async (req: http.IncomingMessage): Promise<string> => {
251
+ const chunks: Buffer[] = []
252
+ for await (const chunk of req) {
253
+ chunks.push(chunk as Buffer)
254
+ }
255
+ return Buffer.concat(chunks).toString('utf-8')
256
+ }
257
+
258
+ const jsonResponse = (
259
+ res: http.ServerResponse,
260
+ status: number,
261
+ body: unknown,
262
+ ): void => {
263
+ res.writeHead(status, { 'Content-Type': 'application/json' })
264
+ res.end(JSON.stringify(body))
265
+ }
266
+
267
+ const httpServer = http.createServer((req, res) => {
268
+ if (req.method === 'GET' && req.url === '/health') {
269
+ jsonResponse(res, 200, { status: 'ok' })
270
+ return
271
+ }
272
+
273
+ if (req.method === 'POST' && req.url === '/task') {
274
+ readBody(req)
275
+ .then((raw) => {
276
+ let parsed: Record<string, unknown>
277
+ try {
278
+ parsed = JSON.parse(raw) as Record<string, unknown>
279
+ } catch {
280
+ jsonResponse(res, 400, { error: 'invalid_json' })
281
+ return
282
+ }
283
+
284
+ const taskId = parsed['task_id']
285
+ const type = parsed['type']
286
+ const payload = parsed['payload']
287
+ const callbackUrl = parsed['callback_url']
288
+
289
+ if (typeof taskId !== 'string' || taskId === '') {
290
+ jsonResponse(res, 400, {
291
+ error: 'missing_field',
292
+ message: 'task_id is required',
293
+ })
294
+ return
295
+ }
296
+ if (typeof type !== 'string' || type === '') {
297
+ jsonResponse(res, 400, {
298
+ error: 'missing_field',
299
+ message: 'type is required',
300
+ })
301
+ return
302
+ }
303
+
304
+ const resolvedCallback =
305
+ typeof callbackUrl === 'string' && callbackUrl !== ''
306
+ ? callbackUrl
307
+ : DEFAULT_CALLBACK_URL
308
+
309
+ taskCallbacks.set(taskId, resolvedCallback)
310
+
311
+ const content = buildTaskAssignedText(taskId, type, payload)
312
+
313
+ mcp
314
+ .notification({
315
+ method: 'notifications/claude/channel',
316
+ params: { content, meta: { task_id: taskId, type } },
317
+ })
318
+ .then(() => {
319
+ jsonResponse(res, 200, { ok: true, task_id: taskId })
320
+ })
321
+ .catch((err: unknown) => {
322
+ taskCallbacks.delete(taskId)
323
+ const msg = err instanceof Error ? err.message : String(err)
324
+ jsonResponse(res, 502, {
325
+ error: 'notification_failed',
326
+ message: msg,
327
+ })
328
+ })
329
+ })
330
+ .catch(() => {
331
+ jsonResponse(res, 500, { error: 'internal' })
332
+ })
333
+ return
334
+ }
335
+
336
+ jsonResponse(res, 404, { error: 'not_found' })
337
+ })
338
+
339
+ // ---------------------------------------------------------------------------
340
+ // Exports for testing (the module is also runnable as a script)
341
+ // ---------------------------------------------------------------------------
342
+
343
+ export { mcp, httpServer, taskCallbacks, buildTaskAssignedText, INSTRUCTIONS }
344
+
345
+ // ---------------------------------------------------------------------------
346
+ // Main — only runs when executed directly, not when imported by tests
347
+ // ---------------------------------------------------------------------------
348
+
349
+ const isDirectRun =
350
+ process.argv[1] !== undefined &&
351
+ (process.argv[1].endsWith('/server.mjs') ||
352
+ process.argv[1].endsWith('/server.js') ||
353
+ process.argv[1].endsWith('/server.ts'))
354
+
355
+ if (isDirectRun) {
356
+ const transport = new StdioServerTransport()
357
+ mcp.connect(transport)
358
+ .then(() => {
359
+ httpServer.listen(CHANNEL_PORT, '127.0.0.1', () => {
360
+ process.stderr.write(
361
+ `agntcms channel server listening on 127.0.0.1:${String(CHANNEL_PORT)}\n`,
362
+ )
363
+ })
364
+ })
365
+ .catch((err: unknown) => {
366
+ process.stderr.write(`agntcms channel server fatal: ${String(err)}\n`)
367
+ process.exit(1)
368
+ })
369
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
+ "name": "agntcms",
4
+ "version": "0.1.0",
5
+ "description": "agntcms channel: pushes UI-initiated tasks from the Next.js dev server into the Claude Code session.",
6
+ "mcpServers": {
7
+ "agntcms": {
8
+ "command": "node",
9
+ "args": ["${CLAUDE_PLUGIN_ROOT}/channel/server.mjs"]
10
+ }
11
+ },
12
+ "channels": [
13
+ {
14
+ "server": "agntcms"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "agntcms": {
4
+ "command": "node",
5
+ "args": [".claude-plugin/channel/server.mjs"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,49 @@
1
+ # agntcms — Brand Guide
2
+
3
+ This is the brand and design guide for the demo content shipped with
4
+ this template. When `/agntcms-init` runs against a real Claude Design
5
+ bundle, this file is rewritten to point at that bundle. Until then,
6
+ these rules describe the in-tree demo.
7
+
8
+ ## Source of truth
9
+
10
+ - Tokens live verbatim in `styles/theme.css` (in the `:root` block at
11
+ the top of the file). They are consumed via Tailwind utility classes
12
+ through the `@theme` block — utilities such as `bg-paper`,
13
+ `text-ink`, `text-ink-2`, `text-ink-3`, `border-hairline`,
14
+ `font-display`, `font-mono` all resolve through these tokens.
15
+ Running `grep -rE '#[0-9a-fA-F]{3,6}' styles/ agntcms/sections/`
16
+ should match only token definition lines or SVG markup.
17
+ - Page copy and section ordering live in `content/pages/<slug>.json`
18
+ and `content/globals/*.json`. The JSON is the content source —
19
+ never inline a layout into a page component.
20
+ - Every visible block becomes a registered agntcms section under
21
+ `agntcms/sections/<Name>/` with two-line registration in
22
+ `agntcms/config.ts`. If a needed block has no matching section
23
+ type, stop and run `/agntcms-section-new`.
24
+
25
+ ## Accent policy
26
+
27
+ Monochrome by default — paper + ink. The brand still owns Electric
28
+ Teal (`var(--teal)`, `#2DD4BF`), but the design system does not apply
29
+ it anywhere automatically. Tokens for `--teal`, `--teal-ink`,
30
+ `--teal-tint`, `--teal-deep`, and `--caret` are defined; they are
31
+ inert unless a component references them by name.
32
+
33
+ The one exception is the wordmark caret — the 7×14px blinking block
34
+ cursor after `agntcms`. That is the only place teal ships on by
35
+ default. Everything else (eyebrows, link decoration, focus rings,
36
+ hover states, pill dots) is ink — `--ink`, `--ink-2`, `--ink-3`. Never
37
+ teal.
38
+
39
+ ## Tone
40
+
41
+ Developer-to-developer, senior, unsentimental. Short sentences.
42
+ Concrete numbers over adjectives. No buzzwords (banned:
43
+ *seamless, robust, leverage, synergy, blazingly, cutting-edge,
44
+ revolutionary, unleash, empower, journey, ecosystem*; `AI-powered`
45
+ as a bare phrase is banned — say what the AI actually does). No
46
+ emoji in marketing copy. Sentence case for everything except product
47
+ names.
48
+
49
+ Frozen zone is off-limits throughout (see `CLAUDE.md`).
@@ -0,0 +1,157 @@
1
+ # agntcms
2
+
3
+ This is an agntcms project. You assist the developer — you can work with code,
4
+ sections, configuration, and content.
5
+
6
+ ## First time here?
7
+
8
+ This template ships with the agntcms demo (brand, home page, blog posts about agntcms
9
+ itself). To turn it into your own project, run `/agntcms-init` and provide a Claude Design
10
+ bundle — either a URL from `claude.ai/design` or a local extracted directory. The skill
11
+ wipes the demo, places the bundle under `design/<bundle-name>/`, reads the bundle end-to-end
12
+ (chats, primary file, all imports), migrates tokens from `colors_and_type.css` into
13
+ `styles/theme.css` verbatim, creates and registers every section type the bundle requires,
14
+ populates `content/pages/<slug>.json` for every content page with copy lifted verbatim from
15
+ the mocks, updates the globals (header, footer, optional announcement) from the mock header
16
+ and footer, and writes `BRAND.md` pointing at the bundle. All steps run to completion in one
17
+ pass before reporting done.
18
+
19
+ ## Skill-check protocol
20
+
21
+ This protocol applies before every non-trivial step — not just at the start of a task, but
22
+ before each individual operation within a task.
23
+
24
+ **Triggers that require a skill check:**
25
+
26
+ - Editing any file in `content/` (page data, drafts, version history)
27
+ - Creating, deleting, or renaming a page
28
+ - Creating, editing, replacing, reordering, or deleting a section
29
+ - Publishing or unpublishing a page, or rolling back to a previous version
30
+ - Renaming a page slug
31
+ - Editing site-wide globals (header, footer, announcement bar)
32
+ - Any operation inside the frozen zone (even to inspect or recover it)
33
+ - Initializing a new project from a Claude Design bundle
34
+ - Any task arriving via the channel from the UI
35
+
36
+ **The gate rule:** Before calling Read, Edit, or Write on any file in `content/`,
37
+ `agntcms/sections/`, or responding to a task from the UI — state aloud which skill
38
+ covers the operation and load it. If no skill covers the operation, say explicitly why
39
+ before proceeding.
40
+
41
+ **No shortcuts.** Even if the task looks trivial ("just change the heading"), check the
42
+ list first. `agntcms-text-edit` covers text field changes, `agntcms-section-edit`
43
+ covers section-level edits, `agntcms-page-edit` covers page-wide changes. Skipping
44
+ the skill check is not permitted even when the answer seems obvious.
45
+
46
+ ## Workflow
47
+
48
+ Skills are a gate, not a suggestion. No action without a skill check first.
49
+
50
+ 1. **Skill check (gate).** Before doing anything: scan the skill list below, identify
51
+ which skill covers the next operation, and load it. If the operation is covered,
52
+ follow the skill's instructions exactly. Do not read project files, run commands,
53
+ or make edits until the relevant skill is loaded.
54
+ 2. **Read project files only if needed.** If no skill covers the task, or if the
55
+ skill leaves gaps, then explore the codebase — start with `agntcms/config.ts`
56
+ (section types and adapters).
57
+ 3. **Brand before design.** Before any design, styling, or copy work, read
58
+ `BRAND.md` (design direction, tone, visual intent) and `styles/theme.css`
59
+ (design tokens). Use Tailwind utility classes that reference theme tokens.
60
+ Never hardcode hex values or use inline style objects.
61
+ If a Claude Design bundle has been set up, `BRAND.md` will point to it
62
+ under `design/<bundle-name>/`. Read `design/<bundle-name>/README.md`
63
+ end-to-end for preview cards, prototype kits, asset sources, and
64
+ iconography detail. If the bundle ever contradicts `BRAND.md`, `BRAND.md` wins.
65
+
66
+ ## Hard rules
67
+
68
+ 1. **Skills first.** Load the relevant skill before touching any content file,
69
+ section file, or frozen file. This is not optional and has no exceptions.
70
+ 2. **Never modify frozen zone files.** If a frozen file looks wrong, load
71
+ `agntcms-frozen-guard` and follow its recovery instructions. Do not attempt
72
+ to repair it manually.
73
+ 3. **Section registration is always explicit.** Create the folder under
74
+ `agntcms/sections/`, then add two lines to `agntcms/config.ts`. No codegen,
75
+ no folder scanning.
76
+ 4. **Content edits go through `content/` JSON files** using native file tools.
77
+ Always follow the skill workflow — skills encode the correct draft/publish
78
+ sequence and prevent silent data loss.
79
+
80
+ ## Project zones
81
+
82
+ **Frozen zone** — framework-owned, do not modify:
83
+ `app/api/agntcms/`, `app/[[...slug]]/`, `app/not-found.tsx`, `app/admin/`, `app/sitemap.ts`,
84
+ `app/robots.ts`, `.claude/`, `.claude-plugin/`
85
+
86
+ **User zone** — section definitions and framework config:
87
+ `agntcms/config.ts`, `agntcms/sections/`
88
+
89
+ **Content zone** — page data, drafts, version history:
90
+ `content/` (managed by the content-editor subagent)
91
+
92
+ **Assets** — content-addressed image files:
93
+ `public/assets/` (filenames are SHA-256 hashes — do not rename them)
94
+
95
+ **Styles zone** — design tokens and Tailwind theme:
96
+ `styles/globals.css`, `styles/theme.css`, `styles/typography.css`
97
+
98
+ ## Developer skills
99
+
100
+ These skills are your primary reference. Check this list before every operation.
101
+ Skills are installed in `.claude/skills/` and loaded with the `/` command.
102
+
103
+ **Project setup**
104
+ - `agntcms-init` — first-run setup: wipe the demo, acquire a Claude Design bundle, migrate tokens, create sections, populate every content page, update globals, write BRAND.md
105
+ - `agntcms-structure` — canonical project layout and zone rules
106
+
107
+ **Section development**
108
+ - `agntcms-sections` — two-step workflow for creating and registering a section
109
+ - `agntcms-section-new` — scaffold a new section from a name and field list (folder, schema, component, config registration)
110
+ - `agntcms-section-validate` — validate section structure and config registration
111
+ - `agntcms-section-replace` — handle section-replace tasks from the UI
112
+
113
+ **Content operations**
114
+ - `agntcms-content-fs` — edit content through native file tools (FS adapter); covers the draft/publish file layout
115
+ - `agntcms-text-edit` — apply free-form instructions to a single text or markdown field
116
+ - `agntcms-section-edit` — apply free-form instructions to a whole section (type unchanged)
117
+ - `agntcms-page-edit` — apply free-form instructions across every section of a page
118
+ - `agntcms-page-create-brief` — read a free-form brief, plan a section sequence, build a populated draft, return a preview URL
119
+
120
+ **Page lifecycle**
121
+ - `agntcms-create-page` — create an empty page draft and return a preview URL
122
+ - `agntcms-delete-page` — delete a published page after explicit editor confirmation
123
+ - `agntcms-publish-draft` — publish a draft via the framework endpoint; never move files manually
124
+ - `agntcms-unpublish-page` — take a published page offline while preserving content as a draft
125
+ - `agntcms-rollback` — roll a published page back to a previous version snapshot
126
+ - `agntcms-rename-slug` — atomically rename a page slug across pages, drafts, and history
127
+ - `agntcms-reorder-sections` — reorder sections within a draft page with disambiguation support
128
+
129
+ **Site-wide content**
130
+ - `agntcms-globals` — site-wide content blocks (header, footer, announcement bar) rendered via GlobalSlot
131
+
132
+ **Infrastructure**
133
+ - `agntcms-frozen-guard` — detect and recover from frozen-file drift
134
+ - `agntcms-git-publish` — commit conventions for agent-driven edits
135
+
136
+ ## Content editing
137
+
138
+ Content operations — creating pages, editing content, publishing drafts, renaming slugs,
139
+ reordering sections, rollback — are handled through the skills listed above. Load the
140
+ relevant skill before acting. Use your native file tools to read and write JSON files
141
+ in `content/` — that is the authoritative content store. Never call MCP CRUD tools for
142
+ content; there are none.
143
+
144
+ When reporting a content change to the user, include the preview URL so they can verify
145
+ the result in the admin panel.
146
+
147
+ ## Debugging discipline
148
+
149
+ - **Hypothesis before fix.** Before changing code to fix a visual or functional
150
+ bug, state one explicit hypothesis — which CSS property, which component,
151
+ which field schema — and verify it (read the code, inspect devtools, open the
152
+ preview URL). Do not try CSS properties "just to see" — that is how three
153
+ wrong fixes land before the real one.
154
+ - **Verify before explore.** When asked "check that X matches Y" (styles vs.
155
+ `BRAND.md`, config registry vs. `agntcms/sections/` folder, etc.), diff
156
+ first. If it already matches, stop and report "aligned, no changes needed".
157
+ Do not open unrelated files or propose refactors.
@@ -0,0 +1,49 @@
1
+ // USER ZONE — section + global registry for this project.
2
+
3
+ import { defineConfig } from '@agntcms/next/config'
4
+
5
+ import { Hero } from './sections/Hero'
6
+ import { LogoStrip } from './sections/LogoStrip'
7
+ import { TabbedFeatures } from './sections/TabbedFeatures'
8
+ import { FeatureGrid } from './sections/FeatureGrid'
9
+ import { ImageText } from './sections/ImageText'
10
+ import { Newsletter } from './sections/Newsletter'
11
+ import { FeaturedArticles } from './sections/FeaturedArticles'
12
+ import { Banner } from './sections/Banner'
13
+ import { Testimonials } from './sections/Testimonials'
14
+ import { CaseStudies } from './sections/CaseStudies'
15
+ import { TeamGrid } from './sections/TeamGrid'
16
+ import { PricingPlans } from './sections/PricingPlans'
17
+ import { FAQ } from './sections/FAQ'
18
+ import { ContactForm } from './sections/ContactForm'
19
+ import { BlogIndexHeader } from './sections/BlogIndexHeader'
20
+ import { ArticleHero } from './sections/ArticleHero'
21
+ import { ArticleBody } from './sections/ArticleBody'
22
+ import { SiteHeader } from './sections/SiteHeader'
23
+ import { SiteFooter } from './sections/SiteFooter'
24
+ import { SiteMeta } from './sections/SiteMeta'
25
+
26
+ export default defineConfig({
27
+ sections: [
28
+ Hero,
29
+ LogoStrip,
30
+ TabbedFeatures,
31
+ FeatureGrid,
32
+ ImageText,
33
+ Newsletter,
34
+ FeaturedArticles,
35
+ Banner,
36
+ Testimonials,
37
+ CaseStudies,
38
+ TeamGrid,
39
+ PricingPlans,
40
+ FAQ,
41
+ ContactForm,
42
+ BlogIndexHeader,
43
+ ArticleHero,
44
+ ArticleBody,
45
+ SiteHeader,
46
+ SiteFooter,
47
+ SiteMeta,
48
+ ],
49
+ })
@@ -0,0 +1,32 @@
1
+ 'use client'
2
+
3
+ import { EditableRichText } from '@agntcms/next/client'
4
+ import type { EditableSlot } from '@agntcms/next/client'
5
+
6
+ interface Props {
7
+ readonly body: EditableSlot<'richText', string>
8
+ }
9
+
10
+ export function ArticleBodyComponent({ body }: Props) {
11
+ return (
12
+ <section className="bg-paper">
13
+ <div className="mx-auto w-full max-w-[720px] px-8 py-12">
14
+ <EditableRichText
15
+ field={body}
16
+ className="
17
+ [&_p]:m-0 [&_p]:mt-4 [&_p]:text-[18px] [&_p]:leading-[1.75] [&_p]:text-ink-2
18
+ [&_h2]:m-0 [&_h2]:mt-10 [&_h2]:mb-4 [&_h2]:font-display [&_h2]:font-semibold [&_h2]:text-ink [&_h2]:text-[32px] [&_h2]:leading-[1.15] [&_h2]:tracking-[-0.02em]
19
+ [&_h3]:m-0 [&_h3]:mt-8 [&_h3]:mb-3 [&_h3]:font-display [&_h3]:font-semibold [&_h3]:text-ink [&_h3]:text-[24px] [&_h3]:leading-[1.2] [&_h3]:tracking-[-0.015em]
20
+ [&_ul]:my-4 [&_ul]:list-disc [&_ul]:pl-6 [&_ul]:text-ink-2
21
+ [&_ol]:my-4 [&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:text-ink-2
22
+ [&_li]:my-1.5 [&_li]:text-[18px] [&_li]:leading-[1.65] [&_li]:text-ink-2
23
+ [&_blockquote]:my-8 [&_blockquote]:border-l-2 [&_blockquote]:border-ink [&_blockquote]:pl-6 [&_blockquote]:text-[22px] [&_blockquote]:italic [&_blockquote]:leading-[1.5] [&_blockquote]:text-ink
24
+ [&_a]:text-ink [&_a]:underline [&_a]:underline-offset-4
25
+ [&_strong]:font-semibold [&_strong]:text-ink
26
+ [&_code]:rounded-sm [&_code]:bg-paper-3 [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[14px] [&_code]:text-ink
27
+ "
28
+ />
29
+ </div>
30
+ </section>
31
+ )
32
+ }