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.
- package/README.md +39 -0
- package/dist/index.mjs +297 -0
- package/dist/template/.claude/settings.json +6 -0
- package/dist/template/.claude/skills/.gitkeep +0 -0
- package/dist/template/.claude-plugin/channel/server.mjs +254 -0
- package/dist/template/.claude-plugin/channel/server.ts +369 -0
- package/dist/template/.claude-plugin/plugin.json +17 -0
- package/dist/template/.mcp.json +8 -0
- package/dist/template/BRAND.md +49 -0
- package/dist/template/CLAUDE.md +157 -0
- package/dist/template/agntcms/config.ts +49 -0
- package/dist/template/agntcms/sections/ArticleBody/component.tsx +32 -0
- package/dist/template/agntcms/sections/ArticleBody/index.ts +10 -0
- package/dist/template/agntcms/sections/ArticleBody/schema.ts +5 -0
- package/dist/template/agntcms/sections/ArticleHero/component.tsx +87 -0
- package/dist/template/agntcms/sections/ArticleHero/index.ts +10 -0
- package/dist/template/agntcms/sections/ArticleHero/schema.ts +12 -0
- package/dist/template/agntcms/sections/Banner/component.tsx +83 -0
- package/dist/template/agntcms/sections/Banner/index.ts +10 -0
- package/dist/template/agntcms/sections/Banner/schema.ts +9 -0
- package/dist/template/agntcms/sections/BlogIndex/component.tsx +173 -0
- package/dist/template/agntcms/sections/BlogIndex/index.ts +10 -0
- package/dist/template/agntcms/sections/BlogIndex/schema.ts +33 -0
- package/dist/template/agntcms/sections/BlogIndexHeader/component.tsx +44 -0
- package/dist/template/agntcms/sections/BlogIndexHeader/index.ts +10 -0
- package/dist/template/agntcms/sections/BlogIndexHeader/schema.ts +8 -0
- package/dist/template/agntcms/sections/BlogPostBody/component.tsx +50 -0
- package/dist/template/agntcms/sections/BlogPostBody/index.ts +10 -0
- package/dist/template/agntcms/sections/BlogPostBody/schema.ts +10 -0
- package/dist/template/agntcms/sections/BlogPostHero/component.tsx +88 -0
- package/dist/template/agntcms/sections/BlogPostHero/index.ts +10 -0
- package/dist/template/agntcms/sections/BlogPostHero/schema.ts +35 -0
- package/dist/template/agntcms/sections/CaseStudies/component.tsx +92 -0
- package/dist/template/agntcms/sections/CaseStudies/index.ts +10 -0
- package/dist/template/agntcms/sections/CaseStudies/schema.ts +17 -0
- package/dist/template/agntcms/sections/ContactForm/component.tsx +119 -0
- package/dist/template/agntcms/sections/ContactForm/index.ts +10 -0
- package/dist/template/agntcms/sections/ContactForm/schema.ts +15 -0
- package/dist/template/agntcms/sections/DocsArticle/component.tsx +266 -0
- package/dist/template/agntcms/sections/DocsArticle/index.ts +10 -0
- package/dist/template/agntcms/sections/DocsArticle/schema.ts +33 -0
- package/dist/template/agntcms/sections/FAQ/component.tsx +57 -0
- package/dist/template/agntcms/sections/FAQ/index.ts +10 -0
- package/dist/template/agntcms/sections/FAQ/schema.ts +11 -0
- package/dist/template/agntcms/sections/FeatureGrid/component.tsx +117 -0
- package/dist/template/agntcms/sections/FeatureGrid/index.ts +10 -0
- package/dist/template/agntcms/sections/FeatureGrid/schema.ts +21 -0
- package/dist/template/agntcms/sections/FeaturedArticles/component.tsx +99 -0
- package/dist/template/agntcms/sections/FeaturedArticles/index.ts +10 -0
- package/dist/template/agntcms/sections/FeaturedArticles/schema.ts +17 -0
- package/dist/template/agntcms/sections/GettingStarted/component.tsx +116 -0
- package/dist/template/agntcms/sections/GettingStarted/index.ts +10 -0
- package/dist/template/agntcms/sections/GettingStarted/schema.ts +11 -0
- package/dist/template/agntcms/sections/Hero/component.tsx +148 -0
- package/dist/template/agntcms/sections/Hero/index.ts +10 -0
- package/dist/template/agntcms/sections/Hero/schema.ts +16 -0
- package/dist/template/agntcms/sections/HowItWorks/component.tsx +57 -0
- package/dist/template/agntcms/sections/HowItWorks/index.ts +10 -0
- package/dist/template/agntcms/sections/HowItWorks/schema.ts +11 -0
- package/dist/template/agntcms/sections/ImageText/component.tsx +110 -0
- package/dist/template/agntcms/sections/ImageText/index.ts +10 -0
- package/dist/template/agntcms/sections/ImageText/schema.ts +14 -0
- package/dist/template/agntcms/sections/LogoStrip/component.tsx +37 -0
- package/dist/template/agntcms/sections/LogoStrip/index.ts +10 -0
- package/dist/template/agntcms/sections/LogoStrip/schema.ts +6 -0
- package/dist/template/agntcms/sections/Newsletter/component.tsx +48 -0
- package/dist/template/agntcms/sections/Newsletter/index.ts +10 -0
- package/dist/template/agntcms/sections/Newsletter/schema.ts +8 -0
- package/dist/template/agntcms/sections/OpenSource/component.tsx +99 -0
- package/dist/template/agntcms/sections/OpenSource/index.ts +10 -0
- package/dist/template/agntcms/sections/OpenSource/schema.ts +13 -0
- package/dist/template/agntcms/sections/PainAnswer/component.tsx +81 -0
- package/dist/template/agntcms/sections/PainAnswer/index.ts +10 -0
- package/dist/template/agntcms/sections/PainAnswer/schema.ts +15 -0
- package/dist/template/agntcms/sections/PricingPlans/component.tsx +100 -0
- package/dist/template/agntcms/sections/PricingPlans/index.ts +10 -0
- package/dist/template/agntcms/sections/PricingPlans/schema.ts +13 -0
- package/dist/template/agntcms/sections/Problem/component.tsx +49 -0
- package/dist/template/agntcms/sections/Problem/index.ts +10 -0
- package/dist/template/agntcms/sections/Problem/schema.ts +12 -0
- package/dist/template/agntcms/sections/SiteFooter/component.tsx +88 -0
- package/dist/template/agntcms/sections/SiteFooter/index.ts +10 -0
- package/dist/template/agntcms/sections/SiteFooter/schema.ts +13 -0
- package/dist/template/agntcms/sections/SiteHeader/component.tsx +99 -0
- package/dist/template/agntcms/sections/SiteHeader/index.ts +10 -0
- package/dist/template/agntcms/sections/SiteHeader/schema.ts +14 -0
- package/dist/template/agntcms/sections/SiteMeta/component.tsx +26 -0
- package/dist/template/agntcms/sections/SiteMeta/index.ts +13 -0
- package/dist/template/agntcms/sections/SiteMeta/schema.ts +18 -0
- package/dist/template/agntcms/sections/TabbedFeatures/component.tsx +120 -0
- package/dist/template/agntcms/sections/TabbedFeatures/index.ts +10 -0
- package/dist/template/agntcms/sections/TabbedFeatures/schema.ts +13 -0
- package/dist/template/agntcms/sections/TeamGrid/component.tsx +77 -0
- package/dist/template/agntcms/sections/TeamGrid/index.ts +10 -0
- package/dist/template/agntcms/sections/TeamGrid/schema.ts +14 -0
- package/dist/template/agntcms/sections/Testimonials/component.tsx +76 -0
- package/dist/template/agntcms/sections/Testimonials/index.ts +10 -0
- package/dist/template/agntcms/sections/Testimonials/schema.ts +12 -0
- package/dist/template/agntcms/sections/WhatIsBuilt/component.tsx +86 -0
- package/dist/template/agntcms/sections/WhatIsBuilt/index.ts +10 -0
- package/dist/template/agntcms/sections/WhatIsBuilt/schema.ts +20 -0
- package/dist/template/agntcms/site-meta.ts +81 -0
- package/dist/template/app/[[...slug]]/page.tsx +123 -0
- package/dist/template/app/admin/AdminPageClient.tsx +77 -0
- package/dist/template/app/admin/AdminPageDynamic.tsx +24 -0
- package/dist/template/app/admin/page.tsx +14 -0
- package/dist/template/app/api/agntcms/_shared.ts +80 -0
- package/dist/template/app/api/agntcms/assets/route.ts +11 -0
- package/dist/template/app/api/agntcms/assets/upload/route.ts +11 -0
- package/dist/template/app/api/agntcms/draft/discard/route.ts +12 -0
- package/dist/template/app/api/agntcms/draft/list/route.ts +11 -0
- package/dist/template/app/api/agntcms/draft/publish/route.ts +11 -0
- package/dist/template/app/api/agntcms/draft/reorder/route.ts +10 -0
- package/dist/template/app/api/agntcms/draft/save/route.ts +11 -0
- package/dist/template/app/api/agntcms/events/route.ts +12 -0
- package/dist/template/app/api/agntcms/forms/delete/route.ts +17 -0
- package/dist/template/app/api/agntcms/forms/list/route.ts +24 -0
- package/dist/template/app/api/agntcms/forms/read/route.ts +23 -0
- package/dist/template/app/api/agntcms/forms/submit/route.ts +17 -0
- package/dist/template/app/api/agntcms/global/delete/route.ts +13 -0
- package/dist/template/app/api/agntcms/global/history/route.ts +10 -0
- package/dist/template/app/api/agntcms/global/list/route.ts +14 -0
- package/dist/template/app/api/agntcms/global/read/route.ts +11 -0
- package/dist/template/app/api/agntcms/global/rollback/route.ts +10 -0
- package/dist/template/app/api/agntcms/global/save/route.ts +14 -0
- package/dist/template/app/api/agntcms/mcp/route.ts +12 -0
- package/dist/template/app/api/agntcms/page/delete/route.ts +10 -0
- package/dist/template/app/api/agntcms/page/duplicate/route.ts +11 -0
- package/dist/template/app/api/agntcms/page/history/route.ts +10 -0
- package/dist/template/app/api/agntcms/page/list/route.ts +10 -0
- package/dist/template/app/api/agntcms/page/read/route.ts +11 -0
- package/dist/template/app/api/agntcms/page/rename/route.ts +10 -0
- package/dist/template/app/api/agntcms/page/rollback/route.ts +10 -0
- package/dist/template/app/api/agntcms/page/unpublish/route.ts +11 -0
- package/dist/template/app/api/agntcms/preview/enter/route.ts +13 -0
- package/dist/template/app/api/agntcms/preview/exit/route.ts +10 -0
- package/dist/template/app/api/agntcms/preview/issue/route.ts +12 -0
- package/dist/template/app/api/agntcms/template/list/route.ts +15 -0
- package/dist/template/app/apple-icon.svg +9 -0
- package/dist/template/app/icon.svg +9 -0
- package/dist/template/app/layout.tsx +107 -0
- package/dist/template/app/not-found.tsx +75 -0
- package/dist/template/app/robots.ts +33 -0
- package/dist/template/app/sitemap.ts +49 -0
- package/dist/template/content/globals/site-footer.json +53 -0
- package/dist/template/content/globals/site-header.json +18 -0
- package/dist/template/content/globals/site-meta.json +13 -0
- package/dist/template/content/pages/404.json +34 -0
- package/dist/template/content/pages/about.json +307 -0
- package/dist/template/content/pages/article-editor.json +61 -0
- package/dist/template/content/pages/article-schemas.json +61 -0
- package/dist/template/content/pages/blog.json +162 -0
- package/dist/template/content/pages/contact.json +29 -0
- package/dist/template/content/pages/home.json +243 -0
- package/dist/template/content/pages/pricing.json +219 -0
- package/dist/template/content/pages/services.json +177 -0
- package/dist/template/fonts/Satoshi-Medium.woff2 +0 -0
- package/dist/template/fonts/Satoshi-Regular.woff2 +0 -0
- package/dist/template/next.config.ts +6 -0
- package/dist/template/package.json +36 -0
- package/dist/template/postcss.config.mjs +5 -0
- package/dist/template/public/assets/.gitkeep +0 -0
- package/dist/template/public/assets/0418d7ed21f57e7b9e0546725c92b8419daeaa355675d9070fab0c2013cf1524.jpg +0 -0
- package/dist/template/public/assets/0d0475f21aa96435a8ed3cdb2fddcc6278492e76ae842f569432454f4d33631a.jpg +0 -0
- package/dist/template/public/assets/27457a1adee2372030d9876b0d52c44d46be98843999935eaef2526b9b961f12.jpg +0 -0
- package/dist/template/public/assets/3855d91192f0c6120b01427b78ef84e52baa9f4b5a17d4271e41c1bfd95a5b0c.jpg +0 -0
- package/dist/template/public/assets/3b3b90c5084635b746be673ede92a328f002f5621a42c9a5cb89c5e2435652cb.jpg +0 -0
- package/dist/template/public/assets/3e76165a78fd3e7b8ed1e93dee50803ae11110c756c8c1c89229a2dec2bc0abf.jpg +0 -0
- package/dist/template/public/assets/4a3e28f85dc850c347ea0fd931696aa936a6bd45f193e7f1c9328b5896fb272c.jpg +0 -0
- package/dist/template/public/assets/579f67d5fd4c9106c6cdf2ef29f50df934ad0fc2b7849bac1e1cfb1e3f92303b.jpg +0 -0
- package/dist/template/public/assets/5b95209269661bb60fb250f1da682e05b9efa64dd42f350608b299e6bf1f2f35.jpg +0 -0
- package/dist/template/public/assets/5e04b46f8317ef95a7ddf85aedfe5c098a755f05056325d0251eccf95ce51172.jpg +0 -0
- package/dist/template/public/assets/6167a9164be2cf1183bdfdd4946bf9b908570e79e92a2380c25f0bb702422bbd.jpg +0 -0
- package/dist/template/public/assets/75e723ec316de28247924e5dfb73a4b266e10de605e749f150883d280ed8ed16.jpg +0 -0
- package/dist/template/public/assets/816a11e6a7245feaf51bbebf09d1bda3f125b334bc24fc3b8f47b5380a7b4294.jpg +0 -0
- package/dist/template/public/assets/81eba6f5654b8746a9b0cba1a9521a67f2b4afaaefc7c88d66dfab1461270d8f.jpg +0 -0
- package/dist/template/public/assets/82a2ce9e49361098f77a28755779dc5a7c026831cbd135175749c1304e21dacc.jpg +0 -0
- package/dist/template/public/assets/8d7b02ba277ba56bdafdbd47b01f7df6d993c714b4dc2305eb65a1307c09647d.jpg +0 -0
- package/dist/template/public/assets/b303185b471678e4d62f678a1549ee26022f4745407d08cae44ecb1c25352293.jpg +0 -0
- package/dist/template/public/assets/b69b49169c11546100d6dd5280073bc0d84cbbcc6d33fa01ecf6a5866fa42237.jpg +0 -0
- package/dist/template/public/assets/c4d2f0d1a310e457ac722a399693652e3c86c55b294243d5ffc679394e12f9d1.jpg +0 -0
- package/dist/template/public/assets/cae09f4729f8a348b67267c2f2a550be0f3bfa420689afe1a5cf8b7e2b146238.png +0 -0
- package/dist/template/public/assets/cb3acf58b57417a4b26474ba04c096af7103c4320ed2f4f3683f79d7670a055c.jpg +0 -0
- package/dist/template/public/assets/d5a0701b2d156284e0ce851cd2534ec632db34f91fbcbee3b8a7784d45ce78d2.jpg +0 -0
- package/dist/template/public/assets/d6ef1c3f48b0e488521794fb60701da1fd2c3a1621d6ac5f17ccfd4909d3be60.jpg +0 -0
- package/dist/template/public/assets/de249ff9be2539cf0d1ce092de3c57001839b6c3e14fcee3fc31a7b7673ae007.jpg +0 -0
- package/dist/template/public/assets/eac45438956be187b010e24b3289757aa00f227c190d49ee99fea510552dd2ba.jpg +0 -0
- package/dist/template/public/assets/f8b9200065b5436c6a88361839edc2b89be88d3037c84a80d3ee95c32891510b.jpg +0 -0
- package/dist/template/public/assets/placeholder.png +0 -0
- package/dist/template/public/brand/mark.svg +6 -0
- package/dist/template/public/brand/wordmark-light.svg +6 -0
- package/dist/template/public/brand/wordmark.svg +6 -0
- package/dist/template/styles/globals.css +69 -0
- package/dist/template/styles/theme.css +492 -0
- package/dist/template/styles/typography.css +469 -0
- package/dist/template/tsconfig.json +30 -0
- 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,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
|
+
}
|