payload-mcp-toolkit 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,55 +32,47 @@
32
32
  return map;
33
33
  }
34
34
  /**
35
- * Introspect block configs into a block catalog with section/leaf hierarchy and nesting rules.
36
- *
37
- * NOTE: In the Payload plugin context, blockReferences may or may not be resolved
38
- * depending on when introspection runs. This function handles both cases:
39
- * - If field.blocks contains resolved block objects, reads slugs from them
40
- * - If field.blocks is empty and field.blockReferences exists, uses those slugs directly
41
- */ export function introspectBlocks(sectionBlocks, leafBlocks) {
42
- const leafSlugs = new Set(leafBlocks.map((b)=>b.slug));
43
- const sections = sectionBlocks.map((section)=>{
44
- const blockFields = findBlockFields(section.fields);
45
- if (blockFields.length === 0) {
46
- // Fixed section — no nested blocks
47
- return {
48
- slug: section.slug,
49
- nestingType: 'fixed',
50
- acceptedLeafSlugs: [],
51
- fields: extractFields(section.fields)
52
- };
53
- }
54
- // Determine accepted leaf slugs from all blocks-type fields
55
- const acceptedSlugs = new Set();
56
- let maxRows;
57
- for (const bf of blockFields){
58
- const slugs = getBlockSlugsFromField(bf);
59
- for (const s of slugs){
60
- if (leafSlugs.has(s)) acceptedSlugs.add(s);
61
- }
62
- if (bf.maxRows) maxRows = bf.maxRows;
63
- }
64
- const nestingType = acceptedSlugs.size < leafSlugs.size || maxRows ? 'constrained' : 'composable';
65
- return {
66
- slug: section.slug,
67
- nestingType,
68
- acceptedLeafSlugs: [
69
- ...acceptedSlugs
70
- ],
71
- maxRows,
72
- fields: extractFields(section.fields)
73
- };
74
- });
75
- const leaves = leafBlocks.map((leaf)=>({
76
- slug: leaf.slug,
77
- fields: extractFields(leaf.fields)
35
+ * Build a flat catalog of every block in the schema. Whether a block can
36
+ * nest other blocks is represented separately in the BlockNestingMap, not
37
+ * as a section/leaf classification the AI reads both and composes
38
+ * arbitrarily-nested layouts from there.
39
+ */ export function introspectBlocks(blocks) {
40
+ const catalog = blocks.map((block)=>({
41
+ slug: block.slug,
42
+ fields: extractFields(block.fields)
78
43
  }));
79
44
  return {
80
- sections,
81
- leaves
45
+ blocks: catalog
82
46
  };
83
47
  }
48
+ /**
49
+ * Walk every collection and every block, recording each `blocks`-typed
50
+ * field's owner, dotted path, and accepted slugs.
51
+ *
52
+ * The AI uses this to compose layouts at any depth: it looks up which
53
+ * slugs the relevant field accepts, picks one, then if that block has
54
+ * its own `blocks` fields it recurses against the same map.
55
+ */ export function buildBlockNestingMap(collections, blocks) {
56
+ const knownSlugs = new Set(blocks.map((b)=>b.slug));
57
+ const edges = [];
58
+ for (const collection of collections){
59
+ edges.push(...collectBlocksFieldEdges(collection.fields, {
60
+ owner: collection.slug,
61
+ ownerType: 'collection',
62
+ prefix: '',
63
+ knownSlugs
64
+ }));
65
+ }
66
+ for (const block of blocks){
67
+ edges.push(...collectBlocksFieldEdges(block.fields, {
68
+ owner: block.slug,
69
+ ownerType: 'block',
70
+ prefix: '',
71
+ knownSlugs
72
+ }));
73
+ }
74
+ return edges;
75
+ }
84
76
  /**
85
77
  * Build a relationship graph from introspected collection schemas.
86
78
  */ export function buildRelationshipGraph(schemas) {
@@ -111,9 +103,10 @@
111
103
  };
112
104
  if ('required' in field && field.required) schema.required = true;
113
105
  if ('hasMany' in field && field.hasMany) schema.hasMany = true;
114
- if ('relationTo' in field && field.relationTo) schema.relationTo = field.relationTo;
106
+ if ('relationTo' in field && field.relationTo) {
107
+ schema.relationTo = field.relationTo;
108
+ }
115
109
  if ('maxRows' in field && field.maxRows) schema.maxRows = field.maxRows;
116
- // Extract select options
117
110
  if (field.type === 'select' && 'options' in field && Array.isArray(field.options)) {
118
111
  schema.options = field.options.map((opt)=>typeof opt === 'string' ? {
119
112
  label: opt,
@@ -123,7 +116,6 @@
123
116
  value: String(opt.value)
124
117
  });
125
118
  }
126
- // Recurse into nested fields (arrays, groups)
127
119
  if (field.type === 'array' && 'fields' in field) {
128
120
  schema.fields = extractFields(field.fields);
129
121
  }
@@ -132,7 +124,6 @@
132
124
  }
133
125
  result.push(schema);
134
126
  }
135
- // Transparent containers — recurse without creating a named field
136
127
  if (field.type === 'tabs' && 'tabs' in field) {
137
128
  for (const tab of field.tabs){
138
129
  if ('fields' in tab) {
@@ -169,7 +160,6 @@
169
160
  hasMany: !!('hasMany' in field && field.hasMany)
170
161
  });
171
162
  }
172
- // Recurse into containers
173
163
  if (field.type === 'tabs' && 'tabs' in field) {
174
164
  for (const tab of field.tabs){
175
165
  if ('fields' in tab) {
@@ -193,42 +183,76 @@
193
183
  return rels;
194
184
  }
195
185
  /**
196
- * Find all blocks-type fields within a field array (recursing into tabs/rows/etc).
197
- */ function findBlockFields(fields) {
198
- const result = [];
186
+ * Walk fields recording every `blocks`-typed field encountered, including
187
+ * those nested in tabs/rows/groups/arrays/collapsibles. Each entry carries
188
+ * the dotted path from the owner root to the field.
189
+ */ function collectBlocksFieldEdges(fields, ctx) {
190
+ const edges = [];
199
191
  for (const field of fields){
200
192
  if (field.type === 'blocks') {
201
- result.push(field);
193
+ const fieldName = 'name' in field && field.name ? field.name : '';
194
+ const fullPath = ctx.prefix ? `${ctx.prefix}.${fieldName}` : fieldName;
195
+ const allSlugs = readBlockSlugs(field);
196
+ const acceptedSlugs = allSlugs.filter((s)=>ctx.knownSlugs.has(s));
197
+ const edge = {
198
+ owner: ctx.owner,
199
+ ownerType: ctx.ownerType,
200
+ fieldPath: fullPath,
201
+ acceptedBlockSlugs: acceptedSlugs
202
+ };
203
+ const maxRows = field.maxRows;
204
+ if (typeof maxRows === 'number') edge.maxRows = maxRows;
205
+ edges.push(edge);
206
+ continue;
202
207
  }
203
208
  if (field.type === 'tabs' && 'tabs' in field) {
204
209
  for (const tab of field.tabs){
205
- if ('fields' in tab) {
206
- result.push(...findBlockFields(tab.fields));
207
- }
210
+ if (!('fields' in tab)) continue;
211
+ const tabName = 'name' in tab && tab.name ? tab.name : '';
212
+ const tabPrefix = tabName ? ctx.prefix ? `${ctx.prefix}.${tabName}` : tabName : ctx.prefix;
213
+ edges.push(...collectBlocksFieldEdges(tab.fields, {
214
+ ...ctx,
215
+ prefix: tabPrefix
216
+ }));
208
217
  }
218
+ continue;
209
219
  }
210
220
  if (field.type === 'row' && 'fields' in field) {
211
- result.push(...findBlockFields(field.fields));
221
+ edges.push(...collectBlocksFieldEdges(field.fields, ctx));
222
+ continue;
212
223
  }
213
224
  if (field.type === 'collapsible' && 'fields' in field) {
214
- result.push(...findBlockFields(field.fields));
225
+ edges.push(...collectBlocksFieldEdges(field.fields, ctx));
226
+ continue;
215
227
  }
216
- if (field.type === 'group' && 'fields' in field) {
217
- result.push(...findBlockFields(field.fields));
228
+ if (field.type === 'group' && 'fields' in field && 'name' in field && field.name) {
229
+ const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}` : field.name;
230
+ edges.push(...collectBlocksFieldEdges(field.fields, {
231
+ ...ctx,
232
+ prefix: newPrefix
233
+ }));
234
+ continue;
235
+ }
236
+ if (field.type === 'array' && 'fields' in field && 'name' in field && field.name) {
237
+ const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}[]` : `${field.name}[]`;
238
+ edges.push(...collectBlocksFieldEdges(field.fields, {
239
+ ...ctx,
240
+ prefix: newPrefix
241
+ }));
242
+ continue;
218
243
  }
219
244
  }
220
- return result;
245
+ return edges;
221
246
  }
222
247
  /**
223
- * Get block slugs from a blocks-type field.
224
- * Handles both resolved blocks (field.blocks has objects) and unresolved blockReferences.
225
- */ function getBlockSlugsFromField(field) {
248
+ * Read block slugs from a blocks-typed field, handling both resolved
249
+ * (field.blocks contains objects) and unresolved (field.blockReferences
250
+ * holds slug strings) forms.
251
+ */ function readBlockSlugs(field) {
226
252
  const f = field;
227
- // Resolved blocks field.blocks contains full block objects
228
- if (Array.isArray(f.blocks) && f.blocks.length > 0 && typeof f.blocks[0] === 'object' && f.blocks[0].slug) {
253
+ if (Array.isArray(f.blocks) && f.blocks.length > 0 && typeof f.blocks[0] === 'object' && f.blocks[0]?.slug) {
229
254
  return f.blocks.map((b)=>b.slug);
230
255
  }
231
- // Unresolved — blockReferences contains slug strings
232
256
  if (Array.isArray(f.blockReferences) && f.blockReferences.length > 0) {
233
257
  return f.blockReferences.filter((ref)=>typeof ref === 'string');
234
258
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/introspection.ts"],"sourcesContent":["import type { Block, CollectionConfig, Field, GlobalConfig } from 'payload'\nimport type {\n BlockCatalog,\n BlockNestingType,\n CollectionSchema,\n FieldSchema,\n LeafBlockSchema,\n RelationshipEdge,\n SectionBlockSchema,\n} from './types'\n\n/**\n * Introspect a Payload collection config into structured metadata.\n */\nexport function introspectCollection(collection: CollectionConfig): CollectionSchema {\n const fields = extractFields(collection.fields)\n const relationships = extractRelationships(collection.fields)\n const searchableFields = fields\n .filter((f) => ['text', 'email'].includes(f.type) && ['name', 'title', 'slug'].includes(f.name))\n .map((f) => f.name)\n\n const hasDrafts = !!(collection.versions && typeof collection.versions === 'object' && 'drafts' in collection.versions && collection.versions.drafts)\n const hasLivePreview = !!(collection.admin && typeof collection.admin === 'object' && 'livePreview' in collection.admin && collection.admin.livePreview)\n\n return {\n slug: collection.slug,\n fields,\n hasDrafts,\n hasLivePreview,\n relationships,\n searchableFields,\n }\n}\n\n/**\n * Introspect all collections into a map keyed by slug.\n */\nexport function introspectCollections(\n collections: CollectionConfig[],\n): Map<string, CollectionSchema> {\n const map = new Map<string, CollectionSchema>()\n for (const collection of collections) {\n map.set(collection.slug, introspectCollection(collection))\n }\n return map\n}\n\n/**\n * Introspect block configs into a block catalog with section/leaf hierarchy and nesting rules.\n *\n * NOTE: In the Payload plugin context, blockReferences may or may not be resolved\n * depending on when introspection runs. This function handles both cases:\n * - If field.blocks contains resolved block objects, reads slugs from them\n * - If field.blocks is empty and field.blockReferences exists, uses those slugs directly\n */\nexport function introspectBlocks(\n sectionBlocks: Block[],\n leafBlocks: Block[],\n): BlockCatalog {\n const leafSlugs = new Set(leafBlocks.map((b) => b.slug))\n\n const sections: SectionBlockSchema[] = sectionBlocks.map((section) => {\n const blockFields = findBlockFields(section.fields)\n\n if (blockFields.length === 0) {\n // Fixed section — no nested blocks\n return {\n slug: section.slug,\n nestingType: 'fixed' as BlockNestingType,\n acceptedLeafSlugs: [],\n fields: extractFields(section.fields),\n }\n }\n\n // Determine accepted leaf slugs from all blocks-type fields\n const acceptedSlugs = new Set<string>()\n let maxRows: number | undefined\n\n for (const bf of blockFields) {\n const slugs = getBlockSlugsFromField(bf)\n for (const s of slugs) {\n if (leafSlugs.has(s)) acceptedSlugs.add(s)\n }\n if (bf.maxRows) maxRows = bf.maxRows\n }\n\n const nestingType: BlockNestingType =\n acceptedSlugs.size < leafSlugs.size || maxRows ? 'constrained' : 'composable'\n\n return {\n slug: section.slug,\n nestingType,\n acceptedLeafSlugs: [...acceptedSlugs],\n maxRows,\n fields: extractFields(section.fields),\n }\n })\n\n const leaves: LeafBlockSchema[] = leafBlocks.map((leaf) => ({\n slug: leaf.slug,\n fields: extractFields(leaf.fields),\n }))\n\n return { sections, leaves }\n}\n\n/**\n * Build a relationship graph from introspected collection schemas.\n */\nexport function buildRelationshipGraph(\n schemas: Map<string, CollectionSchema>,\n): RelationshipEdge[] {\n const edges: RelationshipEdge[] = []\n for (const [slug, schema] of schemas) {\n for (const rel of schema.relationships) {\n edges.push({\n fromCollection: slug,\n fieldName: rel.fieldName,\n toCollection: rel.relationTo,\n hasMany: rel.hasMany,\n })\n }\n }\n return edges\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────\n\n/**\n * Recursively extract field metadata from a Payload fields array.\n * Handles tabs, groups, arrays, rows, and collapsible containers.\n */\nfunction extractFields(fields: Field[]): FieldSchema[] {\n const result: FieldSchema[] = []\n\n for (const field of fields) {\n if ('name' in field && field.name) {\n const schema: FieldSchema = {\n name: field.name,\n type: field.type,\n }\n\n if ('required' in field && field.required) schema.required = true\n if ('hasMany' in field && field.hasMany) schema.hasMany = true\n if ('relationTo' in field && field.relationTo) schema.relationTo = field.relationTo as string | string[]\n if ('maxRows' in field && field.maxRows) schema.maxRows = field.maxRows\n\n // Extract select options\n if (field.type === 'select' && 'options' in field && Array.isArray(field.options)) {\n schema.options = field.options.map((opt) =>\n typeof opt === 'string' ? { label: opt, value: opt } : { label: String(opt.label), value: String(opt.value) },\n )\n }\n\n // Recurse into nested fields (arrays, groups)\n if (field.type === 'array' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n if (field.type === 'group' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n\n result.push(schema)\n }\n\n // Transparent containers — recurse without creating a named field\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n result.push(...extractFields(tab.fields))\n }\n }\n }\n if (field.type === 'row' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n }\n\n return result\n}\n\n/**\n * Extract relationship metadata from fields (for the relationship graph).\n */\nfunction extractRelationships(\n fields: Field[],\n prefix = '',\n): Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> {\n const rels: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> = []\n\n for (const field of fields) {\n const fieldName = 'name' in field && field.name ? `${prefix}${field.name}` : prefix\n\n if (field.type === 'relationship' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string | string[],\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'upload' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string,\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n // Recurse into containers\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n rels.push(...extractRelationships(tab.fields, prefix))\n }\n }\n }\n if (field.type === 'group' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}.`))\n }\n if (field.type === 'array' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}[].`))\n }\n if (field.type === 'row' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n }\n\n return rels\n}\n\n/**\n * Find all blocks-type fields within a field array (recursing into tabs/rows/etc).\n */\nfunction findBlockFields(fields: Field[]): Array<Field & { type: 'blocks' }> {\n const result: Array<Field & { type: 'blocks' }> = []\n\n for (const field of fields) {\n if (field.type === 'blocks') {\n result.push(field as Field & { type: 'blocks' })\n }\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n result.push(...findBlockFields(tab.fields))\n }\n }\n }\n if (field.type === 'row' && 'fields' in field) {\n result.push(...findBlockFields(field.fields))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n result.push(...findBlockFields(field.fields))\n }\n if (field.type === 'group' && 'fields' in field) {\n result.push(...findBlockFields(field.fields))\n }\n }\n\n return result\n}\n\n/**\n * Get block slugs from a blocks-type field.\n * Handles both resolved blocks (field.blocks has objects) and unresolved blockReferences.\n */\nfunction getBlockSlugsFromField(field: Field & { type: 'blocks' }): string[] {\n const f = field as any\n\n // Resolved blocks — field.blocks contains full block objects\n if (Array.isArray(f.blocks) && f.blocks.length > 0 && typeof f.blocks[0] === 'object' && f.blocks[0].slug) {\n return f.blocks.map((b: { slug: string }) => b.slug)\n }\n\n // Unresolved — blockReferences contains slug strings\n if (Array.isArray(f.blockReferences) && f.blockReferences.length > 0) {\n return f.blockReferences.filter((ref: unknown) => typeof ref === 'string') as string[]\n }\n\n return []\n}\n"],"names":["introspectCollection","collection","fields","extractFields","relationships","extractRelationships","searchableFields","filter","f","includes","type","name","map","hasDrafts","versions","drafts","hasLivePreview","admin","livePreview","slug","introspectCollections","collections","Map","set","introspectBlocks","sectionBlocks","leafBlocks","leafSlugs","Set","b","sections","section","blockFields","findBlockFields","length","nestingType","acceptedLeafSlugs","acceptedSlugs","maxRows","bf","slugs","getBlockSlugsFromField","s","has","add","size","leaves","leaf","buildRelationshipGraph","schemas","edges","schema","rel","push","fromCollection","fieldName","toCollection","relationTo","hasMany","result","field","required","Array","isArray","options","opt","label","value","String","tab","tabs","prefix","rels","blocks","blockReferences","ref"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/introspection.ts"],"sourcesContent":["import type { Block, CollectionConfig, Field } from 'payload'\nimport type {\n BlockCatalog,\n BlockNestingMap,\n BlockSchema,\n CollectionSchema,\n FieldSchema,\n RelationshipEdge,\n} from './types'\n\n/**\n * Introspect a Payload collection config into structured metadata.\n */\nexport function introspectCollection(collection: CollectionConfig): CollectionSchema {\n const fields = extractFields(collection.fields)\n const relationships = extractRelationships(collection.fields)\n const searchableFields = fields\n .filter((f) => ['text', 'email'].includes(f.type) && ['name', 'title', 'slug'].includes(f.name))\n .map((f) => f.name)\n\n const hasDrafts = !!(\n collection.versions &&\n typeof collection.versions === 'object' &&\n 'drafts' in collection.versions &&\n collection.versions.drafts\n )\n const hasLivePreview = !!(\n collection.admin &&\n typeof collection.admin === 'object' &&\n 'livePreview' in collection.admin &&\n collection.admin.livePreview\n )\n\n return {\n slug: collection.slug,\n fields,\n hasDrafts,\n hasLivePreview,\n relationships,\n searchableFields,\n }\n}\n\n/**\n * Introspect all collections into a map keyed by slug.\n */\nexport function introspectCollections(\n collections: CollectionConfig[],\n): Map<string, CollectionSchema> {\n const map = new Map<string, CollectionSchema>()\n for (const collection of collections) {\n map.set(collection.slug, introspectCollection(collection))\n }\n return map\n}\n\n/**\n * Build a flat catalog of every block in the schema. Whether a block can\n * nest other blocks is represented separately in the BlockNestingMap, not\n * as a section/leaf classification — the AI reads both and composes\n * arbitrarily-nested layouts from there.\n */\nexport function introspectBlocks(blocks: Block[]): BlockCatalog {\n const catalog: BlockSchema[] = blocks.map((block) => ({\n slug: block.slug,\n fields: extractFields(block.fields),\n }))\n return { blocks: catalog }\n}\n\n/**\n * Walk every collection and every block, recording each `blocks`-typed\n * field's owner, dotted path, and accepted slugs.\n *\n * The AI uses this to compose layouts at any depth: it looks up which\n * slugs the relevant field accepts, picks one, then if that block has\n * its own `blocks` fields it recurses against the same map.\n */\nexport function buildBlockNestingMap(\n collections: CollectionConfig[],\n blocks: Block[],\n): BlockNestingMap {\n const knownSlugs = new Set(blocks.map((b) => b.slug))\n const edges: BlockNestingMap = []\n\n for (const collection of collections) {\n edges.push(\n ...collectBlocksFieldEdges(collection.fields, {\n owner: collection.slug,\n ownerType: 'collection',\n prefix: '',\n knownSlugs,\n }),\n )\n }\n\n for (const block of blocks) {\n edges.push(\n ...collectBlocksFieldEdges(block.fields, {\n owner: block.slug,\n ownerType: 'block',\n prefix: '',\n knownSlugs,\n }),\n )\n }\n\n return edges\n}\n\n/**\n * Build a relationship graph from introspected collection schemas.\n */\nexport function buildRelationshipGraph(\n schemas: Map<string, CollectionSchema>,\n): RelationshipEdge[] {\n const edges: RelationshipEdge[] = []\n for (const [slug, schema] of schemas) {\n for (const rel of schema.relationships) {\n edges.push({\n fromCollection: slug,\n fieldName: rel.fieldName,\n toCollection: rel.relationTo,\n hasMany: rel.hasMany,\n })\n }\n }\n return edges\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────\n\n/**\n * Recursively extract field metadata from a Payload fields array.\n * Handles tabs, groups, arrays, rows, and collapsible containers.\n */\nfunction extractFields(fields: Field[]): FieldSchema[] {\n const result: FieldSchema[] = []\n\n for (const field of fields) {\n if ('name' in field && field.name) {\n const schema: FieldSchema = {\n name: field.name,\n type: field.type,\n }\n\n if ('required' in field && field.required) schema.required = true\n if ('hasMany' in field && field.hasMany) schema.hasMany = true\n if ('relationTo' in field && field.relationTo) {\n schema.relationTo = field.relationTo as string | string[]\n }\n if ('maxRows' in field && field.maxRows) schema.maxRows = field.maxRows\n\n if (field.type === 'select' && 'options' in field && Array.isArray(field.options)) {\n schema.options = field.options.map((opt) =>\n typeof opt === 'string'\n ? { label: opt, value: opt }\n : { label: String(opt.label), value: String(opt.value) },\n )\n }\n\n if (field.type === 'array' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n if (field.type === 'group' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n\n result.push(schema)\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n result.push(...extractFields(tab.fields))\n }\n }\n }\n if (field.type === 'row' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n }\n\n return result\n}\n\n/**\n * Extract relationship metadata from fields (for the relationship graph).\n */\nfunction extractRelationships(\n fields: Field[],\n prefix = '',\n): Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> {\n const rels: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> = []\n\n for (const field of fields) {\n const fieldName = 'name' in field && field.name ? `${prefix}${field.name}` : prefix\n\n if (field.type === 'relationship' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string | string[],\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'upload' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string,\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n rels.push(...extractRelationships(tab.fields, prefix))\n }\n }\n }\n if (field.type === 'group' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}.`))\n }\n if (field.type === 'array' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}[].`))\n }\n if (field.type === 'row' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n }\n\n return rels\n}\n\ninterface NestingScanContext {\n owner: string\n ownerType: 'collection' | 'block'\n prefix: string\n knownSlugs: Set<string>\n}\n\n/**\n * Walk fields recording every `blocks`-typed field encountered, including\n * those nested in tabs/rows/groups/arrays/collapsibles. Each entry carries\n * the dotted path from the owner root to the field.\n */\nfunction collectBlocksFieldEdges(fields: Field[], ctx: NestingScanContext): BlockNestingMap {\n const edges: BlockNestingMap = []\n\n for (const field of fields) {\n if (field.type === 'blocks') {\n const fieldName = 'name' in field && field.name ? field.name : ''\n const fullPath = ctx.prefix ? `${ctx.prefix}.${fieldName}` : fieldName\n const allSlugs = readBlockSlugs(field as Field & { type: 'blocks' })\n const acceptedSlugs = allSlugs.filter((s) => ctx.knownSlugs.has(s))\n\n const edge: BlockNestingMap[number] = {\n owner: ctx.owner,\n ownerType: ctx.ownerType,\n fieldPath: fullPath,\n acceptedBlockSlugs: acceptedSlugs,\n }\n const maxRows = (field as Field & { type: 'blocks' }).maxRows\n if (typeof maxRows === 'number') edge.maxRows = maxRows\n edges.push(edge)\n continue\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if (!('fields' in tab)) continue\n const tabName = 'name' in tab && tab.name ? tab.name : ''\n const tabPrefix = tabName\n ? ctx.prefix\n ? `${ctx.prefix}.${tabName}`\n : tabName\n : ctx.prefix\n edges.push(...collectBlocksFieldEdges(tab.fields, { ...ctx, prefix: tabPrefix }))\n }\n continue\n }\n if (field.type === 'row' && 'fields' in field) {\n edges.push(...collectBlocksFieldEdges(field.fields, ctx))\n continue\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n edges.push(...collectBlocksFieldEdges(field.fields, ctx))\n continue\n }\n if (field.type === 'group' && 'fields' in field && 'name' in field && field.name) {\n const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}` : field.name\n edges.push(...collectBlocksFieldEdges(field.fields, { ...ctx, prefix: newPrefix }))\n continue\n }\n if (field.type === 'array' && 'fields' in field && 'name' in field && field.name) {\n const newPrefix = ctx.prefix ? `${ctx.prefix}.${field.name}[]` : `${field.name}[]`\n edges.push(...collectBlocksFieldEdges(field.fields, { ...ctx, prefix: newPrefix }))\n continue\n }\n }\n\n return edges\n}\n\n/**\n * Read block slugs from a blocks-typed field, handling both resolved\n * (field.blocks contains objects) and unresolved (field.blockReferences\n * holds slug strings) forms.\n */\nfunction readBlockSlugs(field: Field & { type: 'blocks' }): string[] {\n const f = field as any\n\n if (\n Array.isArray(f.blocks) &&\n f.blocks.length > 0 &&\n typeof f.blocks[0] === 'object' &&\n f.blocks[0]?.slug\n ) {\n return f.blocks.map((b: { slug: string }) => b.slug)\n }\n\n if (Array.isArray(f.blockReferences) && f.blockReferences.length > 0) {\n return f.blockReferences.filter((ref: unknown) => typeof ref === 'string') as string[]\n }\n\n return []\n}\n"],"names":["introspectCollection","collection","fields","extractFields","relationships","extractRelationships","searchableFields","filter","f","includes","type","name","map","hasDrafts","versions","drafts","hasLivePreview","admin","livePreview","slug","introspectCollections","collections","Map","set","introspectBlocks","blocks","catalog","block","buildBlockNestingMap","knownSlugs","Set","b","edges","push","collectBlocksFieldEdges","owner","ownerType","prefix","buildRelationshipGraph","schemas","schema","rel","fromCollection","fieldName","toCollection","relationTo","hasMany","result","field","required","maxRows","Array","isArray","options","opt","label","value","String","tab","tabs","rels","ctx","fullPath","allSlugs","readBlockSlugs","acceptedSlugs","s","has","edge","fieldPath","acceptedBlockSlugs","tabName","tabPrefix","newPrefix","length","blockReferences","ref"],"mappings":"AAUA;;CAEC,GACD,OAAO,SAASA,qBAAqBC,UAA4B;IAC/D,MAAMC,SAASC,cAAcF,WAAWC,MAAM;IAC9C,MAAME,gBAAgBC,qBAAqBJ,WAAWC,MAAM;IAC5D,MAAMI,mBAAmBJ,OACtBK,MAAM,CAAC,CAACC,IAAM;YAAC;YAAQ;SAAQ,CAACC,QAAQ,CAACD,EAAEE,IAAI,KAAK;YAAC;YAAQ;YAAS;SAAO,CAACD,QAAQ,CAACD,EAAEG,IAAI,GAC7FC,GAAG,CAAC,CAACJ,IAAMA,EAAEG,IAAI;IAEpB,MAAME,YAAY,CAAC,CACjBZ,CAAAA,WAAWa,QAAQ,IACnB,OAAOb,WAAWa,QAAQ,KAAK,YAC/B,YAAYb,WAAWa,QAAQ,IAC/Bb,WAAWa,QAAQ,CAACC,MAAM,AAAD;IAE3B,MAAMC,iBAAiB,CAAC,CACtBf,CAAAA,WAAWgB,KAAK,IAChB,OAAOhB,WAAWgB,KAAK,KAAK,YAC5B,iBAAiBhB,WAAWgB,KAAK,IACjChB,WAAWgB,KAAK,CAACC,WAAW,AAAD;IAG7B,OAAO;QACLC,MAAMlB,WAAWkB,IAAI;QACrBjB;QACAW;QACAG;QACAZ;QACAE;IACF;AACF;AAEA;;CAEC,GACD,OAAO,SAASc,sBACdC,WAA+B;IAE/B,MAAMT,MAAM,IAAIU;IAChB,KAAK,MAAMrB,cAAcoB,YAAa;QACpCT,IAAIW,GAAG,CAACtB,WAAWkB,IAAI,EAAEnB,qBAAqBC;IAChD;IACA,OAAOW;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASY,iBAAiBC,MAAe;IAC9C,MAAMC,UAAyBD,OAAOb,GAAG,CAAC,CAACe,QAAW,CAAA;YACpDR,MAAMQ,MAAMR,IAAI;YAChBjB,QAAQC,cAAcwB,MAAMzB,MAAM;QACpC,CAAA;IACA,OAAO;QAAEuB,QAAQC;IAAQ;AAC3B;AAEA;;;;;;;CAOC,GACD,OAAO,SAASE,qBACdP,WAA+B,EAC/BI,MAAe;IAEf,MAAMI,aAAa,IAAIC,IAAIL,OAAOb,GAAG,CAAC,CAACmB,IAAMA,EAAEZ,IAAI;IACnD,MAAMa,QAAyB,EAAE;IAEjC,KAAK,MAAM/B,cAAcoB,YAAa;QACpCW,MAAMC,IAAI,IACLC,wBAAwBjC,WAAWC,MAAM,EAAE;YAC5CiC,OAAOlC,WAAWkB,IAAI;YACtBiB,WAAW;YACXC,QAAQ;YACRR;QACF;IAEJ;IAEA,KAAK,MAAMF,SAASF,OAAQ;QAC1BO,MAAMC,IAAI,IACLC,wBAAwBP,MAAMzB,MAAM,EAAE;YACvCiC,OAAOR,MAAMR,IAAI;YACjBiB,WAAW;YACXC,QAAQ;YACRR;QACF;IAEJ;IAEA,OAAOG;AACT;AAEA;;CAEC,GACD,OAAO,SAASM,uBACdC,OAAsC;IAEtC,MAAMP,QAA4B,EAAE;IACpC,KAAK,MAAM,CAACb,MAAMqB,OAAO,IAAID,QAAS;QACpC,KAAK,MAAME,OAAOD,OAAOpC,aAAa,CAAE;YACtC4B,MAAMC,IAAI,CAAC;gBACTS,gBAAgBvB;gBAChBwB,WAAWF,IAAIE,SAAS;gBACxBC,cAAcH,IAAII,UAAU;gBAC5BC,SAASL,IAAIK,OAAO;YACtB;QACF;IACF;IACA,OAAOd;AACT;AAEA,sEAAsE;AAEtE;;;CAGC,GACD,SAAS7B,cAAcD,MAAe;IACpC,MAAM6C,SAAwB,EAAE;IAEhC,KAAK,MAAMC,SAAS9C,OAAQ;QAC1B,IAAI,UAAU8C,SAASA,MAAMrC,IAAI,EAAE;YACjC,MAAM6B,SAAsB;gBAC1B7B,MAAMqC,MAAMrC,IAAI;gBAChBD,MAAMsC,MAAMtC,IAAI;YAClB;YAEA,IAAI,cAAcsC,SAASA,MAAMC,QAAQ,EAAET,OAAOS,QAAQ,GAAG;YAC7D,IAAI,aAAaD,SAASA,MAAMF,OAAO,EAAEN,OAAOM,OAAO,GAAG;YAC1D,IAAI,gBAAgBE,SAASA,MAAMH,UAAU,EAAE;gBAC7CL,OAAOK,UAAU,GAAGG,MAAMH,UAAU;YACtC;YACA,IAAI,aAAaG,SAASA,MAAME,OAAO,EAAEV,OAAOU,OAAO,GAAGF,MAAME,OAAO;YAEvE,IAAIF,MAAMtC,IAAI,KAAK,YAAY,aAAasC,SAASG,MAAMC,OAAO,CAACJ,MAAMK,OAAO,GAAG;gBACjFb,OAAOa,OAAO,GAAGL,MAAMK,OAAO,CAACzC,GAAG,CAAC,CAAC0C,MAClC,OAAOA,QAAQ,WACX;wBAAEC,OAAOD;wBAAKE,OAAOF;oBAAI,IACzB;wBAAEC,OAAOE,OAAOH,IAAIC,KAAK;wBAAGC,OAAOC,OAAOH,IAAIE,KAAK;oBAAE;YAE7D;YAEA,IAAIR,MAAMtC,IAAI,KAAK,WAAW,YAAYsC,OAAO;gBAC/CR,OAAOtC,MAAM,GAAGC,cAAc6C,MAAM9C,MAAM;YAC5C;YACA,IAAI8C,MAAMtC,IAAI,KAAK,WAAW,YAAYsC,OAAO;gBAC/CR,OAAOtC,MAAM,GAAGC,cAAc6C,MAAM9C,MAAM;YAC5C;YAEA6C,OAAOd,IAAI,CAACO;QACd;QAEA,IAAIQ,MAAMtC,IAAI,KAAK,UAAU,UAAUsC,OAAO;YAC5C,KAAK,MAAMU,OAAOV,MAAMW,IAAI,CAAE;gBAC5B,IAAI,YAAYD,KAAK;oBACnBX,OAAOd,IAAI,IAAI9B,cAAcuD,IAAIxD,MAAM;gBACzC;YACF;QACF;QACA,IAAI8C,MAAMtC,IAAI,KAAK,SAAS,YAAYsC,OAAO;YAC7CD,OAAOd,IAAI,IAAI9B,cAAc6C,MAAM9C,MAAM;QAC3C;QACA,IAAI8C,MAAMtC,IAAI,KAAK,iBAAiB,YAAYsC,OAAO;YACrDD,OAAOd,IAAI,IAAI9B,cAAc6C,MAAM9C,MAAM;QAC3C;IACF;IAEA,OAAO6C;AACT;AAEA;;CAEC,GACD,SAAS1C,qBACPH,MAAe,EACfmC,SAAS,EAAE;IAEX,MAAMuB,OAAsF,EAAE;IAE9F,KAAK,MAAMZ,SAAS9C,OAAQ;QAC1B,MAAMyC,YAAY,UAAUK,SAASA,MAAMrC,IAAI,GAAG,GAAG0B,SAASW,MAAMrC,IAAI,EAAE,GAAG0B;QAE7E,IAAIW,MAAMtC,IAAI,KAAK,kBAAkB,gBAAgBsC,OAAO;YAC1DY,KAAK3B,IAAI,CAAC;gBACRU;gBACAE,YAAYG,MAAMH,UAAU;gBAC5BC,SAAS,CAAC,CAAE,CAAA,aAAaE,SAASA,MAAMF,OAAO,AAAD;YAChD;QACF;QAEA,IAAIE,MAAMtC,IAAI,KAAK,YAAY,gBAAgBsC,OAAO;YACpDY,KAAK3B,IAAI,CAAC;gBACRU;gBACAE,YAAYG,MAAMH,UAAU;gBAC5BC,SAAS,CAAC,CAAE,CAAA,aAAaE,SAASA,MAAMF,OAAO,AAAD;YAChD;QACF;QAEA,IAAIE,MAAMtC,IAAI,KAAK,UAAU,UAAUsC,OAAO;YAC5C,KAAK,MAAMU,OAAOV,MAAMW,IAAI,CAAE;gBAC5B,IAAI,YAAYD,KAAK;oBACnBE,KAAK3B,IAAI,IAAI5B,qBAAqBqD,IAAIxD,MAAM,EAAEmC;gBAChD;YACF;QACF;QACA,IAAIW,MAAMtC,IAAI,KAAK,WAAW,YAAYsC,OAAO;YAC/CY,KAAK3B,IAAI,IAAI5B,qBAAqB2C,MAAM9C,MAAM,EAAE,GAAGyC,UAAU,CAAC,CAAC;QACjE;QACA,IAAIK,MAAMtC,IAAI,KAAK,WAAW,YAAYsC,OAAO;YAC/CY,KAAK3B,IAAI,IAAI5B,qBAAqB2C,MAAM9C,MAAM,EAAE,GAAGyC,UAAU,GAAG,CAAC;QACnE;QACA,IAAIK,MAAMtC,IAAI,KAAK,SAAS,YAAYsC,OAAO;YAC7CY,KAAK3B,IAAI,IAAI5B,qBAAqB2C,MAAM9C,MAAM,EAAEmC;QAClD;QACA,IAAIW,MAAMtC,IAAI,KAAK,iBAAiB,YAAYsC,OAAO;YACrDY,KAAK3B,IAAI,IAAI5B,qBAAqB2C,MAAM9C,MAAM,EAAEmC;QAClD;IACF;IAEA,OAAOuB;AACT;AASA;;;;CAIC,GACD,SAAS1B,wBAAwBhC,MAAe,EAAE2D,GAAuB;IACvE,MAAM7B,QAAyB,EAAE;IAEjC,KAAK,MAAMgB,SAAS9C,OAAQ;QAC1B,IAAI8C,MAAMtC,IAAI,KAAK,UAAU;YAC3B,MAAMiC,YAAY,UAAUK,SAASA,MAAMrC,IAAI,GAAGqC,MAAMrC,IAAI,GAAG;YAC/D,MAAMmD,WAAWD,IAAIxB,MAAM,GAAG,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEM,WAAW,GAAGA;YAC7D,MAAMoB,WAAWC,eAAehB;YAChC,MAAMiB,gBAAgBF,SAASxD,MAAM,CAAC,CAAC2D,IAAML,IAAIhC,UAAU,CAACsC,GAAG,CAACD;YAEhE,MAAME,OAAgC;gBACpCjC,OAAO0B,IAAI1B,KAAK;gBAChBC,WAAWyB,IAAIzB,SAAS;gBACxBiC,WAAWP;gBACXQ,oBAAoBL;YACtB;YACA,MAAMf,UAAU,AAACF,MAAqCE,OAAO;YAC7D,IAAI,OAAOA,YAAY,UAAUkB,KAAKlB,OAAO,GAAGA;YAChDlB,MAAMC,IAAI,CAACmC;YACX;QACF;QAEA,IAAIpB,MAAMtC,IAAI,KAAK,UAAU,UAAUsC,OAAO;YAC5C,KAAK,MAAMU,OAAOV,MAAMW,IAAI,CAAE;gBAC5B,IAAI,CAAE,CAAA,YAAYD,GAAE,GAAI;gBACxB,MAAMa,UAAU,UAAUb,OAAOA,IAAI/C,IAAI,GAAG+C,IAAI/C,IAAI,GAAG;gBACvD,MAAM6D,YAAYD,UACdV,IAAIxB,MAAM,GACR,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEkC,SAAS,GAC1BA,UACFV,IAAIxB,MAAM;gBACdL,MAAMC,IAAI,IAAIC,wBAAwBwB,IAAIxD,MAAM,EAAE;oBAAE,GAAG2D,GAAG;oBAAExB,QAAQmC;gBAAU;YAChF;YACA;QACF;QACA,IAAIxB,MAAMtC,IAAI,KAAK,SAAS,YAAYsC,OAAO;YAC7ChB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM9C,MAAM,EAAE2D;YACpD;QACF;QACA,IAAIb,MAAMtC,IAAI,KAAK,iBAAiB,YAAYsC,OAAO;YACrDhB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM9C,MAAM,EAAE2D;YACpD;QACF;QACA,IAAIb,MAAMtC,IAAI,KAAK,WAAW,YAAYsC,SAAS,UAAUA,SAASA,MAAMrC,IAAI,EAAE;YAChF,MAAM8D,YAAYZ,IAAIxB,MAAM,GAAG,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEW,MAAMrC,IAAI,EAAE,GAAGqC,MAAMrC,IAAI;YACzEqB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM9C,MAAM,EAAE;gBAAE,GAAG2D,GAAG;gBAAExB,QAAQoC;YAAU;YAChF;QACF;QACA,IAAIzB,MAAMtC,IAAI,KAAK,WAAW,YAAYsC,SAAS,UAAUA,SAASA,MAAMrC,IAAI,EAAE;YAChF,MAAM8D,YAAYZ,IAAIxB,MAAM,GAAG,GAAGwB,IAAIxB,MAAM,CAAC,CAAC,EAAEW,MAAMrC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAGqC,MAAMrC,IAAI,CAAC,EAAE,CAAC;YAClFqB,MAAMC,IAAI,IAAIC,wBAAwBc,MAAM9C,MAAM,EAAE;gBAAE,GAAG2D,GAAG;gBAAExB,QAAQoC;YAAU;YAChF;QACF;IACF;IAEA,OAAOzC;AACT;AAEA;;;;CAIC,GACD,SAASgC,eAAehB,KAAiC;IACvD,MAAMxC,IAAIwC;IAEV,IACEG,MAAMC,OAAO,CAAC5C,EAAEiB,MAAM,KACtBjB,EAAEiB,MAAM,CAACiD,MAAM,GAAG,KAClB,OAAOlE,EAAEiB,MAAM,CAAC,EAAE,KAAK,YACvBjB,EAAEiB,MAAM,CAAC,EAAE,EAAEN,MACb;QACA,OAAOX,EAAEiB,MAAM,CAACb,GAAG,CAAC,CAACmB,IAAwBA,EAAEZ,IAAI;IACrD;IAEA,IAAIgC,MAAMC,OAAO,CAAC5C,EAAEmE,eAAe,KAAKnE,EAAEmE,eAAe,CAACD,MAAM,GAAG,GAAG;QACpE,OAAOlE,EAAEmE,eAAe,CAACpE,MAAM,CAAC,CAACqE,MAAiB,OAAOA,QAAQ;IACnE;IAEA,OAAO,EAAE;AACX"}
package/dist/prompts.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import type { BlockCatalog, CollectionSchema, DomainPrompt, RelationshipEdge } from './types';
1
+ import type { BlockCatalog, BlockNestingMap, CollectionSchema, DomainPrompt, RelationshipEdge } from './types';
2
2
  /**
3
3
  * Generate MCP prompts that teach the AI about the content model.
4
4
  *
5
- * Auto-generates three prompts (content model overview, block composition guide,
6
- * draft workflow guide) and merges with any user-provided domain prompts.
5
+ * Auto-generates content model overview, block composition guide, and
6
+ * draft workflow guide. User-supplied domain prompts are appended.
7
7
  */
8
- export declare function generatePrompts(schemas: Map<string, CollectionSchema>, catalog: BlockCatalog, relationships: RelationshipEdge[], domainPrompts?: DomainPrompt[]): {
8
+ export declare function generatePrompts(schemas: Map<string, CollectionSchema>, catalog: BlockCatalog, nesting: BlockNestingMap, relationships: RelationshipEdge[], domainPrompts?: DomainPrompt[]): {
9
9
  name: string;
10
10
  title: string;
11
11
  description: string;
package/dist/prompts.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Generate MCP prompts that teach the AI about the content model.
3
3
  *
4
- * Auto-generates three prompts (content model overview, block composition guide,
5
- * draft workflow guide) and merges with any user-provided domain prompts.
6
- */ export function generatePrompts(schemas, catalog, relationships, domainPrompts) {
4
+ * Auto-generates content model overview, block composition guide, and
5
+ * draft workflow guide. User-supplied domain prompts are appended.
6
+ */ export function generatePrompts(schemas, catalog, nesting, relationships, domainPrompts) {
7
7
  const prompts = [
8
8
  buildContentModelOverview(schemas, relationships),
9
- buildBlockCompositionGuide(catalog),
9
+ buildBlockCompositionGuide(catalog, nesting),
10
10
  buildDraftWorkflowGuide(schemas)
11
11
  ];
12
12
  if (domainPrompts?.length) {
@@ -49,7 +49,6 @@ function buildContentModelOverview(schemas, relationships) {
49
49
  lines.push(`Draft support: ${schema.hasDrafts ? 'yes' : 'no'}`);
50
50
  lines.push(`Live preview: ${schema.hasLivePreview ? 'yes' : 'no'}`);
51
51
  lines.push('');
52
- // Fields summary
53
52
  lines.push('### Fields');
54
53
  for (const field of schema.fields){
55
54
  const parts = [
@@ -69,7 +68,6 @@ function buildContentModelOverview(schemas, relationships) {
69
68
  lines.push(parts.join(''));
70
69
  }
71
70
  lines.push('');
72
- // Relationships
73
71
  const collRels = relationships.filter((r)=>r.fromCollection === slug);
74
72
  if (collRels.length > 0) {
75
73
  lines.push('### Relationships');
@@ -94,47 +92,58 @@ function buildContentModelOverview(schemas, relationships) {
94
92
  }
95
93
  };
96
94
  }
97
- function buildBlockCompositionGuide(catalog) {
95
+ function buildBlockCompositionGuide(catalog, nesting) {
98
96
  return {
99
97
  name: 'blockCompositionGuide',
100
98
  title: 'Block Composition Guide',
101
- description: 'Explains the section/leaf block hierarchy, valid nesting rules, and how to compose page layouts.',
99
+ description: 'Lists every block type, its fields, and which slugs each blocks-typed field accepts. Use this with the relationshipGraph and collectionSchema resources to compose layouts at any depth.',
102
100
  handler () {
103
101
  const lines = [
104
102
  '# Block Composition Guide',
105
103
  '',
106
- 'Pages are built from **section** blocks. Each section can contain **leaf** blocks according to its nesting rules.',
104
+ 'Every block has a `blockType` discriminator plus its own fields. A block may include one or more `blocks`-typed fields that nest other blocks. The accepted slugs per field are listed below — recurse into the same map for deeper nesting.',
107
105
  ''
108
106
  ];
109
- // Section blocks
110
- lines.push('## Section Blocks');
111
- for (const section of catalog.sections){
112
- lines.push(`### ${section.slug} (${section.nestingType})`);
113
- if (section.nestingType === 'fixed') {
114
- lines.push('This section has no nested blocks — configure it with its own fields only.');
115
- } else if (section.nestingType === 'constrained') {
116
- lines.push(`Accepts only: ${section.acceptedLeafSlugs.join(', ')}`);
117
- if (section.maxRows) {
118
- lines.push(`Maximum ${section.maxRows} leaf block(s).`);
119
- }
120
- } else {
121
- lines.push(`Accepts all leaf blocks: ${section.acceptedLeafSlugs.join(', ')}`);
107
+ lines.push('## Where blocks can nest');
108
+ lines.push('');
109
+ const collectionEdges = nesting.filter((e)=>e.ownerType === 'collection');
110
+ const blockEdges = nesting.filter((e)=>e.ownerType === 'block');
111
+ if (collectionEdges.length > 0) {
112
+ lines.push('### In collections');
113
+ for (const edge of collectionEdges){
114
+ const cap = edge.maxRows ? ` (max ${edge.maxRows})` : '';
115
+ lines.push(`- \`${edge.owner}.${edge.fieldPath}\`${cap} accepts: ${edge.acceptedBlockSlugs.join(', ') || '(none)'}`);
122
116
  }
123
- if (section.fields.length > 0) {
124
- lines.push('Section-level fields:');
125
- for (const f of section.fields){
126
- lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`);
127
- }
117
+ lines.push('');
118
+ }
119
+ if (blockEdges.length > 0) {
120
+ lines.push('### In blocks (nested composition)');
121
+ for (const edge of blockEdges){
122
+ const cap = edge.maxRows ? ` (max ${edge.maxRows})` : '';
123
+ lines.push(`- block \`${edge.owner}\` field \`${edge.fieldPath}\`${cap} accepts: ${edge.acceptedBlockSlugs.join(', ') || '(none)'}`);
128
124
  }
129
125
  lines.push('');
130
126
  }
131
- // Leaf blocks
132
- lines.push('## Leaf Blocks');
133
- for (const leaf of catalog.leaves){
134
- lines.push(`### ${leaf.slug}`);
135
- if (leaf.fields.length > 0) {
136
- for (const f of leaf.fields){
137
- lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`);
127
+ lines.push('## Block fields');
128
+ lines.push('');
129
+ for (const block of catalog.blocks){
130
+ lines.push(`### ${block.slug}`);
131
+ if (block.fields.length === 0) {
132
+ lines.push('(no fields)');
133
+ } else {
134
+ for (const f of block.fields){
135
+ const parts = [
136
+ `- ${f.name} (${f.type})`
137
+ ];
138
+ if (f.required) parts.push(' *required*');
139
+ if (f.options?.length) {
140
+ parts.push(` [${f.options.map((o)=>o.value).join(', ')}]`);
141
+ }
142
+ if (f.relationTo) {
143
+ const targets = Array.isArray(f.relationTo) ? f.relationTo.join(', ') : f.relationTo;
144
+ parts.push(` → ${targets}`);
145
+ }
146
+ lines.push(parts.join(''));
138
147
  }
139
148
  }
140
149
  lines.push('');
@@ -184,7 +193,7 @@ function buildDraftWorkflowGuide(schemas) {
184
193
  lines.push('### How drafts work');
185
194
  lines.push('1. When you create or update a document in a draft-enabled collection, set `_status: "draft"` to keep it unpublished.');
186
195
  lines.push('2. Draft documents are only visible via preview URLs or the admin panel — they are not public.');
187
- lines.push('3. To publish a draft, update the document with `_status: "published"` or use the `publishDraft` tool.');
196
+ lines.push('3. To publish a draft, use the `publishDraft` tool (raw `update` is locked on always-draft collections).');
188
197
  lines.push('4. You can review a draft via its preview URL before publishing.');
189
198
  }
190
199
  lines.push('');
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/prompts.ts"],"sourcesContent":["import type {\n BlockCatalog,\n CollectionSchema,\n DomainPrompt,\n RelationshipEdge,\n} from './types'\n\n/**\n * Generate MCP prompts that teach the AI about the content model.\n *\n * Auto-generates three prompts (content model overview, block composition guide,\n * draft workflow guide) and merges with any user-provided domain prompts.\n */\nexport function generatePrompts(\n schemas: Map<string, CollectionSchema>,\n catalog: BlockCatalog,\n relationships: RelationshipEdge[],\n domainPrompts?: DomainPrompt[],\n) {\n const prompts = [\n buildContentModelOverview(schemas, relationships),\n buildBlockCompositionGuide(catalog),\n buildDraftWorkflowGuide(schemas),\n ]\n\n if (domainPrompts?.length) {\n for (const dp of domainPrompts) {\n prompts.push({\n name: dp.name,\n title: dp.title,\n description: dp.description,\n handler() {\n return {\n messages: [\n {\n content: { type: 'text' as const, text: dp.content },\n role: 'user' as const,\n },\n ],\n }\n },\n })\n }\n }\n\n return prompts\n}\n\n// ─── Prompt builders ──────────────────────────────────────────────\n\nfunction buildContentModelOverview(\n schemas: Map<string, CollectionSchema>,\n relationships: RelationshipEdge[],\n) {\n return {\n name: 'contentModelOverview',\n title: 'Content Model Overview',\n description:\n 'Describes every collection in the CMS — its purpose, fields, and relationships to other collections.',\n handler() {\n const lines: string[] = ['# Content Model Overview', '']\n\n for (const [slug, schema] of schemas) {\n lines.push(`## Collection: ${slug}`)\n lines.push(`Draft support: ${schema.hasDrafts ? 'yes' : 'no'}`)\n lines.push(`Live preview: ${schema.hasLivePreview ? 'yes' : 'no'}`)\n lines.push('')\n\n // Fields summary\n lines.push('### Fields')\n for (const field of schema.fields) {\n const parts = [`- **${field.name}** (${field.type})`]\n if (field.required) parts.push(' *required*')\n if (field.hasMany) parts.push(' hasMany')\n if (field.relationTo) {\n const targets = Array.isArray(field.relationTo)\n ? field.relationTo.join(', ')\n : field.relationTo\n parts.push(` → ${targets}`)\n }\n if (field.options?.length) {\n const vals = field.options.map((o) => o.value).join(', ')\n parts.push(` [${vals}]`)\n }\n if (field.maxRows) parts.push(` maxRows: ${field.maxRows}`)\n lines.push(parts.join(''))\n }\n lines.push('')\n\n // Relationships\n const collRels = relationships.filter((r) => r.fromCollection === slug)\n if (collRels.length > 0) {\n lines.push('### Relationships')\n for (const rel of collRels) {\n const targets = Array.isArray(rel.toCollection)\n ? rel.toCollection.join(', ')\n : rel.toCollection\n lines.push(\n `- ${rel.fieldName} → ${targets}${rel.hasMany ? ' (hasMany)' : ''}`,\n )\n }\n lines.push('')\n }\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n\nfunction buildBlockCompositionGuide(catalog: BlockCatalog) {\n return {\n name: 'blockCompositionGuide',\n title: 'Block Composition Guide',\n description:\n 'Explains the section/leaf block hierarchy, valid nesting rules, and how to compose page layouts.',\n handler() {\n const lines: string[] = [\n '# Block Composition Guide',\n '',\n 'Pages are built from **section** blocks. Each section can contain **leaf** blocks according to its nesting rules.',\n '',\n ]\n\n // Section blocks\n lines.push('## Section Blocks')\n for (const section of catalog.sections) {\n lines.push(`### ${section.slug} (${section.nestingType})`)\n\n if (section.nestingType === 'fixed') {\n lines.push('This section has no nested blocks — configure it with its own fields only.')\n } else if (section.nestingType === 'constrained') {\n lines.push(\n `Accepts only: ${section.acceptedLeafSlugs.join(', ')}`,\n )\n if (section.maxRows) {\n lines.push(`Maximum ${section.maxRows} leaf block(s).`)\n }\n } else {\n lines.push(\n `Accepts all leaf blocks: ${section.acceptedLeafSlugs.join(', ')}`,\n )\n }\n\n if (section.fields.length > 0) {\n lines.push('Section-level fields:')\n for (const f of section.fields) {\n lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`)\n }\n }\n lines.push('')\n }\n\n // Leaf blocks\n lines.push('## Leaf Blocks')\n for (const leaf of catalog.leaves) {\n lines.push(`### ${leaf.slug}`)\n if (leaf.fields.length > 0) {\n for (const f of leaf.fields) {\n lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`)\n }\n }\n lines.push('')\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n\nfunction buildDraftWorkflowGuide(schemas: Map<string, CollectionSchema>) {\n return {\n name: 'draftWorkflowGuide',\n title: 'Draft Workflow Guide',\n description:\n 'Explains which collections support drafts, how to create drafts, review them, and publish.',\n handler() {\n const draftCollections: string[] = []\n const publishCollections: string[] = []\n\n for (const [slug, schema] of schemas) {\n if (schema.hasDrafts) {\n draftCollections.push(slug)\n } else {\n publishCollections.push(slug)\n }\n }\n\n const lines: string[] = [\n '# Draft Workflow Guide',\n '',\n '## Collections with draft support',\n '',\n ]\n\n if (draftCollections.length === 0) {\n lines.push('No collections have draft support enabled.')\n } else {\n for (const slug of draftCollections) {\n lines.push(`- **${slug}**`)\n }\n lines.push('')\n lines.push('### How drafts work')\n lines.push(\n '1. When you create or update a document in a draft-enabled collection, set `_status: \"draft\"` to keep it unpublished.',\n )\n lines.push(\n '2. Draft documents are only visible via preview URLs or the admin panel — they are not public.',\n )\n lines.push(\n '3. To publish a draft, update the document with `_status: \"published\"` or use the `publishDraft` tool.',\n )\n lines.push(\n '4. You can review a draft via its preview URL before publishing.',\n )\n }\n\n lines.push('')\n lines.push('## Collections without draft support')\n lines.push('')\n\n if (publishCollections.length === 0) {\n lines.push('All collections support drafts.')\n } else {\n for (const slug of publishCollections) {\n lines.push(`- **${slug}** — changes are published immediately`)\n }\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n"],"names":["generatePrompts","schemas","catalog","relationships","domainPrompts","prompts","buildContentModelOverview","buildBlockCompositionGuide","buildDraftWorkflowGuide","length","dp","push","name","title","description","handler","messages","content","type","text","role","lines","slug","schema","hasDrafts","hasLivePreview","field","fields","parts","required","hasMany","relationTo","targets","Array","isArray","join","options","vals","map","o","value","maxRows","collRels","filter","r","fromCollection","rel","toCollection","fieldName","section","sections","nestingType","acceptedLeafSlugs","f","leaf","leaves","draftCollections","publishCollections"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/prompts.ts"],"sourcesContent":["import type {\n BlockCatalog,\n BlockNestingMap,\n CollectionSchema,\n DomainPrompt,\n RelationshipEdge,\n} from './types'\n\n/**\n * Generate MCP prompts that teach the AI about the content model.\n *\n * Auto-generates content model overview, block composition guide, and\n * draft workflow guide. User-supplied domain prompts are appended.\n */\nexport function generatePrompts(\n schemas: Map<string, CollectionSchema>,\n catalog: BlockCatalog,\n nesting: BlockNestingMap,\n relationships: RelationshipEdge[],\n domainPrompts?: DomainPrompt[],\n) {\n const prompts = [\n buildContentModelOverview(schemas, relationships),\n buildBlockCompositionGuide(catalog, nesting),\n buildDraftWorkflowGuide(schemas),\n ]\n\n if (domainPrompts?.length) {\n for (const dp of domainPrompts) {\n prompts.push({\n name: dp.name,\n title: dp.title,\n description: dp.description,\n handler() {\n return {\n messages: [\n {\n content: { type: 'text' as const, text: dp.content },\n role: 'user' as const,\n },\n ],\n }\n },\n })\n }\n }\n\n return prompts\n}\n\n// ─── Prompt builders ──────────────────────────────────────────────\n\nfunction buildContentModelOverview(\n schemas: Map<string, CollectionSchema>,\n relationships: RelationshipEdge[],\n) {\n return {\n name: 'contentModelOverview',\n title: 'Content Model Overview',\n description:\n 'Describes every collection in the CMS — its purpose, fields, and relationships to other collections.',\n handler() {\n const lines: string[] = ['# Content Model Overview', '']\n\n for (const [slug, schema] of schemas) {\n lines.push(`## Collection: ${slug}`)\n lines.push(`Draft support: ${schema.hasDrafts ? 'yes' : 'no'}`)\n lines.push(`Live preview: ${schema.hasLivePreview ? 'yes' : 'no'}`)\n lines.push('')\n\n lines.push('### Fields')\n for (const field of schema.fields) {\n const parts = [`- **${field.name}** (${field.type})`]\n if (field.required) parts.push(' *required*')\n if (field.hasMany) parts.push(' hasMany')\n if (field.relationTo) {\n const targets = Array.isArray(field.relationTo)\n ? field.relationTo.join(', ')\n : field.relationTo\n parts.push(` → ${targets}`)\n }\n if (field.options?.length) {\n const vals = field.options.map((o) => o.value).join(', ')\n parts.push(` [${vals}]`)\n }\n if (field.maxRows) parts.push(` maxRows: ${field.maxRows}`)\n lines.push(parts.join(''))\n }\n lines.push('')\n\n const collRels = relationships.filter((r) => r.fromCollection === slug)\n if (collRels.length > 0) {\n lines.push('### Relationships')\n for (const rel of collRels) {\n const targets = Array.isArray(rel.toCollection)\n ? rel.toCollection.join(', ')\n : rel.toCollection\n lines.push(`- ${rel.fieldName} → ${targets}${rel.hasMany ? ' (hasMany)' : ''}`)\n }\n lines.push('')\n }\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n\nfunction buildBlockCompositionGuide(catalog: BlockCatalog, nesting: BlockNestingMap) {\n return {\n name: 'blockCompositionGuide',\n title: 'Block Composition Guide',\n description:\n 'Lists every block type, its fields, and which slugs each blocks-typed field accepts. Use this with the relationshipGraph and collectionSchema resources to compose layouts at any depth.',\n handler() {\n const lines: string[] = [\n '# Block Composition Guide',\n '',\n 'Every block has a `blockType` discriminator plus its own fields. A block may include one or more `blocks`-typed fields that nest other blocks. The accepted slugs per field are listed below — recurse into the same map for deeper nesting.',\n '',\n ]\n\n lines.push('## Where blocks can nest')\n lines.push('')\n const collectionEdges = nesting.filter((e) => e.ownerType === 'collection')\n const blockEdges = nesting.filter((e) => e.ownerType === 'block')\n\n if (collectionEdges.length > 0) {\n lines.push('### In collections')\n for (const edge of collectionEdges) {\n const cap = edge.maxRows ? ` (max ${edge.maxRows})` : ''\n lines.push(\n `- \\`${edge.owner}.${edge.fieldPath}\\`${cap} accepts: ${edge.acceptedBlockSlugs.join(', ') || '(none)'}`,\n )\n }\n lines.push('')\n }\n\n if (blockEdges.length > 0) {\n lines.push('### In blocks (nested composition)')\n for (const edge of blockEdges) {\n const cap = edge.maxRows ? ` (max ${edge.maxRows})` : ''\n lines.push(\n `- block \\`${edge.owner}\\` field \\`${edge.fieldPath}\\`${cap} accepts: ${edge.acceptedBlockSlugs.join(', ') || '(none)'}`,\n )\n }\n lines.push('')\n }\n\n lines.push('## Block fields')\n lines.push('')\n for (const block of catalog.blocks) {\n lines.push(`### ${block.slug}`)\n if (block.fields.length === 0) {\n lines.push('(no fields)')\n } else {\n for (const f of block.fields) {\n const parts = [`- ${f.name} (${f.type})`]\n if (f.required) parts.push(' *required*')\n if (f.options?.length) {\n parts.push(` [${f.options.map((o) => o.value).join(', ')}]`)\n }\n if (f.relationTo) {\n const targets = Array.isArray(f.relationTo)\n ? f.relationTo.join(', ')\n : f.relationTo\n parts.push(` → ${targets}`)\n }\n lines.push(parts.join(''))\n }\n }\n lines.push('')\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n\nfunction buildDraftWorkflowGuide(schemas: Map<string, CollectionSchema>) {\n return {\n name: 'draftWorkflowGuide',\n title: 'Draft Workflow Guide',\n description:\n 'Explains which collections support drafts, how to create drafts, review them, and publish.',\n handler() {\n const draftCollections: string[] = []\n const publishCollections: string[] = []\n\n for (const [slug, schema] of schemas) {\n if (schema.hasDrafts) {\n draftCollections.push(slug)\n } else {\n publishCollections.push(slug)\n }\n }\n\n const lines: string[] = ['# Draft Workflow Guide', '', '## Collections with draft support', '']\n\n if (draftCollections.length === 0) {\n lines.push('No collections have draft support enabled.')\n } else {\n for (const slug of draftCollections) {\n lines.push(`- **${slug}**`)\n }\n lines.push('')\n lines.push('### How drafts work')\n lines.push(\n '1. When you create or update a document in a draft-enabled collection, set `_status: \"draft\"` to keep it unpublished.',\n )\n lines.push(\n '2. Draft documents are only visible via preview URLs or the admin panel — they are not public.',\n )\n lines.push(\n '3. To publish a draft, use the `publishDraft` tool (raw `update` is locked on always-draft collections).',\n )\n lines.push('4. You can review a draft via its preview URL before publishing.')\n }\n\n lines.push('')\n lines.push('## Collections without draft support')\n lines.push('')\n\n if (publishCollections.length === 0) {\n lines.push('All collections support drafts.')\n } else {\n for (const slug of publishCollections) {\n lines.push(`- **${slug}** — changes are published immediately`)\n }\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n"],"names":["generatePrompts","schemas","catalog","nesting","relationships","domainPrompts","prompts","buildContentModelOverview","buildBlockCompositionGuide","buildDraftWorkflowGuide","length","dp","push","name","title","description","handler","messages","content","type","text","role","lines","slug","schema","hasDrafts","hasLivePreview","field","fields","parts","required","hasMany","relationTo","targets","Array","isArray","join","options","vals","map","o","value","maxRows","collRels","filter","r","fromCollection","rel","toCollection","fieldName","collectionEdges","e","ownerType","blockEdges","edge","cap","owner","fieldPath","acceptedBlockSlugs","block","blocks","f","draftCollections","publishCollections"],"mappings":"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"}
@@ -1,9 +1,14 @@
1
- import type { BlockCatalog, CollectionSchema, RelationshipEdge } from './types';
1
+ import type { BlockCatalog, BlockNestingMap, CollectionSchema, RelationshipEdge } from './types';
2
2
  /**
3
- * Generate MCP resources that expose the block catalog,
4
- * collection schemas, and relationship graph as static JSON.
3
+ * Generate MCP resources that expose the introspected schema as static JSON.
4
+ *
5
+ * Four resources:
6
+ * - blocks://catalog — flat list of every block and its fields
7
+ * - blocks://nesting — per-blocks-field map of which slugs each field accepts
8
+ * - collections://schema — collection field metadata
9
+ * - collections://relationships — collection relationship graph
5
10
  */
6
- export declare function generateResources(schemas: Map<string, CollectionSchema>, catalog: BlockCatalog, relationships: RelationshipEdge[]): {
11
+ export declare function generateResources(schemas: Map<string, CollectionSchema>, catalog: BlockCatalog, nesting: BlockNestingMap, relationships: RelationshipEdge[]): {
7
12
  name: string;
8
13
  title: string;
9
14
  description: string;