@contentrain/rules 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Contentrain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # `@contentrain/rules`
2
+
3
+ Shared AI-agent rules for Contentrain.
4
+
5
+ This package is the policy layer of the Contentrain ecosystem. It defines how agents should behave when they work with:
6
+
7
+ - Contentrain MCP tools
8
+ - schema and model design
9
+ - content quality
10
+ - normalize workflows
11
+ - git-backed review flows
12
+ - IDE rule distribution
13
+
14
+ If `@contentrain/mcp` is the deterministic execution layer, `@contentrain/rules` is the behavioral contract.
15
+
16
+ ## 🚀 Install
17
+
18
+ ```bash
19
+ pnpm add @contentrain/rules
20
+ ```
21
+
22
+ ## 📦 What It Contains
23
+
24
+ ### Shared rule sets
25
+
26
+ Published under `shared/*`:
27
+
28
+ - `content-quality.md`
29
+ - `seo-rules.md`
30
+ - `i18n-quality.md`
31
+ - `accessibility-rules.md`
32
+ - `security-rules.md`
33
+ - `media-rules.md`
34
+ - `content-conventions.md`
35
+ - `schema-rules.md`
36
+ - `mcp-usage.md`
37
+ - `workflow-rules.md`
38
+ - `normalize-rules.md`
39
+
40
+ ### Prompt layers
41
+
42
+ Published under `prompts/*`:
43
+
44
+ - `common.md`
45
+ - `generate-mode.md`
46
+ - `normalize-mode.md`
47
+ - `review-mode.md`
48
+
49
+ ### Context bridge
50
+
51
+ Published under `context/*`:
52
+
53
+ - `context-bridge.md`
54
+
55
+ ### IDE bundles
56
+
57
+ Published under `ide/*`:
58
+
59
+ - `ide/claude-code/contentrain.md`
60
+ - `ide/cursor/contentrain.cursorrules`
61
+ - `ide/windsurf/contentrain.windsurfrules`
62
+ - `ide/generic/contentrain.md`
63
+
64
+ ## 🧰 Public Exports
65
+
66
+ The package root exports constants that can be used by tooling:
67
+
68
+ - `FIELD_TYPES`
69
+ - `MODEL_KINDS`
70
+ - `MCP_TOOLS`
71
+ - `CONTENT_QUALITY_RULES`
72
+ - `ARCHITECTURE_RULES`
73
+ - `ALL_SHARED_RULES`
74
+ - `IDE_RULE_FILES`
75
+ - `STACKS`
76
+
77
+ ## 🧪 Example
78
+
79
+ ```ts
80
+ import { IDE_RULE_FILES, MCP_TOOLS, ALL_SHARED_RULES } from '@contentrain/rules'
81
+
82
+ console.log(MCP_TOOLS.includes('contentrain_validate'))
83
+ console.log(IDE_RULE_FILES['claude-code'])
84
+ console.log(ALL_SHARED_RULES)
85
+ ```
86
+
87
+ ## 🧠 Design Role
88
+
89
+ `@contentrain/rules` exists to keep agent behavior aligned across tools and environments.
90
+
91
+ It should answer questions like:
92
+
93
+ - What is acceptable content quality?
94
+ - How should an agent use MCP tools?
95
+ - What is the normalize contract?
96
+ - What workflow and review constraints exist?
97
+ - How should rules be packaged for Claude Code, Cursor, Windsurf, and generic agents?
98
+
99
+ ## 🔗 Relationship To Other Packages
100
+
101
+ - `@contentrain/mcp` enforces file, validation, and git behavior
102
+ - `@contentrain/skills` provides step-by-step workflow playbooks
103
+ - `contentrain` exposes CLI and serve UX
104
+ - `@contentrain/query` is the generated runtime consumption layer
105
+
106
+ Rule of thumb:
107
+
108
+ - `rules` = policy and constraints
109
+ - `skills` = procedures and playbooks
110
+
111
+ ## 🛠 Build
112
+
113
+ The build does two things:
114
+
115
+ 1. builds the typed JS entry from `src/index.ts`
116
+ 2. generates IDE bundle files from the shared markdown rules
117
+
118
+ From the monorepo root:
119
+
120
+ ```bash
121
+ pnpm --filter @contentrain/rules build
122
+ pnpm --filter @contentrain/rules test
123
+ pnpm --filter @contentrain/rules typecheck
124
+ ```
125
+
126
+ ## 📄 License
127
+
128
+ MIT
@@ -0,0 +1,241 @@
1
+ # Context Bridge — `.contentrain/context.json` Specification
2
+
3
+ ## Purpose
4
+
5
+ `context.json` is the project intelligence file. It provides AI agents and Contentrain Studio with structured knowledge about the project: its tech stack, content patterns, conventions, and normalize configuration.
6
+
7
+ - **Written by:** MCP tools (updated after every write operation)
8
+ - **Read by:** AI agents (at the start of every session) and Studio (for UI configuration)
9
+ - **Location:** `.contentrain/context.json`
10
+
11
+ ---
12
+
13
+ ## Agent Reading Protocol
14
+
15
+ Follow this exact sequence when starting any session:
16
+
17
+ ```
18
+ 1. config.json — Project configuration (stack, locales, workflow, domains)
19
+ 2. context.json — Project intelligence (conventions, scan settings, content patterns)
20
+ 3. vocabulary.json — Canonical terms, brand terms, forbidden terms
21
+ 4. models/ — Model definitions (contentrain_status provides a summary)
22
+ 5. content/ — Existing content (contentrain_describe provides samples)
23
+ ```
24
+
25
+ ### When `context.json` Does Not Exist
26
+
27
+ If the file does not exist, use defaults from `config.json`:
28
+ - `stack`: from `config.json > stack`
29
+ - `locales`: from `config.json > locales`
30
+ - `tone`: default to `"professional"`
31
+ - `scan_dirs`: default to `["src"]` or `["."]` depending on stack
32
+ - `scan_extensions`: default to framework-appropriate extensions
33
+
34
+ ### Override Priority
35
+
36
+ `context.json` values take priority over static rule defaults. For example:
37
+ - If `context.json > conventions.max_title_length` is set to `80`, use `80` instead of the default `60` from `content-quality.md`.
38
+ - If `context.json > conventions.allowed_richtext_tags` is defined, use that list instead of the default from `security-rules.md`.
39
+ - Static rules are the fallback when `context.json` does not specify a value.
40
+
41
+ ---
42
+
43
+ ## Schema
44
+
45
+ ```json
46
+ {
47
+ "version": 1,
48
+ "project": {
49
+ "name": "my-project",
50
+ "description": "Project description for agent context",
51
+ "stack": "nuxt",
52
+ "stack_version": "3.x",
53
+ "package_manager": "pnpm",
54
+ "src_dir": ".",
55
+ "content_consumption": "build-time"
56
+ },
57
+ "content": {
58
+ "import_pattern": "import {model} from '~/.contentrain/content/{domain}/{model}/{locale}.json'",
59
+ "i18n_strategy": "file-per-locale",
60
+ "default_locale": "en",
61
+ "supported_locales": ["en", "tr"],
62
+ "domains": [
63
+ {
64
+ "id": "marketing",
65
+ "description": "Landing page content",
66
+ "models": ["hero", "features", "pricing"]
67
+ },
68
+ {
69
+ "id": "blog",
70
+ "description": "Blog posts and categories",
71
+ "models": ["blog-post", "categories"]
72
+ }
73
+ ]
74
+ },
75
+ "normalize": {
76
+ "scan_dirs": ["components", "pages", "layouts"],
77
+ "scan_extensions": [".vue", ".ts"],
78
+ "ignore_patterns": ["node_modules", ".nuxt", "dist", ".contentrain"],
79
+ "import_rewrite": "import { t } from '~/composables/useContent'"
80
+ },
81
+ "conventions": {
82
+ "tone": "Professional, concise. Avoid jargon.",
83
+ "brand_terms": {
84
+ "product_name": "Acme Platform",
85
+ "company_name": "Acme Inc"
86
+ },
87
+ "forbidden_terms": ["click here", "lorem ipsum", "TODO"],
88
+ "max_title_length": 80,
89
+ "max_description_length": 160,
90
+ "target_reading_level": "grade-8",
91
+ "allowed_richtext_tags": [
92
+ "p", "strong", "em", "a", "ul", "ol", "li",
93
+ "h2", "h3", "h4", "h5", "h6",
94
+ "blockquote", "code", "pre", "img", "br", "hr",
95
+ "table", "thead", "tbody", "tr", "th", "td",
96
+ "del", "ins", "sup", "sub", "abbr", "mark",
97
+ "details", "summary"
98
+ ]
99
+ },
100
+ "lastOperation": {
101
+ "tool": "content_save",
102
+ "model": "blog-post",
103
+ "locale": "en",
104
+ "entries": ["a1b2c3d4e5f6"],
105
+ "timestamp": "2026-03-11T14:30:00Z",
106
+ "source": "mcp-local"
107
+ },
108
+ "stats": {
109
+ "models": 5,
110
+ "entries": 142,
111
+ "locales": ["en", "tr"],
112
+ "lastSync": "2026-03-11T14:30:00Z"
113
+ }
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Field Reference
120
+
121
+ ### `version` (number)
122
+
123
+ Schema version. Currently `1`. Incremented on breaking schema changes.
124
+
125
+ ### `project` (object)
126
+
127
+ | Field | Type | Description |
128
+ |-------|------|-------------|
129
+ | `name` | string | Project name. Used in branch names and commit messages. |
130
+ | `description` | string | Brief description for agent context. Helps agents understand the project purpose. |
131
+ | `stack` | string | Framework: `nuxt`, `next`, `astro`, `sveltekit`, `react`, `vue`, `node`. |
132
+ | `stack_version` | string | Framework version: `"3.x"`, `"14.x"`, `"4.x"`. Affects API patterns. |
133
+ | `package_manager` | string | `npm`, `pnpm`, `yarn`, `bun`. Affects install commands in suggestions. |
134
+ | `src_dir` | string | Source directory relative to project root. `"."` for Nuxt, `"src"` for most others. |
135
+ | `content_consumption` | string | How content is consumed: `"build-time"`, `"runtime"`, `"hybrid"`. |
136
+
137
+ ### `content` (object)
138
+
139
+ | Field | Type | Description |
140
+ |-------|------|-------------|
141
+ | `import_pattern` | string | Content import template with `{model}`, `{domain}`, `{locale}` placeholders. |
142
+ | `i18n_strategy` | string | Localization strategy: `"file-per-locale"` or `"single-file"`. |
143
+ | `default_locale` | string | Default locale code (ISO 639-1): `"en"`, `"tr"`, `"de"`. |
144
+ | `supported_locales` | string[] | All supported locale codes. |
145
+ | `domains` | object[] | Content domain definitions. Each has `id` (string), `description` (string), and `models` (string[]). |
146
+
147
+ ### `normalize` (object)
148
+
149
+ | Field | Type | Description |
150
+ |-------|------|-------------|
151
+ | `scan_dirs` | string[] | Directories to scan for hardcoded strings (relative to project root). |
152
+ | `scan_extensions` | string[] | File extensions to include in scan. |
153
+ | `ignore_patterns` | string[] | Glob patterns to exclude from scanning. |
154
+ | `import_rewrite` | string | Import statement used when rewriting source files after normalization. |
155
+
156
+ ### `conventions` (object)
157
+
158
+ | Field | Type | Description |
159
+ |-------|------|-------------|
160
+ | `tone` | string | Content tone: `"professional"`, `"casual"`, `"technical"`, `"friendly"`, `"formal"`. |
161
+ | `brand_terms` | object | Key-value pairs of brand terms that must be used with exact casing and spelling. |
162
+ | `forbidden_terms` | string[] | Terms that must never appear in content. |
163
+ | `max_title_length` | number | Override for maximum title character count. Default: `60`. |
164
+ | `max_description_length` | number | Override for maximum description character count. Default: `160`. |
165
+ | `target_reading_level` | string | Target readability: `"grade-6"`, `"grade-8"`, `"grade-10"`, `"technical"`. |
166
+ | `allowed_richtext_tags` | string[] | Override for allowed HTML tags in richtext fields. |
167
+
168
+ ### `lastOperation` (object)
169
+
170
+ Updated after every MCP write operation. Agents can use this to understand what changed most recently.
171
+
172
+ | Field | Type | Description |
173
+ |-------|------|-------------|
174
+ | `tool` | string | MCP tool that performed the operation: `"content_save"`, `"model_save"`, `"scaffold"`, `"apply"`. |
175
+ | `model` | string | Model ID that was affected. |
176
+ | `locale` | string | Locale of the operation (if applicable). |
177
+ | `entries` | string[] | Entry IDs that were created or modified. |
178
+ | `timestamp` | string | ISO 8601 timestamp of the operation. |
179
+ | `source` | string | Origin: `"mcp-local"` (IDE), `"mcp-studio"` (Studio server-side MCP), `"studio-ui"` (Studio direct). |
180
+
181
+ ### `stats` (object)
182
+
183
+ Aggregate project statistics, updated after every write operation.
184
+
185
+ | Field | Type | Description |
186
+ |-------|------|-------------|
187
+ | `models` | number | Total number of models in the project. |
188
+ | `entries` | number | Total number of content entries across all models and locales. |
189
+ | `locales` | string[] | All locales that have content. |
190
+ | `lastSync` | string | ISO 8601 timestamp of the last sync operation. |
191
+
192
+ ---
193
+
194
+ ## Stack-Specific Defaults
195
+
196
+ When creating `context.json` for the first time, use stack-appropriate defaults. See template files in `context/templates/` for each supported stack:
197
+
198
+ | Stack | Template | `src_dir` | `scan_extensions` | `scan_dirs` |
199
+ |-------|----------|-----------|-------------------|-------------|
200
+ | Nuxt | `nuxt.context.json` | `"app/"` | `[".vue", ".ts"]` | `["app/components/", "app/pages/", "app/layouts/"]` |
201
+ | Next.js | `next.context.json` | `"src/"` | `[".tsx", ".ts", ".jsx"]` | `["src/components/", "src/app/"]` |
202
+ | Astro | `astro.context.json` | `"src/"` | `[".astro", ".ts", ".tsx"]` | `["src/components/", "src/pages/", "src/layouts/"]` |
203
+ | SvelteKit | `sveltekit.context.json` | `"src/"` | `[".svelte", ".ts"]` | `["src/lib/", "src/routes/"]` |
204
+
205
+ ---
206
+
207
+ ## Update Behavior
208
+
209
+ MCP tools update `context.json` after every write operation:
210
+ - `contentrain_init` creates the initial file
211
+ - `contentrain_model_save` updates the domains list if a new domain is used
212
+ - `contentrain_content_save` updates content stats
213
+ - `contentrain_apply` updates normalize state
214
+ - `contentrain_submit` updates last operation timestamp
215
+
216
+ Agents MUST NOT write to `context.json` directly. It is managed exclusively by MCP tools. Agents read it for configuration and intelligence.
217
+
218
+ ---
219
+
220
+ ## Example: Reading Context in an Agent Session
221
+
222
+ ```
223
+ 1. Agent receives user request: "Add a blog post about deployment"
224
+ 2. Agent reads config.json:
225
+ - stack: "nuxt"
226
+ - locales: ["en", "tr"]
227
+ - workflow: "review"
228
+ 3. Agent reads context.json:
229
+ - tone: "professional"
230
+ - i18n_strategy: "@nuxtjs/i18n"
231
+ - domains: ["blog", "marketing"]
232
+ 4. Agent reads vocabulary.json:
233
+ - brand_terms: ["Contentrain"]
234
+ - forbidden_terms: ["click here"]
235
+ 5. Agent calls contentrain_status:
236
+ - Models: blog-post (document), categories (collection), authors (collection)
237
+ 6. Agent calls contentrain_describe(model: "blog-post"):
238
+ - Fields: title (string, required), slug (slug, required, unique), excerpt (text), ...
239
+ 7. Agent creates content following all rules, using vocabulary terms, matching tone
240
+ 8. Agent validates and submits
241
+ ```
@@ -0,0 +1,74 @@
1
+ {
2
+ "version": 1,
3
+ "project": {
4
+ "name": "my-astro-site",
5
+ "description": "Content-driven marketing site with blog and portfolio",
6
+ "stack": "astro",
7
+ "stack_version": "5.x",
8
+ "package_manager": "pnpm",
9
+ "src_dir": "src/",
10
+ "content_consumption": "build-time"
11
+ },
12
+ "content": {
13
+ "import_pattern": "import {model} from '~/.contentrain/content/{domain}/{model}/{locale}.json'",
14
+ "i18n_strategy": "file-per-locale",
15
+ "default_locale": "en",
16
+ "supported_locales": ["en", "de"],
17
+ "domains": [
18
+ {
19
+ "id": "marketing",
20
+ "description": "Landing page content — hero, features, testimonials",
21
+ "models": ["hero", "features", "testimonials"]
22
+ },
23
+ {
24
+ "id": "blog",
25
+ "description": "Blog posts, authors, and categories",
26
+ "models": ["blog-post", "authors", "categories"]
27
+ },
28
+ {
29
+ "id": "portfolio",
30
+ "description": "Project showcase entries",
31
+ "models": ["projects"]
32
+ }
33
+ ]
34
+ },
35
+ "normalize": {
36
+ "scan_dirs": ["src/components/", "src/pages/", "src/layouts/"],
37
+ "scan_extensions": [".astro", ".ts", ".tsx"],
38
+ "ignore_patterns": ["dist/", "node_modules/", ".astro/", ".contentrain/", "*.test.*", "*.spec.*"],
39
+ "import_rewrite": "import { t } from '../i18n/utils'"
40
+ },
41
+ "conventions": {
42
+ "tone": "Friendly, informative. Approachable but professional.",
43
+ "brand_terms": {
44
+ "product_name": "My Astro Site",
45
+ "company_name": "Stellar Labs"
46
+ },
47
+ "forbidden_terms": ["click here", "lorem ipsum", "TODO"],
48
+ "max_title_length": 80,
49
+ "max_description_length": 160,
50
+ "target_reading_level": "grade-8",
51
+ "allowed_richtext_tags": [
52
+ "p", "strong", "em", "a", "ul", "ol", "li",
53
+ "h2", "h3", "h4", "h5", "h6",
54
+ "blockquote", "code", "pre", "img", "br", "hr",
55
+ "table", "thead", "tbody", "tr", "th", "td",
56
+ "del", "ins", "sup", "sub", "abbr", "mark",
57
+ "details", "summary"
58
+ ]
59
+ },
60
+ "lastOperation": {
61
+ "tool": "content_save",
62
+ "model": "blog-post",
63
+ "locale": "en",
64
+ "entries": ["c3d4e5f6a7b8"],
65
+ "timestamp": "2026-03-09T12:15:00Z",
66
+ "source": "mcp-local"
67
+ },
68
+ "stats": {
69
+ "models": 6,
70
+ "entries": 38,
71
+ "locales": ["en", "de"],
72
+ "lastSync": "2026-03-09T12:15:00Z"
73
+ }
74
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "version": 1,
3
+ "project": {
4
+ "name": "my-next-app",
5
+ "description": "SaaS marketing site with documentation and blog",
6
+ "stack": "next",
7
+ "stack_version": "15.x",
8
+ "package_manager": "npm",
9
+ "src_dir": "src/",
10
+ "content_consumption": "build-time"
11
+ },
12
+ "content": {
13
+ "import_pattern": "import {model} from '@/.contentrain/content/{domain}/{model}/{locale}.json'",
14
+ "i18n_strategy": "file-per-locale",
15
+ "default_locale": "en",
16
+ "supported_locales": ["en", "es", "fr"],
17
+ "domains": [
18
+ {
19
+ "id": "marketing",
20
+ "description": "Landing page content — hero, features, pricing, FAQ",
21
+ "models": ["hero", "features", "pricing", "faq"]
22
+ },
23
+ {
24
+ "id": "docs",
25
+ "description": "Documentation pages and sidebar navigation",
26
+ "models": ["doc-page", "doc-sidebar"]
27
+ },
28
+ {
29
+ "id": "blog",
30
+ "description": "Blog articles and tags",
31
+ "models": ["blog-post", "tags"]
32
+ }
33
+ ]
34
+ },
35
+ "normalize": {
36
+ "scan_dirs": ["src/components/", "src/app/"],
37
+ "scan_extensions": [".tsx", ".ts", ".jsx"],
38
+ "ignore_patterns": [".next/", "node_modules/", "out/", ".contentrain/", "*.test.*", "*.spec.*"],
39
+ "import_rewrite": "import { useTranslations } from 'next-intl'"
40
+ },
41
+ "conventions": {
42
+ "tone": "Professional, clear. Developer-friendly language.",
43
+ "brand_terms": {
44
+ "product_name": "My Next App",
45
+ "company_name": "Acme Corp"
46
+ },
47
+ "forbidden_terms": ["click here", "lorem ipsum", "TODO", "TBD"],
48
+ "max_title_length": 70,
49
+ "max_description_length": 155,
50
+ "target_reading_level": "grade-8",
51
+ "allowed_richtext_tags": [
52
+ "p", "strong", "em", "a", "ul", "ol", "li",
53
+ "h2", "h3", "h4", "h5", "h6",
54
+ "blockquote", "code", "pre", "img", "br", "hr",
55
+ "table", "thead", "tbody", "tr", "th", "td",
56
+ "del", "ins", "sup", "sub", "abbr", "mark",
57
+ "details", "summary"
58
+ ]
59
+ },
60
+ "lastOperation": {
61
+ "tool": "model_save",
62
+ "model": "blog-post",
63
+ "locale": "en",
64
+ "entries": [],
65
+ "timestamp": "2026-03-10T16:45:00Z",
66
+ "source": "mcp-local"
67
+ },
68
+ "stats": {
69
+ "models": 7,
70
+ "entries": 54,
71
+ "locales": ["en", "es", "fr"],
72
+ "lastSync": "2026-03-10T16:45:00Z"
73
+ }
74
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "version": 1,
3
+ "project": {
4
+ "name": "my-nuxt-app",
5
+ "description": "Marketing website with blog and i18n support",
6
+ "stack": "nuxt",
7
+ "stack_version": "4.0",
8
+ "package_manager": "pnpm",
9
+ "src_dir": "app/",
10
+ "content_consumption": "build-time"
11
+ },
12
+ "content": {
13
+ "import_pattern": "import {model} from '~/.contentrain/content/{domain}/{model}/{locale}.json'",
14
+ "i18n_strategy": "file-per-locale",
15
+ "default_locale": "en",
16
+ "supported_locales": ["en", "tr"],
17
+ "domains": [
18
+ {
19
+ "id": "marketing",
20
+ "description": "Landing page content — hero, features, pricing, testimonials",
21
+ "models": ["hero", "features", "pricing", "testimonials"]
22
+ },
23
+ {
24
+ "id": "blog",
25
+ "description": "Blog posts and categories",
26
+ "models": ["blog-post", "categories"]
27
+ },
28
+ {
29
+ "id": "ui",
30
+ "description": "UI strings — navigation, forms, errors",
31
+ "models": ["navigation", "forms", "error-messages"]
32
+ }
33
+ ]
34
+ },
35
+ "normalize": {
36
+ "scan_dirs": ["app/components/", "app/pages/", "app/layouts/"],
37
+ "scan_extensions": [".vue", ".ts"],
38
+ "ignore_patterns": [".nuxt/", "node_modules/", "dist/", ".contentrain/", "*.test.*", "*.spec.*"],
39
+ "import_rewrite": "import { t } from '~/composables/useContent'"
40
+ },
41
+ "conventions": {
42
+ "tone": "Professional, concise. Avoid jargon.",
43
+ "brand_terms": {
44
+ "product_name": "My Nuxt App",
45
+ "company_name": "Acme Inc"
46
+ },
47
+ "forbidden_terms": ["click here", "lorem ipsum", "TODO"],
48
+ "max_title_length": 80,
49
+ "max_description_length": 160,
50
+ "target_reading_level": "grade-8",
51
+ "allowed_richtext_tags": [
52
+ "p", "strong", "em", "a", "ul", "ol", "li",
53
+ "h2", "h3", "h4", "h5", "h6",
54
+ "blockquote", "code", "pre", "img", "br", "hr",
55
+ "table", "thead", "tbody", "tr", "th", "td",
56
+ "del", "ins", "sup", "sub", "abbr", "mark",
57
+ "details", "summary"
58
+ ]
59
+ },
60
+ "lastOperation": {
61
+ "tool": "content_save",
62
+ "model": "hero",
63
+ "locale": "en",
64
+ "entries": ["f7e8d9c0b1a2"],
65
+ "timestamp": "2026-03-11T10:00:00Z",
66
+ "source": "mcp-local"
67
+ },
68
+ "stats": {
69
+ "models": 7,
70
+ "entries": 86,
71
+ "locales": ["en", "tr"],
72
+ "lastSync": "2026-03-11T10:00:00Z"
73
+ }
74
+ }