@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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Media Rules
|
|
2
|
+
|
|
3
|
+
These rules are MANDATORY for all AI agents creating or editing content in Contentrain projects.
|
|
4
|
+
Follow these guidelines when populating image, video, or file fields in any content model.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Image Dimensions (Recommended)
|
|
9
|
+
|
|
10
|
+
1. Use these standard dimensions unless the project's `context.json` specifies overrides:
|
|
11
|
+
|
|
12
|
+
| Purpose | Width | Height | Aspect Ratio |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
| Hero / Banner | 1200px | 630px | ~1.91:1 |
|
|
15
|
+
| Thumbnail / Card | 400px | 300px | 4:3 |
|
|
16
|
+
| Avatar / Profile | 200px | 200px | 1:1 (square) |
|
|
17
|
+
| Icon | 64px or 128px | 64px or 128px | 1:1 (square) |
|
|
18
|
+
| Full-width content | 1200px | proportional | varies |
|
|
19
|
+
| Social share (og:image) | 1200px | 630px | 1.91:1 |
|
|
20
|
+
|
|
21
|
+
2. Hero/banner dimensions (1200x630) double as optimal `og:image` dimensions for social sharing.
|
|
22
|
+
|
|
23
|
+
3. All images MUST be usable at mobile widths. Minimum renderable width: **320px**. Do not provide images narrower than this.
|
|
24
|
+
|
|
25
|
+
4. When a model defines `width` and `height` fields alongside an image field, populate them with the actual image dimensions to prevent layout shift.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## File Formats
|
|
30
|
+
|
|
31
|
+
1. **WebP** is the preferred format for all web images. Use it as the default unless a specific reason requires another format.
|
|
32
|
+
|
|
33
|
+
2. **PNG**: use only when transparency is required and SVG is not suitable.
|
|
34
|
+
|
|
35
|
+
3. **JPEG/JPG**: acceptable for photographs when WebP is not available. Use quality 75-85 for a good compression/quality balance.
|
|
36
|
+
|
|
37
|
+
4. **SVG**: REQUIRED for logos, icons, and any vector graphics. Do not rasterize vector content.
|
|
38
|
+
|
|
39
|
+
5. **AVIF**: acceptable as a progressive enhancement for browsers that support it. Always provide a WebP or JPEG fallback.
|
|
40
|
+
|
|
41
|
+
6. **GIF**: do NOT use for animations. Use MP4 video instead for better compression and performance. Static GIFs are acceptable but WebP is preferred.
|
|
42
|
+
|
|
43
|
+
7. **Never use** these formats for web content: BMP, TIFF, RAW, PSD, AI, EPS. Convert to a web-safe format before storing.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## File Size Limits
|
|
48
|
+
|
|
49
|
+
1. Enforce these maximum file sizes:
|
|
50
|
+
|
|
51
|
+
| Asset Type | Max Size |
|
|
52
|
+
|---|---|
|
|
53
|
+
| Hero / Banner image | 200 KB |
|
|
54
|
+
| Thumbnail | 50 KB |
|
|
55
|
+
| Avatar / Icon | 30 KB |
|
|
56
|
+
| Content image | 150 KB |
|
|
57
|
+
| SVG file | 50 KB |
|
|
58
|
+
| Video poster image | 100 KB |
|
|
59
|
+
|
|
60
|
+
2. **Total images per page**: aim for under **1 MB combined**. Flag content entries that reference more than 1 MB of images.
|
|
61
|
+
|
|
62
|
+
3. If an image exceeds the size limit, instruct the user to compress or resize it before storing. Do not silently accept oversized files.
|
|
63
|
+
|
|
64
|
+
4. SVG files SHOULD be optimized (e.g., with SVGO) to remove editor metadata, comments, and unnecessary attributes.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## File Naming Convention
|
|
69
|
+
|
|
70
|
+
1. Use **kebab-case** for all media file names: lowercase, words separated by hyphens.
|
|
71
|
+
|
|
72
|
+
2. File names MUST be **descriptive** — include context and subject:
|
|
73
|
+
- GOOD: `homepage-hero-banner.webp`, `blog-author-jane-doe.jpg`, `product-dashboard-preview.webp`
|
|
74
|
+
- BAD: `IMG_4523.jpg`, `photo1.png`, `untitled.webp`, `Screenshot 2024-01-15.png`, `image.jpg`
|
|
75
|
+
|
|
76
|
+
3. When providing multiple sizes of the same image, include dimensions in the filename:
|
|
77
|
+
- `hero-1200x630.webp`, `hero-600x315.webp`
|
|
78
|
+
- Or use size suffixes: `hero-lg.webp`, `hero-md.webp`, `hero-sm.webp`
|
|
79
|
+
|
|
80
|
+
4. Allowed characters in file names: lowercase letters (`a-z`), digits (`0-9`), hyphens (`-`), and a single dot before the extension. No spaces, underscores, or special characters.
|
|
81
|
+
|
|
82
|
+
5. File extensions MUST be lowercase: `.webp` not `.WebP`, `.jpg` not `.JPG`.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Alt Text Pairing
|
|
87
|
+
|
|
88
|
+
1. Every content model with an `image` field SHOULD define a companion `image_alt` field (type: `string`). If the model does not have one, request that it be added.
|
|
89
|
+
|
|
90
|
+
2. When both `image` and `image_alt` fields exist, ALWAYS populate both. Never leave `image_alt` empty unless the image is decorative (set to `""`).
|
|
91
|
+
|
|
92
|
+
3. If no dedicated alt text field exists, include alt text guidance in the image field's `description` metadata so future content editors know to provide it.
|
|
93
|
+
|
|
94
|
+
4. In document/markdown models, always use the full image syntax with alt text: ``. Never use ``.
|
|
95
|
+
|
|
96
|
+
5. See `accessibility-rules.md` for detailed alt text writing guidelines.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Video Guidelines
|
|
101
|
+
|
|
102
|
+
1. **Format**: MP4 with H.264 codec for maximum browser compatibility. WebM (VP9) is acceptable as a secondary format.
|
|
103
|
+
|
|
104
|
+
2. Every video MUST have a **poster/thumbnail image**. If the model has a `poster` or `thumbnail` field, populate it. The poster image follows the same format and size rules as other images.
|
|
105
|
+
|
|
106
|
+
3. If the model defines a `duration` field, populate it in seconds (integer). This helps the frontend display duration badges and improves accessibility.
|
|
107
|
+
|
|
108
|
+
4. Every video MUST reference a transcript or caption:
|
|
109
|
+
- A `transcript_url` field pointing to a text/VTT file
|
|
110
|
+
- A relation to a document entry containing the transcript
|
|
111
|
+
- Or inline transcript in an adjacent richtext/markdown field
|
|
112
|
+
|
|
113
|
+
5. **Max file size** for self-hosted embedded video: **50 MB**. For larger videos, use external hosting.
|
|
114
|
+
|
|
115
|
+
6. Prefer **external hosting** (YouTube, Vimeo, or CDN) for videos over 50 MB. Store the external URL in a `url` type field, not the video binary.
|
|
116
|
+
|
|
117
|
+
7. Do not auto-play videos with audio. If the model has an `autoplay` field, set it to `false` by default.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Asset Organization
|
|
122
|
+
|
|
123
|
+
1. All media MUST be stored under the path defined in `config.json > assets_path`. The default is `.contentrain/assets/`.
|
|
124
|
+
|
|
125
|
+
2. Organize assets using ONE of these conventions per project (check existing structure first):
|
|
126
|
+
|
|
127
|
+
**By domain:**
|
|
128
|
+
```
|
|
129
|
+
assets/blog/
|
|
130
|
+
assets/team/
|
|
131
|
+
assets/products/
|
|
132
|
+
assets/marketing/
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**By type:**
|
|
136
|
+
```
|
|
137
|
+
assets/images/
|
|
138
|
+
assets/videos/
|
|
139
|
+
assets/documents/
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
3. Check the existing asset directory structure before adding new files. Follow the established convention. Do not mix conventions within a project.
|
|
143
|
+
|
|
144
|
+
4. Reference all media with **relative paths from the project root**:
|
|
145
|
+
- GOOD: `.contentrain/assets/blog/post-hero.webp`
|
|
146
|
+
- BAD: `/Users/jane/project/.contentrain/assets/blog/post-hero.webp`
|
|
147
|
+
|
|
148
|
+
5. Do not create deeply nested directories (max 3 levels under `assets/`). Deep nesting makes paths fragile and hard to manage.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Responsive Images
|
|
153
|
+
|
|
154
|
+
1. When the content model supports multiple image sizes, provide variants for:
|
|
155
|
+
- **Small** (mobile): 320-768px wide
|
|
156
|
+
- **Medium** (tablet): 768-1024px wide
|
|
157
|
+
- **Large** (desktop): 1024px+ wide
|
|
158
|
+
|
|
159
|
+
2. Use descriptive size suffixes in filenames: `-sm`, `-md`, `-lg` or explicit dimensions (`-400w`, `-800w`, `-1200w`).
|
|
160
|
+
|
|
161
|
+
3. Always provide at least the **largest size**. Smaller sizes are recommended but optional — CDN or image optimization services can generate them from the original.
|
|
162
|
+
|
|
163
|
+
4. If the model has a single `image` field (no responsive variants), provide the largest recommended size. The frontend is responsible for responsive rendering.
|
|
164
|
+
|
|
165
|
+
5. Store the original/largest image. Never store only a downscaled version — you lose the ability to generate other sizes later.
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Contentrain Normalize Rules
|
|
2
|
+
|
|
3
|
+
> These rules govern the normalize flow: extracting hardcoded content from source code into `.contentrain/` (Phase 1) and patching source files to reference extracted content (Phase 2).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Purpose
|
|
8
|
+
|
|
9
|
+
Normalize converts a codebase with hardcoded strings into a Contentrain-managed content architecture:
|
|
10
|
+
|
|
11
|
+
- **Phase 1 (Extraction):** Pull content from source code into `.contentrain/` structure. Source files are NOT modified.
|
|
12
|
+
- **Phase 2 (Reuse):** Patch source files to replace hardcoded strings with content references. Requires completed extraction.
|
|
13
|
+
|
|
14
|
+
Each phase produces a separate branch for independent review. This separation ensures extraction can be reviewed and merged before any source file modifications occur.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 2. Two-Phase Architecture
|
|
19
|
+
|
|
20
|
+
| Aspect | Phase 1: Extraction | Phase 2: Reuse |
|
|
21
|
+
|--------|-------------------|----------------|
|
|
22
|
+
| Purpose | Pull content from source to `.contentrain/` | Patch source files with content references |
|
|
23
|
+
| Scope | Full project scan | Per model or per domain |
|
|
24
|
+
| Source files modified | No | Yes |
|
|
25
|
+
| Branch pattern | `contentrain/normalize/extract/{domain}/{timestamp}` | `contentrain/normalize/reuse/{model}/{locale}/{timestamp}` |
|
|
26
|
+
| Prerequisite | Initialized `.contentrain/` | Completed extraction (content exists in `.contentrain/`) |
|
|
27
|
+
| Workflow mode | Always `review` | Always `review` |
|
|
28
|
+
| Standalone value | Yes -- content is manageable in Studio immediately | Depends on Phase 1 |
|
|
29
|
+
|
|
30
|
+
### Why Two Phases?
|
|
31
|
+
|
|
32
|
+
- Phase 1 alone is valuable: content is extracted, Studio can manage it, new locales can be added.
|
|
33
|
+
- Phase 2 can be done incrementally, model by model, reducing risk.
|
|
34
|
+
- Separate reviews mean clearer diffs and easier rollback.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 3. Agent vs MCP Responsibilities
|
|
39
|
+
|
|
40
|
+
### Agent (Intelligence Layer -- You)
|
|
41
|
+
|
|
42
|
+
- **Decide what is content vs code.** This is a semantic judgment that requires understanding context.
|
|
43
|
+
- **Assign domain grouping.** Determine which domain each piece of content belongs to.
|
|
44
|
+
- **Determine model structure.** Group related content into models, choose the right kind.
|
|
45
|
+
- **Create replacement expressions.** These are stack-specific and require framework knowledge:
|
|
46
|
+
- Vue/Nuxt: `{{ $t('key') }}` or `{{ t('key') }}`
|
|
47
|
+
- React/Next: `{t('key')}` or `{intl.formatMessage({id: 'key'})}`
|
|
48
|
+
- Svelte/SvelteKit: `{$t('key')}`
|
|
49
|
+
- Astro: `{t('key')}`
|
|
50
|
+
- Generic: direct import from `#contentrain`
|
|
51
|
+
- **Filter false positives** from scan candidates.
|
|
52
|
+
- **Evaluate and group** candidates into logical models.
|
|
53
|
+
|
|
54
|
+
### MCP (Deterministic Infrastructure)
|
|
55
|
+
|
|
56
|
+
- **Scan the file system** (build graph, find candidates).
|
|
57
|
+
- **Read and write files** in `.contentrain/`.
|
|
58
|
+
- **Patch source files** with exact string replacement (agent provides the replacement expression).
|
|
59
|
+
- **Create branches, commit, validate** all changes.
|
|
60
|
+
- **Enforce guardrails** (file limits, type restrictions, dry-run requirement).
|
|
61
|
+
|
|
62
|
+
**Rule:** MCP is framework-agnostic. It does NOT know how `{t('key')}` differs from `{{ $t('key') }}`. The agent provides all stack-specific logic.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 4. Guardrails
|
|
67
|
+
|
|
68
|
+
These limits protect against runaway operations and ensure reviewable changes.
|
|
69
|
+
|
|
70
|
+
| Guardrail | Limit | Rationale |
|
|
71
|
+
|-----------|-------|-----------|
|
|
72
|
+
| Allowed source file types | `.vue`, `.tsx`, `.jsx`, `.ts`, `.js`, `.mjs`, `.astro`, `.svelte` | Only scan files that can contain UI content |
|
|
73
|
+
| Max files per scan | 500 | Prevent scanning entire monorepos |
|
|
74
|
+
| Max files per apply | 100 | Keep diffs reviewable |
|
|
75
|
+
| Dry-run before apply | **MANDATORY** | Preview all changes before executing |
|
|
76
|
+
| Workflow mode | Always `review` | Normalize changes are never auto-merged |
|
|
77
|
+
| Reuse scope | Per model or per domain | No whole-project reuse in one operation |
|
|
78
|
+
| Reuse prerequisite | Extraction must be complete | Content must exist in `.contentrain/` before patching source |
|
|
79
|
+
|
|
80
|
+
### Enforcement
|
|
81
|
+
|
|
82
|
+
- Attempting to apply without a prior dry-run will be rejected.
|
|
83
|
+
- Attempting to reuse without completed extraction will be rejected.
|
|
84
|
+
- Exceeding file limits will truncate results with a warning.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 5. What IS Content
|
|
89
|
+
|
|
90
|
+
Use these heuristics to identify content strings in source code. Content is user-visible text that should be managed separately from code.
|
|
91
|
+
|
|
92
|
+
### Extract These
|
|
93
|
+
|
|
94
|
+
- **Headings and titles** in templates/JSX (`<h1>`, `<h2>`, etc.)
|
|
95
|
+
- **Paragraph text** and body copy
|
|
96
|
+
- **Button labels** (`<button>Submit</button>`)
|
|
97
|
+
- **Link text** (`<a>Learn more</a>`)
|
|
98
|
+
- **Form labels and placeholders** (`<label>`, `placeholder="..."`)
|
|
99
|
+
- **Error messages** shown to users
|
|
100
|
+
- **Success/notification messages**
|
|
101
|
+
- **Alt text** on images (`alt="..."`)
|
|
102
|
+
- **ARIA labels** (`aria-label="..."`)
|
|
103
|
+
- **Meta descriptions and page titles** (`<title>`, `<meta name="description">`)
|
|
104
|
+
- **Navigation items** (menu labels, breadcrumbs)
|
|
105
|
+
- **Tooltip text**
|
|
106
|
+
- **Empty state messages** ("No results found")
|
|
107
|
+
- **CTA (call-to-action) text**
|
|
108
|
+
|
|
109
|
+
### Do NOT Extract These
|
|
110
|
+
|
|
111
|
+
- CSS class names, HTML IDs
|
|
112
|
+
- Variable names, function names, parameter names
|
|
113
|
+
- Technical identifiers (API endpoints, route paths, event names)
|
|
114
|
+
- Import paths and file paths
|
|
115
|
+
- Numbers used as constants (port numbers, HTTP status codes, pixel dimensions, timeouts)
|
|
116
|
+
- Strings shorter than 3 characters (unless semantically meaningful: "OK", "No", "Yes")
|
|
117
|
+
- Regular expressions
|
|
118
|
+
- Configuration values (environment variables, feature flags)
|
|
119
|
+
- Log messages (internal, not user-facing)
|
|
120
|
+
- Code comments
|
|
121
|
+
- Test assertion strings
|
|
122
|
+
- JSON keys
|
|
123
|
+
- Enum values used as code identifiers (not displayed to users)
|
|
124
|
+
|
|
125
|
+
### Dictionary Interpolation Limitation
|
|
126
|
+
|
|
127
|
+
Strings containing dynamic expressions (`${variable}`, template literals with runtime values) cannot be stored in dictionaries. These must remain as hardcoded expressions in source code.
|
|
128
|
+
|
|
129
|
+
**CANNOT extract:**
|
|
130
|
+
|
|
131
|
+
- `"Add a new entry to ${modelId}"` — contains runtime variable
|
|
132
|
+
- `` `Hello, ${user.name}!` `` — template literal with variable
|
|
133
|
+
|
|
134
|
+
**CAN extract:**
|
|
135
|
+
|
|
136
|
+
- `"Add a new entry"` — static string (parameterize separately if needed)
|
|
137
|
+
- `"Hello, World!"` — no runtime variables
|
|
138
|
+
|
|
139
|
+
Agent should split parameterized strings: extract the static template pattern and leave the interpolation in code.
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
// Instead of: `"Add a new entry to ${modelId}"`
|
|
143
|
+
// Extract: dictionary key "add-entry-to" = "Add a new entry to"
|
|
144
|
+
// Code: `${t['add-entry-to']} ${modelId}`
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Edge Cases
|
|
148
|
+
|
|
149
|
+
| String | Extract? | Reason |
|
|
150
|
+
|--------|----------|--------|
|
|
151
|
+
| `"OK"` | Yes | User-visible confirmation |
|
|
152
|
+
| `"px"` | No | CSS unit, not content |
|
|
153
|
+
| `"Loading..."` | Yes | User-visible state message |
|
|
154
|
+
| `"GET"` | No | HTTP method, technical |
|
|
155
|
+
| `"en"` | No | Locale code, technical |
|
|
156
|
+
| `"Submit Form"` | Yes | User-visible button label |
|
|
157
|
+
| `"/api/users"` | No | API endpoint |
|
|
158
|
+
| `"flex"` | No | CSS value |
|
|
159
|
+
| `"user.name"` | No | Object property path |
|
|
160
|
+
| `"An error occurred"` | Yes | User-facing error message |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 6. Phase 1: Extraction Flow
|
|
165
|
+
|
|
166
|
+
### Step-by-Step
|
|
167
|
+
|
|
168
|
+
1. **Build project graph:**
|
|
169
|
+
```
|
|
170
|
+
contentrain_scan(mode: "graph")
|
|
171
|
+
```
|
|
172
|
+
Returns import/component dependency graph. Use this to understand project structure and prioritize files.
|
|
173
|
+
|
|
174
|
+
2. **Find candidates:**
|
|
175
|
+
```
|
|
176
|
+
contentrain_scan(mode: "candidates")
|
|
177
|
+
```
|
|
178
|
+
Returns hardcoded string candidates with file locations, line numbers, and surrounding context.
|
|
179
|
+
|
|
180
|
+
3. **Agent evaluation (your job):**
|
|
181
|
+
- Filter out false positives using the heuristics in Section 5.
|
|
182
|
+
- Group candidates by domain (e.g., `marketing`, `blog`, `system`).
|
|
183
|
+
- Determine model structure: which kind (singleton, collection, dictionary), which fields.
|
|
184
|
+
- Assign field names and keys.
|
|
185
|
+
|
|
186
|
+
4. **Preview extraction:**
|
|
187
|
+
```
|
|
188
|
+
contentrain_apply(mode: "extract", dry_run: true)
|
|
189
|
+
```
|
|
190
|
+
Returns a preview of what will be created in `.contentrain/` without making changes.
|
|
191
|
+
|
|
192
|
+
5. **Review dry-run output:**
|
|
193
|
+
- Verify models are structured correctly.
|
|
194
|
+
- Verify content assignments are accurate.
|
|
195
|
+
- Check for missing or misclassified candidates.
|
|
196
|
+
|
|
197
|
+
6. **Execute extraction:**
|
|
198
|
+
```
|
|
199
|
+
contentrain_apply(mode: "extract")
|
|
200
|
+
```
|
|
201
|
+
Creates model definitions and content files in `.contentrain/`.
|
|
202
|
+
|
|
203
|
+
7. **Validate and submit:**
|
|
204
|
+
```
|
|
205
|
+
contentrain_validate
|
|
206
|
+
contentrain_submit
|
|
207
|
+
```
|
|
208
|
+
Submit always uses `review` mode for normalize operations.
|
|
209
|
+
|
|
210
|
+
### Extraction Rules
|
|
211
|
+
|
|
212
|
+
- Extraction creates content in `.contentrain/` but does NOT modify source files.
|
|
213
|
+
- Each extraction produces a single branch with all extracted content.
|
|
214
|
+
- Group related content into the fewest models that make semantic sense.
|
|
215
|
+
- Prefer dictionary kind for UI labels and error messages.
|
|
216
|
+
- Prefer singleton kind for page-specific content (hero, features).
|
|
217
|
+
- Prefer collection kind for repeating items (team members, FAQs, testimonials).
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 7. Phase 2: Reuse Flow
|
|
222
|
+
|
|
223
|
+
### Step-by-Step
|
|
224
|
+
|
|
225
|
+
1. **Select scope:** Choose one model or domain to process.
|
|
226
|
+
|
|
227
|
+
2. **Determine replacement expressions (your job):**
|
|
228
|
+
Based on the project's tech stack, determine how source files should reference content:
|
|
229
|
+
|
|
230
|
+
| Stack | Pattern | Example |
|
|
231
|
+
|-------|---------|---------|
|
|
232
|
+
| Vue/Nuxt (i18n) | `{{ $t('key') }}` | `{{ $t('hero.title') }}` |
|
|
233
|
+
| React/Next (react-intl) | `{intl.formatMessage({id: 'key'})}` | `{intl.formatMessage({id: 'hero.title'})}` |
|
|
234
|
+
| React/Next (next-intl) | `{t('key')}` | `{t('hero.title')}` |
|
|
235
|
+
| Svelte/SvelteKit | `{$t('key')}` | `{$t('hero.title')}` |
|
|
236
|
+
| Astro | `{t('key')}` | `{t('hero.title')}` |
|
|
237
|
+
| SDK import | `query('model').all()` | Direct data access via `#contentrain` |
|
|
238
|
+
|
|
239
|
+
3. **Preview reuse:**
|
|
240
|
+
```
|
|
241
|
+
contentrain_apply(mode: "reuse", scope: "model-id", dry_run: true)
|
|
242
|
+
```
|
|
243
|
+
Returns a preview of source file patches.
|
|
244
|
+
|
|
245
|
+
4. **Review dry-run output:**
|
|
246
|
+
- Verify each replacement is correct.
|
|
247
|
+
- Verify import statements will be added where needed.
|
|
248
|
+
- Check that no non-content strings are being replaced.
|
|
249
|
+
|
|
250
|
+
5. **Execute reuse:**
|
|
251
|
+
```
|
|
252
|
+
contentrain_apply(mode: "reuse", scope: "model-id")
|
|
253
|
+
```
|
|
254
|
+
Patches source files with content references.
|
|
255
|
+
|
|
256
|
+
6. **Validate and submit:**
|
|
257
|
+
```
|
|
258
|
+
contentrain_validate
|
|
259
|
+
contentrain_submit
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
7. **Repeat** for each model/domain until all extracted content is referenced.
|
|
263
|
+
|
|
264
|
+
### Reuse Rules
|
|
265
|
+
|
|
266
|
+
- Process one model or domain at a time. Do NOT reuse the entire project in one operation.
|
|
267
|
+
- Ensure the i18n/content library is properly configured in the project before reuse.
|
|
268
|
+
- Add necessary imports to patched files (e.g., `import { useTranslation } from 'next-intl'`).
|
|
269
|
+
- Do NOT change the structure or behavior of components -- only replace string literals with content references.
|
|
270
|
+
- If a source file requires setup code (composable import, hook call), include it in the patch.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 8. Domain and Model Grouping Guidelines
|
|
275
|
+
|
|
276
|
+
When evaluating scan candidates, group content logically:
|
|
277
|
+
|
|
278
|
+
### Domain Assignment
|
|
279
|
+
|
|
280
|
+
| Content Location | Suggested Domain |
|
|
281
|
+
|------------------|-----------------|
|
|
282
|
+
| Landing page, marketing sections | `marketing` |
|
|
283
|
+
| Blog, articles, posts | `blog` |
|
|
284
|
+
| Navigation, footer, header | `ui` |
|
|
285
|
+
| Error messages, validation | `system` |
|
|
286
|
+
| Product pages, e-commerce | `product` |
|
|
287
|
+
| Documentation, help | `docs` |
|
|
288
|
+
| User-facing app strings | `app` |
|
|
289
|
+
|
|
290
|
+
### Model Kind Selection
|
|
291
|
+
|
|
292
|
+
| Content Pattern | Kind | Rationale |
|
|
293
|
+
|----------------|------|-----------|
|
|
294
|
+
| One set of fields per page section | `singleton` | Hero, features, pricing header |
|
|
295
|
+
| Multiple items of same type | `collection` | Team members, FAQs, testimonials |
|
|
296
|
+
| Long-form with metadata | `document` | Blog posts, documentation pages |
|
|
297
|
+
| Key-value UI strings | `dictionary` | Error messages, button labels, form labels |
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## 9. Common Mistakes to Avoid
|
|
302
|
+
|
|
303
|
+
| Mistake | Correct Approach |
|
|
304
|
+
|---------|-----------------|
|
|
305
|
+
| Extracting CSS values as content | Only extract user-visible text |
|
|
306
|
+
| Creating one model per component | Group related content into shared models |
|
|
307
|
+
| Skipping dry-run | ALWAYS preview before apply |
|
|
308
|
+
| Auto-merging normalize changes | Normalize ALWAYS uses review mode |
|
|
309
|
+
| Reusing before extraction is merged | Wait for extraction review and merge first |
|
|
310
|
+
| Processing all models in one reuse | Scope reuse to one model/domain at a time |
|
|
311
|
+
| Ignoring project graph | Use graph output to understand component relationships |
|
|
312
|
+
| Hardcoding replacement patterns | Detect the project's i18n stack and use its conventions |
|