cmx-sdk 0.2.7 → 0.2.9

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 (45) hide show
  1. package/dist/add-studio-YUDYE2OH.js +0 -0
  2. package/dist/chunk-7TDMLYBI.js +0 -0
  3. package/dist/chunk-EDXXR5BE.js +0 -0
  4. package/dist/{chunk-S3TFTN6H.js → chunk-EZMBZWH7.js} +57 -0
  5. package/dist/chunk-FPQYL5GE.js +0 -0
  6. package/dist/chunk-IIQLQIDP.js +0 -0
  7. package/dist/chunk-NZQ6SBFS.js +0 -0
  8. package/dist/cli.js +567 -121
  9. package/dist/index.d.ts +343 -2
  10. package/dist/index.js +79 -31
  11. package/dist/index.js.map +1 -1
  12. package/dist/init-FLRQXJX4.js +0 -0
  13. package/dist/{interactive-menu-QKE6FMPN.js → interactive-menu-FYVOQSTL.js} +1 -1
  14. package/dist/studio-HAS6DYLO.js +0 -0
  15. package/dist/{update-sdk-ZDFOSMN4.js → update-sdk-KJZ6VB4M.js} +1 -1
  16. package/dist/update-studio-TWCYSYIS.js +0 -0
  17. package/package.json +16 -14
  18. package/templates/AGENTS.md +173 -0
  19. package/templates/CLAUDE.md +28 -0
  20. package/templates/claude/commands/check.md +64 -0
  21. package/templates/claude/commands/next-action.md +66 -0
  22. package/templates/claude/skills/cmx-cache/SKILL.md +50 -0
  23. package/templates/claude/skills/cmx-cache/references/cache-patterns.md +153 -0
  24. package/templates/claude/skills/cmx-component/SKILL.md +108 -0
  25. package/templates/claude/skills/cmx-component/references/component-schema.md +123 -0
  26. package/templates/claude/skills/cmx-content/SKILL.md +158 -0
  27. package/templates/claude/skills/cmx-content/references/migration-patterns.md +120 -0
  28. package/templates/claude/skills/cmx-content/references/seed-patterns.md +146 -0
  29. package/templates/claude/skills/cmx-dev/SKILL.md +266 -0
  30. package/templates/claude/skills/cmx-dev/references/api-patterns.md +220 -0
  31. package/templates/claude/skills/cmx-dev/references/cli-reference.md +54 -0
  32. package/templates/claude/skills/cmx-form/SKILL.md +103 -0
  33. package/templates/claude/skills/cmx-form/references/form-template.md +152 -0
  34. package/templates/claude/skills/cmx-migrate/SKILL.md +501 -0
  35. package/templates/claude/skills/cmx-migrate/references/analysis-guide.md +127 -0
  36. package/templates/claude/skills/cmx-migrate/references/html-to-mdx.md +99 -0
  37. package/templates/claude/skills/cmx-migrate/references/intermediate-format.md +196 -0
  38. package/templates/claude/skills/cmx-migrate/references/tool-setup.md +150 -0
  39. package/templates/claude/skills/cmx-schema/SKILL.md +159 -0
  40. package/templates/claude/skills/cmx-schema/references/field-types.md +164 -0
  41. package/templates/claude/skills/cmx-schema/references/migration-scenarios.md +44 -0
  42. package/templates/claude/skills/cmx-seo/SKILL.md +54 -0
  43. package/templates/claude/skills/cmx-seo/references/seo-patterns.md +216 -0
  44. package/templates/claude/skills/cmx-style/SKILL.md +48 -0
  45. package/templates/claude/skills/cmx-style/references/style-patterns.md +114 -0
@@ -0,0 +1,266 @@
1
+ ---
2
+ name: cmx-dev
3
+ description: |
4
+ CMX Starter Kitの開発ガイド。cmx-sdkを使ったページ作成、データ取得、MDXレンダリング、コード生成を支援。
5
+ トリガー: 「ページを追加」「ブログページを作りたい」「データを表示」「コレクションページ」
6
+ 「データ型のページ」「MDXをレンダリング」「型を生成」「npx cmx-sdk codegen types」
7
+ 「CMXからデータを取得」「新しいルートを作成」「プレビューページ」など。
8
+ ---
9
+
10
+ # CMX Starter Kit 開発ガイド
11
+
12
+ ## プロジェクト構成
13
+
14
+ ```
15
+ src/
16
+ ├── app/ # Next.js App Router ページ
17
+ │ ├── blog/page.tsx # コレクション一覧ページ
18
+ │ ├── blog/[slug]/page.tsx # コレクション記事詳細ページ
19
+ │ └── preview/[token]/page.tsx # プレビューページ
20
+ ├── components/custom/ # カスタムMDXコンポーネント
21
+ ├── lib/
22
+ │ ├── api/
23
+ │ │ ├── admin-client.ts # cmx-sdk の re-export
24
+ │ │ └── public-client.ts # エラーハンドリング付きラッパー
25
+ │ ├── mdx/render.tsx # MDXレンダリング(カスタムコンポーネント注入)
26
+ │ ├── utils/
27
+ │ │ ├── data-fetching.ts # requireFetchContents, requireFetchContent
28
+ │ │ ├── metadata.ts # generateCollectionMetadata, generateContentMetadata
29
+ │ │ └── date.ts # formatContentDate
30
+ │ └── constants/collections.ts # COLLECTION_SLUGS 定義
31
+ ```
32
+
33
+ ## タスク判定ツリー
34
+
35
+ - **コレクション(ブログ/ニュース等)のページを追加** → [コレクションページ作成](#コレクションページ作成)
36
+ - **データ型(チームメンバー/FAQ等)のページを追加** → [データ型ページ作成](#データ型ページ作成)
37
+ - **静的ページを追加** → [静的ページ作成](#静的ページ作成)
38
+ - **型安全なAPIコードを生成** → [コード生成](#コード生成)
39
+ - **API関数の詳細を知りたい** → [references/api-patterns.md](references/api-patterns.md) を参照
40
+
41
+ ## コレクションページ作成
42
+
43
+ CMX Admin で管理されたコレクション(ブログ、ニュース等)のページを作成する。
44
+
45
+ ### 一覧ページ
46
+
47
+ ```tsx
48
+ // src/app/{collection}/page.tsx
49
+ import type { Metadata } from "next"
50
+ import { COLLECTION_SLUGS } from "@/lib/constants/collections"
51
+ import { requireFetchContents } from "@/lib/utils/data-fetching"
52
+ import { generateCollectionMetadata } from "@/lib/utils/metadata"
53
+ import { formatContentDate } from "@/lib/utils/date"
54
+
55
+ export const dynamic = "force-dynamic"
56
+
57
+ export async function generateMetadata(): Promise<Metadata> {
58
+ return generateCollectionMetadata(COLLECTION_SLUGS.blog)
59
+ }
60
+
61
+ export default async function BlogListPage() {
62
+ const { collection, contents } = await requireFetchContents(COLLECTION_SLUGS.blog)
63
+
64
+ return (
65
+ <div className="container mx-auto px-4 py-12">
66
+ <h1 className="text-4xl font-bold mb-8">{collection.name}</h1>
67
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
68
+ {contents.map((item) => (
69
+ <a key={item.id} href={`/blog/${item.slug}`}>
70
+ <h2>{item.title}</h2>
71
+ <p>{item.description}</p>
72
+ {item.publishedAt && <time>{formatContentDate(item.publishedAt)}</time>}
73
+ </a>
74
+ ))}
75
+ </div>
76
+ </div>
77
+ )
78
+ }
79
+ ```
80
+
81
+ ### 詳細ページ(MDXレンダリング付き)
82
+
83
+ ```tsx
84
+ // src/app/{collection}/[slug]/page.tsx
85
+ import type { Metadata } from "next"
86
+ import { COLLECTION_SLUGS } from "@/lib/constants/collections"
87
+ import { requireFetchContent } from "@/lib/utils/data-fetching"
88
+ import { generateContentMetadata } from "@/lib/utils/metadata"
89
+ import { renderMdx } from "@/lib/mdx/render"
90
+
91
+ export const dynamic = "force-dynamic"
92
+
93
+ interface PageProps {
94
+ params: Promise<{ slug: string }>
95
+ }
96
+
97
+ export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
98
+ const { slug } = await params
99
+ return generateContentMetadata(COLLECTION_SLUGS.blog, slug)
100
+ }
101
+
102
+ export default async function BlogPostPage({ params }: PageProps) {
103
+ const { slug } = await params
104
+ const { content, references } = await requireFetchContent(COLLECTION_SLUGS.blog, slug)
105
+ const { content: renderedContent } = await renderMdx(content.mdx, references)
106
+
107
+ return (
108
+ <article className="container mx-auto px-4 py-12">
109
+ <div className="max-w-3xl mx-auto">
110
+ <h1 className="text-4xl font-bold mb-4">{content.title}</h1>
111
+ <div className="prose prose-lg max-w-none">{renderedContent}</div>
112
+ </div>
113
+ </article>
114
+ )
115
+ }
116
+ ```
117
+
118
+ ### 新しいコレクションを追加する際の手順
119
+
120
+ 1. `src/lib/constants/collections.ts` に slug を追加
121
+ 2. `src/app/{slug}/page.tsx` — 一覧ページ作成
122
+ 3. `src/app/{slug}/[slug]/page.tsx` — 詳細ページ作成
123
+
124
+ ## データ型ページ作成
125
+
126
+ CMX Admin で管理されたデータ型(チームメンバー、FAQ等)を表示するページ。
127
+
128
+ ```tsx
129
+ // src/app/team/page.tsx
130
+ import { getDataEntries } from "@/lib/api/admin-client"
131
+
132
+ export const dynamic = "force-dynamic"
133
+
134
+ interface TeamMember {
135
+ id: string
136
+ name?: string
137
+ role?: string
138
+ bio?: string
139
+ image?: string
140
+ }
141
+
142
+ export default async function TeamPage() {
143
+ const data = await getDataEntries("team-members", {
144
+ sortBy: "createdAt",
145
+ sortOrder: "asc",
146
+ // status: "published", // デフォルト。全ステータス取得は "all"
147
+ })
148
+ if (!data) throw new Error("Failed to fetch team members")
149
+ const members = data.items as unknown as TeamMember[]
150
+
151
+ return (
152
+ <div className="container mx-auto px-4 py-12">
153
+ <h1 className="text-4xl font-bold mb-8">チーム</h1>
154
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
155
+ {members.map((m) => (
156
+ <div key={m.id} className="text-center">
157
+ <h3 className="font-semibold">{m.name}</h3>
158
+ <p className="text-muted-foreground">{m.role}</p>
159
+ </div>
160
+ ))}
161
+ </div>
162
+ </div>
163
+ )
164
+ }
165
+ ```
166
+
167
+ **型安全にしたい場合:** [コード生成](#コード生成)で型付き関数を生成。
168
+
169
+ ## 静的ページ作成
170
+
171
+ CMX APIを使わない単純なページ。
172
+
173
+ ```tsx
174
+ // src/app/about/page.tsx
175
+ export const metadata = {
176
+ title: "About | CMX",
177
+ description: "About us page",
178
+ }
179
+
180
+ export default function AboutPage() {
181
+ return (
182
+ <div className="container mx-auto px-4 py-12">
183
+ <h1 className="text-4xl font-bold mb-4">About</h1>
184
+ <p>Your content here</p>
185
+ </div>
186
+ )
187
+ }
188
+ ```
189
+
190
+ ## コード生成
191
+
192
+ `cmx-sdk` CLI でワークスペースのスキーマから型付きTypeScript関数を自動生成。
193
+
194
+ ### 実行
195
+
196
+ <!-- cmx-sync:start id="skill-cmx-dev-codegen-command-block" -->
197
+ ```bash
198
+ npx cmx-sdk codegen types # デフォルト: cmx/generated/
199
+ npx cmx-sdk codegen types --output cmx/generated # 出力先を明示(通常は省略可)
200
+ ```
201
+ <!-- cmx-sync:end -->
202
+
203
+ **前提:** `.env.local`(または `.env`)に `CMX_API_URL` と `CMX_API_KEY` が設定されていること。
204
+
205
+ ### 生成されるもの
206
+
207
+ ```
208
+ cmx/generated/
209
+ ├── index.ts # 全エクスポート
210
+ ├── collections/
211
+ │ ├── blog.ts # getBlogContents(), getBlogContentDetail()
212
+ │ └── pages.ts # getPagesContents(), getPagesContentDetail()
213
+ └── data-types/
214
+ ├── team-members.ts # TeamMember型, getTeamMembers(), getTeamMemberById()
215
+ └── faq.ts # Faq型, getFaqs(), getFaqById()
216
+ ```
217
+
218
+ ### 使い方
219
+
220
+ ```tsx
221
+ // Before(汎用、型なし)
222
+ import { getDataEntries } from "cmx-sdk"
223
+ const { items } = await getDataEntries("team-members")
224
+ // items[0].name は unknown
225
+
226
+ // After(型付き、自動生成)
227
+ import { getTeamMembers, getTeamMemberById } from "@cmx/generated"
228
+ const { items } = await getTeamMembers()
229
+ // items[0].name は string ✅
230
+ const member = await getTeamMemberById("uuid")
231
+ // member.name は string ✅
232
+ ```
233
+
234
+ ### スキーマ変更時
235
+
236
+ CMX Admin でコレクションやデータ型を変更した場合は再実行:
237
+
238
+ <!-- cmx-sync:start id="skill-cmx-dev-regenerate-command-block" -->
239
+ ```bash
240
+ npx cmx-sdk codegen types
241
+ ```
242
+ <!-- cmx-sync:end -->
243
+
244
+ ## 共通パターン
245
+
246
+ ### `export const dynamic = "force-dynamic"`
247
+
248
+ 全てのCMXデータ取得ページに必須。ビルド時にAPIコールが失敗しないようにする。
249
+
250
+ ### MDXレンダリング
251
+
252
+ 常に `src/lib/mdx/render.tsx` の `renderMdx` を使用。カスタムコンポーネントが自動注入される。
253
+
254
+ ```tsx
255
+ import { renderMdx } from "@/lib/mdx/render"
256
+ const { content } = await renderMdx(content.mdx, references)
257
+ // content を JSX として直接レンダリング
258
+ ```
259
+
260
+ ### メタデータ生成
261
+
262
+ コレクションページには既存ユーティリティを使用:
263
+
264
+ ```tsx
265
+ import { generateCollectionMetadata, generateContentMetadata } from "@/lib/utils/metadata"
266
+ ```
@@ -0,0 +1,220 @@
1
+ # CMX SDK API リファレンス
2
+
3
+ ## コンテンツ取得関数
4
+
5
+ ### getCollectionContents(slug, options?)
6
+
7
+ コレクションの記事一覧を取得。
8
+
9
+ ```tsx
10
+ import { getCollectionContents } from "@/lib/api/admin-client"
11
+ // または直接: import { getCollectionContents } from "cmx-sdk"
12
+
13
+ const data = await getCollectionContents("blog", {
14
+ sortBy: "publishedAt",
15
+ sortOrder: "desc",
16
+ limit: 10,
17
+ status: "published", // デフォルト。"all" で全ステータス、"draft,review" でカンマ区切り複数指定
18
+ })
19
+ // data.collection: { name, slug, description }
20
+ // data.contents: Array<{ id, title, slug, description, publishedAt, status }>
21
+ ```
22
+
23
+ **ユースケース:**
24
+ - `status: "published"`(デフォルト): 公開済みのみ取得(フロント表示用)
25
+ - `status: "all"`: 全ステータス取得(AI執筆時の参照用)
26
+ - `status: "draft,review"`: 複数ステータス指定
27
+
28
+ ### getCollectionContentDetail(collectionSlug, contentSlug)
29
+
30
+ 記事の詳細とMDX本文・リファレンスを取得。
31
+
32
+ ```tsx
33
+ import { getCollectionContentDetail } from "@/lib/api/admin-client"
34
+
35
+ const data = await getCollectionContentDetail("blog", "my-post")
36
+ // data.collection: { name, slug }
37
+ // data.content: { id, title, slug, description, mdx, publishedAt, ... }
38
+ // data.references: { contents: {...}, assets: {...} }
39
+ ```
40
+
41
+ ### getDataEntries(typeSlug, options?)
42
+
43
+ データ型のエントリ一覧を取得。
44
+
45
+ ```tsx
46
+ import { getDataEntries } from "@/lib/api/admin-client"
47
+
48
+ const data = await getDataEntries("team-members", {
49
+ sortBy: "createdAt", // ソートフィールド
50
+ sortOrder: "desc", // "asc" | "desc"
51
+ limit: 10, // 取得件数
52
+ status: "published", // デフォルト。"all" で全ステータス
53
+ })
54
+ // data.items: Array<{ id, ...フィールド }>
55
+ ```
56
+
57
+ **ステータスフィルタ:**
58
+ - `status: "published"`(デフォルト): 公開済みのみ
59
+ - `status: "all"`: 全ステータス(draft, published)
60
+
61
+ ### getDataEntry(typeSlug, id)
62
+
63
+ データ型の単一エントリを取得。
64
+
65
+ ```tsx
66
+ import { getDataEntry } from "@/lib/api/admin-client"
67
+
68
+ const entry = await getDataEntry("team-members", "entry-uuid")
69
+ ```
70
+
71
+ ### getPreviewByToken(token)
72
+
73
+ プレビュートークンでコンテンツを取得(認証不要)。
74
+
75
+ ```tsx
76
+ import { getPreviewByToken } from "@/lib/api/admin-client"
77
+
78
+ const data = await getPreviewByToken(token)
79
+ if (data) {
80
+ // data.content: { title, mdx, status, environment, ... }
81
+ // data.collection: { name, slug }
82
+ // data.references: { contents, assets }
83
+ }
84
+ ```
85
+
86
+ ## ラッパー関数
87
+
88
+ `src/lib/utils/data-fetching.ts` のヘルパー。失敗時にエラーをthrowする。
89
+
90
+ ```tsx
91
+ import {
92
+ requireFetchContents, // getCollectionContents のラッパー
93
+ requireFetchContent, // getCollectionContentDetail のラッパー
94
+ requireDataEntries, // getDataEntries のラッパー
95
+ } from "@/lib/utils/data-fetching"
96
+
97
+ // 失敗時は Error をthrow(ページレベルでcatch)
98
+ const { collection, contents } = await requireFetchContents("blog")
99
+ const { content, references } = await requireFetchContent("blog", "my-post")
100
+ const { items } = await requireDataEntries("faq")
101
+ ```
102
+
103
+ ## MDXレンダリング
104
+
105
+ ```tsx
106
+ import { renderMdx } from "@/lib/mdx/render"
107
+
108
+ // references はAPI応答から取得したもの
109
+ const { content } = await renderMdx(item.mdx, references)
110
+
111
+ // content はReactElement → JSXに直接埋め込み可能
112
+ <div className="prose prose-lg max-w-none">{content}</div>
113
+ ```
114
+
115
+ `renderMdx` は内部で `src/components/custom/index.ts` の全エクスポートを自動注入する。
116
+
117
+ ## コレクション付属データタイプ API
118
+
119
+ ### コレクションのデータタイプ一覧(SDK API)
120
+
121
+ ```
122
+ GET /api/v1/sdk/manage/collections/{slug}/data-types
123
+ ```
124
+
125
+ ### コレクションにデータタイプを追加(SDK API)
126
+
127
+ ```
128
+ POST /api/v1/sdk/manage/collections/{slug}/data-types
129
+ Body: { "presetSlug": "categories" } // プリセットから追加
130
+ Body: { "slug": "custom", "name": "カスタム", "referenceType": "single", "fields": [...] } // カスタム
131
+ ```
132
+
133
+ ### コレクションからデータタイプを削除(SDK API)
134
+
135
+ ```
136
+ DELETE /api/v1/sdk/manage/collections/{slug}/data-types/{dtSlug}
137
+ ```
138
+
139
+ ### プリセット一覧(SDK API)
140
+
141
+ ```
142
+ GET /api/v1/sdk/manage/collection-presets?type=post
143
+ → { "recommended": [...], "others": [...] }
144
+ ```
145
+
146
+ ### コンテンツの参照取得/設定(SDK API)
147
+
148
+ ```
149
+ GET /api/v1/sdk/manage/contents/{id}/references
150
+ PUT /api/v1/sdk/manage/contents/{id}/references
151
+ Body: { "references": [{ "fieldSlug": "categories", "dataEntryIds": ["uuid"] }] }
152
+ ```
153
+
154
+ ### MCP API(AI向け)
155
+
156
+ ```
157
+ GET /api/mcp/collections/{slug}/data-types # データタイプ一覧
158
+ GET /api/mcp/collections/{slug}/data-types/{dtSlug}/entries # エントリ一覧
159
+ GET /api/mcp/collection-presets?type=post # プリセット一覧
160
+ ```
161
+
162
+ ## キャッシュタグ
163
+
164
+ Next.js のオンデマンド再検証用:
165
+
166
+ ```tsx
167
+ import { CACHE_TAGS } from "@/lib/api/admin-client"
168
+ import { revalidateTag } from "next/cache"
169
+
170
+ CACHE_TAGS.collections // 全コレクション
171
+ CACHE_TAGS.collection("blog") // 特定コレクション
172
+ CACHE_TAGS.content("blog", "slug") // 特定記事
173
+ CACHE_TAGS.data // 全データ型
174
+ CACHE_TAGS.dataType("faq") // 特定データ型
175
+
176
+ // 再検証
177
+ revalidateTag(CACHE_TAGS.collection("blog"))
178
+ ```
179
+
180
+ ## メタデータ生成
181
+
182
+ ```tsx
183
+ import {
184
+ generateCollectionMetadata, // 一覧ページ用
185
+ generateContentMetadata, // 詳細ページ用
186
+ } from "@/lib/utils/metadata"
187
+
188
+ // 一覧ページ
189
+ export async function generateMetadata() {
190
+ return generateCollectionMetadata("blog")
191
+ // → { title: "ブログ | CMX", description: "..." }
192
+ }
193
+
194
+ // 詳細ページ
195
+ export async function generateMetadata({ params }) {
196
+ const { slug } = await params
197
+ return generateContentMetadata("blog", slug)
198
+ // → { title: "記事タイトル | ブログ | CMX", description: "..." }
199
+ }
200
+ ```
201
+
202
+ ## 型インポート
203
+
204
+ ```tsx
205
+ import type {
206
+ CollectionContentsResponse,
207
+ CollectionContentDetailResponse,
208
+ DataListResponse,
209
+ DataEntryItem,
210
+ PreviewResponse,
211
+ References,
212
+ } from "cmx-sdk"
213
+
214
+ // エイリアス(admin-client.ts で定義済み)
215
+ import type {
216
+ CollectionInfo, // PublicCollectionInfo のエイリアス
217
+ ContentListItem, // PublicContentListItem のエイリアス
218
+ ContentDetail, // PublicContentDetail のエイリアス
219
+ } from "@/lib/api/admin-client"
220
+ ```
@@ -0,0 +1,54 @@
1
+ # cmx-sdk CLI リファレンス
2
+
3
+ ## コード生成
4
+
5
+ <!-- cmx-sync:start id="skill-cmx-dev-cli-codegen-table" -->
6
+ | コマンド | 説明 | 冪等性 |
7
+ |---------|------|--------|
8
+ | `npx cmx-sdk codegen types` | スキーマから TypeScript 型・取得関数を生成(`cmx/generated/`) | はい(何度でも再実行可能) |
9
+ | `npx cmx-sdk codegen types --output <dir>` | 出力先を明示指定 | はい |
10
+ | `npx cmx-sdk codegen pages --template layered` | ページ雛形を生成(`src/app/`, `src/features/`) | 既存ファイルは上書きしない |
11
+ | `npx cmx-sdk codegen pages --template layered --only {category}:{slug}` | 特定のコレクション/データタイプ/フォームのみ生成 | 既存ファイルは上書きしない |
12
+ <!-- cmx-sync:end -->
13
+
14
+ **category** は `collections`, `data-types`, `forms` のいずれか。
15
+
16
+ ## スキーマ管理
17
+
18
+ | コマンド | 説明 |
19
+ |---------|------|
20
+ | `npx cmx-sdk list-collections` | コレクション一覧 |
21
+ | `npx cmx-sdk list-data-types` | グローバルデータタイプ一覧 |
22
+ | `npx cmx-sdk list-forms` | フォーム一覧 |
23
+ | `npx cmx-sdk list-collection-data-types --collection {slug}` | コレクション付属データタイプ一覧 |
24
+ | `npx cmx-sdk list-collection-presets --type {type}` | プリセット一覧 |
25
+ | `npx cmx-sdk create-collection --json '{...}'` | コレクション作成 |
26
+ | `npx cmx-sdk create-data-type --json '{...}'` | グローバルデータタイプ作成 |
27
+ | `npx cmx-sdk create-form --json '{...}'` | フォーム作成 |
28
+ | `npx cmx-sdk add-collection-data-type --collection {slug} --preset {preset}` | 付属データタイプ追加(プリセット) |
29
+ | `npx cmx-sdk add-collection-data-type --collection {slug} --json '{...}'` | 付属データタイプ追加(カスタム) |
30
+
31
+ ## コンテンツ管理
32
+
33
+ | コマンド | 説明 |
34
+ |---------|------|
35
+ | `npx cmx-sdk create-content --collection {slug} --json '{...}'` | コンテンツ作成(draft ステータス) |
36
+ | `npx cmx-sdk create-content --collection {slug} --file {path}` | ファイルから作成 |
37
+ | `npx cmx-sdk request-review-content --id {contentId}` | レビュー依頼(draft → review) |
38
+ | `npx cmx-sdk publish-content --id {contentId}` | 公開(review → published) |
39
+ | `npx cmx-sdk set-content-references --id {contentId} --json '{...}'` | 参照設定(カテゴリ・タグ) |
40
+ | `npx cmx-sdk create-data-entry --type-slug {slug} --json '{...}'` | データタイプエントリ作成 |
41
+
42
+ ## コンポーネント
43
+
44
+ <!-- cmx-sync:start id="skill-cmx-dev-cli-components-table" -->
45
+ | コマンド | 説明 |
46
+ |---------|------|
47
+ | `npx cmx-sdk components scaffold --name {Name}` | コンポーネント雛形生成 |
48
+ | `npx cmx-sdk sync-components` | `cmx/components/` の JSON 定義を Admin に同期 |
49
+ <!-- cmx-sync:end -->
50
+
51
+ ## 前提
52
+
53
+ - `.env.local`(または `.env`)に `CMX_API_KEY` と `CMX_API_URL` が設定されていること
54
+ - `npx cmx-sdk` はプロジェクトルートから実行すること
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: cmx-form
3
+ description: |
4
+ CMX Starter Kit のフォーム実装スキル。Admin 側フォーム定義からフロント UI、submissions API 送信、バリデーション、スパム対策までの一貫ワークフロー。
5
+ トリガー: 「フォームを作りたい」「お問い合わせ」「コンタクトフォーム」「送信機能」
6
+ 「フォームを追加」「submissions」「フォームバリデーション」など。
7
+ ---
8
+
9
+ # CMX フォーム実装
10
+
11
+ ## ワークフロー
12
+
13
+ ### 1. フォーム設計
14
+
15
+ ユーザーに確認:
16
+ - 用途(お問い合わせ、資料請求、予約等)
17
+ - 必要なフィールドと種類
18
+ - バリデーション要件
19
+ - 送信後の挙動(サンクスメッセージ、リダイレクト)
20
+
21
+ ### 2. Admin 側フォーム定義
22
+
23
+ **2-1. 既存フォームの確認**
24
+
25
+ まず既存のフォーム定義を確認して、重複がないことを確認:
26
+
27
+ ```bash
28
+ npx cmx-sdk list-forms
29
+ ```
30
+
31
+ **2-2. JSON生成とユーザー確認**
32
+
33
+ フォーム定義 JSON を生成し、ユーザーに確認:
34
+
35
+ ```
36
+ 以下のフォームを Admin に登録します:
37
+
38
+ - 名前: {name}
39
+ - スラッグ: {slug}
40
+ - フィールド数: {n}
41
+
42
+ 重複はありません。こちらで登録してもよろしいですか?
43
+ ```
44
+
45
+ **2-3. cmx-sdk で登録**
46
+
47
+ 承認されたら、`cmx-sdk` コマンドで登録:
48
+
49
+ ```bash
50
+ npx cmx-sdk create-form --json '{
51
+ "slug": "contact",
52
+ "name": "お問い合わせ",
53
+ "description": "お問い合わせフォーム",
54
+ "fields": [
55
+ {"key": "name", "label": "お名前", "type": "text", "required": true},
56
+ {"key": "email", "label": "メールアドレス", "type": "email", "required": true},
57
+ {"key": "message", "label": "メッセージ", "type": "textarea", "required": true}
58
+ ]
59
+ }'
60
+ ```
61
+
62
+ ### 3. フロント実装
63
+
64
+ クライアントコンポーネント(`"use client"`)として作成。
65
+
66
+ テンプレート: [references/form-template.md](references/form-template.md)
67
+
68
+ 実装ポイント:
69
+ - `useState` でフォーム状態・送信状態・エラーを管理
70
+ - Zod スキーマでクライアントバリデーション
71
+ - ハニーポットフィールド(`_hp`)を隠しフィールドとして設置
72
+ - `POST /api/v1/sdk/submissions/{formSlug}` に送信
73
+ - 送信は `cmx-sdk` の関数 or 直接 fetch(APIキーは不要、公開エンドポイント)
74
+
75
+ ### 4. 送信処理
76
+
77
+ ```tsx
78
+ const response = await fetch(`${process.env.NEXT_PUBLIC_CMX_API_URL}/api/v1/sdk/submissions/${formSlug}`, {
79
+ method: "POST",
80
+ headers: { "Content-Type": "application/json" },
81
+ body: JSON.stringify({ ...formData, _hp: honeypotValue }),
82
+ })
83
+ ```
84
+
85
+ - `_hp` フィールドが空でない場合、サーバー側でスパムとして弾く
86
+ - 成功: 200 → サンクスメッセージ表示
87
+ - エラー: 4xx/5xx → エラーメッセージ表示
88
+
89
+ ### 5. 動作確認
90
+
91
+ - フォーム送信テスト
92
+ - Admin 側で送信一覧に表示されることを確認
93
+ - バリデーションエラーの表示を確認
94
+ - ハニーポットが機能することを確認
95
+
96
+ ## チェックリスト
97
+
98
+ - [ ] クライアントコンポーネント(`"use client"`)として作成
99
+ - [ ] Zod バリデーション
100
+ - [ ] ハニーポットフィールド(`_hp`)設置
101
+ - [ ] 送信中・成功・エラーの UI 状態
102
+ - [ ] site-config.md のトーン・デザインに沿ったスタイリング
103
+ - [ ] アクセシビリティ(label、aria、エラーメッセージの関連付け)