@contentrain/skills 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -60
- package/dist/index.cjs +65 -1
- package/dist/index.d.cts +50 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +50 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +65 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
- package/skills/contentrain/SKILL.md +149 -0
- package/skills/contentrain/references/architecture.md +212 -0
- package/skills/contentrain/references/content-formats.md +279 -0
- package/skills/contentrain/references/i18n.md +298 -0
- package/skills/contentrain/references/mcp-pipelines.md +195 -0
- package/skills/contentrain/references/mcp-tools.md +308 -0
- package/skills/contentrain/references/model-kinds.md +330 -0
- package/skills/contentrain/references/schema-types.md +177 -0
- package/skills/contentrain/references/security.md +146 -0
- package/skills/contentrain/references/workflow.md +285 -0
- package/skills/contentrain-bulk/SKILL.md +99 -0
- package/skills/contentrain-content/SKILL.md +162 -0
- package/skills/contentrain-diff/SKILL.md +62 -0
- package/skills/contentrain-doctor/SKILL.md +62 -0
- package/skills/contentrain-generate/SKILL.md +183 -0
- package/skills/contentrain-generate/references/generated-client.md +198 -0
- package/skills/contentrain-init/SKILL.md +99 -0
- package/skills/contentrain-model/SKILL.md +141 -0
- package/skills/contentrain-normalize/SKILL.md +185 -0
- package/skills/contentrain-normalize/references/extraction.md +164 -0
- package/skills/contentrain-normalize/references/reuse.md +146 -0
- package/skills/contentrain-normalize/references/what-is-content.md +115 -0
- package/skills/contentrain-quality/SKILL.md +180 -0
- package/skills/contentrain-quality/references/accessibility.md +160 -0
- package/skills/contentrain-quality/references/content-quality.md +299 -0
- package/skills/contentrain-quality/references/media.md +170 -0
- package/skills/contentrain-quality/references/seo.md +229 -0
- package/skills/contentrain-review/SKILL.md +170 -0
- package/skills/contentrain-sdk/SKILL.md +145 -0
- package/skills/contentrain-sdk/references/bundler-config.md +135 -0
- package/skills/contentrain-serve/SKILL.md +96 -0
- package/skills/contentrain-translate/SKILL.md +180 -0
- package/skills/contentrain-validate-fix/SKILL.md +92 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architecture
|
|
3
|
+
description: "Contentrain schema and type architecture overview including design principles, type system, model definitions, and storage patterns."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contentrain Architecture
|
|
7
|
+
|
|
8
|
+
## Design Principles
|
|
9
|
+
|
|
10
|
+
1. **Token-efficient:** Flat type system with single keywords. No `format` sub-layer. `type: "email"` is the complete specification.
|
|
11
|
+
2. **AI-first:** Easy to produce, cheap to read, fast to validate. Minimal properties, omit defaults.
|
|
12
|
+
3. **Platform-agnostic:** MCP tools are framework-agnostic. Agent provides stack-specific intelligence.
|
|
13
|
+
4. **Git-native:** All content is Git-tracked. Object-map storage with sorted keys for clean diffs.
|
|
14
|
+
5. **Deterministic:** Canonical serialization ensures identical data always produces identical files.
|
|
15
|
+
|
|
16
|
+
## Type System Overview
|
|
17
|
+
|
|
18
|
+
Contentrain uses a **flat type system** with 27 types organized into 7 families. Each type is a single keyword.
|
|
19
|
+
|
|
20
|
+
| Family | Types | Count |
|
|
21
|
+
|--------|-------|-------|
|
|
22
|
+
| String | `string`, `text`, `email`, `url`, `slug`, `color`, `phone`, `code`, `icon`, `markdown`, `richtext` | 11 |
|
|
23
|
+
| Number | `number`, `integer`, `decimal`, `percent`, `rating` | 5 |
|
|
24
|
+
| Primitives | `boolean`, `date`, `datetime` | 3 |
|
|
25
|
+
| Media | `image`, `video`, `file` | 3 |
|
|
26
|
+
| Relations | `relation`, `relations` | 2 |
|
|
27
|
+
| Structural | `select`, `array`, `object` | 3 |
|
|
28
|
+
|
|
29
|
+
**Total: 27 types**
|
|
30
|
+
|
|
31
|
+
See [Schema Types](schema-types.md) for the complete catalog with validation rules and JSON Schema exports.
|
|
32
|
+
|
|
33
|
+
## Field Definition
|
|
34
|
+
|
|
35
|
+
A field definition describes one field in a model. Include only properties that apply -- omit all defaults.
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"type": "string",
|
|
40
|
+
"required": true,
|
|
41
|
+
"min": 3,
|
|
42
|
+
"max": 100,
|
|
43
|
+
"unique": true,
|
|
44
|
+
"description": "Page title"
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Key Properties
|
|
49
|
+
|
|
50
|
+
| Property | Applicable Types | Description |
|
|
51
|
+
|----------|-----------------|-------------|
|
|
52
|
+
| `type` | ALL | **Required.** One of the 27 types |
|
|
53
|
+
| `required` | ALL | Mark field as mandatory. Default: `false`. Omit if `false` |
|
|
54
|
+
| `unique` | `string`, `email`, `slug`, `integer` | Enforce uniqueness. Default: `false`. Omit if `false` |
|
|
55
|
+
| `default` | ALL | Default value. Omit if `null` |
|
|
56
|
+
| `min` / `max` | strings (chars), numbers (value), arrays (elements) | Constraints |
|
|
57
|
+
| `pattern` | `string`, `text`, `code` | Regex validation |
|
|
58
|
+
| `options` | `select` ONLY | Fixed choices array |
|
|
59
|
+
| `model` | `relation`, `relations` ONLY | Target model ID (string or string array for polymorphic) |
|
|
60
|
+
| `items` | `array` ONLY | Element type definition |
|
|
61
|
+
| `fields` | `object` ONLY | Nested field definitions |
|
|
62
|
+
| `accept` | `image`, `video`, `file` | Allowed MIME types |
|
|
63
|
+
| `maxSize` | `image`, `video`, `file` | Max file size in bytes |
|
|
64
|
+
| `description` | ALL | Human-readable hint |
|
|
65
|
+
|
|
66
|
+
### Omission Rules
|
|
67
|
+
|
|
68
|
+
- `required: false` (default) --> do NOT include
|
|
69
|
+
- `unique: false` (default) --> do NOT include
|
|
70
|
+
- `default: null` --> do NOT include
|
|
71
|
+
- Fewer properties = fewer tokens = faster agent processing
|
|
72
|
+
|
|
73
|
+
## Model Definition
|
|
74
|
+
|
|
75
|
+
Model definitions live at `.contentrain/models/{model-id}.json`. One file per model.
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"id": "blog-post",
|
|
80
|
+
"name": "Blog Post",
|
|
81
|
+
"kind": "document",
|
|
82
|
+
"domain": "blog",
|
|
83
|
+
"i18n": true,
|
|
84
|
+
"description": "Blog articles with markdown body",
|
|
85
|
+
"fields": {
|
|
86
|
+
"title": { "type": "string", "required": true, "max": 120 },
|
|
87
|
+
"slug": { "type": "slug", "required": true, "unique": true },
|
|
88
|
+
"author": { "type": "relation", "model": "team-members", "required": true },
|
|
89
|
+
"tags": { "type": "array", "items": "string" }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Model Properties
|
|
95
|
+
|
|
96
|
+
| Property | Type | Required | Description |
|
|
97
|
+
|----------|------|----------|-------------|
|
|
98
|
+
| `id` | string | Yes | Unique identifier, **kebab-case** |
|
|
99
|
+
| `name` | string | Yes | Human-readable display name |
|
|
100
|
+
| `kind` | string | Yes | `singleton`, `collection`, `document`, `dictionary` |
|
|
101
|
+
| `domain` | string | Yes | Organizational group (maps to content subdirectory) |
|
|
102
|
+
| `i18n` | boolean | Yes | Whether model supports multiple locales |
|
|
103
|
+
| `description` | string | No | Model description for docs and agent context |
|
|
104
|
+
| `fields` | object | Yes (except dictionary) | Field definitions. Dictionary has NO fields |
|
|
105
|
+
| `content_path` | string | No | Framework-relative path. When set, content writes here instead of `.contentrain/content/` |
|
|
106
|
+
| `locale_strategy` | string | No | How locale is encoded: `"file"` (default), `"suffix"`, `"directory"`, `"none"` |
|
|
107
|
+
|
|
108
|
+
## The Four Kinds
|
|
109
|
+
|
|
110
|
+
### Singleton
|
|
111
|
+
One instance per locale. JSON object.
|
|
112
|
+
```json
|
|
113
|
+
{ "cta": "Get Started", "title": "Build faster" }
|
|
114
|
+
```
|
|
115
|
+
Path: `content/{domain}/{model-id}/{locale}.json`
|
|
116
|
+
|
|
117
|
+
### Collection
|
|
118
|
+
Multiple entries. Object-map with entry ID as key, sorted lexicographically.
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"a1b2c3d4e5f6": { "name": "Ahmet", "role": "CEO" },
|
|
122
|
+
"f6e5d4c3b2a1": { "name": "Jane", "role": "CTO" }
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
Path: `content/{domain}/{model-id}/{locale}.json`
|
|
126
|
+
MCP tools return collections as **arrays** with `id` injected.
|
|
127
|
+
|
|
128
|
+
### Document
|
|
129
|
+
Markdown with YAML frontmatter. One file per slug per locale.
|
|
130
|
+
```markdown
|
|
131
|
+
---
|
|
132
|
+
title: Getting Started
|
|
133
|
+
slug: getting-started
|
|
134
|
+
---
|
|
135
|
+
# Getting Started with Contentrain
|
|
136
|
+
```
|
|
137
|
+
Path: `content/{domain}/{model-id}/{slug}/{locale}.md`
|
|
138
|
+
|
|
139
|
+
### Dictionary
|
|
140
|
+
Flat key-value. All values are strings. No `fields` property in model definition.
|
|
141
|
+
```json
|
|
142
|
+
{ "auth.expired": "Session expired", "auth.failed": "Authentication failed" }
|
|
143
|
+
```
|
|
144
|
+
Path: `content/{domain}/{model-id}/{locale}.json`
|
|
145
|
+
|
|
146
|
+
## Entry ID Generation
|
|
147
|
+
|
|
148
|
+
- **Format:** 12-character hexadecimal string (e.g., `a1b2c3d4e5f6`)
|
|
149
|
+
- **Scope:** Collection entries only
|
|
150
|
+
- **Generation:** Auto-generated by MCP tools. Agents never create IDs manually
|
|
151
|
+
- **Usage:** Object-map key on disk, injected as `id` field in MCP tool output
|
|
152
|
+
- **Locale-agnostic:** Same ID used across all locale files for the same entry
|
|
153
|
+
|
|
154
|
+
## Object-Map Rationale
|
|
155
|
+
|
|
156
|
+
Collections use object-map storage (`{ entryId: { fields } }`) instead of arrays because:
|
|
157
|
+
|
|
158
|
+
1. **Sorted keys produce predictable diffs** -- adding entries at deterministic positions
|
|
159
|
+
2. **Minimizes Git merge conflicts** -- two branches adding different entries rarely conflict (~0.3% chance for 350-entry collection)
|
|
160
|
+
3. **No ID duplication** -- key IS the ID, no redundant `id` field inside entries
|
|
161
|
+
4. **Consistent with metadata files** -- same structure in `.contentrain/meta/`
|
|
162
|
+
|
|
163
|
+
## Locale Strategy Rules
|
|
164
|
+
|
|
165
|
+
The `locale_strategy` property controls how locale is encoded in file paths:
|
|
166
|
+
|
|
167
|
+
| Strategy | i18n:true JSON path | i18n:true Document path | i18n:false path |
|
|
168
|
+
|----------|---------------------|------------------------|-----------------|
|
|
169
|
+
| `file` (default) | `{dir}/{locale}.json` | `{dir}/{slug}/{locale}.md` | `{dir}/data.json` |
|
|
170
|
+
| `suffix` | `{dir}/{model}.{locale}.json` | `{dir}/{slug}.{locale}.md` | `{dir}/data.json` |
|
|
171
|
+
| `directory` | `{dir}/{locale}/{model}.json` | `{dir}/{locale}/{slug}.md` | `{dir}/data.json` |
|
|
172
|
+
| `none` | **INVALID** | **INVALID** | `{dir}/{model}.json` or `{dir}/{slug}.md` |
|
|
173
|
+
|
|
174
|
+
- `locale_strategy: "none"` requires `i18n: false`
|
|
175
|
+
- When `content_path` is set, `{dir}` is the content_path. Otherwise `{dir}` is `.contentrain/content/{domain}/{model-id}`
|
|
176
|
+
|
|
177
|
+
## Nesting Limits
|
|
178
|
+
|
|
179
|
+
**Maximum nesting depth: 2 levels.**
|
|
180
|
+
|
|
181
|
+
An `object` inside an `object` is allowed. An `object` inside an `object` inside an `object` is NOT.
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
"address": {
|
|
185
|
+
"type": "object",
|
|
186
|
+
"fields": {
|
|
187
|
+
"city": { "type": "string", "required": true },
|
|
188
|
+
"coordinates": {
|
|
189
|
+
"type": "object",
|
|
190
|
+
"fields": {
|
|
191
|
+
"lat": { "type": "decimal" },
|
|
192
|
+
"lng": { "type": "decimal" }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
- Prefer flat types over deeply nested structures
|
|
200
|
+
- Use relations to model complex data relationships instead of nesting
|
|
201
|
+
- If you need deeper nesting, create a separate model and use a `relation` field
|
|
202
|
+
|
|
203
|
+
## Naming Conventions
|
|
204
|
+
|
|
205
|
+
| Element | Convention | Example |
|
|
206
|
+
|---------|-----------|---------|
|
|
207
|
+
| Model ID | kebab-case | `blog-post`, `team-members` |
|
|
208
|
+
| Field key | snake_case | `hero_image`, `cta_url` |
|
|
209
|
+
| Domain | lowercase, single word or kebab-case | `blog`, `marketing`, `system` |
|
|
210
|
+
| Dictionary key | dot-notation | `auth.failed`, `validation.required` |
|
|
211
|
+
| Slug | lowercase, kebab-case | `getting-started` |
|
|
212
|
+
| Locale | ISO 639-1 | `en`, `tr`, `de` |
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: content-formats
|
|
3
|
+
description: "Directory structure, config.json, content file formats, canonical serialization, system fields, and localization rules."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Content Formats Reference
|
|
7
|
+
|
|
8
|
+
## Directory Structure
|
|
9
|
+
|
|
10
|
+
All Contentrain data lives under `.contentrain/` at the project root.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
.contentrain/
|
|
14
|
+
config.json # Project configuration (stack, workflow, locales, domains)
|
|
15
|
+
vocabulary.json # Canonical terms for consistent terminology
|
|
16
|
+
context.json # Project intelligence -- MCP writes, agents READ ONLY
|
|
17
|
+
assets.json # Media asset registry
|
|
18
|
+
models/
|
|
19
|
+
{model-id}.json # Model definitions (one file per model)
|
|
20
|
+
content/
|
|
21
|
+
{domain}/
|
|
22
|
+
{model-id}/
|
|
23
|
+
en.json # Content per locale (i18n: true)
|
|
24
|
+
tr.json
|
|
25
|
+
data.json # Content without locale (i18n: false)
|
|
26
|
+
{slug}/ # Document kind only
|
|
27
|
+
en.md
|
|
28
|
+
tr.md
|
|
29
|
+
meta/
|
|
30
|
+
{model-id}/
|
|
31
|
+
{locale}.json # System-managed metadata
|
|
32
|
+
{slug}/ # Document kind only
|
|
33
|
+
{locale}.json
|
|
34
|
+
client/ # SDK generated client -- auto-generated, NEVER edit
|
|
35
|
+
assets/ # Media files (images, videos, documents)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Critical Boundaries
|
|
39
|
+
|
|
40
|
+
- **NEVER write to `.contentrain/meta/`** -- metadata is system-managed
|
|
41
|
+
- **NEVER edit `.contentrain/client/`** -- auto-generated by `contentrain generate`
|
|
42
|
+
- **NEVER edit `.contentrain/context.json`** -- MCP writes it; agents read it
|
|
43
|
+
- **ALWAYS use MCP tools** to create/update content and models. Do not write JSON files directly
|
|
44
|
+
|
|
45
|
+
## config.json Structure
|
|
46
|
+
|
|
47
|
+
Created by `contentrain_init`. Controls global behavior.
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"version": 1,
|
|
52
|
+
"stack": "nuxt",
|
|
53
|
+
"workflow": "auto-merge",
|
|
54
|
+
"repository": {
|
|
55
|
+
"provider": "github",
|
|
56
|
+
"owner": "contentrain",
|
|
57
|
+
"name": "demo",
|
|
58
|
+
"default_branch": "main"
|
|
59
|
+
},
|
|
60
|
+
"locales": {
|
|
61
|
+
"default": "en",
|
|
62
|
+
"supported": ["en", "tr"]
|
|
63
|
+
},
|
|
64
|
+
"domains": ["blog", "marketing", "system"],
|
|
65
|
+
"assets_path": ".contentrain/assets",
|
|
66
|
+
"branchRetention": 30
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Field Reference
|
|
71
|
+
|
|
72
|
+
| Field | Type | Description |
|
|
73
|
+
|-------|------|-------------|
|
|
74
|
+
| `version` | number | Config schema version. Always `1` |
|
|
75
|
+
| `stack` | string | Framework: `"nuxt"`, `"next"`, `"astro"`, `"sveltekit"`, `"react"`, `"node"` |
|
|
76
|
+
| `workflow` | string | `"auto-merge"` (solo) or `"review"` (team governance) |
|
|
77
|
+
| `repository` | object | Git remote info. Provider is `"github"` in v1 |
|
|
78
|
+
| `locales.default` | string | Primary locale (ISO 639-1) |
|
|
79
|
+
| `locales.supported` | string[] | All locales including default |
|
|
80
|
+
| `domains` | string[] | Organizational groups (e.g., `"blog"`, `"marketing"`, `"system"`) |
|
|
81
|
+
| `assets_path` | string | Path for media files |
|
|
82
|
+
| `branchRetention` | number | Days to keep merged branches |
|
|
83
|
+
|
|
84
|
+
## Content File Formats by Kind
|
|
85
|
+
|
|
86
|
+
### Singleton
|
|
87
|
+
|
|
88
|
+
One JSON object per locale file. No `id` field.
|
|
89
|
+
|
|
90
|
+
**Path:** `.contentrain/content/{domain}/{model-id}/{locale}.json`
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"cta": "Get Started",
|
|
95
|
+
"subtitle": "The modern content platform",
|
|
96
|
+
"title": "Build faster"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Collection
|
|
101
|
+
|
|
102
|
+
Object-map on disk: keys are entry IDs, sorted lexicographically. The `id` field is the key itself and is NOT stored inside the entry object.
|
|
103
|
+
|
|
104
|
+
**Path:** `.contentrain/content/{domain}/{model-id}/{locale}.json`
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"a1b2c3d4e5f6": {
|
|
109
|
+
"avatar": "assets/ahmet.jpg",
|
|
110
|
+
"name": "Ahmet",
|
|
111
|
+
"role": "CEO"
|
|
112
|
+
},
|
|
113
|
+
"f6e5d4c3b2a1": {
|
|
114
|
+
"avatar": "assets/jane.jpg",
|
|
115
|
+
"name": "Jane",
|
|
116
|
+
"role": "CTO"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**MCP output format differs from storage:** MCP tools return collections as arrays with `id` injected.
|
|
122
|
+
|
|
123
|
+
### Document
|
|
124
|
+
|
|
125
|
+
Markdown with YAML-like frontmatter. One file per slug per locale.
|
|
126
|
+
|
|
127
|
+
**Path:** `.contentrain/content/{domain}/{model-id}/{slug}/{locale}.md`
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
---
|
|
131
|
+
title: Getting Started
|
|
132
|
+
slug: getting-started
|
|
133
|
+
author: a1b2c3d4e5f6
|
|
134
|
+
tags: [tutorial, intro]
|
|
135
|
+
---
|
|
136
|
+
# Getting Started with Contentrain
|
|
137
|
+
|
|
138
|
+
Your markdown body content here...
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Dictionary
|
|
142
|
+
|
|
143
|
+
Flat key-value JSON per locale. No `fields` in model definition -- all values are strings.
|
|
144
|
+
|
|
145
|
+
**Path:** `.contentrain/content/{domain}/{model-id}/{locale}.json`
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"auth.expired": "Session expired",
|
|
150
|
+
"auth.failed": "Authentication failed",
|
|
151
|
+
"validation.required": "{field} is required"
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Canonical Serialization Rules
|
|
156
|
+
|
|
157
|
+
ALL JSON files in `.contentrain/` MUST follow these 7 rules:
|
|
158
|
+
|
|
159
|
+
1. **Keys sorted lexicographically** within every object (including nested objects)
|
|
160
|
+
2. **2-space indent** for all nesting levels
|
|
161
|
+
3. **UTF-8 encoding** without BOM
|
|
162
|
+
4. **Trailing newline** at end of file (single `\n` after closing brace/bracket)
|
|
163
|
+
5. **Omit null values** -- do not write `"field": null`
|
|
164
|
+
6. **Omit default values** -- if `required` is `false`, do not include it; if `default` is `null`, do not include it
|
|
165
|
+
7. **Storage uses lexicographic sort** for determinism (field key order in content follows sort order)
|
|
166
|
+
|
|
167
|
+
### Why This Matters
|
|
168
|
+
|
|
169
|
+
- Deterministic output means identical data always produces identical files
|
|
170
|
+
- Sorted keys prevent artificial Git diffs from key reordering
|
|
171
|
+
- Clean diffs enable meaningful code review of content changes
|
|
172
|
+
|
|
173
|
+
## System Fields
|
|
174
|
+
|
|
175
|
+
Several fields are managed by the platform. Agents MUST NOT set or modify them.
|
|
176
|
+
|
|
177
|
+
| Field | Where | Managed By |
|
|
178
|
+
|-------|-------|------------|
|
|
179
|
+
| `id` | Collection entry key | Auto-generated 12-char hex UUID |
|
|
180
|
+
| `slug` | Document directory name | Derived from content or set by agent via `slug` field |
|
|
181
|
+
| `createdAt` | Not stored | Derived from Git commit history |
|
|
182
|
+
| `updatedAt` | Not stored | Derived from Git commit history |
|
|
183
|
+
| `status` | `.contentrain/meta/` | Platform workflow engine |
|
|
184
|
+
| `source` | `.contentrain/meta/` | Set by MCP: `"agent"`, `"human"`, or `"import"` |
|
|
185
|
+
| `updated_by` | `.contentrain/meta/` | Set by MCP: agent name or user email |
|
|
186
|
+
| `approved_by` | `.contentrain/meta/` | Set by Studio on review approval |
|
|
187
|
+
|
|
188
|
+
**Rule:** When creating content via `contentrain_content_save`, provide only the content fields defined in the model. Do not include `id`, `createdAt`, `updatedAt`, `status`, or any metadata fields.
|
|
189
|
+
|
|
190
|
+
## Localization Rules
|
|
191
|
+
|
|
192
|
+
| Rule | Detail |
|
|
193
|
+
|------|--------|
|
|
194
|
+
| `i18n: true` on model | Each locale gets its own file: `en.json`, `tr.json`, etc. |
|
|
195
|
+
| `i18n: false` on model | Single file: `data.json` (or `{slug}.md` for documents) |
|
|
196
|
+
| Locale code | ISO 639-1: `en`, `tr`, `de`, `fr`, `ja`, `ar` |
|
|
197
|
+
| ID/slug are locale-agnostic | Same entry ID or document slug across all locales |
|
|
198
|
+
| Entry parity (collections) | All locales MUST have the same set of entry IDs |
|
|
199
|
+
| Key parity (dictionaries) | All locales MUST have the same set of keys |
|
|
200
|
+
|
|
201
|
+
### Validation Checks
|
|
202
|
+
|
|
203
|
+
| Check | Severity | Example |
|
|
204
|
+
|-------|----------|---------|
|
|
205
|
+
| Missing locale file | Error | `hero: tr.json missing` |
|
|
206
|
+
| Collection entry ID mismatch | Error | `team-members: entry "member-2" missing in tr` |
|
|
207
|
+
| Dictionary key missing | Warning | `error-messages: tr missing 5 keys` |
|
|
208
|
+
| Document missing translation | Warning | `blog-post: "getting-started" has no tr translation` |
|
|
209
|
+
|
|
210
|
+
## Vocabulary
|
|
211
|
+
|
|
212
|
+
The `vocabulary.json` file provides canonical terms for consistent content.
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"version": 1,
|
|
217
|
+
"terms": {
|
|
218
|
+
"email": { "en": "Email", "tr": "E-posta" },
|
|
219
|
+
"sign-up": { "en": "Sign Up", "tr": "Kayit Ol" }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Rules
|
|
225
|
+
|
|
226
|
+
- Check vocabulary FIRST before writing any content. Use existing terms when they match
|
|
227
|
+
- Add new terms to the vocabulary when creating content with reusable terminology
|
|
228
|
+
- Term keys are kebab-case identifiers
|
|
229
|
+
- Each term maps to locale-specific approved strings
|
|
230
|
+
- Vocabulary ensures consistency across models, locales, and agent sessions
|
|
231
|
+
|
|
232
|
+
## context.json
|
|
233
|
+
|
|
234
|
+
MCP writes `context.json` after every write operation. Provides project intelligence for agents and Studio synchronization.
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"version": "1.0",
|
|
239
|
+
"lastOperation": {
|
|
240
|
+
"tool": "content_save",
|
|
241
|
+
"model": "blog-post",
|
|
242
|
+
"locale": "en",
|
|
243
|
+
"entries": ["a1b2c3d4e5f6"],
|
|
244
|
+
"timestamp": "2026-03-11T14:30:00Z",
|
|
245
|
+
"source": "mcp-local"
|
|
246
|
+
},
|
|
247
|
+
"stats": {
|
|
248
|
+
"models": 5,
|
|
249
|
+
"entries": 142,
|
|
250
|
+
"locales": ["en", "tr"],
|
|
251
|
+
"lastSync": "2026-03-11T14:30:00Z"
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Rules
|
|
257
|
+
|
|
258
|
+
- **Read only** -- agents MUST NOT write to this file
|
|
259
|
+
- **Use it for awareness** -- check last operation, entry counts, locale coverage
|
|
260
|
+
- `source` values: `"mcp-local"` (IDE), `"mcp-studio"` (Studio server-side), `"studio-ui"` (Studio direct)
|
|
261
|
+
- Updated only on write operations (not reads)
|
|
262
|
+
- Git-tracked within `.contentrain/`
|
|
263
|
+
|
|
264
|
+
## Assets
|
|
265
|
+
|
|
266
|
+
Media files are stored in the configured `assets_path`. The `assets.json` file tracks registered assets.
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
[
|
|
270
|
+
{ "path": "assets/hero.webp", "type": "image/webp", "size": 245680, "alt": "Hero background" }
|
|
271
|
+
]
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Rules for Media References
|
|
275
|
+
|
|
276
|
+
- Use relative paths from the `.contentrain/` root in content fields
|
|
277
|
+
- Media field types (`image`, `video`, `file`) store string path references
|
|
278
|
+
- In v1, media fields are URL/path strings only. Upload and processing are out of scope
|
|
279
|
+
- Always provide `alt` text for images when the information is available
|