@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.
Files changed (42) hide show
  1. package/README.md +65 -60
  2. package/dist/index.cjs +65 -1
  3. package/dist/index.d.cts +50 -2
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +50 -2
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +65 -2
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +3 -1
  10. package/skills/contentrain/SKILL.md +149 -0
  11. package/skills/contentrain/references/architecture.md +212 -0
  12. package/skills/contentrain/references/content-formats.md +279 -0
  13. package/skills/contentrain/references/i18n.md +298 -0
  14. package/skills/contentrain/references/mcp-pipelines.md +195 -0
  15. package/skills/contentrain/references/mcp-tools.md +308 -0
  16. package/skills/contentrain/references/model-kinds.md +330 -0
  17. package/skills/contentrain/references/schema-types.md +177 -0
  18. package/skills/contentrain/references/security.md +146 -0
  19. package/skills/contentrain/references/workflow.md +285 -0
  20. package/skills/contentrain-bulk/SKILL.md +99 -0
  21. package/skills/contentrain-content/SKILL.md +162 -0
  22. package/skills/contentrain-diff/SKILL.md +62 -0
  23. package/skills/contentrain-doctor/SKILL.md +62 -0
  24. package/skills/contentrain-generate/SKILL.md +183 -0
  25. package/skills/contentrain-generate/references/generated-client.md +198 -0
  26. package/skills/contentrain-init/SKILL.md +99 -0
  27. package/skills/contentrain-model/SKILL.md +141 -0
  28. package/skills/contentrain-normalize/SKILL.md +185 -0
  29. package/skills/contentrain-normalize/references/extraction.md +164 -0
  30. package/skills/contentrain-normalize/references/reuse.md +146 -0
  31. package/skills/contentrain-normalize/references/what-is-content.md +115 -0
  32. package/skills/contentrain-quality/SKILL.md +180 -0
  33. package/skills/contentrain-quality/references/accessibility.md +160 -0
  34. package/skills/contentrain-quality/references/content-quality.md +299 -0
  35. package/skills/contentrain-quality/references/media.md +170 -0
  36. package/skills/contentrain-quality/references/seo.md +229 -0
  37. package/skills/contentrain-review/SKILL.md +170 -0
  38. package/skills/contentrain-sdk/SKILL.md +145 -0
  39. package/skills/contentrain-sdk/references/bundler-config.md +135 -0
  40. package/skills/contentrain-serve/SKILL.md +96 -0
  41. package/skills/contentrain-translate/SKILL.md +180 -0
  42. 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