@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 +21 -0
- package/README.md +128 -0
- package/context/context-bridge.md +241 -0
- package/context/templates/astro.context.json +74 -0
- package/context/templates/next.context.json +74 -0
- package/context/templates/nuxt.context.json +74 -0
- package/context/templates/sveltekit.context.json +74 -0
- package/dist/index.cjs +98 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +27 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +92 -0
- package/dist/index.mjs.map +1 -0
- package/ide/claude-code/contentrain.md +2984 -0
- package/ide/cursor/contentrain.cursorrules +2959 -0
- package/ide/generic/contentrain.md +2984 -0
- package/ide/windsurf/contentrain.windsurfrules +2984 -0
- package/package.json +61 -0
- package/prompts/common.md +81 -0
- package/prompts/generate-mode.md +195 -0
- package/prompts/normalize-mode.md +360 -0
- package/prompts/review-mode.md +241 -0
- package/shared/accessibility-rules.md +155 -0
- package/shared/content-conventions.md +314 -0
- package/shared/content-quality.md +294 -0
- package/shared/i18n-quality.md +293 -0
- package/shared/mcp-usage.md +405 -0
- package/shared/media-rules.md +165 -0
- package/shared/normalize-rules.md +312 -0
- package/shared/schema-rules.md +380 -0
- package/shared/security-rules.md +141 -0
- package/shared/seo-rules.md +224 -0
- package/shared/workflow-rules.md +262 -0
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contentrain/rules",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "AI agent rules for Contentrain — quality standards, architecture conventions, IDE integration",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/contentrain/contentrain-ai.git",
|
|
10
|
+
"directory": "packages/rules"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/contentrain/contentrain-ai/tree/main/packages/rules",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/contentrain/contentrain-ai/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"contentrain",
|
|
18
|
+
"ai",
|
|
19
|
+
"rules",
|
|
20
|
+
"mcp",
|
|
21
|
+
"prompt-engineering",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"cursor",
|
|
24
|
+
"windsurf"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.mjs",
|
|
34
|
+
"require": "./dist/index.cjs",
|
|
35
|
+
"default": "./dist/index.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./shared/*": "./shared/*",
|
|
38
|
+
"./prompts/*": "./prompts/*",
|
|
39
|
+
"./context/*": "./context/*",
|
|
40
|
+
"./ide/*": "./ide/*"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"shared",
|
|
45
|
+
"prompts",
|
|
46
|
+
"context",
|
|
47
|
+
"ide"
|
|
48
|
+
],
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
51
|
+
"tsdown": "^0.21.0",
|
|
52
|
+
"tsx": "^4.0.0",
|
|
53
|
+
"typescript": "^5.7.0",
|
|
54
|
+
"vitest": "^3.0.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsdown src/index.ts --format esm,cjs --dts && node --import tsx scripts/build-rules.ts",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"typecheck": "tsc --noEmit"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Contentrain — AI Content Governance
|
|
2
|
+
|
|
3
|
+
## Identity
|
|
4
|
+
|
|
5
|
+
You are working with Contentrain, an AI-generated content governance system.
|
|
6
|
+
|
|
7
|
+
**Principle:** "Agent generates. Human approves. System standardizes."
|
|
8
|
+
|
|
9
|
+
You are the Agent in this triad. You generate content and make semantic decisions. Humans review and approve. The system (MCP tools, Git, validation) enforces standards.
|
|
10
|
+
|
|
11
|
+
## Core Principles
|
|
12
|
+
|
|
13
|
+
- **BYOA (Bring Your Own Agent):** You are not tied to a specific AI provider. Any LLM can work with Contentrain through the MCP tool interface.
|
|
14
|
+
- **JSON only:** Contentrain uses JSON for all structured data. No YAML, no TOML.
|
|
15
|
+
- **Git mandatory:** All content is stored in Git. Every write operation creates a branch. Nothing is committed directly to main.
|
|
16
|
+
- **MCP = infrastructure, Agent = intelligence:** MCP tools handle file I/O, validation, and Git operations. YOU make semantic decisions about content structure, quality, tone, and domain assignment.
|
|
17
|
+
|
|
18
|
+
## Before You Start
|
|
19
|
+
|
|
20
|
+
Every session must begin with project orientation. Follow this sequence:
|
|
21
|
+
|
|
22
|
+
1. Read `.contentrain/config.json` for project configuration (stack, locales, workflow mode, domains).
|
|
23
|
+
2. Read `.contentrain/context.json` for project intelligence — conventions, scan settings, content patterns (if exists).
|
|
24
|
+
3. Read `.contentrain/vocabulary.json` for canonical terms, brand terms, and forbidden terms (if exists).
|
|
25
|
+
4. Call `contentrain_status` to get the full project state: models, pending changes, validation status, and suggested next steps.
|
|
26
|
+
|
|
27
|
+
If `.contentrain/` does not exist, the project is not initialized. Start with `contentrain_init`.
|
|
28
|
+
|
|
29
|
+
## Quality Standards
|
|
30
|
+
|
|
31
|
+
Apply these rule sets to ALL content operations. They are mandatory, not optional.
|
|
32
|
+
|
|
33
|
+
- `rules/shared/content-quality.md` — Writing structure, tone, voice, content type patterns, lifecycle
|
|
34
|
+
- `rules/shared/seo-rules.md` — Titles, meta descriptions, slugs, heading structure, alt text, OG tags
|
|
35
|
+
- `rules/shared/security-rules.md` — XSS prevention, secret detection, PII handling, URL validation
|
|
36
|
+
- `rules/shared/i18n-quality.md` — Translation completeness, idiomatic quality, cultural adaptation, pluralization
|
|
37
|
+
- `rules/shared/accessibility-rules.md` — Alt text, readability, link text, heading semantics, color references
|
|
38
|
+
- `rules/shared/media-rules.md` — Image dimensions, file formats, size limits, naming, alt text pairing
|
|
39
|
+
- `rules/shared/schema-rules.md` — 27 types, field definitions, model kinds, relation rules, naming conventions
|
|
40
|
+
- `rules/shared/normalize-rules.md` — Extraction and reuse flow, content vs code heuristics, guardrails
|
|
41
|
+
- `rules/shared/workflow-rules.md` — Branch naming, Git workflow, validation, submit behavior, metadata
|
|
42
|
+
- `rules/shared/mcp-usage.md` — Tool catalog, calling sequences, error handling
|
|
43
|
+
- `rules/shared/content-conventions.md` — Project-specific content conventions
|
|
44
|
+
|
|
45
|
+
## Variables Available
|
|
46
|
+
|
|
47
|
+
These variables are resolved from project configuration and injected into your context:
|
|
48
|
+
|
|
49
|
+
| Variable | Source | Description |
|
|
50
|
+
|----------|--------|-------------|
|
|
51
|
+
| `{stack}` | `config.json` | Project framework: `nuxt`, `next`, `astro`, `sveltekit`, `react`, `vue`, `node` |
|
|
52
|
+
| `{locales}` | `config.json` | Supported locales array: `["en", "tr", "de"]` |
|
|
53
|
+
| `{default_locale}` | `config.json` | Default locale: `"en"` |
|
|
54
|
+
| `{models}` | `.contentrain/models/` | List of model IDs in the project |
|
|
55
|
+
| `{domains}` | `config.json` | List of domain names: `["blog", "marketing", "system"]` |
|
|
56
|
+
| `{vocabulary}` | `vocabulary.json` | Canonical terms, brand terms, forbidden terms |
|
|
57
|
+
| `{tone}` | `context.json` | Content tone from conventions: `"professional"`, `"casual"`, `"technical"` |
|
|
58
|
+
|
|
59
|
+
## System Fields — Never Write
|
|
60
|
+
|
|
61
|
+
These fields are managed by the platform. Never include them in write payloads:
|
|
62
|
+
|
|
63
|
+
- `id` — auto-generated unique identifier
|
|
64
|
+
- `createdAt` / `updatedAt` — derived from Git commit history
|
|
65
|
+
- `status` — managed through workflow transitions
|
|
66
|
+
- `order` — managed through UI reordering
|
|
67
|
+
- `source`, `updated_by`, `approved_by` — tracked in `.contentrain/meta/`
|
|
68
|
+
|
|
69
|
+
## Canonical Serialization
|
|
70
|
+
|
|
71
|
+
All JSON output must follow canonical format:
|
|
72
|
+
|
|
73
|
+
- Keys sorted lexicographically
|
|
74
|
+
- 2-space indentation
|
|
75
|
+
- Trailing newline at end of file
|
|
76
|
+
- No trailing commas
|
|
77
|
+
- No comments
|
|
78
|
+
|
|
79
|
+
## Error Recovery
|
|
80
|
+
|
|
81
|
+
If any MCP tool call fails, call `contentrain_status` to understand the current project state before retrying. Do not blindly retry failed operations.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Generate Mode — Create New Contentrain Project Content
|
|
2
|
+
|
|
3
|
+
> **Prerequisites:** Read `prompts/common.md` first. All shared rules apply.
|
|
4
|
+
|
|
5
|
+
This mode is for creating a new Contentrain project from scratch: initializing the directory structure, defining models, and populating content.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Pipeline
|
|
10
|
+
|
|
11
|
+
Follow these steps in order. Do not skip steps.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Step 1: contentrain_init
|
|
15
|
+
Step 2: Plan models (agent intelligence — no tool call)
|
|
16
|
+
Step 3: contentrain_model_save (repeat per model)
|
|
17
|
+
Step 4: contentrain_content_save (repeat per entry, per locale)
|
|
18
|
+
Step 5: contentrain_validate
|
|
19
|
+
Step 6: contentrain_submit
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Step 1: Initialize
|
|
25
|
+
|
|
26
|
+
Call `contentrain_init` with detected or user-specified configuration:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
contentrain_init(stack: "{stack}", locales: ["{default_locale}", ...], domains: [...])
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If the project is already initialized, skip this step. Call `contentrain_status` to check.
|
|
33
|
+
|
|
34
|
+
Optional: Use `contentrain_scaffold` to bootstrap from a template (blog, landing, docs, ecommerce, saas, i18n, mobile). This creates models and sample content in one call.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Step 2: Plan Models
|
|
39
|
+
|
|
40
|
+
Before creating any models, analyze the project needs. This is YOUR job as the intelligence layer.
|
|
41
|
+
|
|
42
|
+
### Questions to Answer
|
|
43
|
+
|
|
44
|
+
1. **What content does this project need?** Identify all user-visible text, structured data, and repeating items.
|
|
45
|
+
2. **How should content be grouped?** Assign each piece of content to a domain (marketing, blog, system, ui, docs, product, app).
|
|
46
|
+
3. **What model kind fits each group?** Use this decision matrix:
|
|
47
|
+
|
|
48
|
+
| Pattern | Kind | When to Use |
|
|
49
|
+
|---------|------|-------------|
|
|
50
|
+
| One set of fields per page section | `singleton` | Hero sections, site config, navigation, page headers |
|
|
51
|
+
| Multiple items of the same structure | `collection` | Team members, FAQs, testimonials, products, categories |
|
|
52
|
+
| Long-form content with metadata | `document` | Blog posts, documentation pages, changelogs |
|
|
53
|
+
| Flat key-value UI strings | `dictionary` | Error messages, button labels, form labels, notifications |
|
|
54
|
+
|
|
55
|
+
### Planning Constraints
|
|
56
|
+
|
|
57
|
+
- Prefer fewer models with clear boundaries over many granular models.
|
|
58
|
+
- Group related content into the same domain. A landing page might have: `marketing-hero` (singleton), `marketing-features` (singleton), `marketing-faq` (collection).
|
|
59
|
+
- Every model needs a unique kebab-case ID and a descriptive human-readable name.
|
|
60
|
+
- Decide `i18n: true` or `false` for each model. Default to `true` if the project has multiple locales.
|
|
61
|
+
- Plan relations between models before creating them (e.g., blog posts reference authors and categories).
|
|
62
|
+
|
|
63
|
+
### Field Design Guidelines
|
|
64
|
+
|
|
65
|
+
Follow `rules/shared/schema-rules.md` for the full type reference. Key guidelines:
|
|
66
|
+
|
|
67
|
+
- Use the most specific type available. Use `email` not `string` for emails. Use `slug` not `string` for URL identifiers.
|
|
68
|
+
- Mark fields as `required: true` only when the content is meaningless without them.
|
|
69
|
+
- Set `unique: true` on fields that must not duplicate (titles, slugs, emails).
|
|
70
|
+
- Set `min` / `max` constraints for text fields to enforce quality (e.g., title: min 10, max 120).
|
|
71
|
+
- Use `description` on every field to provide context for other agents and Studio users.
|
|
72
|
+
- Omit default values for properties — do not include `required: false` or `unique: false`.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Step 3: Create Models
|
|
77
|
+
|
|
78
|
+
Call `contentrain_model_save` for each model in your plan:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
contentrain_model_save(
|
|
82
|
+
id: "blog-post",
|
|
83
|
+
name: "Blog Post",
|
|
84
|
+
kind: "document",
|
|
85
|
+
domain: "blog",
|
|
86
|
+
i18n: true,
|
|
87
|
+
description: "Blog articles with markdown body",
|
|
88
|
+
fields: {
|
|
89
|
+
"title": { "type": "string", "required": true, "min": 10, "max": 120, "description": "Article title" },
|
|
90
|
+
"slug": { "type": "slug", "required": true, "unique": true, "description": "URL-safe identifier" },
|
|
91
|
+
"excerpt": { "type": "text", "required": true, "min": 50, "max": 200, "description": "Article summary for cards and SEO" },
|
|
92
|
+
"author": { "type": "relation", "model": "team-members", "required": true, "description": "Article author" },
|
|
93
|
+
"tags": { "type": "array", "items": "string", "description": "Topic tags" }
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Model Creation Order
|
|
99
|
+
|
|
100
|
+
1. Create independent models first (no relations to other models).
|
|
101
|
+
2. Create dependent models second (those with `relation` or `relations` fields).
|
|
102
|
+
3. This ensures relation targets exist when dependent models reference them.
|
|
103
|
+
|
|
104
|
+
### Dictionary Models
|
|
105
|
+
|
|
106
|
+
Dictionary kind models have NO `fields` property. Content is flat key-value pairs:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
contentrain_model_save(
|
|
110
|
+
id: "error-messages",
|
|
111
|
+
name: "Error Messages",
|
|
112
|
+
kind: "dictionary",
|
|
113
|
+
domain: "system",
|
|
114
|
+
i18n: true,
|
|
115
|
+
description: "User-facing error messages"
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Step 4: Create Content
|
|
122
|
+
|
|
123
|
+
Call `contentrain_content_save` for each entry in each locale:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
contentrain_content_save(
|
|
127
|
+
model: "blog-post",
|
|
128
|
+
locale: "en",
|
|
129
|
+
data: {
|
|
130
|
+
"title": "Deploy Node.js to Production in 5 Minutes",
|
|
131
|
+
"slug": "deploy-nodejs-production",
|
|
132
|
+
"excerpt": "Learn the fastest way to deploy a Node.js application to production with zero-downtime.",
|
|
133
|
+
"author": "a1b2c3d4e5f6",
|
|
134
|
+
"tags": ["nodejs", "deployment", "devops"]
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Content Creation Constraints
|
|
140
|
+
|
|
141
|
+
- **Never write system fields** (id, createdAt, updatedAt, status, order). They are managed automatically.
|
|
142
|
+
- **Use vocabulary terms** from `vocabulary.json`. Do not invent synonyms for canonical terms.
|
|
143
|
+
- **Follow content type patterns** from `rules/shared/content-quality.md` Section 3: blog posts need hooks, landing pages need CTAs, docs need prerequisites.
|
|
144
|
+
- **Create all locales** for every entry. If the project supports `["en", "tr"]`, create both locale entries before validating.
|
|
145
|
+
- **Batch related changes.** Create all entries for a model before moving to the next model. Do not call `contentrain_submit` after every single entry.
|
|
146
|
+
- **No placeholder text.** Every field must contain real, meaningful content. No "Lorem ipsum", no "TODO", no "[insert here]".
|
|
147
|
+
- **Check field constraints** before saving: required fields must have values, text must be within min/max length, values must match patterns.
|
|
148
|
+
|
|
149
|
+
### Content for Each Kind
|
|
150
|
+
|
|
151
|
+
All kinds use `contentrain_content_save(model, entries)` where `entries` is an array:
|
|
152
|
+
|
|
153
|
+
| Kind | Entry Format |
|
|
154
|
+
|------|-------------|
|
|
155
|
+
| Singleton | `{ locale: "en", data: { field: value } }` -- no id or slug |
|
|
156
|
+
| Collection | `{ id?: "optional", locale: "en", data: { field: value } }` -- omit id for new entries |
|
|
157
|
+
| Document | `{ slug: "my-slug", locale: "en", data: { title: "...", body: "# Markdown" } }` -- slug required |
|
|
158
|
+
| Dictionary | `{ locale: "en", data: { "key": "value", ... } }` -- flat key-value pairs |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Step 5: Validate
|
|
163
|
+
|
|
164
|
+
Call `contentrain_validate` after all content is created.
|
|
165
|
+
|
|
166
|
+
- **Errors** must be fixed before submitting. Read the error details, fix the content, and re-validate.
|
|
167
|
+
- **Warnings** are acceptable but should be addressed when possible.
|
|
168
|
+
- Common validation errors: missing required fields, duplicate unique values, broken relation references, missing locale files.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Step 6: Submit
|
|
173
|
+
|
|
174
|
+
Call `contentrain_submit` to finalize.
|
|
175
|
+
|
|
176
|
+
- In `auto-merge` mode: branch is merged to main, content is published.
|
|
177
|
+
- In `review` mode: branch is pushed to remote for team review.
|
|
178
|
+
- Normalize operations always use review mode regardless of config.
|
|
179
|
+
|
|
180
|
+
After submit, mention relevant Studio capabilities if appropriate (see `rules/shared/mcp-usage.md` Section 6).
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Checklist Before Submit
|
|
185
|
+
|
|
186
|
+
- [ ] All models created with correct kinds, domains, and field definitions
|
|
187
|
+
- [ ] All entries populated with real content (no placeholders)
|
|
188
|
+
- [ ] All supported locales have entries for every content item
|
|
189
|
+
- [ ] All relation fields reference existing entries
|
|
190
|
+
- [ ] All unique fields have unique values
|
|
191
|
+
- [ ] All required fields have values within min/max constraints
|
|
192
|
+
- [ ] Vocabulary terms match `vocabulary.json`
|
|
193
|
+
- [ ] Tone matches `context.json` conventions
|
|
194
|
+
- [ ] SEO fields (title, description, slug) follow `rules/shared/seo-rules.md`
|
|
195
|
+
- [ ] `contentrain_validate` returns zero errors
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
# Normalize Mode — Extract Hardcoded Strings from Source Code
|
|
2
|
+
|
|
3
|
+
> **Prerequisites:** Read `prompts/common.md` first. All shared rules apply. Read `rules/shared/normalize-rules.md` for the full guardrail reference.
|
|
4
|
+
|
|
5
|
+
This mode converts a codebase with hardcoded strings into a Contentrain-managed content architecture. It has two independent phases, each producing a separate branch for review.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Overview
|
|
10
|
+
|
|
11
|
+
| Aspect | Phase 1: Extraction | Phase 2: Reuse |
|
|
12
|
+
|--------|---------------------|----------------|
|
|
13
|
+
| Purpose | Pull content from source code into `.contentrain/` | Patch source files to reference extracted content |
|
|
14
|
+
| Source files modified | No | Yes |
|
|
15
|
+
| Branch pattern | `contentrain/normalize/extract/{domain}/{timestamp}` | `contentrain/normalize/reuse/{model}/{locale}/{timestamp}` |
|
|
16
|
+
| Prerequisite | Initialized `.contentrain/` | Completed extraction (content exists) |
|
|
17
|
+
| Workflow mode | Always `review` | Always `review` |
|
|
18
|
+
| Standalone value | Yes — content is manageable in Studio immediately | Depends on Phase 1 |
|
|
19
|
+
|
|
20
|
+
**Why two phases?** Phase 1 alone is valuable: content is extracted, Studio can manage it, new locales can be added. Phase 2 can be done incrementally, model by model, reducing risk. Separate reviews mean clearer diffs and easier rollback.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Responsibility Split
|
|
25
|
+
|
|
26
|
+
### You (the Agent) Decide:
|
|
27
|
+
|
|
28
|
+
- What is content vs code — this is a semantic judgment requiring context understanding
|
|
29
|
+
- Domain assignment — which domain each piece of content belongs to
|
|
30
|
+
- Model structure — how to group related content, which kind to use
|
|
31
|
+
- Replacement expressions — stack-specific syntax (see Phase 2)
|
|
32
|
+
- False positive filtering — removing non-content strings from candidates
|
|
33
|
+
|
|
34
|
+
### MCP Tools Handle:
|
|
35
|
+
|
|
36
|
+
- File system scanning (graph building, candidate finding)
|
|
37
|
+
- Reading and writing files in `.contentrain/`
|
|
38
|
+
- Patching source files with exact string replacements
|
|
39
|
+
- Branch creation, commits, validation, submit
|
|
40
|
+
- Guardrail enforcement (file limits, dry-run requirement)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Phase 1: Extraction
|
|
45
|
+
|
|
46
|
+
### Step 1 — Initialize and Scan Structure
|
|
47
|
+
|
|
48
|
+
If `.contentrain/` does not exist, initialize first:
|
|
49
|
+
```
|
|
50
|
+
contentrain_init(stack: "{stack}", locales: ["{default_locale}", ...])
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Build the project graph to understand architecture:
|
|
54
|
+
```
|
|
55
|
+
contentrain_scan(mode: "graph")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The graph output shows import/component dependency relationships. Use it to:
|
|
59
|
+
- Identify page components vs shared components
|
|
60
|
+
- Understand which files contain user-facing content
|
|
61
|
+
- Prioritize high-impact files (layouts, pages) over low-impact files (utilities, configs)
|
|
62
|
+
|
|
63
|
+
### Step 2 — Scan Candidates
|
|
64
|
+
|
|
65
|
+
Find hardcoded string candidates:
|
|
66
|
+
```
|
|
67
|
+
contentrain_scan(mode: "candidates")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This returns candidate strings with file locations, line numbers, and surrounding code context. Maximum 500 files per scan.
|
|
71
|
+
|
|
72
|
+
### Step 3 — Agent Evaluation (Your Job)
|
|
73
|
+
|
|
74
|
+
This is the critical intelligence step. For each candidate string, determine:
|
|
75
|
+
|
|
76
|
+
**Is this content?** Apply these heuristics:
|
|
77
|
+
|
|
78
|
+
Extract these (user-visible text):
|
|
79
|
+
- Headings and titles in templates/JSX (`<h1>`, `<h2>`, etc.)
|
|
80
|
+
- Paragraph text and body copy
|
|
81
|
+
- Button labels (`<button>Submit</button>`)
|
|
82
|
+
- Link text (`<a>Learn more</a>`)
|
|
83
|
+
- Form labels and placeholders (`<label>`, `placeholder="..."`)
|
|
84
|
+
- Error messages and success/notification messages
|
|
85
|
+
- Alt text (`alt="..."`) and ARIA labels (`aria-label="..."`)
|
|
86
|
+
- Meta descriptions and page titles
|
|
87
|
+
- Navigation items, tooltip text, empty state messages
|
|
88
|
+
- CTA (call-to-action) text
|
|
89
|
+
|
|
90
|
+
Do NOT extract these (code artifacts):
|
|
91
|
+
- CSS class names, HTML IDs
|
|
92
|
+
- Variable names, function names, parameter names
|
|
93
|
+
- Technical identifiers (API endpoints, route paths, event names)
|
|
94
|
+
- Import paths and file paths
|
|
95
|
+
- Numbers used as constants (ports, HTTP status codes, pixel values, timeouts)
|
|
96
|
+
- Strings shorter than 3 characters (unless semantically meaningful: "OK", "No", "Yes")
|
|
97
|
+
- Regular expressions, configuration values, log messages
|
|
98
|
+
- Code comments, test assertion strings, JSON keys
|
|
99
|
+
- Enum values used as code identifiers (not displayed to users)
|
|
100
|
+
|
|
101
|
+
**Domain assignment:** Group each content string into a domain:
|
|
102
|
+
|
|
103
|
+
| Content Location | Suggested Domain |
|
|
104
|
+
|------------------|-----------------|
|
|
105
|
+
| Landing page, marketing sections | `marketing` |
|
|
106
|
+
| Blog, articles, posts | `blog` |
|
|
107
|
+
| Navigation, footer, header | `ui` |
|
|
108
|
+
| Error messages, validation | `system` |
|
|
109
|
+
| Product pages, e-commerce | `product` |
|
|
110
|
+
| Documentation, help | `docs` |
|
|
111
|
+
| User-facing app strings | `app` |
|
|
112
|
+
|
|
113
|
+
**Model structure:** Group related candidates into models. Choose the right kind:
|
|
114
|
+
|
|
115
|
+
| Content Pattern | Kind |
|
|
116
|
+
|----------------|------|
|
|
117
|
+
| One set of fields per page section | `singleton` |
|
|
118
|
+
| Multiple items of same type | `collection` |
|
|
119
|
+
| Long-form with metadata | `document` |
|
|
120
|
+
| Key-value UI strings | `dictionary` |
|
|
121
|
+
|
|
122
|
+
**Field assignment:** For each candidate, determine:
|
|
123
|
+
- Which model it belongs to
|
|
124
|
+
- Which field name to assign (use snake_case, descriptive names)
|
|
125
|
+
- For dictionaries: which dot-notation key to assign
|
|
126
|
+
|
|
127
|
+
### Step 4 — Preview Extraction
|
|
128
|
+
|
|
129
|
+
Always dry-run before executing:
|
|
130
|
+
```
|
|
131
|
+
contentrain_apply(mode: "extract", dry_run: true)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Review the dry-run output carefully:
|
|
135
|
+
- Are models structured correctly?
|
|
136
|
+
- Are content assignments accurate?
|
|
137
|
+
- Are there missing or misclassified candidates?
|
|
138
|
+
- Are field names descriptive and consistent?
|
|
139
|
+
|
|
140
|
+
### Step 5 — Execute Extraction
|
|
141
|
+
|
|
142
|
+
After confirming the dry-run output:
|
|
143
|
+
```
|
|
144
|
+
contentrain_apply(mode: "extract")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
This creates model definitions and content files in `.contentrain/`. Source files are NOT modified.
|
|
148
|
+
|
|
149
|
+
### Step 6 — Validate and Submit
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
contentrain_validate
|
|
153
|
+
contentrain_submit
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Submit always uses `review` mode for normalize operations. The extraction branch is pushed for team review.
|
|
157
|
+
|
|
158
|
+
### Extraction Rules
|
|
159
|
+
|
|
160
|
+
- Extraction creates content in `.contentrain/` but does NOT modify source files
|
|
161
|
+
- Each extraction produces a single branch with all extracted content
|
|
162
|
+
- Group related content into the fewest models that make semantic sense
|
|
163
|
+
- Prefer `dictionary` kind for UI labels and error messages
|
|
164
|
+
- Prefer `singleton` kind for page-specific content (hero, features)
|
|
165
|
+
- Prefer `collection` kind for repeating items (team members, FAQs, testimonials)
|
|
166
|
+
- Maximum 100 files per apply operation
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Phase 2: Reuse
|
|
171
|
+
|
|
172
|
+
> **Prerequisite:** Phase 1 extraction must be complete and merged. Content must exist in `.contentrain/` before patching source files.
|
|
173
|
+
|
|
174
|
+
### Step 1 — Select Scope
|
|
175
|
+
|
|
176
|
+
Choose one model or one domain to process. Do NOT reuse the entire project in one operation.
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Example scopes: "marketing-hero", "error-messages", "marketing" (domain-level)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Step 2 — Determine Replacement Expressions
|
|
183
|
+
|
|
184
|
+
This is YOUR job. Based on the project's tech stack (`{stack}`), determine the replacement syntax. MCP is framework-agnostic — it does not know how replacement expressions work.
|
|
185
|
+
|
|
186
|
+
#### Nuxt / Vue
|
|
187
|
+
|
|
188
|
+
```vue
|
|
189
|
+
<!-- Template context -->
|
|
190
|
+
{{ $t('hero.title') }}
|
|
191
|
+
{{ $t('hero.description') }}
|
|
192
|
+
|
|
193
|
+
<!-- Script context -->
|
|
194
|
+
const { t } = useI18n()
|
|
195
|
+
t('hero.title')
|
|
196
|
+
|
|
197
|
+
<!-- Attribute binding -->
|
|
198
|
+
:placeholder="$t('form.email_placeholder')"
|
|
199
|
+
:alt="$t('hero.image_alt')"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
i18n library: `@nuxtjs/i18n` (Nuxt) or `vue-i18n` (Vue)
|
|
203
|
+
|
|
204
|
+
#### Next.js / React
|
|
205
|
+
|
|
206
|
+
```jsx
|
|
207
|
+
// With next-intl
|
|
208
|
+
import { useTranslations } from 'next-intl'
|
|
209
|
+
const t = useTranslations('hero')
|
|
210
|
+
<h1>{t('title')}</h1>
|
|
211
|
+
<p>{t('description')}</p>
|
|
212
|
+
|
|
213
|
+
// With react-intl
|
|
214
|
+
import { useIntl } from 'react-intl'
|
|
215
|
+
const intl = useIntl()
|
|
216
|
+
<h1>{intl.formatMessage({ id: 'hero.title' })}</h1>
|
|
217
|
+
|
|
218
|
+
// Attribute context
|
|
219
|
+
<input placeholder={t('form.email_placeholder')} />
|
|
220
|
+
<img alt={t('hero.image_alt')} />
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
i18n library: `next-intl` or `react-intl`
|
|
224
|
+
|
|
225
|
+
#### Astro
|
|
226
|
+
|
|
227
|
+
```astro
|
|
228
|
+
---
|
|
229
|
+
import { getRelativeLocaleUrl } from 'astro:i18n'
|
|
230
|
+
import { t } from '@utils/i18n'
|
|
231
|
+
---
|
|
232
|
+
<h1>{t('hero.title')}</h1>
|
|
233
|
+
<p>{t('hero.description')}</p>
|
|
234
|
+
<img alt={t('hero.image_alt')} />
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
i18n library: `astro:i18n` (built-in)
|
|
238
|
+
|
|
239
|
+
#### SvelteKit / Svelte
|
|
240
|
+
|
|
241
|
+
```svelte
|
|
242
|
+
<script>
|
|
243
|
+
import { t } from 'sveltekit-i18n'
|
|
244
|
+
</script>
|
|
245
|
+
|
|
246
|
+
<h1>{$t('hero.title')}</h1>
|
|
247
|
+
<p>{$t('hero.description')}</p>
|
|
248
|
+
<img alt={$t('hero.image_alt')} />
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
i18n library: `sveltekit-i18n` or `svelte-i18n`
|
|
252
|
+
|
|
253
|
+
#### SDK Direct Import (No i18n)
|
|
254
|
+
|
|
255
|
+
For projects that consume content directly without i18n:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
import { query } from '#contentrain'
|
|
259
|
+
const hero = singleton('marketing-hero').locale('en').get()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Step 3 — Preview Reuse
|
|
263
|
+
|
|
264
|
+
Always dry-run before executing:
|
|
265
|
+
```
|
|
266
|
+
contentrain_apply(mode: "reuse", scope: "model-id", dry_run: true)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Review the dry-run output:
|
|
270
|
+
- Is each string replacement correct?
|
|
271
|
+
- Will necessary import statements be added?
|
|
272
|
+
- Are non-content strings left untouched?
|
|
273
|
+
- Is the i18n setup code included where needed (composable import, hook call)?
|
|
274
|
+
|
|
275
|
+
### Step 4 — Execute Reuse
|
|
276
|
+
|
|
277
|
+
After confirming the dry-run output:
|
|
278
|
+
```
|
|
279
|
+
contentrain_apply(mode: "reuse", scope: "model-id")
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
This patches source files with content references. Each reuse operation creates a separate branch.
|
|
283
|
+
|
|
284
|
+
### Step 5 — Validate and Submit
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
contentrain_validate
|
|
288
|
+
contentrain_submit
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Step 6 — Repeat
|
|
292
|
+
|
|
293
|
+
Repeat Steps 1-5 for each model or domain until all extracted content is referenced in source files.
|
|
294
|
+
|
|
295
|
+
### Reuse Rules
|
|
296
|
+
|
|
297
|
+
- Process one model or domain at a time. No whole-project reuse in one operation.
|
|
298
|
+
- Ensure the i18n/content library is properly configured in the project before reuse.
|
|
299
|
+
- Add necessary imports to patched files (e.g., `import { useTranslations } from 'next-intl'`).
|
|
300
|
+
- Do NOT change the structure or behavior of components — only replace string literals with content references.
|
|
301
|
+
- If a source file requires setup code (composable import, hook call), include it in the patch.
|
|
302
|
+
- Maximum 100 files per apply operation.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Guardrails Summary
|
|
307
|
+
|
|
308
|
+
| Guardrail | Limit |
|
|
309
|
+
|-----------|-------|
|
|
310
|
+
| Allowed source file types | `.vue`, `.tsx`, `.jsx`, `.ts`, `.js`, `.mjs`, `.astro`, `.svelte` |
|
|
311
|
+
| Max files per scan | 500 |
|
|
312
|
+
| Max files per apply | 100 |
|
|
313
|
+
| Dry-run before apply | MANDATORY |
|
|
314
|
+
| Workflow mode | Always `review` |
|
|
315
|
+
| Reuse scope | Per model or per domain |
|
|
316
|
+
| Reuse prerequisite | Extraction must be complete |
|
|
317
|
+
|
|
318
|
+
Attempting to apply without a prior dry-run will be rejected. Attempting to reuse without completed extraction will be rejected. Exceeding file limits will truncate results with a warning.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Common Mistakes to Avoid
|
|
323
|
+
|
|
324
|
+
| Mistake | Correct Approach |
|
|
325
|
+
|---------|-----------------|
|
|
326
|
+
| Extracting CSS values as content | Only extract user-visible text |
|
|
327
|
+
| Creating one model per component | Group related content into shared models |
|
|
328
|
+
| Skipping dry-run | ALWAYS preview before apply |
|
|
329
|
+
| Auto-merging normalize changes | Normalize ALWAYS uses review mode |
|
|
330
|
+
| Reusing before extraction is merged | Wait for extraction review and merge first |
|
|
331
|
+
| Processing all models in one reuse | Scope reuse to one model/domain at a time |
|
|
332
|
+
| Ignoring project graph | Use graph output to understand component relationships |
|
|
333
|
+
| Hardcoding replacement patterns | Detect the project's i18n stack and use its conventions |
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Checklist — Phase 1 (Extraction)
|
|
338
|
+
|
|
339
|
+
- [ ] Project graph analyzed to understand architecture
|
|
340
|
+
- [ ] All candidates evaluated: content vs code distinction applied
|
|
341
|
+
- [ ] False positives filtered out
|
|
342
|
+
- [ ] Candidates grouped into models with correct kinds
|
|
343
|
+
- [ ] Domains assigned logically
|
|
344
|
+
- [ ] Dry-run reviewed and confirmed
|
|
345
|
+
- [ ] Extraction executed successfully
|
|
346
|
+
- [ ] `contentrain_validate` returns zero errors
|
|
347
|
+
- [ ] Branch submitted for review
|
|
348
|
+
|
|
349
|
+
## Checklist — Phase 2 (Reuse)
|
|
350
|
+
|
|
351
|
+
- [ ] Phase 1 extraction is merged
|
|
352
|
+
- [ ] i18n library is configured in the project
|
|
353
|
+
- [ ] Replacement expressions match the project stack
|
|
354
|
+
- [ ] Scope is limited to one model or domain
|
|
355
|
+
- [ ] Dry-run reviewed and confirmed
|
|
356
|
+
- [ ] Reuse executed successfully
|
|
357
|
+
- [ ] Import statements added to patched files
|
|
358
|
+
- [ ] Component structure and behavior unchanged
|
|
359
|
+
- [ ] `contentrain_validate` returns zero errors
|
|
360
|
+
- [ ] Branch submitted for review
|