@contentrain/skills 0.3.0 → 0.5.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/README.md CHANGED
@@ -10,10 +10,10 @@ Workflow skills and framework guides for Contentrain-aware AI agents.
10
10
  Start here:
11
11
 
12
12
  - [2-minute product demo](https://ai.contentrain.io/demo)
13
- - [Rules & skills docs](https://ai.contentrain.io/packages/rules)
13
+ - [Rules & skills docs](https://ai.contentrain.io/packages/rules)
14
14
  - [Framework integration guide](https://ai.contentrain.io/guides/frameworks)
15
15
 
16
- This package follows the [Agent Skills standard](https://agentskills.io) for progressive disclosure: each skill has a `SKILL.md` (loaded on activation) and optional `references/` (loaded on demand).
16
+ This package follows the [Agent Skills standard](https://agentskills.io) for **progressive disclosure**: each skill has a `SKILL.md` (loaded on activation) and optional `references/*.md` (loaded on demand).
17
17
 
18
18
  ## Install
19
19
 
@@ -45,54 +45,71 @@ Works with Claude Code, Cursor, Windsurf, GitHub Copilot, OpenAI Codex, Gemini C
45
45
 
46
46
  ### Agent Skills (standard format)
47
47
 
48
- Published under `skills/`:
48
+ Published under `skills/` — 15 production skills:
49
49
 
50
50
  | Skill | Description |
51
51
  |-------|-------------|
52
- | `contentrain` | Core architecture, MCP tools, content formats |
53
- | `contentrain-normalize` | Two-phase normalize (extract + reuse) |
54
- | `contentrain-quality` | Content quality, SEO, accessibility, media |
55
- | `contentrain-sdk` | @contentrain/query SDK usage (local + CDN) |
56
- | `contentrain-content` | Content CRUD operations |
57
- | `contentrain-model` | Model creation/update |
58
- | `contentrain-init` | Project initialization |
59
- | `contentrain-bulk` | Batch operations |
60
- | `contentrain-validate-fix` | Validation and auto-fix |
61
- | `contentrain-review` | Content quality review |
62
- | `contentrain-translate` | Multi-locale translation |
63
- | `contentrain-generate` | SDK client generation |
64
- | `contentrain-serve` | Local review/normalize UI |
65
- | `contentrain-diff` | Branch content diffs |
66
- | `contentrain-doctor` | Project health diagnostics |
52
+ | `contentrain` | Core architecture, MCP tools, content formats, i18n, security |
53
+ | `contentrain-normalize` | Two-phase normalize (extract hardcoded strings + patch source files) |
54
+ | `contentrain-quality` | Content quality, SEO, accessibility, media optimization |
55
+ | `contentrain-sdk` | Query SDK usage (#contentrain imports, QueryBuilder, type-safe access) |
56
+ | `contentrain-content` | Create and manage content entries for existing models |
57
+ | `contentrain-model` | Design and save model definitions |
58
+ | `contentrain-init` | Initialize a new Contentrain project |
59
+ | `contentrain-bulk` | Batch operations on content entries |
60
+ | `contentrain-validate-fix` | Validate content and auto-fix structural issues |
61
+ | `contentrain-review` | Review content changes before publishing |
62
+ | `contentrain-translate` | Multi-locale translation workflows |
63
+ | `contentrain-generate` | Generate the typed SDK client from models |
64
+ | `contentrain-serve` | Start the local review and normalize UI |
65
+ | `contentrain-diff` | View content diffs between branches |
66
+ | `contentrain-doctor` | Diagnose project health issues |
67
67
 
68
68
  Each skill directory contains:
69
+
69
70
  ```
70
71
  skills/{name}/
71
- ├── SKILL.md # Instructions (< 500 lines)
72
+ ├── SKILL.md # Instructions (< 500 lines, quick reference)
72
73
  └── references/ # Detailed docs (loaded on demand)
73
- └── *.md
74
+ └── *.md # Deep dives: architecture, patterns, examples
74
75
  ```
75
76
 
77
+ Example references: `contentrain/references/mcp-tools.md`, `contentrain-normalize/references/extraction.md`, `contentrain-quality/references/seo.md`.
78
+
76
79
  ### Workflow skills (backward compat)
77
80
 
78
- Published under `workflows/*` — flat markdown files from previous versions. Still functional.
81
+ Published under `workflows/` — flat markdown files retained for compatibility:
82
+
83
+ - `contentrain-init.md`, `contentrain-model.md`, `contentrain-content.md`, `contentrain-bulk.md`
84
+ - `contentrain-normalize.md`, `contentrain-validate-fix.md`, `contentrain-review.md`
85
+ - `contentrain-diff.md`, `contentrain-doctor.md`, `contentrain-serve.md`
86
+ - `contentrain-generate.md`, `contentrain-translate.md`
87
+
88
+ Still fully functional; new projects should prefer the `skills/` structure.
79
89
 
80
90
  ### Framework guides
81
91
 
82
- Published under `frameworks/*`:
92
+ Published under `frameworks/` — per-framework integration patterns:
83
93
 
84
- - `nuxt.md`, `next.md`, `astro.md`, `sveltekit.md`, `vue.md`, `react.md`, `expo.md`, `react-native.md`, `node.md`
94
+ - `nuxt.md`, `next.md`, `astro.md`, `sveltekit.md` (meta frameworks)
95
+ - `react.md`, `vue.md` (UI libraries)
96
+ - `expo.md`, `react-native.md` (mobile)
97
+ - `node.md` (backend)
85
98
 
86
99
  ## Public Exports
87
100
 
88
101
  ```ts
89
102
  import { AGENT_SKILLS, WORKFLOW_SKILLS, FRAMEWORK_GUIDES } from '@contentrain/skills'
90
103
 
91
- // Agent Skills catalog (name + description for Tier 1 discovery)
92
- console.log(AGENT_SKILLS)
104
+ // Discover skills: name + description for agent activation
105
+ AGENT_SKILLS.forEach(({ name, description }) => {
106
+ console.log(`${name}: ${description}`)
107
+ })
108
+
109
+ // Backward compat: flat workflow list
110
+ console.log(WORKFLOW_SKILLS.length) // 12
93
111
 
94
- // Backward compat
95
- console.log(WORKFLOW_SKILLS)
112
+ // Framework discovery
96
113
  console.log(FRAMEWORK_GUIDES)
97
114
  ```
98
115
 
@@ -100,15 +117,24 @@ console.log(FRAMEWORK_GUIDES)
100
117
 
101
118
  | Tier | What's Loaded | When | Token Cost |
102
119
  |------|--------------|------|------------|
103
- | 1. Catalog | `name` + `description` | Session start | ~50 tokens/skill |
120
+ | 1. Catalog | `name` + `description` (AGENT_SKILLS) | Session start | ~50 tokens/skill |
104
121
  | 2. Instructions | Full `SKILL.md` body | Skill activated | < 5000 tokens |
105
- | 3. References | `references/*.md` | Agent needs detail | Varies |
122
+ | 3. References | `references/*.md` files | Agent needs detail | Varies (50-500 tokens/file) |
123
+
124
+ This reduces always-loaded context from thousands of lines to just the essentials plus catalog.
125
+
126
+ ## Parity with `@contentrain/mcp`
127
+
128
+ `@contentrain/skills` is kept in lockstep with the MCP tool registry via cross-package parity tests (`tests/mcp-parity.test.ts`):
129
+
130
+ - `skills/contentrain/references/mcp-tools.md` must have an `### <tool>` heading for every tool in the MCP `TOOL_NAMES` registry (currently 17).
131
+ - Key skills (normalize, translate) must not reference legacy `contentrain/{operation}/...` branch prefixes — MCP now emits `cr/*`.
106
132
 
107
- This reduces always-loaded context from ~4,700 lines to ~86 lines (essential guardrails only).
133
+ When MCP's surface changes, these tests fail until the skill docs catch up.
108
134
 
109
135
  ## IDE Support
110
136
 
111
- Skills are installed by `contentrain init` for detected IDEs:
137
+ Skills are installed by `contentrain init` (and re-applied with `contentrain skills --update`) for detected IDEs:
112
138
 
113
139
  | IDE | Rules Dir | Skills Dir |
114
140
  |-----|-----------|------------|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentrain/skills",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "description": "AI agent skills for Contentrain — workflow procedures, framework integration guides",
6
6
  "type": "module",
@@ -60,7 +60,8 @@
60
60
  "@types/node": "^22.0.0",
61
61
  "tsdown": "^0.21.0",
62
62
  "typescript": "^5.7.0",
63
- "vitest": "^3.0.0"
63
+ "vitest": "^3.0.0",
64
+ "@contentrain/mcp": "1.5.0"
64
65
  },
65
66
  "scripts": {
66
67
  "build": "tsdown src/index.ts --format esm,cjs --dts",
@@ -16,7 +16,7 @@ Contentrain consists of 6 packages that work together:
16
16
 
17
17
  | Package | Role | How agent uses it |
18
18
  |---|---|---|
19
- | @contentrain/mcp | 16 MCP tools (scan, apply, validate, merge...) | MCP tool calls |
19
+ | @contentrain/mcp | 19 MCP tools (scan, apply, validate, merge, doctor...) | MCP tool calls |
20
20
  | contentrain (CLI) | init, serve, generate, doctor, diff, status | Shell commands |
21
21
  | @contentrain/types | Shared TypeScript contracts | Type safety |
22
22
  | @contentrain/query | Generated SDK client (Prisma-pattern) | `import from '#contentrain'` |
@@ -147,7 +147,7 @@ contentrain_submit # Push (always review mode)
147
147
 
148
148
  - A dedicated `contentrain` branch is the single source of truth for content state, created at init and protected from deletion
149
149
  - Every write operation creates a temporary worktree on a new feature branch forked from `contentrain`
150
- - Branch naming: `contentrain/{operation}/{model}/{timestamp}` (locale included when applicable)
150
+ - Branch naming: `cr/{operation}/{model}/{timestamp}` (locale included when applicable)
151
151
  - Do not create branches manually. MCP handles Git transactions
152
152
  - Developer's working tree is never mutated during MCP operations (no stash, no checkout, no merge on the developer's tree)
153
153
  - context.json is committed together with content changes, not as a separate commit
@@ -269,13 +269,64 @@ Validate project content against model schemas.
269
269
 
270
270
  ### contentrain_submit
271
271
 
272
- Push contentrain/* branches to remote.
272
+ Push `cr/*` feature branches to remote.
273
273
 
274
274
  | Parameter | Type | Required | Description |
275
275
  |-----------|------|----------|-------------|
276
- | `branches` | string[] | No | Specific branches to push (omit for all contentrain/* branches) |
276
+ | `branches` | string[] | No | Specific branches to push (omit for all `cr/*` branches) |
277
277
  | `message` | string | No | Optional message for the push operation |
278
278
 
279
+ ### contentrain_merge
280
+
281
+ Merge a single review-mode `cr/*` branch into the content-tracking `contentrain` branch and advance the base branch via `update-ref`. Runs the worktree transaction with selective sync — dirty files in the developer's working tree are preserved rather than overwritten. The merged feature branch is pruned afterward.
282
+
283
+ Target the branch by exact name, or resolve it by model:
284
+
285
+ | Parameter | Type | Required | Description |
286
+ |-----------|------|----------|-------------|
287
+ | `branch` | string | No* | Exact feature branch name (must start with `cr/`) |
288
+ | `model` | string | No* | Resolve the branch by model id (e.g. `blog-post`) |
289
+ | `locale` | string | No | Narrow model resolution to a locale |
290
+ | `latest` | boolean | No | When multiple branches match the model, merge the most recently committed one |
291
+ | `confirm` | `true` | Yes | Must be `true` to confirm |
292
+
293
+ \* Provide either `branch` or `model`. Ambiguous model matches return the candidate branches so you can pick one (or pass `latest: true`).
294
+
295
+ Returns `{ branch, action, commit, sync }` — `sync.skipped[]` lists files the selective sync skipped because the developer has uncommitted changes. The CLI surfaces this as a warning.
296
+
297
+ ### contentrain_branch_list
298
+
299
+ List pending `cr/*` branches with their merge status against the `contentrain` branch. Use it to discover branch names for `contentrain_merge` / `contentrain_branch_delete` and to monitor branch-health pressure (warning at 50, blocked at 80 unmerged). Read-only. Local-filesystem only (`localWorktree`).
300
+
301
+ | Parameter | Type | Required | Description |
302
+ |-----------|------|----------|-------------|
303
+ | `unmerged_only` | boolean | No | Only list branches not yet merged into `contentrain`. Default `false` |
304
+
305
+ Returns `{ total, unmerged, branches: [{ name, sha, merged, lastCommit }], health }`.
306
+
307
+ ### contentrain_branch_delete
308
+
309
+ Delete a pending `cr/*` branch that will not be merged — e.g. one left by a failed operation, or a superseded draft. Only `cr/*` branches can be deleted; the `contentrain` branch is protected. Destructive: the branch and its unmerged commits are removed.
310
+
311
+ | Parameter | Type | Required | Description |
312
+ |-----------|------|----------|-------------|
313
+ | `branch` | string | Yes | The `cr/*` branch to delete |
314
+ | `confirm` | `true` | Yes | Must be `true` to confirm deletion |
315
+
316
+ Returns `{ status: 'deleted', branch, was_merged }`. Note: in normal operation Contentrain prunes feature branches automatically after auto-merge / `contentrain_merge`, so this tool is for cleanup of leftover branches only.
317
+
318
+ ## Doctor Tools
319
+
320
+ ### contentrain_doctor
321
+
322
+ Structured project health report: git install, Node version, `.contentrain/` structure, model parse, orphan content, pending branch pressure, SDK client freshness. Read-only. Local-filesystem only (`localWorktree` capability — unavailable over remote providers).
323
+
324
+ | Parameter | Type | Required | Description |
325
+ |-----------|------|----------|-------------|
326
+ | `usage` | boolean | No | Run the heavier usage-analysis branch (unused content keys, duplicate dictionary values, locale coverage). Default `false` |
327
+
328
+ Returns `{ checks[], summary: { total, passed, failed, warnings }, usage? }`. Each check carries `name`, `pass`, `detail`, optional `severity: 'error' | 'warning' | 'info'`.
329
+
279
330
  ## Bulk Tools
280
331
 
281
332
  ### contentrain_bulk
@@ -45,21 +45,21 @@ Contentrain supports two workflow modes, configured in `.contentrain/config.json
45
45
 
46
46
  ## 2. Branch Naming Convention
47
47
 
48
- All Contentrain branches follow a strict naming pattern:
48
+ All Contentrain feature branches follow a strict naming pattern:
49
49
 
50
50
  ```
51
- contentrain/{operation}/{model}/{locale}/{timestamp}
51
+ cr/{operation}/{model}/{locale}/{timestamp}-{suffix}
52
52
  ```
53
53
 
54
54
  ### Examples
55
55
 
56
56
  | Scenario | Branch Name |
57
57
  |----------|-------------|
58
- | Content update | `contentrain/content/blog-post/en/1710300000` |
59
- | Model creation | `contentrain/model/team-member/1710300000` |
60
- | Normalize extraction | `contentrain/normalize/extract/blog/1710300000` |
61
- | Normalize reuse | `contentrain/normalize/reuse/marketing-hero/en/1710300000` |
62
- | Scaffold | `contentrain/new/scaffold-landing/en/1710300000` |
58
+ | Content update | `cr/content/blog-post/en/1710300000-a1b2` |
59
+ | Model creation | `cr/model/team-member/1710300000-c3d4` |
60
+ | Normalize extraction | `cr/normalize/extract/blog/1710300000-e5f6` |
61
+ | Normalize reuse | `cr/normalize/reuse/marketing-hero/en/1710300000-0789` |
62
+ | Scaffold | `cr/new/scaffold-landing/en/1710300000-1234` |
63
63
 
64
64
  ### Rules
65
65
 
@@ -63,6 +63,7 @@ If the user's request does not match an existing model, offer to create one firs
63
63
 
64
64
  - Check `.contentrain/vocabulary.json` for canonical terms. All content must use these terms consistently across locales. Do not use alternative spellings or synonyms for vocabulary-defined terms.
65
65
  - Check `.contentrain/context.json` for tone conventions (professional, casual, technical).
66
+ - **For dictionaries:** Before creating any new key, call `contentrain_content_list` on the target model and scan existing values. If the value you intend to write already exists under a different key, REUSE the existing key instead of creating a new one. MCP will also warn you via `advisories` in the save response if duplicates are detected.
66
67
 
67
68
  ### 5. Generate Content
68
69
 
@@ -124,7 +125,17 @@ contentrain_content_save({
124
125
  - Prefer batch mode -- send multiple entries in a single call when possible.
125
126
  - Locale defaults to the project's default locale if omitted, but explicit locale is recommended.
126
127
 
127
- ### 9. Handle i18n Completeness
128
+ ### 9. Review Save Advisories
129
+
130
+ After `contentrain_content_save`, check the response for an `advisories` field. If present:
131
+
132
+ - Review each advisory for duplicate value warnings.
133
+ - If a duplicate is flagged, consider whether to:
134
+ - Remove the new key and reuse the existing one instead.
135
+ - Keep both keys if they serve semantically different purposes (document the reason).
136
+ - Report any advisories to the user for their decision.
137
+
138
+ ### 10. Handle i18n Completeness
128
139
 
129
140
  If the project has multiple supported locales and the model has `i18n: true`:
130
141
 
@@ -138,7 +149,7 @@ If the project has multiple supported locales and the model has `i18n: true`:
138
149
 
139
150
  If the user defers translations, note incomplete locales in the final summary.
140
151
 
141
- ### 10. Validate
152
+ ### 11. Validate
142
153
 
143
154
  Call `contentrain_validate` to check all changes:
144
155
 
@@ -149,7 +160,9 @@ Call `contentrain_validate` to check all changes:
149
160
 
150
161
  Fix any errors reported. Acknowledge any warnings.
151
162
 
152
- ### 11. Submit
163
+ For large content operations (10+ entries), also recommend running `contentrain doctor --usage` to detect unused keys and duplicate values across the project.
164
+
165
+ ### 12. Submit
153
166
 
154
167
  Call `contentrain_submit` to push branches to remote:
155
168
 
@@ -170,7 +183,7 @@ npx contentrain generate
170
183
 
171
184
  This updates TypeScript types for the new content. See: **contentrain-generate** skill.
172
185
 
173
- ### 12. Final Summary
186
+ ### 13. Final Summary
174
187
 
175
188
  Report to the user:
176
189
 
@@ -54,6 +54,10 @@ Execute the generation command:
54
54
  npx contentrain generate
55
55
  ```
56
56
 
57
+ > **Two ways to invoke the generator:**
58
+ > - `contentrain generate` — via the `contentrain` CLI (recommended; requires the `contentrain` package installed). This is what all examples in this skill use.
59
+ > - `npx contentrain-query generate` — directly via the `@contentrain/query` package's own bin, for projects that depend only on `@contentrain/query` (programmatic / build-tool flows). Both run the same generator.
60
+
57
61
  If the project uses a non-standard root directory, specify it:
58
62
 
59
63
  ```bash
@@ -170,6 +174,25 @@ For convenience, suggest adding a script to `package.json`:
170
174
  }
171
175
  ```
172
176
 
177
+ ### 9b. Wire generate into build/CI (REQUIRED for fresh clones)
178
+
179
+ `.contentrain/client/` is git-ignored (it is generated output, like Prisma's client). That means a **fresh clone or CI checkout has no client**, so any `#contentrain` import fails at typecheck/build until `generate` runs.
180
+
181
+ Wire generation into the build lifecycle so it always runs before a build:
182
+
183
+ ```json
184
+ {
185
+ "scripts": {
186
+ "prebuild": "contentrain generate",
187
+ "predev": "contentrain generate"
188
+ }
189
+ }
190
+ ```
191
+
192
+ - `prebuild`/`predev` run automatically before `build`/`dev` (npm/pnpm lifecycle), so CI and teammates regenerate the client without a manual step.
193
+ - Alternatively use a `postinstall` hook, but `prebuild`/`predev` are preferred (they don't run on every dependency install and stay close to when the client is actually needed).
194
+ - If you only commit content occasionally, the watch script above covers local dev; the `prebuild` hook covers CI and fresh clones.
195
+
173
196
  ### 10. Final Summary
174
197
 
175
198
  Report to the user:
@@ -12,12 +12,12 @@ The `#contentrain` subpath import works natively in Node.js 22+ but **does NOT r
12
12
  ### Vite (Vue, React, Svelte, Astro)
13
13
 
14
14
  ```ts
15
- // vite.config.ts
16
- import { resolve } from 'node:path'
15
+ // vite.config.ts (ESM — __dirname is not defined; use import.meta.url)
16
+ import { fileURLToPath } from 'node:url'
17
17
  export default defineConfig({
18
18
  resolve: {
19
19
  alias: {
20
- '#contentrain': resolve(__dirname, '.contentrain/client/index.mjs'),
20
+ '#contentrain': fileURLToPath(new URL('.contentrain/client/index.mjs', import.meta.url)),
21
21
  },
22
22
  },
23
23
  })
@@ -60,9 +60,9 @@ Add the same `tsconfig.json` paths entry:
60
60
  }
61
61
  ```
62
62
 
63
- ### Nuxt 3
63
+ ### Nuxt 3 / Nuxt 4
64
64
 
65
- Nuxt provides a top-level `alias` option — no Vite config needed:
65
+ Nuxt provides a top-level `alias` option — no Vite config needed (the relative string avoids `__dirname` entirely):
66
66
 
67
67
  ```ts
68
68
  // nuxt.config.ts
@@ -27,6 +27,30 @@ Phase 1 alone is valuable: content becomes manageable in Studio, translatable, a
27
27
  - MUST NOT patch `.contentrain/` files via reuse (content files are read-only for reuse)
28
28
  - MUST NOT exceed 100 patches per `contentrain_apply` call
29
29
 
30
+ ## Transport Requirements
31
+
32
+ Normalize (`contentrain_scan` and `contentrain_apply`) requires **local
33
+ disk access** — AST scanners walk the source tree and patch files in
34
+ place. It runs only on a `LocalProvider` (stdio transport, or an HTTP
35
+ transport configured with a `LocalProvider`).
36
+
37
+ Remote providers (`GitHubProvider`, `GitLabProvider`, future
38
+ `BitbucketProvider`) expose `astScan: false`, `sourceRead: false`, and
39
+ `sourceWrite: false`. Calling these tools over a remote provider
40
+ returns a uniform capability error:
41
+
42
+ ```json
43
+ {
44
+ "error": "contentrain_scan requires local filesystem access.",
45
+ "capability_required": "astScan",
46
+ "hint": "This tool is unavailable when MCP is driven by a remote provider. Use a LocalProvider or the stdio transport."
47
+ }
48
+ ```
49
+
50
+ If the agent is driving a remote-only MCP session, normalize must run
51
+ in a separate local-checkout session before the extracted content
52
+ branch is pushed.
53
+
30
54
  ---
31
55
 
32
56
  ## Two-Phase Architecture
@@ -36,7 +60,7 @@ Phase 1 alone is valuable: content becomes manageable in Studio, translatable, a
36
60
  | Purpose | Pull content from source to `.contentrain/` | Patch source files with content references |
37
61
  | Scope | Full project scan | Per model or per domain |
38
62
  | Source files modified | No | Yes |
39
- | Branch pattern | `contentrain/normalize/extract/{domain}/{timestamp}` | `contentrain/normalize/reuse/{model}/{locale}/{timestamp}` |
63
+ | Branch pattern | `cr/normalize/extract/{domain}/{timestamp}` | `cr/normalize/reuse/{model}/{locale}/{timestamp}` |
40
64
  | Prerequisite | Initialized `.contentrain/` | Completed extraction (content exists in `.contentrain/`) |
41
65
  | Workflow mode | Always `review` | Always `review` |
42
66
  | Standalone value | Yes -- content is manageable in Studio immediately | Depends on Phase 1 |
@@ -109,7 +133,7 @@ Call `contentrain_apply(mode: "extract", dry_run: true)` to generate a preview.
109
133
 
110
134
  ### 6. Execute Extraction
111
135
 
112
- After user approval, call `contentrain_apply(mode: "extract", dry_run: false)`. This creates model definitions and content files in `.contentrain/` on a `contentrain/normalize/extract/{timestamp}` branch. Source files are NOT modified.
136
+ After user approval, call `contentrain_apply(mode: "extract", dry_run: false)`. This creates model definitions and content files in `.contentrain/` on a `cr/normalize/extract/{timestamp}` branch. Source files are NOT modified.
113
137
 
114
138
  ### 7. Validate and Submit
115
139
 
@@ -183,7 +207,7 @@ Call `contentrain_apply(mode: "reuse", scope: { model: "<model-id>" }, patches:
183
207
 
184
208
  ### 4. Execute Reuse
185
209
 
186
- After user confirmation, call `contentrain_apply(mode: "reuse", scope: { model: "<model-id>" }, patches: [...], dry_run: false)`. This patches source files and creates a `contentrain/normalize/reuse/{model}/{timestamp}` branch.
210
+ After user confirmation, call `contentrain_apply(mode: "reuse", scope: { model: "<model-id>" }, patches: [...], dry_run: false)`. This patches source files and creates a `cr/normalize/reuse/{model}/{timestamp}` branch.
187
211
 
188
212
  ### 5. Validate and Submit
189
213
 
@@ -113,7 +113,7 @@ Returns a preview of what will be created in `.contentrain/` without making chan
113
113
  contentrain_apply(mode: "extract", dry_run: false)
114
114
  ```
115
115
 
116
- Creates model definitions and content files in `.contentrain/` on a `contentrain/normalize/extract/{timestamp}` branch. `dry_run` defaults to `true`, so you MUST explicitly set `dry_run: false` to execute.
116
+ Creates model definitions and content files in `.contentrain/` on a `cr/normalize/extract/{timestamp}` branch. `dry_run` defaults to `true`, so you MUST explicitly set `dry_run: false` to execute.
117
117
 
118
118
  ### 7. Validate and Submit
119
119
 
@@ -42,7 +42,7 @@ Review the dry-run output:
42
42
  contentrain_apply(mode: "reuse", scope: { model: "<model-id>" }, patches: [...], dry_run: false)
43
43
  ```
44
44
 
45
- `dry_run` defaults to `true`, so you MUST explicitly set `dry_run: false` to execute. This patches source files and creates a `contentrain/normalize/reuse/{model}/{timestamp}` branch.
45
+ `dry_run` defaults to `true`, so you MUST explicitly set `dry_run: false` to execute. This patches source files and creates a `cr/normalize/reuse/{model}/{timestamp}` branch.
46
46
 
47
47
  ### 5. Validate and Submit
48
48
 
@@ -74,6 +74,8 @@ If the agent cannot produce real content, it must leave the entry in `draft` sta
74
74
  - Descriptions/excerpts must be unique across all entries in the same collection.
75
75
  - If content is shared across entries, extract it to a referenced entry and use a `relation` field.
76
76
  - Before writing, query existing entries to confirm no duplication.
77
+ - **Dictionary values:** Different keys must not map to identical values. If "Cancel" exists under `dialog.cancel`, do not create `form.cancel` with the same value — reuse the existing key. MCP warns automatically via `advisories` in the save response.
78
+ - **Cross-model deduplication:** If the same text appears across multiple dictionary models, consider moving it to a shared dictionary or using vocabulary terms for consistency.
77
79
 
78
80
  ---
79
81
 
@@ -20,14 +20,14 @@ The `#contentrain` import requires subpath imports configuration in `package.jso
20
20
  ## Vite (Vue, React, Svelte)
21
21
 
22
22
  ```typescript
23
- // vite.config.ts
24
- import { resolve } from 'node:path'
23
+ // vite.config.ts (ESM — __dirname is not defined here)
24
+ import { fileURLToPath } from 'node:url'
25
25
  import { defineConfig } from 'vite'
26
26
 
27
27
  export default defineConfig({
28
28
  resolve: {
29
29
  alias: {
30
- '#contentrain': resolve(__dirname, '.contentrain/client/index.mjs')
30
+ '#contentrain': fileURLToPath(new URL('.contentrain/client/index.mjs', import.meta.url))
31
31
  }
32
32
  }
33
33
  })
@@ -47,20 +47,22 @@ export default {
47
47
  }
48
48
  ```
49
49
 
50
- ## Nuxt 3
50
+ ## Nuxt 3 / Nuxt 4
51
51
 
52
52
  ```typescript
53
- // nuxt.config.ts
54
- import { resolve } from 'node:path'
53
+ // nuxt.config.ts (ESM — use import.meta.url, not __dirname)
54
+ import { fileURLToPath } from 'node:url'
55
55
 
56
56
  export default defineNuxtConfig({
57
57
  alias: {
58
- '#contentrain': resolve(__dirname, '.contentrain/client/index.mjs')
58
+ '#contentrain': fileURLToPath(new URL('.contentrain/client/index.mjs', import.meta.url))
59
59
  }
60
60
  })
61
61
  ```
62
62
 
63
- **Important:** Treat `#contentrain` as **server-only** in Nuxt. Use in server routes, server plugins, and `useAsyncData` callbacks only.
63
+ **Important:** Treat `#contentrain` as **server-only** in Nuxt. Use it in server routes (`server/`), server plugins, and `useAsyncData` callbacks only.
64
+
65
+ **Nuxt 4 note:** Nuxt 4 moves app code under `app/` and keeps server code under `server/`. The alias above is resolved relative to `nuxt.config.ts` (project root), so it is unchanged — but make sure your `#contentrain` calls live in `server/` (e.g. `server/api/*`), never in `app/` components that run on the client.
64
66
 
65
67
  ## SvelteKit
66
68
 
@@ -170,7 +170,7 @@ If validation fails, fix issues and re-save.
170
170
 
171
171
  Call `contentrain_submit` to commit the translations:
172
172
 
173
- - Branch: `contentrain/content/{model}/{targetLocale}/{timestamp}`.
173
+ - Branch: `cr/content/{model}/{targetLocale}/{timestamp}`.
174
174
  - Each locale can be submitted independently.
175
175
 
176
176
  ### 11. Final Summary
@@ -103,12 +103,12 @@ The `#contentrain` subpath import works natively in Node.js 22+ but **does NOT r
103
103
  #### Vite (Vue, React, Svelte, Astro)
104
104
 
105
105
  ```ts
106
- // vite.config.ts
107
- import { resolve } from 'node:path'
106
+ // vite.config.ts (ESM — __dirname is not defined; use import.meta.url)
107
+ import { fileURLToPath } from 'node:url'
108
108
  export default defineConfig({
109
109
  resolve: {
110
110
  alias: {
111
- '#contentrain': resolve(__dirname, '.contentrain/client/index.mjs'),
111
+ '#contentrain': fileURLToPath(new URL('.contentrain/client/index.mjs', import.meta.url)),
112
112
  },
113
113
  },
114
114
  })
@@ -134,7 +134,7 @@ Do NOT proceed to apply unless the user approves (either via UI or explicit conf
134
134
 
135
135
  4. **Detect the user's decision.** After directing the user to the UI, check the plan file to determine the outcome:
136
136
  - **Plan file exists** (`status: "pending"`) → user has not decided yet — wait
137
- - **Plan file deleted + new `contentrain/normalize/extract/*` branch exists** → user approved, extraction applied
137
+ - **Plan file deleted + new `cr/normalize/extract/*` branch exists** → user approved, extraction applied
138
138
  - **Plan file deleted + no new branch** → user rejected the plan
139
139
 
140
140
  If rejected, ask the user what to change and iterate from Step 4 (re-evaluate candidates).
@@ -161,7 +161,7 @@ After user approval (via UI or chat), call `contentrain_apply(mode: "extract", d
161
161
 
162
162
  Note: `dry_run` defaults to `true`, so you MUST explicitly set `dry_run: false` to execute.
163
163
 
164
- This creates model definitions and content files in `.contentrain/` on a `contentrain/normalize/extract/{timestamp}` branch. Source files are NOT modified.
164
+ This creates model definitions and content files in `.contentrain/` on a `cr/normalize/extract/{timestamp}-{suffix}` branch. Source files are NOT modified.
165
165
 
166
166
  If approved via UI, the UI calls this automatically — no additional agent action needed.
167
167
 
@@ -230,7 +230,7 @@ After user confirmation, call `contentrain_apply(mode: "reuse", scope: { model:
230
230
 
231
231
  Note: `dry_run` defaults to `true`, so you MUST explicitly set `dry_run: false` to execute.
232
232
 
233
- This patches source files and creates a `contentrain/normalize/reuse/{model}/{timestamp}` branch.
233
+ This patches source files and creates a `cr/normalize/reuse/{model}/{timestamp}-{suffix}` branch.
234
234
 
235
235
  ### Step 5. Validate and Submit
236
236
 
@@ -154,7 +154,7 @@ If validation fails, fix issues and re-save.
154
154
 
155
155
  Call `contentrain_submit` to commit the translations:
156
156
 
157
- - Branch: `contentrain/content/{model}/{targetLocale}/{timestamp}`.
157
+ - Branch: `cr/content/{model}/{targetLocale}/{timestamp}-{suffix}`.
158
158
  - Each locale can be submitted independently.
159
159
 
160
160
  ### 11. Final Summary