@contentrain/rules 0.5.3 → 0.7.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.
- package/README.md +2 -2
- package/dist/index.cjs +51 -1
- package/dist/index.d.cts +34 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +34 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +51 -2
- package/dist/index.mjs.map +1 -1
- package/essential/contentrain-essentials.md +13 -0
- package/package.json +2 -2
- package/shared/mcp-usage.md +6 -2
- package/shared/media-rules.md +6 -0
- package/shared/schema-rules.md +35 -17
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ The package root exports constants for tooling:
|
|
|
75
75
|
|
|
76
76
|
- `FIELD_TYPES` — 27 flat field types
|
|
77
77
|
- `MODEL_KINDS` — `singleton`, `collection`, `document`, `dictionary`
|
|
78
|
-
- `MCP_TOOLS` —
|
|
78
|
+
- `MCP_TOOLS` — 26 MCP tool names: 21 core + 5 media (`contentrain_media_*`, registered only on media-capable providers)
|
|
79
79
|
- `ESSENTIAL_RULES_FILE` — path to essential guardrails markdown
|
|
80
80
|
- `STACKS` — supported framework stacks
|
|
81
81
|
|
|
@@ -84,7 +84,7 @@ The package root exports constants for tooling:
|
|
|
84
84
|
```ts
|
|
85
85
|
import { MCP_TOOLS, ESSENTIAL_RULES_FILE, FIELD_TYPES } from '@contentrain/rules'
|
|
86
86
|
|
|
87
|
-
console.log(MCP_TOOLS.length) //
|
|
87
|
+
console.log(MCP_TOOLS.length) // 24
|
|
88
88
|
console.log(MCP_TOOLS.includes('contentrain_merge')) // true
|
|
89
89
|
console.log(MCP_TOOLS.includes('contentrain_doctor')) // true
|
|
90
90
|
console.log(ESSENTIAL_RULES_FILE) // 'essential/contentrain-essentials.md'
|
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,48 @@ const MODEL_KINDS = [
|
|
|
40
40
|
"document",
|
|
41
41
|
"dictionary"
|
|
42
42
|
];
|
|
43
|
+
const MODEL_PROPERTIES = [
|
|
44
|
+
{
|
|
45
|
+
name: "id",
|
|
46
|
+
required: true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "name",
|
|
50
|
+
required: true
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "kind",
|
|
54
|
+
required: true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "domain",
|
|
58
|
+
required: true
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "i18n",
|
|
62
|
+
required: true
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "title_field",
|
|
66
|
+
required: true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "description",
|
|
70
|
+
required: false
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "content_path",
|
|
74
|
+
required: false
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "locale_strategy",
|
|
78
|
+
required: false
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "fields",
|
|
82
|
+
required: true
|
|
83
|
+
}
|
|
84
|
+
];
|
|
43
85
|
const MCP_TOOLS = [
|
|
44
86
|
"contentrain_status",
|
|
45
87
|
"contentrain_describe",
|
|
@@ -51,6 +93,8 @@ const MCP_TOOLS = [
|
|
|
51
93
|
"contentrain_content_save",
|
|
52
94
|
"contentrain_content_delete",
|
|
53
95
|
"contentrain_content_list",
|
|
96
|
+
"contentrain_vocabulary_save",
|
|
97
|
+
"contentrain_vocabulary_delete",
|
|
54
98
|
"contentrain_scan",
|
|
55
99
|
"contentrain_apply",
|
|
56
100
|
"contentrain_validate",
|
|
@@ -59,7 +103,12 @@ const MCP_TOOLS = [
|
|
|
59
103
|
"contentrain_branch_list",
|
|
60
104
|
"contentrain_branch_delete",
|
|
61
105
|
"contentrain_bulk",
|
|
62
|
-
"contentrain_doctor"
|
|
106
|
+
"contentrain_doctor",
|
|
107
|
+
"contentrain_media_list",
|
|
108
|
+
"contentrain_media_get",
|
|
109
|
+
"contentrain_media_ingest",
|
|
110
|
+
"contentrain_media_update",
|
|
111
|
+
"contentrain_media_delete"
|
|
63
112
|
];
|
|
64
113
|
const ESSENTIAL_RULES_FILE = "essential/contentrain-essentials.md";
|
|
65
114
|
const STACKS = [
|
|
@@ -75,4 +124,5 @@ exports.ESSENTIAL_RULES_FILE = ESSENTIAL_RULES_FILE;
|
|
|
75
124
|
exports.FIELD_TYPES = FIELD_TYPES;
|
|
76
125
|
exports.MCP_TOOLS = MCP_TOOLS;
|
|
77
126
|
exports.MODEL_KINDS = MODEL_KINDS;
|
|
127
|
+
exports.MODEL_PROPERTIES = MODEL_PROPERTIES;
|
|
78
128
|
exports.STACKS = STACKS;
|
package/dist/index.d.cts
CHANGED
|
@@ -8,11 +8,43 @@ declare const FIELD_TYPES: readonly ["string", "text", "email", "url", "slug", "
|
|
|
8
8
|
type FieldType = (typeof FIELD_TYPES)[number];
|
|
9
9
|
declare const MODEL_KINDS: readonly ["singleton", "collection", "document", "dictionary"];
|
|
10
10
|
type ModelKind = (typeof MODEL_KINDS)[number];
|
|
11
|
-
declare const
|
|
11
|
+
declare const MODEL_PROPERTIES: readonly [{
|
|
12
|
+
readonly name: "id";
|
|
13
|
+
readonly required: true;
|
|
14
|
+
}, {
|
|
15
|
+
readonly name: "name";
|
|
16
|
+
readonly required: true;
|
|
17
|
+
}, {
|
|
18
|
+
readonly name: "kind";
|
|
19
|
+
readonly required: true;
|
|
20
|
+
}, {
|
|
21
|
+
readonly name: "domain";
|
|
22
|
+
readonly required: true;
|
|
23
|
+
}, {
|
|
24
|
+
readonly name: "i18n";
|
|
25
|
+
readonly required: true;
|
|
26
|
+
}, {
|
|
27
|
+
readonly name: "title_field";
|
|
28
|
+
readonly required: true;
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "description";
|
|
31
|
+
readonly required: false;
|
|
32
|
+
}, {
|
|
33
|
+
readonly name: "content_path";
|
|
34
|
+
readonly required: false;
|
|
35
|
+
}, {
|
|
36
|
+
readonly name: "locale_strategy";
|
|
37
|
+
readonly required: false;
|
|
38
|
+
}, {
|
|
39
|
+
readonly name: "fields";
|
|
40
|
+
readonly required: true;
|
|
41
|
+
}];
|
|
42
|
+
type ModelProperty = (typeof MODEL_PROPERTIES)[number]['name'];
|
|
43
|
+
declare const MCP_TOOLS: readonly ["contentrain_status", "contentrain_describe", "contentrain_describe_format", "contentrain_init", "contentrain_scaffold", "contentrain_model_save", "contentrain_model_delete", "contentrain_content_save", "contentrain_content_delete", "contentrain_content_list", "contentrain_vocabulary_save", "contentrain_vocabulary_delete", "contentrain_scan", "contentrain_apply", "contentrain_validate", "contentrain_submit", "contentrain_merge", "contentrain_branch_list", "contentrain_branch_delete", "contentrain_bulk", "contentrain_doctor", "contentrain_media_list", "contentrain_media_get", "contentrain_media_ingest", "contentrain_media_update", "contentrain_media_delete"];
|
|
12
44
|
type McpTool = (typeof MCP_TOOLS)[number];
|
|
13
45
|
declare const ESSENTIAL_RULES_FILE: "essential/contentrain-essentials.md";
|
|
14
46
|
declare const STACKS: readonly ["nuxt", "next", "astro", "sveltekit", "react", "node"];
|
|
15
47
|
type Stack = (typeof STACKS)[number];
|
|
16
48
|
//#endregion
|
|
17
|
-
export { ESSENTIAL_RULES_FILE, FIELD_TYPES, FieldType, MCP_TOOLS, MODEL_KINDS, McpTool, ModelKind, STACKS, Stack };
|
|
49
|
+
export { ESSENTIAL_RULES_FILE, FIELD_TYPES, FieldType, MCP_TOOLS, MODEL_KINDS, MODEL_PROPERTIES, McpTool, ModelKind, ModelProperty, STACKS, Stack };
|
|
18
50
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;AAQA;;;;cAAa,WAAA;AAAA,KAUD,SAAA,WAAoB,WAAA;AAAA,cAInB,WAAA;AAAA,KACD,SAAA,WAAoB,WAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;AAQA;;;;cAAa,WAAA;AAAA,KAUD,SAAA,WAAoB,WAAA;AAAA,cAInB,WAAA;AAAA,KACD,SAAA,WAAoB,WAAA;AAAA,cASnB,gBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiBD,aAAA,WAAwB,gBAAA;AAAA,cAIvB,SAAA;AAAA,KAkBD,OAAA,WAAkB,SAAA;AAAA,cAIjB,oBAAA;AAAA,cAIA,MAAA;AAAA,KACD,KAAA,WAAgB,MAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -8,11 +8,43 @@ declare const FIELD_TYPES: readonly ["string", "text", "email", "url", "slug", "
|
|
|
8
8
|
type FieldType = (typeof FIELD_TYPES)[number];
|
|
9
9
|
declare const MODEL_KINDS: readonly ["singleton", "collection", "document", "dictionary"];
|
|
10
10
|
type ModelKind = (typeof MODEL_KINDS)[number];
|
|
11
|
-
declare const
|
|
11
|
+
declare const MODEL_PROPERTIES: readonly [{
|
|
12
|
+
readonly name: "id";
|
|
13
|
+
readonly required: true;
|
|
14
|
+
}, {
|
|
15
|
+
readonly name: "name";
|
|
16
|
+
readonly required: true;
|
|
17
|
+
}, {
|
|
18
|
+
readonly name: "kind";
|
|
19
|
+
readonly required: true;
|
|
20
|
+
}, {
|
|
21
|
+
readonly name: "domain";
|
|
22
|
+
readonly required: true;
|
|
23
|
+
}, {
|
|
24
|
+
readonly name: "i18n";
|
|
25
|
+
readonly required: true;
|
|
26
|
+
}, {
|
|
27
|
+
readonly name: "title_field";
|
|
28
|
+
readonly required: true;
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "description";
|
|
31
|
+
readonly required: false;
|
|
32
|
+
}, {
|
|
33
|
+
readonly name: "content_path";
|
|
34
|
+
readonly required: false;
|
|
35
|
+
}, {
|
|
36
|
+
readonly name: "locale_strategy";
|
|
37
|
+
readonly required: false;
|
|
38
|
+
}, {
|
|
39
|
+
readonly name: "fields";
|
|
40
|
+
readonly required: true;
|
|
41
|
+
}];
|
|
42
|
+
type ModelProperty = (typeof MODEL_PROPERTIES)[number]['name'];
|
|
43
|
+
declare const MCP_TOOLS: readonly ["contentrain_status", "contentrain_describe", "contentrain_describe_format", "contentrain_init", "contentrain_scaffold", "contentrain_model_save", "contentrain_model_delete", "contentrain_content_save", "contentrain_content_delete", "contentrain_content_list", "contentrain_vocabulary_save", "contentrain_vocabulary_delete", "contentrain_scan", "contentrain_apply", "contentrain_validate", "contentrain_submit", "contentrain_merge", "contentrain_branch_list", "contentrain_branch_delete", "contentrain_bulk", "contentrain_doctor", "contentrain_media_list", "contentrain_media_get", "contentrain_media_ingest", "contentrain_media_update", "contentrain_media_delete"];
|
|
12
44
|
type McpTool = (typeof MCP_TOOLS)[number];
|
|
13
45
|
declare const ESSENTIAL_RULES_FILE: "essential/contentrain-essentials.md";
|
|
14
46
|
declare const STACKS: readonly ["nuxt", "next", "astro", "sveltekit", "react", "node"];
|
|
15
47
|
type Stack = (typeof STACKS)[number];
|
|
16
48
|
//#endregion
|
|
17
|
-
export { ESSENTIAL_RULES_FILE, FIELD_TYPES, FieldType, MCP_TOOLS, MODEL_KINDS, McpTool, ModelKind, STACKS, Stack };
|
|
49
|
+
export { ESSENTIAL_RULES_FILE, FIELD_TYPES, FieldType, MCP_TOOLS, MODEL_KINDS, MODEL_PROPERTIES, McpTool, ModelKind, ModelProperty, STACKS, Stack };
|
|
18
50
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;AAQA;;;;cAAa,WAAA;AAAA,KAUD,SAAA,WAAoB,WAAA;AAAA,cAInB,WAAA;AAAA,KACD,SAAA,WAAoB,WAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;AAQA;;;;cAAa,WAAA;AAAA,KAUD,SAAA,WAAoB,WAAA;AAAA,cAInB,WAAA;AAAA,KACD,SAAA,WAAoB,WAAA;AAAA,cASnB,gBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiBD,aAAA,WAAwB,gBAAA;AAAA,cAIvB,SAAA;AAAA,KAkBD,OAAA,WAAkB,SAAA;AAAA,cAIjB,oBAAA;AAAA,cAIA,MAAA;AAAA,KACD,KAAA,WAAgB,MAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -39,6 +39,48 @@ const MODEL_KINDS = [
|
|
|
39
39
|
"document",
|
|
40
40
|
"dictionary"
|
|
41
41
|
];
|
|
42
|
+
const MODEL_PROPERTIES = [
|
|
43
|
+
{
|
|
44
|
+
name: "id",
|
|
45
|
+
required: true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "name",
|
|
49
|
+
required: true
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "kind",
|
|
53
|
+
required: true
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "domain",
|
|
57
|
+
required: true
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "i18n",
|
|
61
|
+
required: true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "title_field",
|
|
65
|
+
required: true
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "description",
|
|
69
|
+
required: false
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "content_path",
|
|
73
|
+
required: false
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "locale_strategy",
|
|
77
|
+
required: false
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "fields",
|
|
81
|
+
required: true
|
|
82
|
+
}
|
|
83
|
+
];
|
|
42
84
|
const MCP_TOOLS = [
|
|
43
85
|
"contentrain_status",
|
|
44
86
|
"contentrain_describe",
|
|
@@ -50,6 +92,8 @@ const MCP_TOOLS = [
|
|
|
50
92
|
"contentrain_content_save",
|
|
51
93
|
"contentrain_content_delete",
|
|
52
94
|
"contentrain_content_list",
|
|
95
|
+
"contentrain_vocabulary_save",
|
|
96
|
+
"contentrain_vocabulary_delete",
|
|
53
97
|
"contentrain_scan",
|
|
54
98
|
"contentrain_apply",
|
|
55
99
|
"contentrain_validate",
|
|
@@ -58,7 +102,12 @@ const MCP_TOOLS = [
|
|
|
58
102
|
"contentrain_branch_list",
|
|
59
103
|
"contentrain_branch_delete",
|
|
60
104
|
"contentrain_bulk",
|
|
61
|
-
"contentrain_doctor"
|
|
105
|
+
"contentrain_doctor",
|
|
106
|
+
"contentrain_media_list",
|
|
107
|
+
"contentrain_media_get",
|
|
108
|
+
"contentrain_media_ingest",
|
|
109
|
+
"contentrain_media_update",
|
|
110
|
+
"contentrain_media_delete"
|
|
62
111
|
];
|
|
63
112
|
const ESSENTIAL_RULES_FILE = "essential/contentrain-essentials.md";
|
|
64
113
|
const STACKS = [
|
|
@@ -70,6 +119,6 @@ const STACKS = [
|
|
|
70
119
|
"node"
|
|
71
120
|
];
|
|
72
121
|
//#endregion
|
|
73
|
-
export { ESSENTIAL_RULES_FILE, FIELD_TYPES, MCP_TOOLS, MODEL_KINDS, STACKS };
|
|
122
|
+
export { ESSENTIAL_RULES_FILE, FIELD_TYPES, MCP_TOOLS, MODEL_KINDS, MODEL_PROPERTIES, STACKS };
|
|
74
123
|
|
|
75
124
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @contentrain/rules — AI agent rules for Contentrain\n *\n * Quality standards, architecture conventions, and IDE integration.\n */\n\n// ─── Field Types (27 flat types) ───\n\nexport const FIELD_TYPES = [\n 'string', 'text', 'email', 'url', 'slug', 'color', 'phone', 'code', 'icon',\n 'markdown', 'richtext',\n 'number', 'integer', 'decimal', 'percent', 'rating',\n 'boolean', 'date', 'datetime',\n 'image', 'video', 'file',\n 'relation', 'relations',\n 'select', 'array', 'object',\n] as const\n\nexport type FieldType = (typeof FIELD_TYPES)[number]\n\n// ─── Model Kinds ───\n\nexport const MODEL_KINDS = ['singleton', 'collection', 'document', 'dictionary'] as const\nexport type ModelKind = (typeof MODEL_KINDS)[number]\n\n// ─── MCP Tools (
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @contentrain/rules — AI agent rules for Contentrain\n *\n * Quality standards, architecture conventions, and IDE integration.\n */\n\n// ─── Field Types (27 flat types) ───\n\nexport const FIELD_TYPES = [\n 'string', 'text', 'email', 'url', 'slug', 'color', 'phone', 'code', 'icon',\n 'markdown', 'richtext',\n 'number', 'integer', 'decimal', 'percent', 'rating',\n 'boolean', 'date', 'datetime',\n 'image', 'video', 'file',\n 'relation', 'relations',\n 'select', 'array', 'object',\n] as const\n\nexport type FieldType = (typeof FIELD_TYPES)[number]\n\n// ─── Model Kinds ───\n\nexport const MODEL_KINDS = ['singleton', 'collection', 'document', 'dictionary'] as const\nexport type ModelKind = (typeof MODEL_KINDS)[number]\n\n// ─── Model Definition Properties ───\n//\n// The parity anchor for the model schema. Docs describing `ModelDefinition` drift\n// silently: nothing failed when a property landed in @contentrain/types and never\n// reached schema-rules.md. The tests in tests/mcp-parity.test.ts assert this list\n// against MODEL_FIELD_ORDER in @contentrain/mcp and against the prose tables.\n\nexport const MODEL_PROPERTIES = [\n { name: 'id', required: true },\n { name: 'name', required: true },\n { name: 'kind', required: true },\n { name: 'domain', required: true },\n { name: 'i18n', required: true },\n { name: 'title_field', required: true },\n { name: 'description', required: false },\n { name: 'content_path', required: false },\n { name: 'locale_strategy', required: false },\n // `required` here means \"documented as required\", not the TS optionality.\n // `fields` is `fields?` on the interface but every non-dictionary model needs\n // it — a collection with no fields cannot even name a title_field. The\n // per-kind nuance lives in the docs cell (\"Yes (except dictionary)\").\n { name: 'fields', required: true },\n] as const\n\nexport type ModelProperty = (typeof MODEL_PROPERTIES)[number]['name']\n\n// ─── MCP Tools (26 tools: 21 core + 5 media) ───\n\nexport const MCP_TOOLS = [\n 'contentrain_status', 'contentrain_describe', 'contentrain_describe_format',\n 'contentrain_init', 'contentrain_scaffold',\n 'contentrain_model_save', 'contentrain_model_delete',\n 'contentrain_content_save', 'contentrain_content_delete', 'contentrain_content_list',\n 'contentrain_vocabulary_save', 'contentrain_vocabulary_delete',\n 'contentrain_scan', 'contentrain_apply',\n 'contentrain_validate', 'contentrain_submit',\n 'contentrain_merge',\n 'contentrain_branch_list', 'contentrain_branch_delete',\n 'contentrain_bulk',\n 'contentrain_doctor',\n // Media tools — registered only when the provider exposes a media stack\n // (Studio MCP Cloud); absent from local stdio servers.\n 'contentrain_media_list', 'contentrain_media_get', 'contentrain_media_ingest',\n 'contentrain_media_update', 'contentrain_media_delete',\n] as const\n\nexport type McpTool = (typeof MCP_TOOLS)[number]\n\n// ─── Essential Rules ───\n\nexport const ESSENTIAL_RULES_FILE = 'essential/contentrain-essentials.md' as const\n\n// ─── Stacks ───\n\nexport const STACKS = ['nuxt', 'next', 'astro', 'sveltekit', 'react', 'node'] as const\nexport type Stack = (typeof STACKS)[number]\n"],"mappings":";;;;;;AAQA,MAAa,cAAc;CACzB;CAAU;CAAQ;CAAS;CAAO;CAAQ;CAAS;CAAS;CAAQ;CACpE;CAAY;CACZ;CAAU;CAAW;CAAW;CAAW;CAC3C;CAAW;CAAQ;CACnB;CAAS;CAAS;CAClB;CAAY;CACZ;CAAU;CAAS;CACpB;AAMD,MAAa,cAAc;CAAC;CAAa;CAAc;CAAY;CAAa;AAUhF,MAAa,mBAAmB;CAC9B;EAAE,MAAM;EAAM,UAAU;EAAM;CAC9B;EAAE,MAAM;EAAQ,UAAU;EAAM;CAChC;EAAE,MAAM;EAAQ,UAAU;EAAM;CAChC;EAAE,MAAM;EAAU,UAAU;EAAM;CAClC;EAAE,MAAM;EAAQ,UAAU;EAAM;CAChC;EAAE,MAAM;EAAe,UAAU;EAAM;CACvC;EAAE,MAAM;EAAe,UAAU;EAAO;CACxC;EAAE,MAAM;EAAgB,UAAU;EAAO;CACzC;EAAE,MAAM;EAAmB,UAAU;EAAO;CAK5C;EAAE,MAAM;EAAU,UAAU;EAAM;CACnC;AAMD,MAAa,YAAY;CACvB;CAAsB;CAAwB;CAC9C;CAAoB;CACpB;CAA0B;CAC1B;CAA4B;CAA8B;CAC1D;CAA+B;CAC/B;CAAoB;CACpB;CAAwB;CACxB;CACA;CAA2B;CAC3B;CACA;CAGA;CAA0B;CAAyB;CACnD;CAA4B;CAC7B;AAMD,MAAa,uBAAuB;AAIpC,MAAa,SAAS;CAAC;CAAQ;CAAQ;CAAS;CAAa;CAAS;CAAO"}
|
|
@@ -33,6 +33,10 @@ MCP is **deterministic infrastructure**. The agent (you) is the **intelligence l
|
|
|
33
33
|
- **NEVER** edit `.contentrain/context.json` — MCP writes, agents read
|
|
34
34
|
- **NEVER** include system fields in content data: `id`, `status`, `source`, `updated_by`, `updated_at`, `createdAt`, `updatedAt`
|
|
35
35
|
- **ALWAYS** use MCP tools — do not write `.contentrain/` JSON files directly
|
|
36
|
+
- **Publishing is yours, not MCP's.** `contentrain_content_save` preserves an existing entry's `status`; only a brand-new entry starts at `draft`. To change publish state, call `contentrain_bulk update_status` deliberately — and note that on the CDN a collection entry is delivered only when its status is `published`.
|
|
37
|
+
- **Schema validity is MCP's.** `contentrain_content_save` validates before it writes and **refuses** the save on any error — nothing is committed, so there is no branch to clean up. Fix the values and call it again. Warnings (email/url/colour/phone heuristics, `accept` extension checks) do not block; they come back in the response.
|
|
38
|
+
- **Every model declares its `title_field`.** Required on create, and it must stay correct when you rename or drop a field. It names the field shown as an entry's title and must be a `string`/`text`/`slug`/`email`/`url`/`code`/`markdown`/`richtext` field on that model; dictionaries have no fields, so they use the reserved value `"key"`. Pick what a human reads to tell entries apart — the headline, the name, the question — never the slug, icon or a relation ID. Consumers used to guess this and titled rows `i-lucide-bot`.
|
|
39
|
+
- **A declared constraint is a real constraint.** `model_save` rejects a property declared where it cannot apply (`options` on a non-select, `accept` on a non-media field, `unique` on a singleton) rather than accepting it and doing nothing. The one exception is `maxSize`, which MCP cannot enforce — your media provider does, at ingest, and `model_save` says so.
|
|
36
40
|
|
|
37
41
|
## MCP Tools
|
|
38
42
|
|
|
@@ -46,6 +50,8 @@ MCP is **deterministic infrastructure**. The agent (you) is the **intelligence l
|
|
|
46
50
|
| `contentrain_model_save` | Create/update model definition (upsert) |
|
|
47
51
|
| `contentrain_model_delete` | Delete model (checks referential integrity) |
|
|
48
52
|
| `contentrain_content_save` | Create/update content entries (upsert) |
|
|
53
|
+
| `contentrain_vocabulary_save` | Add/update canonical vocabulary terms — the only supported way to write `vocabulary.json` |
|
|
54
|
+
| `contentrain_vocabulary_delete` | Remove canonical terms (`confirm: true`) |
|
|
49
55
|
| `contentrain_content_delete` | Delete content entry |
|
|
50
56
|
| `contentrain_content_list` | List entries for a model |
|
|
51
57
|
| `contentrain_scan` | Scan project (graph/candidates/summary) |
|
|
@@ -57,6 +63,13 @@ MCP is **deterministic infrastructure**. The agent (you) is the **intelligence l
|
|
|
57
63
|
| `contentrain_branch_delete` | Delete a stale/failed `cr/*` branch (the contentrain branch is protected) |
|
|
58
64
|
| `contentrain_bulk` | Batch operations (copy_locale/update_status/delete_entries) |
|
|
59
65
|
| `contentrain_doctor` | Project health report (env + structure + orphan content + branch pressure + SDK freshness) |
|
|
66
|
+
| `contentrain_media_list` | List media assets (media-capable providers only, e.g. Studio MCP Cloud) |
|
|
67
|
+
| `contentrain_media_get` | Get one media asset by id |
|
|
68
|
+
| `contentrain_media_ingest` | Ingest an asset from a source URL (provider fetches server-side) |
|
|
69
|
+
| `contentrain_media_update` | Update asset metadata (alt/tags/filename) |
|
|
70
|
+
| `contentrain_media_delete` | Delete an asset (references in content are NOT rewritten) |
|
|
71
|
+
|
|
72
|
+
Media tools appear only when the provider exposes a media stack; discover paths with `contentrain_media_list`, then reference them in media/image/file fields via `contentrain_content_save`.
|
|
60
73
|
|
|
61
74
|
## Mandatory Protocols
|
|
62
75
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/rules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "AI agent rules for Contentrain — quality standards, architecture conventions, IDE integration",
|
|
6
6
|
"type": "module",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"tsdown": "^0.21.0",
|
|
56
56
|
"typescript": "^5.7.0",
|
|
57
57
|
"vitest": "^3.0.0",
|
|
58
|
-
"@contentrain/mcp": "
|
|
58
|
+
"@contentrain/mcp": "3.0.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "tsdown src/index.ts --format esm,cjs --dts",
|
package/shared/mcp-usage.md
CHANGED
|
@@ -48,7 +48,7 @@ MCP is **deterministic infrastructure**. The agent is the **intelligence layer**
|
|
|
48
48
|
|
|
49
49
|
| Tool | Purpose | Parameters |
|
|
50
50
|
|------|---------|------------|
|
|
51
|
-
| `contentrain_model_save` | Create or update a model definition (upsert) | `id`, `name`, `kind`, `domain`, `i18n`, `fields?`, `description?`, `content_path?`, `locale_strategy?` |
|
|
51
|
+
| `contentrain_model_save` | Create or update a model definition (upsert) | `id`, `name`, `kind`, `domain`, `i18n`, `title_field`, `fields?`, `description?`, `content_path?`, `locale_strategy?` |
|
|
52
52
|
| `contentrain_model_delete` | Delete a model and its content | `model`, `confirm: true` |
|
|
53
53
|
|
|
54
54
|
### 2.4 Content Tools
|
|
@@ -58,6 +58,8 @@ MCP is **deterministic infrastructure**. The agent is the **intelligence layer**
|
|
|
58
58
|
| `contentrain_content_save` | Create or update content entries (upsert) | `model`, `entries` (array of entry objects) |
|
|
59
59
|
| `contentrain_content_delete` | Delete a content entry | `model`, `id?`, `slug?`, `locale?`, `confirm: true` |
|
|
60
60
|
| `contentrain_content_list` | List content entries for a model (read-only) | `model`, `locale?`, `filter?`, `resolve?`, `limit?`, `offset?` |
|
|
61
|
+
| `contentrain_vocabulary_save` | Add or update canonical vocabulary terms (merges) | `terms` |
|
|
62
|
+
| `contentrain_vocabulary_delete` | Remove canonical terms | `terms`, `confirm: true` |
|
|
61
63
|
|
|
62
64
|
#### contentrain_content_save entry format
|
|
63
65
|
|
|
@@ -83,6 +85,7 @@ Each entry in the `entries` array has this shape:
|
|
|
83
85
|
- For **collection**: use `id` to identify the entry.
|
|
84
86
|
- For **document**: use `slug` to identify the entry.
|
|
85
87
|
- For **singleton/dictionary**: use `locale` to identify which locale file to delete.
|
|
88
|
+
- On an **`i18n: false` model**, do NOT pass `locale` — content is locale-agnostic (`data.json`), so a locale-scoped delete is rejected. Omit `locale` to delete the entry; use `contentrain_validate` with `fix: true` to remove stray per-locale meta.
|
|
86
89
|
|
|
87
90
|
### 2.5 Normalize Tools
|
|
88
91
|
|
|
@@ -181,7 +184,7 @@ Each entry in the `entries` array has this shape:
|
|
|
181
184
|
#### contentrain_validate parameters
|
|
182
185
|
|
|
183
186
|
- `model`: validate a specific model only (omit for all models).
|
|
184
|
-
- `fix`: auto-fix structural issues like canonical sort, orphan meta, missing locale files (default: false).
|
|
187
|
+
- `fix`: auto-fix structural issues like canonical sort, orphan meta, missing locale files, and stray non-i18n meta layout (default: false). Stray-meta cleanup is deterministic: it prunes extras when the default-locale meta is authoritative, or migrates a lone stray to the default path — it never merges or downgrades a status.
|
|
185
188
|
|
|
186
189
|
#### contentrain_submit parameters
|
|
187
190
|
|
|
@@ -384,6 +387,7 @@ Creates or updates a model definition. Key parameters:
|
|
|
384
387
|
- `id`: kebab-case model ID (e.g., `"blog-post"`).
|
|
385
388
|
- `kind`: one of `singleton`, `collection`, `document`, `dictionary`.
|
|
386
389
|
- `i18n`: whether the model supports localization.
|
|
390
|
+
- `title_field`: **required** — the field shown as an entry's title. Must name a field declared on this model whose type is `string`, `text`, `slug`, `email`, `url`, `code`, `markdown` or `richtext`. Dictionary models use the reserved value `"key"`. Set it when creating a model, and keep it correct when renaming or removing fields.
|
|
387
391
|
- `content_path`: optional framework-relative path (e.g., `"content/blog"`, `"locales"`). When set, content is written here instead of `.contentrain/content/`.
|
|
388
392
|
- `locale_strategy`: how locale is encoded in filenames: `"file"` (default), `"suffix"`, `"directory"`, `"none"`.
|
|
389
393
|
- `locale_strategy: "none"` requires `i18n: false`. All other strategies work with both `i18n: true` and `i18n: false`.
|
package/shared/media-rules.md
CHANGED
|
@@ -73,6 +73,12 @@ Never hand-build delivery URLs or guess the CDN base. Save the path or URL the m
|
|
|
73
73
|
|
|
74
74
|
3. If an image exceeds the size limit, instruct the user to compress or resize it before storing. Do not silently accept oversized files.
|
|
75
75
|
|
|
76
|
+
**This one is on you.** A `maxSize` on the field does not enforce it: MCP stores a
|
|
77
|
+
path and never sees the bytes, so it cannot check the size — your media provider
|
|
78
|
+
does, when the asset is ingested. `model_save` says as much when you declare
|
|
79
|
+
`maxSize`. A field's `accept` is checked, but only against the file **extension**,
|
|
80
|
+
which can lie.
|
|
81
|
+
|
|
76
82
|
4. SVG files SHOULD be optimized (e.g., with SVGO) to remove editor metadata, comments, and unnecessary attributes.
|
|
77
83
|
|
|
78
84
|
---
|
package/shared/schema-rules.md
CHANGED
|
@@ -92,22 +92,27 @@ A field definition describes one field in a model. Include only the properties t
|
|
|
92
92
|
|
|
93
93
|
### 3.1 Field Properties
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
|
99
|
-
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
95
|
+
A property declared where it does not apply is **rejected by `model_save`** — it is
|
|
96
|
+
not silently ignored. The "Enforced by" column says who actually checks each one.
|
|
97
|
+
|
|
98
|
+
| Property | Applicable Types | Enforced by | Description |
|
|
99
|
+
|----------|-----------------|-------------|-------------|
|
|
100
|
+
| `type` | ALL | content_save (blocks) | **Required.** One of the 27 types. |
|
|
101
|
+
| `required` | ALL | content_save (blocks) | Mark field as mandatory. Default: `false`. Omit if `false`. |
|
|
102
|
+
| `unique` | Any type, on `collection` and `document` models. **Not `singleton`** — it holds one record per locale, so there is nothing to be unique against. | content_save (blocks) | Enforce uniqueness within the model, per locale. Default: `false`. Omit if `false`. |
|
|
103
|
+
| `default` | ALL | model_save (coherence only) | Default value. Checked against its own `type` and `options`; **not** written into content — the agent supplies values. Omit if `null`. |
|
|
104
|
+
| `min` | string/text: char count; numbers: value; array: element count | content_save (blocks) | Minimum constraint. Must not exceed `max`. |
|
|
105
|
+
| `max` | Same as `min` | content_save (blocks) | Maximum constraint. On a media field this limits the **path string length**, not the file — use `maxSize` for bytes (model_save warns). |
|
|
106
|
+
| `pattern` | Any string-shaped type | content_save (blocks) | Regex validation pattern. Compiled at model_save, so a broken regex is rejected rather than silently disabling the rule. |
|
|
107
|
+
| `options` | `select` ONLY | content_save (blocks) | Fixed choices: `["draft", "published", "archived"]` |
|
|
108
|
+
| `model` | `relation`, `relations` ONLY | content_save (blocks) | Target model ID. String or string array for polymorphic. |
|
|
109
|
+
| `items` | `array` ONLY | content_save (blocks) | Element type: `"string"`, or a full FieldDef — `{ "type": "string", "max": 50 }`. Items are validated by the same rules as a scalar of that type. |
|
|
110
|
+
| `fields` | `object` ONLY | content_save (blocks) | Nested field definitions. Validated recursively, at model_save and at content_save. |
|
|
111
|
+
| `accept` | `image`, `video`, `file` | content_save (**warns**) | Allowed MIME types: `"image/png,image/jpeg"`, `"image/*"`, `".pdf"`. MCP checks the value's **file extension** — an approximation, since it holds a path, not the bytes. Your provider enforces the real MIME type at ingest. |
|
|
112
|
+
| `maxSize` | `image`, `video`, `file` | **Provider, at ingest — not MCP** | Maximum file size in bytes. MCP never sees the file, so it cannot check this; model_save says so. |
|
|
113
|
+
| `description` | ALL | — | Human-readable hint (shown in Studio UI tooltip, used as agent context). |
|
|
114
|
+
| `label` | ALL | — | Editor label: a string, or an object keyed by locale. Absent means the raw field name is shown. |
|
|
115
|
+
| `order` | ALL | — | Display position, ascending. Absent sorts last, alphabetically. Fractional values allowed. |
|
|
111
116
|
|
|
112
117
|
### 3.2 Omission Rules
|
|
113
118
|
|
|
@@ -131,6 +136,7 @@ Model definitions live at `.contentrain/models/{model-id}.json`. One file per mo
|
|
|
131
136
|
"kind": "document",
|
|
132
137
|
"domain": "blog",
|
|
133
138
|
"i18n": true,
|
|
139
|
+
"title_field": "title",
|
|
134
140
|
"description": "Blog articles with markdown body",
|
|
135
141
|
"fields": {
|
|
136
142
|
"title": { "type": "string", "required": true, "max": 120 },
|
|
@@ -150,11 +156,23 @@ Model definitions live at `.contentrain/models/{model-id}.json`. One file per mo
|
|
|
150
156
|
| `kind` | `string` | Yes | One of: `singleton`, `collection`, `document`, `dictionary`. |
|
|
151
157
|
| `domain` | `string` | Yes | Organizational group (maps to content subdirectory). |
|
|
152
158
|
| `i18n` | `boolean` | Yes | Whether the model supports multiple locales. |
|
|
159
|
+
| `title_field` | `string` | Yes | Field shown as an entry's title in listings, pickers and relation references. Must name a field declared on this model whose type is `string`, `text`, `slug`, `email`, `url`, `code`, `markdown` or `richtext`. Dictionary models have no fields — they use the reserved value `"key"`, meaning the entry key is the title. |
|
|
153
160
|
| `description` | `string` | No | Model description for documentation and agent context. |
|
|
154
161
|
| `fields` | `object` | Yes (except dictionary) | Field definitions. Dictionary kind has NO fields. |
|
|
155
162
|
| `content_path` | `string` | No | Framework-relative path for content files (e.g., `"content/blog"`, `"locales"`). When set, content is written here instead of `.contentrain/content/`. |
|
|
156
163
|
| `locale_strategy` | `string` | No | How locale is encoded in file names: `"file"` (default), `"suffix"`, `"directory"`, `"none"`. |
|
|
157
164
|
|
|
165
|
+
#### Choosing `title_field`
|
|
166
|
+
|
|
167
|
+
Declare it on create AND keep it correct when renaming or removing fields — a
|
|
168
|
+
`title_field` pointing at a field that no longer exists fails validation.
|
|
169
|
+
|
|
170
|
+
Pick the field a human would read to tell one entry from another: the headline,
|
|
171
|
+
the name, the question. Not the slug, not the icon, not a relation ID — those are
|
|
172
|
+
identifiers, and a list titled `instagram-launch-post` or `i-lucide-bot` tells the
|
|
173
|
+
reader nothing. That is exactly what consumers produced when they had to guess,
|
|
174
|
+
which is why the property exists.
|
|
175
|
+
|
|
158
176
|
### 4.2 System Fields
|
|
159
177
|
|
|
160
178
|
Do NOT define these in the schema. The platform manages them automatically:
|
|
@@ -164,7 +182,7 @@ Do NOT define these in the schema. The platform manages them automatically:
|
|
|
164
182
|
| `id` | Collection | 12-char hex, used as object-map key |
|
|
165
183
|
| `slug` | Document | Directory name in content path |
|
|
166
184
|
| `createdAt` / `updatedAt` | All | Derived from Git commit history (not stored) |
|
|
167
|
-
| `status`, `source`, `updated_by`, `approved_by` | All | Stored in `.contentrain/meta/` |
|
|
185
|
+
| `status`, `source`, `updated_by`, `updated_at`, `approved_by` | All | Stored in `.contentrain/meta/` |
|
|
168
186
|
|
|
169
187
|
---
|
|
170
188
|
|