@contentrain/mcp 1.0.5 → 1.0.7

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.
@@ -3,7 +3,7 @@ import { t as readConfig } from "./config-CPlhDwsv.mjs";
3
3
  import { S as writeContent, _ as resolveMdFilePath, c as validateModelDefinition, h as resolveJsonFilePath, l as writeModel, m as resolveContentDir, o as listModels, s as readModel } from "./model-manager-kl78AAMg.mjs";
4
4
  import { n as writeContext } from "./context-CUFulSi2.mjs";
5
5
  import { t as checkBranchHealth } from "./branch-lifecycle-DB6sgeJi.mjs";
6
- import { n as createTransaction, t as buildBranchName } from "./transaction-BhSvWfSV.mjs";
6
+ import { n as createTransaction, t as buildBranchName } from "./transaction-_AiXqGuD.mjs";
7
7
  import { extname, join } from "node:path";
8
8
  //#region src/core/apply-manager.ts
9
9
  const MAX_PATCHES = 100;
@@ -748,4 +748,4 @@ function addImportsToLines(lines, imports) {
748
748
  //#endregion
749
749
  export { detectFileFramework as a, validatePatchPath as c, checkSyntax as i, applyExtract as n, replaceInLine as o, applyReuse as r, validateFrameworkExpression as s, PATCHABLE_EXTENSIONS as t };
750
750
 
751
- //# sourceMappingURL=apply-manager-BzpUJEbA.mjs.map
751
+ //# sourceMappingURL=apply-manager-BDtHM8z-.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"apply-manager-BzpUJEbA.mjs","names":[],"sources":["../src/core/apply-manager.ts"],"sourcesContent":["import type { ModelDefinition, FieldDef, FileFramework } from '@contentrain/types'\nimport { join, extname } from 'node:path'\nimport { readText, writeText, pathExists } from '../util/fs.js'\nimport { readModel, writeModel, listModels, validateModelDefinition } from './model-manager.js'\nimport { writeContent, resolveContentDir, resolveJsonFilePath, resolveMdFilePath, type ContentEntry } from './content-manager.js'\nimport { readConfig } from './config.js'\nimport { writeContext } from './context.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\n\n// ─── Types ───\n\nexport interface ExtractionEntry {\n model: string\n kind: 'singleton' | 'collection' | 'dictionary' | 'document'\n domain: string\n i18n?: boolean\n fields?: Record<string, FieldDef>\n entries: Array<{\n locale?: string\n slug?: string\n data: Record<string, unknown>\n source?: { file: string; line: number; value: string }\n sources?: Array<{ file: string; line: number; key: string; value: string }>\n }>\n}\n\nexport interface ExtractionInput {\n extractions: ExtractionEntry[]\n dry_run?: boolean\n}\n\nexport interface ExtractionPreview {\n models_to_create: string[]\n models_to_update: string[]\n total_entries: number\n content_files: string[]\n}\n\nexport interface ExtractionResult {\n dry_run: boolean\n preview?: ExtractionPreview\n error?: string\n validation_errors?: string[]\n results?: {\n models_created: string[]\n models_updated: string[]\n entries_written: number\n source_map: Array<{ model: string; locale: string; value: string; file: string; line: number }>\n }\n git?: { branch: string; action: string; commit: string }\n context_updated?: boolean\n next_steps: string[]\n}\n\nexport interface PatchEntry {\n file: string\n line: number\n old_value: string\n new_expression: string\n import_statement?: string\n}\n\nexport interface ReuseInput {\n scope: { model?: string; domain?: string }\n patches: PatchEntry[]\n dry_run?: boolean\n}\n\nexport interface SyntaxError {\n file: string\n error: string\n}\n\nexport interface ReuseResult {\n dry_run: boolean\n error?: string\n scope_warnings?: string[]\n preview?: {\n files_to_modify: string[]\n patches_count: number\n imports_to_add: number\n }\n results?: {\n files_modified: string[]\n patches_applied: number\n patches_skipped: Array<{ file: string; line: number; reason: string }>\n imports_added: number\n framework_warnings?: Array<{ file: string; warning: string }>\n syntax_errors?: SyntaxError[]\n }\n git?: { branch: string; action: string; commit: string }\n next_steps: string[]\n}\n\n// ─── Constants ───\n\nconst MAX_PATCHES = 100\n\n/** File extensions allowed for patching — scannable source files only */\nexport const PATCHABLE_EXTENSIONS = new Set([\n '.vue', '.tsx', '.jsx', '.ts', '.js', '.mjs', '.astro', '.svelte',\n])\n\n/** Directories that must never be patched */\nconst FORBIDDEN_PATH_SEGMENTS = new Set([\n '.contentrain', 'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',\n])\n\n// ─── Framework Detection (Guardrail #2) ───\n\nexport type { FileFramework } from '@contentrain/types'\n\nexport function detectFileFramework(filePath: string): FileFramework {\n const ext = extname(filePath).toLowerCase()\n switch (ext) {\n case '.vue': return 'vue'\n case '.svelte': return 'svelte'\n case '.tsx':\n case '.jsx': return 'jsx'\n case '.astro': return 'astro'\n case '.ts':\n case '.js':\n case '.mjs': return 'script'\n default: return 'script'\n }\n}\n\n/**\n * Validate that a replacement expression uses the correct template syntax\n * for the target file's framework. Returns a warning string or null.\n */\nexport function validateFrameworkExpression(\n filePath: string,\n newExpression: string,\n context: 'tag_text' | 'other',\n): string | null {\n if (context !== 'tag_text') return null\n\n const framework = detectFileFramework(filePath)\n\n switch (framework) {\n case 'vue':\n if (!newExpression.includes('{{')) {\n return `Vue file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{{\" — expected Vue template syntax like {{ $t('key') }}`\n }\n break\n case 'jsx':\n if (!newExpression.includes('{')) {\n return `JSX file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{\" — expected JSX syntax like {t('key')}`\n }\n break\n case 'svelte':\n if (!newExpression.includes('{')) {\n return `Svelte file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{\" — expected Svelte syntax like {$t('key')}`\n }\n break\n case 'astro':\n if (!newExpression.includes('{')) {\n return `Astro file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{\" — expected Astro syntax like {t('key')}`\n }\n break\n case 'script':\n // Script files don't have template interpolation — warn if attempting tag text replacement\n return `Script file \"${filePath}\": tag text replacement not applicable for .ts/.js files`\n }\n\n return null\n}\n\n// ─── Scope Validation (Guardrail #1) ───\n\n/**\n * Validate that a patch file path is safe and within allowed scope.\n * Returns an error message or null if valid.\n */\nexport function validatePatchPath(filePath: string): string | null {\n const normalizedPath = filePath.replace(/\\\\/g, '/')\n\n // Reject path traversal\n if (normalizedPath.includes('..')) {\n return `Path traversal detected: \"${filePath}\"`\n }\n\n // Reject absolute paths\n if (normalizedPath.startsWith('/')) {\n return `Absolute path not allowed: \"${filePath}\"`\n }\n\n // Reject forbidden directories\n const segments = normalizedPath.split('/')\n for (const seg of segments) {\n if (FORBIDDEN_PATH_SEGMENTS.has(seg)) {\n return `Patching files inside \"${seg}/\" is not allowed: \"${filePath}\"`\n }\n }\n\n // Reject non-scannable extensions\n const ext = extname(filePath).toLowerCase()\n if (!PATCHABLE_EXTENSIONS.has(ext)) {\n return `File extension \"${ext}\" is not patchable. Allowed: ${[...PATCHABLE_EXTENSIONS].join(', ')}. Path: \"${filePath}\"`\n }\n\n return null\n}\n\n// ─── Syntax Check (Guardrail #5) ───\n\n/**\n * Perform a basic syntax check on a patched file.\n * Returns an error message or null if syntax appears valid.\n */\nexport function checkSyntax(filePath: string, content: string): string | null {\n const ext = extname(filePath).toLowerCase()\n\n switch (ext) {\n case '.ts':\n case '.tsx':\n case '.js':\n case '.jsx':\n case '.mjs':\n return checkJsSyntax(content)\n case '.vue':\n return checkVueSyntax(content)\n case '.svelte':\n case '.astro':\n return checkTagBalance(content)\n default:\n return null\n }\n}\n\n/**\n * Basic JS/TS syntax check: bracket/paren/brace balance + string literal closure.\n * This is intentionally conservative — it catches obvious breakage without\n * requiring a full parser.\n */\nfunction checkJsSyntax(content: string): string | null {\n const stack: string[] = []\n const pairs: Record<string, string> = { ')': '(', ']': '[', '}': '{' }\n let inString: string | null = null\n let escaped = false\n let inLineComment = false\n let inBlockComment = false\n\n for (let i = 0; i < content.length; i++) {\n const ch = content[i]!\n const next = content[i + 1]\n\n // Handle escape sequences inside strings\n if (escaped) {\n escaped = false\n continue\n }\n\n if (ch === '\\\\' && inString !== null) {\n escaped = true\n continue\n }\n\n // Line comment\n if (!inString && !inBlockComment && ch === '/' && next === '/') {\n inLineComment = true\n continue\n }\n if (inLineComment) {\n if (ch === '\\n') inLineComment = false\n continue\n }\n\n // Block comment\n if (!inString && !inBlockComment && ch === '/' && next === '*') {\n inBlockComment = true\n i++ // skip *\n continue\n }\n if (inBlockComment) {\n if (ch === '*' && next === '/') {\n inBlockComment = false\n i++ // skip /\n }\n continue\n }\n\n // String handling\n if (inString !== null) {\n if (ch === inString) {\n // Template literal allows multi-line, others don't\n inString = null\n } else if (inString !== '`' && ch === '\\n') {\n return `Unterminated string literal near offset ${i}`\n }\n continue\n }\n\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n inString = ch\n continue\n }\n\n // Bracket matching\n if (ch === '(' || ch === '[' || ch === '{') {\n stack.push(ch)\n } else if (ch === ')' || ch === ']' || ch === '}') {\n const expected = pairs[ch]!\n if (stack.length === 0) {\n return `Unmatched closing \"${ch}\" near offset ${i}`\n }\n const top = stack.pop()!\n if (top !== expected) {\n return `Mismatched bracket: expected closing for \"${top}\" but found \"${ch}\" near offset ${i}`\n }\n }\n }\n\n if (inString !== null) {\n return `Unterminated string literal (opened with ${inString})`\n }\n\n if (stack.length > 0) {\n return `Unclosed bracket \"${stack[stack.length - 1]}\" — ${stack.length} unclosed bracket(s)`\n }\n\n return null\n}\n\n/**\n * Vue SFC syntax check: ensure <template>, <script>, <style> tags are balanced.\n */\nfunction checkVueSyntax(content: string): string | null {\n const tagBalance = checkTagBalance(content)\n if (tagBalance) return tagBalance\n\n // Vue-specific: check that SFC root tags are present and balanced\n for (const tag of ['template', 'script']) {\n const openRe = new RegExp(`<${tag}[\\\\s>]`, 'g')\n const closeRe = new RegExp(`</${tag}>`, 'g')\n const opens = content.match(openRe)?.length ?? 0\n const closes = content.match(closeRe)?.length ?? 0\n if (opens !== closes) {\n return `Unbalanced <${tag}> tag: ${opens} opening vs ${closes} closing`\n }\n }\n\n return null\n}\n\n/**\n * Basic tag balance check for HTML-like files.\n * Checks that self-closing tags are handled and major structural tags are balanced.\n */\nfunction checkTagBalance(content: string): string | null {\n // Check for common structural tags balance\n const structuralTags = ['div', 'section', 'main', 'header', 'footer', 'nav', 'article', 'aside', 'ul', 'ol', 'table']\n\n for (const tag of structuralTags) {\n const openRe = new RegExp(`<${tag}[\\\\s>]`, 'g')\n const closeRe = new RegExp(`</${tag}>`, 'g')\n const opens = content.match(openRe)?.length ?? 0\n const closes = content.match(closeRe)?.length ?? 0\n if (opens !== closes) {\n return `Unbalanced <${tag}> tag: ${opens} opening vs ${closes} closing`\n }\n }\n\n return null\n}\n\n// ─── Extract Mode ───\n\nexport async function applyExtract(\n projectRoot: string,\n input: ExtractionInput,\n): Promise<ExtractionResult> {\n const config = await readConfig(projectRoot)\n if (!config) throw new Error('Project not initialized. Run contentrain_init first.')\n\n const { extractions, dry_run } = input\n\n // Analyze what will happen\n const existingModels = await listModels(projectRoot)\n const existingIds = new Set(existingModels.map(m => m.id))\n\n const modelsToCreate: string[] = []\n const modelsToUpdate: string[] = []\n const contentFiles: string[] = []\n let totalEntries = 0\n\n const validationErrors: string[] = []\n\n for (const ext of extractions) {\n // Validate model definition with same rules as model_save\n const modelErrors = validateModelDefinition({\n id: ext.model,\n kind: ext.kind,\n fields: ext.fields as Record<string, unknown> | undefined,\n })\n if (modelErrors.length > 0) {\n validationErrors.push(...modelErrors.map(e => `[${ext.model}] ${e}`))\n }\n\n for (const entry of ext.entries) {\n if (ext.kind === 'dictionary') {\n if (entry.data['id'] !== undefined || entry.data['slug'] !== undefined) {\n validationErrors.push(`[${ext.model}] Dictionary entries should not have id or slug`)\n }\n for (const [key, val] of Object.entries(entry.data)) {\n if (typeof val !== 'string') {\n validationErrors.push(`[${ext.model}] Dictionary entry value for key \"${key}\" must be a string, got ${typeof val}`)\n }\n }\n } else if (ext.kind === 'document') {\n if (!entry.slug && !entry.data['slug']) {\n validationErrors.push(`[${ext.model}] Document entries must have a slug`)\n }\n } else if (ext.kind === 'collection') {\n if (entry.slug !== undefined || entry.data['slug'] !== undefined) {\n validationErrors.push(`[${ext.model}] Collection entries should not have slug`)\n }\n } else if (ext.kind === 'singleton') {\n if (entry.data['id'] !== undefined || entry.slug !== undefined || entry.data['slug'] !== undefined) {\n validationErrors.push(`[${ext.model}] Singleton entries should not have id or slug`)\n }\n }\n }\n\n if (existingIds.has(ext.model)) {\n modelsToUpdate.push(ext.model)\n } else {\n modelsToCreate.push(ext.model)\n }\n totalEntries += ext.entries.length\n\n // Guardrail #3: Preview-Execute Parity — use real model metadata if it exists\n let previewModel: ModelDefinition\n if (existingIds.has(ext.model)) {\n const real = await readModel(projectRoot, ext.model)\n if (real) {\n previewModel = real\n } else {\n previewModel = { id: ext.model, kind: ext.kind, domain: ext.domain, i18n: ext.i18n ?? true } as ModelDefinition\n }\n } else {\n previewModel = { id: ext.model, kind: ext.kind, domain: ext.domain, i18n: ext.i18n ?? true } as ModelDefinition\n }\n\n const cDir = resolveContentDir(projectRoot, previewModel)\n for (const entry of ext.entries) {\n const locale = entry.locale ?? config.locales.default\n if (ext.kind === 'document' && entry.slug) {\n contentFiles.push(resolveMdFilePath(cDir, previewModel, locale, entry.slug))\n } else {\n contentFiles.push(resolveJsonFilePath(cDir, previewModel, locale))\n }\n }\n }\n\n const preview: ExtractionPreview = {\n models_to_create: modelsToCreate,\n models_to_update: modelsToUpdate,\n total_entries: totalEntries,\n content_files: [...new Set(contentFiles)],\n }\n\n // Dry run — return preview only (include validation errors if any)\n if (dry_run !== false) {\n return {\n dry_run: true,\n preview,\n ...(validationErrors.length > 0 ? { validation_errors: validationErrors } : {}),\n next_steps: [\n ...(validationErrors.length > 0\n ? [`WARNING: ${validationErrors.length} validation error(s) found — fix before executing`]\n : []),\n 'Review the preview above',\n 'Call contentrain_apply with mode:extract and dry_run:false to execute',\n ],\n }\n }\n\n // Block execute if validation errors exist\n if (validationErrors.length > 0) {\n return {\n dry_run: false,\n error: 'Model validation failed — cannot execute extract with invalid model definitions',\n validation_errors: validationErrors,\n next_steps: ['Fix the validation errors and retry'],\n }\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n error: health.message,\n action: 'blocked' as const,\n hint: 'Merge or delete old contentrain/* branches before executing normalize.',\n } as unknown as ExtractionResult\n }\n\n // Execute — git transaction (always review mode for normalize)\n const branchName = buildBranchName('normalize', 'extract')\n const tx = await createTransaction(projectRoot, branchName, { workflowOverride: 'review' })\n const sourceMap: Array<{ model: string; locale: string; value: string; file: string; line: number }> = []\n const modelsCreated: string[] = []\n const modelsUpdated: string[] = []\n let entriesWritten = 0\n\n try {\n await tx.write(async (wt) => {\n for (const ext of extractions) {\n // Create or merge model\n const existing = await readModel(wt, ext.model)\n if (existing) {\n // Merge fields: add new, keep existing\n if (ext.fields) {\n const merged = { ...existing.fields, ...ext.fields }\n // Only overwrite if new fields were actually added\n const newFieldNames = Object.keys(ext.fields).filter(k => !(k in (existing.fields ?? {})))\n if (newFieldNames.length > 0) {\n existing.fields = merged\n await writeModel(wt, existing)\n modelsUpdated.push(ext.model)\n }\n }\n } else {\n // Create new model\n const newModel: ModelDefinition = {\n id: ext.model,\n name: ext.model.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),\n kind: ext.kind,\n domain: ext.domain,\n i18n: ext.i18n ?? true,\n fields: ext.fields,\n }\n await writeModel(wt, newModel)\n modelsCreated.push(ext.model)\n }\n\n // Write content entries\n const model = (await readModel(wt, ext.model))!\n const entries: ContentEntry[] = ext.entries.map(e => ({\n locale: e.locale,\n slug: e.slug,\n data: e.data,\n }))\n\n const wtConfig = await readConfig(wt) ?? config\n await writeContent(wt, model, entries, wtConfig)\n entriesWritten += entries.length\n\n // Track source map\n for (const entry of ext.entries) {\n // For dictionary entries with per-key source tracking\n if (ext.kind === 'dictionary' && entry.sources) {\n for (const s of entry.sources) {\n sourceMap.push({\n model: ext.model,\n locale: entry.locale ?? config.locales.default,\n value: s.value,\n file: s.file,\n line: s.line,\n })\n }\n } else if (entry.source) {\n sourceMap.push({\n model: ext.model,\n locale: entry.locale ?? config.locales.default,\n value: entry.source.value,\n file: entry.source.file,\n line: entry.source.line,\n })\n }\n }\n }\n\n // Write source map for reuse scope enforcement\n if (sourceMap.length > 0) {\n const sourcesByModel: Record<string, { source_files: string[]; entry_count: number }> = {}\n for (const s of sourceMap) {\n if (!sourcesByModel[s.model]) {\n sourcesByModel[s.model] = { source_files: [], entry_count: 0 }\n }\n const modelEntry = sourcesByModel[s.model]!\n if (!modelEntry.source_files.includes(s.file)) {\n modelEntry.source_files.push(s.file)\n }\n modelEntry.entry_count++\n }\n const sourcesJson = JSON.stringify({\n version: 1,\n created_at: new Date().toISOString(),\n models: sourcesByModel,\n }, null, 2) + '\\n'\n // Write to worktree only — merge brings it to main\n // Phase 2 (reuse) must wait for extract branch to be merged first\n await writeText(join(wt, '.contentrain', 'normalize-sources.json'), sourcesJson)\n }\n\n // Update context\n await writeContext(wt, {\n tool: 'contentrain_apply',\n model: extractions.map(e => e.model).join(','),\n locale: config.locales.default,\n entries: extractions.flatMap(e => e.entries.map(en => en.slug ?? 'entry')),\n })\n })\n\n const commitMsg = `[contentrain] normalize: extract ${entriesWritten} entries to ${extractions.length} models`\n await tx.commit(commitMsg)\n\n const gitResult = { branch: branchName, action: 'pending-review', commit: '' }\n try {\n const completed = await tx.complete()\n gitResult.action = completed.action\n gitResult.commit = completed.commit\n } catch {\n gitResult.action = 'pending-review'\n } finally {\n await tx.cleanup()\n }\n\n return {\n dry_run: false,\n results: {\n models_created: modelsCreated,\n models_updated: modelsUpdated,\n entries_written: entriesWritten,\n source_map: sourceMap,\n },\n git: gitResult,\n context_updated: true,\n next_steps: [\n 'Run contentrain_validate to check the extracted content',\n 'Run contentrain_submit to push the branch for review',\n 'After review, proceed with mode:reuse to patch source files',\n ],\n }\n } catch (error) {\n await tx.cleanup()\n throw error\n }\n}\n\n// ─── Reuse Mode ───\n\nexport async function applyReuse(\n projectRoot: string,\n input: ReuseInput,\n): Promise<ReuseResult> {\n const config = await readConfig(projectRoot)\n if (!config) throw new Error('Project not initialized. Run contentrain_init first.')\n\n const { scope, patches, dry_run } = input\n\n // Validate scope\n if (!scope.model && !scope.domain) {\n throw new Error('Scope required: provide model or domain. Whole-project patching is not allowed.')\n }\n\n // Validate patch count\n if (patches.length > MAX_PATCHES) {\n throw new Error(`Too many patches (${patches.length}). Maximum ${MAX_PATCHES} per operation. Split into multiple calls.`)\n }\n\n // Check content exists for scope (soft warning)\n if (scope.model) {\n const model = await readModel(projectRoot, scope.model)\n if (!model) {\n throw new Error(`Model \"${scope.model}\" not found. Run extract phase first.`)\n }\n }\n\n const scopeWarnings: string[] = []\n\n // Guardrail #1: Scope Real Enforcement\n // Step 1: Path safety — every patch must target a valid, patchable source file\n for (const patch of patches) {\n const pathError = validatePatchPath(patch.file)\n if (pathError) {\n throw new Error(`Invalid patch path: ${pathError}`)\n }\n }\n\n // Step 2: Semantic scope — verify scope model/domain exists and cross-check patch files\n if (scope.model || scope.domain) {\n const models = await listModels(projectRoot)\n const scopeModels = scope.model\n ? models.filter(m => m.id === scope.model)\n : scope.domain\n ? models.filter(m => m.domain === scope.domain)\n : models\n\n if (scopeModels.length === 0) {\n throw new Error(`No models found for scope ${scope.model ? `model=\"${scope.model}\"` : `domain=\"${scope.domain}\"`}`)\n }\n\n // Step 3: Verify patch files are source files (not content/config/meta files)\n // and belong to detectable source directories (not random locations)\n const { autoDetectSourceDirs } = await import('./scan-config.js')\n const sourceDirs = await autoDetectSourceDirs(projectRoot)\n\n // Build allowed file prefixes from source dirs\n const allowedPrefixes = sourceDirs.map(d => d === '.' ? '' : d + '/')\n\n for (const patch of patches) {\n const normalizedPath = patch.file.replace(/\\\\/g, '/')\n\n // Reject patches targeting .contentrain/ directory (content files should never be patched by reuse)\n if (normalizedPath.startsWith('.contentrain/') || normalizedPath.includes('/.contentrain/')) {\n throw new Error(`Cannot patch content/config files directly: \"${patch.file}\". Reuse patches source files only.`)\n }\n\n // If source dirs were detected (not just \".\"), verify patch files are within them\n if (sourceDirs.length > 0 && sourceDirs[0] !== '.') {\n const inSourceDir = allowedPrefixes.some(prefix =>\n prefix === '' || normalizedPath.startsWith(prefix),\n )\n if (!inSourceDir) {\n throw new Error(\n `Patch file \"${patch.file}\" is outside detected source directories (${sourceDirs.join(', ')}). ` +\n `Reuse patches must target source files within the project's source tree.`,\n )\n }\n }\n }\n\n // Step 4: Semantic source→model cross-check via normalize-sources.json\n // Source map is written by extract into the review branch worktree.\n // It only exists on base after extract branch is merged.\n // If missing: dry_run proceeds with warning, execute is blocked.\n if (scope.model || scope.domain) {\n const sourcesPath = join(projectRoot, '.contentrain', 'normalize-sources.json')\n const sourcesRaw = await readText(sourcesPath)\n\n if (!sourcesRaw) {\n // Source map not found — check if scoped model is a dictionary\n // Dictionaries store all keys in one entry, so per-file source tracking\n // is not available. Allow reuse with a warning instead of blocking.\n const scopedModel = scopeModels.find(m => m.id === scope.model)\n const isDictionary = scopedModel?.kind === 'dictionary'\n\n if (isDictionary) {\n scopeWarnings.push(\n 'normalize-sources.json not found. Dictionary models do not generate per-file source maps. ' +\n 'Scope enforcement is based on source-tree locality only.',\n )\n } else if (dry_run !== false) {\n scopeWarnings.push(\n 'normalize-sources.json not found. Semantic scope enforcement is unavailable. ' +\n 'Merge the extract branch first, then reuse will have full scope protection.',\n )\n } else {\n throw new Error(\n 'Cannot execute reuse: normalize-sources.json not found on base branch. ' +\n 'The extract branch must be merged before reuse can execute. ' +\n 'This ensures semantic scope enforcement protects against out-of-scope patching.',\n )\n }\n } else {\n const sourcesData = JSON.parse(sourcesRaw) as {\n models?: Record<string, { source_files?: string[] }>\n }\n\n // Collect all allowed source files for the scope\n let allowedSourceFiles: string[] = []\n let scopeLabel = ''\n\n if (scope.model) {\n const modelSources = sourcesData.models?.[scope.model]?.source_files\n if (modelSources) allowedSourceFiles = modelSources\n scopeLabel = `model \"${scope.model}\"`\n } else if (scope.domain) {\n const domainModelIds = scopeModels.map(m => m.id)\n for (const modelId of domainModelIds) {\n const modelSources = sourcesData.models?.[modelId]?.source_files\n if (modelSources) allowedSourceFiles.push(...modelSources)\n }\n allowedSourceFiles = [...new Set(allowedSourceFiles)]\n scopeLabel = `domain \"${scope.domain}\"`\n }\n\n if (allowedSourceFiles.length > 0) {\n const outOfScopePatches: string[] = []\n for (const patch of patches) {\n const normalizedPath = patch.file.replace(/\\\\/g, '/')\n if (!allowedSourceFiles.includes(normalizedPath)) {\n outOfScopePatches.push(patch.file)\n }\n }\n if (outOfScopePatches.length > 0) {\n throw new Error(\n `Scope enforcement: ${outOfScopePatches.length} patch file(s) are not associated with ${scopeLabel}. ` +\n `Out-of-scope files: ${outOfScopePatches.join(', ')}. ` +\n `Known source files: ${allowedSourceFiles.join(', ')}.`,\n )\n }\n }\n }\n }\n }\n\n // Group patches by file\n const patchesByFile = new Map<string, PatchEntry[]>()\n for (const patch of patches) {\n if (!patchesByFile.has(patch.file)) {\n patchesByFile.set(patch.file, [])\n }\n patchesByFile.get(patch.file)!.push(patch)\n }\n\n const filesToModify = [...patchesByFile.keys()]\n const importsToAdd = patches.filter(p => p.import_statement).length\n\n // Dry run — return preview only\n if (dry_run !== false) {\n return {\n dry_run: true,\n preview: {\n files_to_modify: filesToModify,\n patches_count: patches.length,\n imports_to_add: importsToAdd,\n },\n ...(scopeWarnings.length > 0 ? { scope_warnings: scopeWarnings } : {}),\n next_steps: [\n ...(scopeWarnings.length > 0 ? [`WARNING: ${scopeWarnings.length} patch file(s) not in extract source map — verify intent`] : []),\n 'Review the files and patches above',\n 'Call contentrain_apply with mode:reuse and dry_run:false to execute',\n ],\n }\n }\n\n // Branch health gate\n const reuseHealth = await checkBranchHealth(projectRoot)\n if (reuseHealth.blocked) {\n return {\n dry_run: false,\n error: `Branch blocked: ${reuseHealth.message}`,\n next_steps: ['Merge or delete old contentrain/* branches before executing reuse.'],\n }\n }\n\n // Execute — git transaction\n const scopeTarget = scope.model ?? scope.domain!\n const branchName = buildBranchName('normalize/reuse', scopeTarget)\n const tx = await createTransaction(projectRoot, branchName, { workflowOverride: 'review' })\n\n const filesModified: string[] = []\n let patchesApplied = 0\n let importsAdded = 0\n const patchesSkipped: Array<{ file: string; line: number; reason: string }> = []\n const frameworkWarnings: Array<{ file: string; warning: string }> = []\n const syntaxErrors: SyntaxError[] = []\n\n try {\n await tx.write(async (wt) => {\n for (const [relFile, filePatches] of patchesByFile) {\n const absPath = join(wt, relFile)\n\n if (!(await pathExists(absPath))) {\n for (const p of filePatches) {\n patchesSkipped.push({ file: relFile, line: p.line, reason: 'file not found' })\n }\n continue\n }\n\n const content = await readText(absPath)\n if (content === null) {\n for (const p of filePatches) {\n patchesSkipped.push({ file: relFile, line: p.line, reason: 'file unreadable' })\n }\n continue\n }\n\n // Sort patches by line DESC (bottom-up to avoid line shifts)\n const sorted = [...filePatches].toSorted((a, b) => b.line - a.line)\n\n const lines = content.split('\\n')\n let fileModified = false\n\n for (const patch of sorted) {\n // Determine replacement context before applying\n const targetLine = lines[patch.line - 1] ?? ''\n const isTagTextContext = targetLine.includes(`>${patch.old_value}<`)\n const replacementContext = isTagTextContext ? 'tag_text' as const : 'other' as const\n\n // Guardrail #2: Framework-aware validation — only warn for tag text replacements\n if (isTagTextContext) {\n const fwWarning = validateFrameworkExpression(relFile, patch.new_expression, replacementContext)\n if (fwWarning) {\n frameworkWarnings.push({ file: relFile, warning: fwWarning })\n }\n }\n\n const applied = applyPatchToLines(lines, patch)\n if (applied) {\n patchesApplied++\n fileModified = true\n } else {\n patchesSkipped.push({ file: relFile, line: patch.line, reason: 'old_value not found at or near specified line' })\n }\n }\n\n // Add imports (deduplicate)\n const importStatements = new Set(\n filePatches\n .filter(p => p.import_statement)\n .map(p => p.import_statement!),\n )\n\n if (importStatements.size > 0) {\n const added = addImportsToLines(lines, importStatements)\n importsAdded += added\n if (added > 0) fileModified = true\n }\n\n if (fileModified) {\n const newContent = lines.join('\\n')\n await writeText(absPath, newContent)\n filesModified.push(relFile)\n\n // Guardrail #5: Syntax check after patching\n const syntaxError = checkSyntax(relFile, newContent)\n if (syntaxError) {\n syntaxErrors.push({ file: relFile, error: syntaxError })\n }\n }\n }\n\n // Update context\n await writeContext(wt, {\n tool: 'contentrain_apply',\n model: scopeTarget,\n locale: config.locales.default,\n })\n })\n\n if (filesModified.length === 0) {\n await tx.cleanup()\n return {\n dry_run: false,\n results: {\n files_modified: [],\n patches_applied: 0,\n patches_skipped: patchesSkipped,\n imports_added: 0,\n framework_warnings: frameworkWarnings.length > 0 ? frameworkWarnings : undefined,\n },\n next_steps: ['No files were modified. Check patch definitions and try again.'],\n }\n }\n\n const commitMsg = `[contentrain] normalize: reuse ${scopeTarget} — patch ${filesModified.length} files (${patchesApplied} replacements)`\n await tx.commit(commitMsg)\n\n const gitResult = { branch: branchName, action: 'pending-review', commit: '' }\n try {\n const completed = await tx.complete()\n gitResult.action = completed.action\n gitResult.commit = completed.commit\n } catch {\n gitResult.action = 'pending-review'\n } finally {\n await tx.cleanup()\n }\n\n return {\n dry_run: false,\n results: {\n files_modified: filesModified,\n patches_applied: patchesApplied,\n patches_skipped: patchesSkipped,\n imports_added: importsAdded,\n framework_warnings: frameworkWarnings.length > 0 ? frameworkWarnings : undefined,\n syntax_errors: syntaxErrors.length > 0 ? syntaxErrors : undefined,\n },\n ...(scopeWarnings.length > 0 ? { scope_warnings: scopeWarnings } : {}),\n git: gitResult,\n next_steps: [\n 'Run contentrain_validate to verify the patched files',\n patchesSkipped.length > 0 ? `${patchesSkipped.length} patches were skipped — review and retry if needed` : '',\n syntaxErrors.length > 0 ? `WARNING: ${syntaxErrors.length} file(s) may have syntax errors after patching — review manually` : '',\n scopeWarnings.length > 0 ? `NOTE: ${scopeWarnings.length} patch file(s) not in extract source map` : '',\n 'Run contentrain_submit to push the branch for review',\n ].filter(Boolean),\n }\n } catch (error) {\n await tx.cleanup()\n throw error\n }\n}\n\n// ─── Patch Helpers ───\n\n/**\n * Apply a single patch to a lines array. Mutates lines in place.\n * Uses line hint for proximity matching — searches ±10 lines from hint.\n */\nfunction applyPatchToLines(lines: string[], patch: PatchEntry): boolean {\n const { line, old_value, new_expression } = patch\n const lineIdx = line - 1 // 0-based\n\n // Search range: ±10 lines from hint\n const searchStart = Math.max(0, lineIdx - 10)\n const searchEnd = Math.min(lines.length, lineIdx + 11)\n\n // First pass: exact line match\n if (lineIdx >= 0 && lineIdx < lines.length) {\n const replaced = replaceInLine(lines[lineIdx]!, old_value, new_expression)\n if (replaced !== null) {\n lines[lineIdx] = replaced\n return true\n }\n }\n\n // Second pass: proximity search\n for (let i = searchStart; i < searchEnd; i++) {\n if (i === lineIdx) continue // already tried\n const replaced = replaceInLine(lines[i]!, old_value, new_expression)\n if (replaced !== null) {\n lines[i] = replaced\n return true\n }\n }\n\n return false\n}\n\n/**\n * Replace old_value with new_expression in a single line.\n * Matches the string literal (quoted or unquoted tag text).\n * Returns the modified line, or null if not found.\n *\n * Guardrail #4: Safer patch matching — word boundary awareness and\n * ambiguity rejection for plain text fallback.\n */\nexport function replaceInLine(line: string, oldValue: string, newExpression: string): string | null {\n // Try exact match of the value in quoted strings\n // Match: \"old_value\", 'old_value', `old_value`\n for (const quote of ['\"', \"'\", '`']) {\n const quoted = `${quote}${oldValue}${quote}`\n if (line.includes(quoted)) {\n return line.replace(quoted, newExpression)\n }\n }\n\n // Try unquoted tag text match: >old_value<\n // The agent provides the complete new_expression with correct framework syntax:\n // JSX: {t('key')}\n // Vue: {{ $t('key') }}\n // Svelte: {$t('key')}\n // So we insert the expression as-is between > and <, without wrapping in braces.\n if (line.includes(`>${oldValue}<`)) {\n return line.replace(`>${oldValue}<`, `>${newExpression}<`)\n }\n\n // Guardrail #4: Safer plain text fallback\n // Only match if old_value appears at a word boundary and is unambiguous\n if (line.includes(oldValue)) {\n // Count occurrences — if multiple, it's ambiguous\n const occurrences = countOccurrences(line, oldValue)\n if (occurrences > 1) {\n return null // ambiguous — let proximity search handle it\n }\n\n // Word boundary check: don't replace \"Submit\" inside \"SubmitButton\"\n // Reject if the old_value is a substring of a larger word — check if\n // adjacent characters are word characters that extend the token.\n const idx = line.indexOf(oldValue)\n const charBefore = idx > 0 ? line[idx - 1]! : ''\n const charAfter = idx + oldValue.length < line.length ? line[idx + oldValue.length]! : ''\n\n const oldStartsWithWord = oldValue.length > 0 && isWordChar(oldValue[0]!)\n const oldEndsWithWord = oldValue.length > 0 && isWordChar(oldValue[oldValue.length - 1]!)\n\n // If old_value starts with a word char and char before is also word char, it's a partial match\n if (oldStartsWithWord && isWordChar(charBefore)) {\n return null\n }\n // If old_value ends with a word char and char after is also word char, it's a partial match\n if (oldEndsWithWord && isWordChar(charAfter)) {\n return null\n }\n\n return line.replace(oldValue, newExpression)\n }\n\n return null\n}\n\n/** Count non-overlapping occurrences of a substring */\nfunction countOccurrences(str: string, sub: string): number {\n let count = 0\n let pos = 0\n while (pos <= str.length - sub.length) {\n const idx = str.indexOf(sub, pos)\n if (idx === -1) break\n count++\n pos = idx + sub.length\n }\n return count\n}\n\n/** Check if a character is a word character (letter, digit, underscore) */\nfunction isWordChar(ch: string): boolean {\n return /\\w/.test(ch)\n}\n\n/**\n * Add import statements to the top of a file (after existing imports).\n * Deduplicates — won't add if the import already exists.\n * Returns number of imports actually added.\n */\nfunction addImportsToLines(lines: string[], imports: Set<string>): number {\n let added = 0\n const existingContent = lines.join('\\n')\n\n // Find the last import line position (handles multi-line imports)\n let lastImportIdx = -1\n let inMultiLineImport = false\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim()\n if (inMultiLineImport) {\n lastImportIdx = i\n if (trimmed.includes('}')) inMultiLineImport = false\n continue\n }\n if (trimmed.startsWith('import ') || trimmed.startsWith('import{')) {\n lastImportIdx = i\n // Check if this is a multi-line import (has { but no closing })\n if (trimmed.includes('{') && !trimmed.includes('}')) {\n inMultiLineImport = true\n }\n continue\n }\n // Stop searching after a non-import, non-empty, non-comment line following imports\n if (lastImportIdx >= 0 && trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && !trimmed.startsWith('*')) {\n break\n }\n }\n\n let insertAt: number\n if (lastImportIdx >= 0) {\n insertAt = lastImportIdx + 1\n } else {\n // No existing imports found — insert after shebang and directive lines\n insertAt = 0\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim()\n if (i === 0 && trimmed.startsWith('#!')) {\n insertAt = i + 1\n continue\n }\n if (trimmed === \"'use client'\" || trimmed === '\"use client\"'\n || trimmed === \"'use server'\" || trimmed === '\"use server\"'\n || trimmed === \"'use client';\" || trimmed === '\"use client\";'\n || trimmed === \"'use server';\" || trimmed === '\"use server\";') {\n insertAt = i + 1\n continue\n }\n if (insertAt > 0 && trimmed.length > 0) break\n if (insertAt === 0 && trimmed.length > 0) break\n }\n }\n const toInsert: string[] = []\n\n for (const imp of imports) {\n // Check if this import already exists (by checking the from clause)\n if (!existingContent.includes(imp)) {\n toInsert.push(imp)\n added++\n }\n }\n\n if (toInsert.length > 0) {\n lines.splice(insertAt, 0, ...toInsert)\n }\n\n return added\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"apply-manager-BDtHM8z-.mjs","names":[],"sources":["../src/core/apply-manager.ts"],"sourcesContent":["import type { ModelDefinition, FieldDef, FileFramework } from '@contentrain/types'\nimport { join, extname } from 'node:path'\nimport { readText, writeText, pathExists } from '../util/fs.js'\nimport { readModel, writeModel, listModels, validateModelDefinition } from './model-manager.js'\nimport { writeContent, resolveContentDir, resolveJsonFilePath, resolveMdFilePath, type ContentEntry } from './content-manager.js'\nimport { readConfig } from './config.js'\nimport { writeContext } from './context.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\n\n// ─── Types ───\n\nexport interface ExtractionEntry {\n model: string\n kind: 'singleton' | 'collection' | 'dictionary' | 'document'\n domain: string\n i18n?: boolean\n fields?: Record<string, FieldDef>\n entries: Array<{\n locale?: string\n slug?: string\n data: Record<string, unknown>\n source?: { file: string; line: number; value: string }\n sources?: Array<{ file: string; line: number; key: string; value: string }>\n }>\n}\n\nexport interface ExtractionInput {\n extractions: ExtractionEntry[]\n dry_run?: boolean\n}\n\nexport interface ExtractionPreview {\n models_to_create: string[]\n models_to_update: string[]\n total_entries: number\n content_files: string[]\n}\n\nexport interface ExtractionResult {\n dry_run: boolean\n preview?: ExtractionPreview\n error?: string\n validation_errors?: string[]\n results?: {\n models_created: string[]\n models_updated: string[]\n entries_written: number\n source_map: Array<{ model: string; locale: string; value: string; file: string; line: number }>\n }\n git?: { branch: string; action: string; commit: string }\n context_updated?: boolean\n next_steps: string[]\n}\n\nexport interface PatchEntry {\n file: string\n line: number\n old_value: string\n new_expression: string\n import_statement?: string\n}\n\nexport interface ReuseInput {\n scope: { model?: string; domain?: string }\n patches: PatchEntry[]\n dry_run?: boolean\n}\n\nexport interface SyntaxError {\n file: string\n error: string\n}\n\nexport interface ReuseResult {\n dry_run: boolean\n error?: string\n scope_warnings?: string[]\n preview?: {\n files_to_modify: string[]\n patches_count: number\n imports_to_add: number\n }\n results?: {\n files_modified: string[]\n patches_applied: number\n patches_skipped: Array<{ file: string; line: number; reason: string }>\n imports_added: number\n framework_warnings?: Array<{ file: string; warning: string }>\n syntax_errors?: SyntaxError[]\n }\n git?: { branch: string; action: string; commit: string }\n next_steps: string[]\n}\n\n// ─── Constants ───\n\nconst MAX_PATCHES = 100\n\n/** File extensions allowed for patching — scannable source files only */\nexport const PATCHABLE_EXTENSIONS = new Set([\n '.vue', '.tsx', '.jsx', '.ts', '.js', '.mjs', '.astro', '.svelte',\n])\n\n/** Directories that must never be patched */\nconst FORBIDDEN_PATH_SEGMENTS = new Set([\n '.contentrain', 'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',\n])\n\n// ─── Framework Detection (Guardrail #2) ───\n\nexport type { FileFramework } from '@contentrain/types'\n\nexport function detectFileFramework(filePath: string): FileFramework {\n const ext = extname(filePath).toLowerCase()\n switch (ext) {\n case '.vue': return 'vue'\n case '.svelte': return 'svelte'\n case '.tsx':\n case '.jsx': return 'jsx'\n case '.astro': return 'astro'\n case '.ts':\n case '.js':\n case '.mjs': return 'script'\n default: return 'script'\n }\n}\n\n/**\n * Validate that a replacement expression uses the correct template syntax\n * for the target file's framework. Returns a warning string or null.\n */\nexport function validateFrameworkExpression(\n filePath: string,\n newExpression: string,\n context: 'tag_text' | 'other',\n): string | null {\n if (context !== 'tag_text') return null\n\n const framework = detectFileFramework(filePath)\n\n switch (framework) {\n case 'vue':\n if (!newExpression.includes('{{')) {\n return `Vue file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{{\" — expected Vue template syntax like {{ $t('key') }}`\n }\n break\n case 'jsx':\n if (!newExpression.includes('{')) {\n return `JSX file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{\" — expected JSX syntax like {t('key')}`\n }\n break\n case 'svelte':\n if (!newExpression.includes('{')) {\n return `Svelte file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{\" — expected Svelte syntax like {$t('key')}`\n }\n break\n case 'astro':\n if (!newExpression.includes('{')) {\n return `Astro file \"${filePath}\": tag text expression \"${newExpression}\" does not contain \"{\" — expected Astro syntax like {t('key')}`\n }\n break\n case 'script':\n // Script files don't have template interpolation — warn if attempting tag text replacement\n return `Script file \"${filePath}\": tag text replacement not applicable for .ts/.js files`\n }\n\n return null\n}\n\n// ─── Scope Validation (Guardrail #1) ───\n\n/**\n * Validate that a patch file path is safe and within allowed scope.\n * Returns an error message or null if valid.\n */\nexport function validatePatchPath(filePath: string): string | null {\n const normalizedPath = filePath.replace(/\\\\/g, '/')\n\n // Reject path traversal\n if (normalizedPath.includes('..')) {\n return `Path traversal detected: \"${filePath}\"`\n }\n\n // Reject absolute paths\n if (normalizedPath.startsWith('/')) {\n return `Absolute path not allowed: \"${filePath}\"`\n }\n\n // Reject forbidden directories\n const segments = normalizedPath.split('/')\n for (const seg of segments) {\n if (FORBIDDEN_PATH_SEGMENTS.has(seg)) {\n return `Patching files inside \"${seg}/\" is not allowed: \"${filePath}\"`\n }\n }\n\n // Reject non-scannable extensions\n const ext = extname(filePath).toLowerCase()\n if (!PATCHABLE_EXTENSIONS.has(ext)) {\n return `File extension \"${ext}\" is not patchable. Allowed: ${[...PATCHABLE_EXTENSIONS].join(', ')}. Path: \"${filePath}\"`\n }\n\n return null\n}\n\n// ─── Syntax Check (Guardrail #5) ───\n\n/**\n * Perform a basic syntax check on a patched file.\n * Returns an error message or null if syntax appears valid.\n */\nexport function checkSyntax(filePath: string, content: string): string | null {\n const ext = extname(filePath).toLowerCase()\n\n switch (ext) {\n case '.ts':\n case '.tsx':\n case '.js':\n case '.jsx':\n case '.mjs':\n return checkJsSyntax(content)\n case '.vue':\n return checkVueSyntax(content)\n case '.svelte':\n case '.astro':\n return checkTagBalance(content)\n default:\n return null\n }\n}\n\n/**\n * Basic JS/TS syntax check: bracket/paren/brace balance + string literal closure.\n * This is intentionally conservative — it catches obvious breakage without\n * requiring a full parser.\n */\nfunction checkJsSyntax(content: string): string | null {\n const stack: string[] = []\n const pairs: Record<string, string> = { ')': '(', ']': '[', '}': '{' }\n let inString: string | null = null\n let escaped = false\n let inLineComment = false\n let inBlockComment = false\n\n for (let i = 0; i < content.length; i++) {\n const ch = content[i]!\n const next = content[i + 1]\n\n // Handle escape sequences inside strings\n if (escaped) {\n escaped = false\n continue\n }\n\n if (ch === '\\\\' && inString !== null) {\n escaped = true\n continue\n }\n\n // Line comment\n if (!inString && !inBlockComment && ch === '/' && next === '/') {\n inLineComment = true\n continue\n }\n if (inLineComment) {\n if (ch === '\\n') inLineComment = false\n continue\n }\n\n // Block comment\n if (!inString && !inBlockComment && ch === '/' && next === '*') {\n inBlockComment = true\n i++ // skip *\n continue\n }\n if (inBlockComment) {\n if (ch === '*' && next === '/') {\n inBlockComment = false\n i++ // skip /\n }\n continue\n }\n\n // String handling\n if (inString !== null) {\n if (ch === inString) {\n // Template literal allows multi-line, others don't\n inString = null\n } else if (inString !== '`' && ch === '\\n') {\n return `Unterminated string literal near offset ${i}`\n }\n continue\n }\n\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n inString = ch\n continue\n }\n\n // Bracket matching\n if (ch === '(' || ch === '[' || ch === '{') {\n stack.push(ch)\n } else if (ch === ')' || ch === ']' || ch === '}') {\n const expected = pairs[ch]!\n if (stack.length === 0) {\n return `Unmatched closing \"${ch}\" near offset ${i}`\n }\n const top = stack.pop()!\n if (top !== expected) {\n return `Mismatched bracket: expected closing for \"${top}\" but found \"${ch}\" near offset ${i}`\n }\n }\n }\n\n if (inString !== null) {\n return `Unterminated string literal (opened with ${inString})`\n }\n\n if (stack.length > 0) {\n return `Unclosed bracket \"${stack[stack.length - 1]}\" — ${stack.length} unclosed bracket(s)`\n }\n\n return null\n}\n\n/**\n * Vue SFC syntax check: ensure <template>, <script>, <style> tags are balanced.\n */\nfunction checkVueSyntax(content: string): string | null {\n const tagBalance = checkTagBalance(content)\n if (tagBalance) return tagBalance\n\n // Vue-specific: check that SFC root tags are present and balanced\n for (const tag of ['template', 'script']) {\n const openRe = new RegExp(`<${tag}[\\\\s>]`, 'g')\n const closeRe = new RegExp(`</${tag}>`, 'g')\n const opens = content.match(openRe)?.length ?? 0\n const closes = content.match(closeRe)?.length ?? 0\n if (opens !== closes) {\n return `Unbalanced <${tag}> tag: ${opens} opening vs ${closes} closing`\n }\n }\n\n return null\n}\n\n/**\n * Basic tag balance check for HTML-like files.\n * Checks that self-closing tags are handled and major structural tags are balanced.\n */\nfunction checkTagBalance(content: string): string | null {\n // Check for common structural tags balance\n const structuralTags = ['div', 'section', 'main', 'header', 'footer', 'nav', 'article', 'aside', 'ul', 'ol', 'table']\n\n for (const tag of structuralTags) {\n const openRe = new RegExp(`<${tag}[\\\\s>]`, 'g')\n const closeRe = new RegExp(`</${tag}>`, 'g')\n const opens = content.match(openRe)?.length ?? 0\n const closes = content.match(closeRe)?.length ?? 0\n if (opens !== closes) {\n return `Unbalanced <${tag}> tag: ${opens} opening vs ${closes} closing`\n }\n }\n\n return null\n}\n\n// ─── Extract Mode ───\n\nexport async function applyExtract(\n projectRoot: string,\n input: ExtractionInput,\n): Promise<ExtractionResult> {\n const config = await readConfig(projectRoot)\n if (!config) throw new Error('Project not initialized. Run contentrain_init first.')\n\n const { extractions, dry_run } = input\n\n // Analyze what will happen\n const existingModels = await listModels(projectRoot)\n const existingIds = new Set(existingModels.map(m => m.id))\n\n const modelsToCreate: string[] = []\n const modelsToUpdate: string[] = []\n const contentFiles: string[] = []\n let totalEntries = 0\n\n const validationErrors: string[] = []\n\n for (const ext of extractions) {\n // Validate model definition with same rules as model_save\n const modelErrors = validateModelDefinition({\n id: ext.model,\n kind: ext.kind,\n fields: ext.fields as Record<string, unknown> | undefined,\n })\n if (modelErrors.length > 0) {\n validationErrors.push(...modelErrors.map(e => `[${ext.model}] ${e}`))\n }\n\n for (const entry of ext.entries) {\n if (ext.kind === 'dictionary') {\n if (entry.data['id'] !== undefined || entry.data['slug'] !== undefined) {\n validationErrors.push(`[${ext.model}] Dictionary entries should not have id or slug`)\n }\n for (const [key, val] of Object.entries(entry.data)) {\n if (typeof val !== 'string') {\n validationErrors.push(`[${ext.model}] Dictionary entry value for key \"${key}\" must be a string, got ${typeof val}`)\n }\n }\n } else if (ext.kind === 'document') {\n if (!entry.slug && !entry.data['slug']) {\n validationErrors.push(`[${ext.model}] Document entries must have a slug`)\n }\n } else if (ext.kind === 'collection') {\n if (entry.slug !== undefined || entry.data['slug'] !== undefined) {\n validationErrors.push(`[${ext.model}] Collection entries should not have slug`)\n }\n } else if (ext.kind === 'singleton') {\n if (entry.data['id'] !== undefined || entry.slug !== undefined || entry.data['slug'] !== undefined) {\n validationErrors.push(`[${ext.model}] Singleton entries should not have id or slug`)\n }\n }\n }\n\n if (existingIds.has(ext.model)) {\n modelsToUpdate.push(ext.model)\n } else {\n modelsToCreate.push(ext.model)\n }\n totalEntries += ext.entries.length\n\n // Guardrail #3: Preview-Execute Parity — use real model metadata if it exists\n let previewModel: ModelDefinition\n if (existingIds.has(ext.model)) {\n const real = await readModel(projectRoot, ext.model)\n if (real) {\n previewModel = real\n } else {\n previewModel = { id: ext.model, kind: ext.kind, domain: ext.domain, i18n: ext.i18n ?? true } as ModelDefinition\n }\n } else {\n previewModel = { id: ext.model, kind: ext.kind, domain: ext.domain, i18n: ext.i18n ?? true } as ModelDefinition\n }\n\n const cDir = resolveContentDir(projectRoot, previewModel)\n for (const entry of ext.entries) {\n const locale = entry.locale ?? config.locales.default\n if (ext.kind === 'document' && entry.slug) {\n contentFiles.push(resolveMdFilePath(cDir, previewModel, locale, entry.slug))\n } else {\n contentFiles.push(resolveJsonFilePath(cDir, previewModel, locale))\n }\n }\n }\n\n const preview: ExtractionPreview = {\n models_to_create: modelsToCreate,\n models_to_update: modelsToUpdate,\n total_entries: totalEntries,\n content_files: [...new Set(contentFiles)],\n }\n\n // Dry run — return preview only (include validation errors if any)\n if (dry_run !== false) {\n return {\n dry_run: true,\n preview,\n ...(validationErrors.length > 0 ? { validation_errors: validationErrors } : {}),\n next_steps: [\n ...(validationErrors.length > 0\n ? [`WARNING: ${validationErrors.length} validation error(s) found — fix before executing`]\n : []),\n 'Review the preview above',\n 'Call contentrain_apply with mode:extract and dry_run:false to execute',\n ],\n }\n }\n\n // Block execute if validation errors exist\n if (validationErrors.length > 0) {\n return {\n dry_run: false,\n error: 'Model validation failed — cannot execute extract with invalid model definitions',\n validation_errors: validationErrors,\n next_steps: ['Fix the validation errors and retry'],\n }\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n error: health.message,\n action: 'blocked' as const,\n hint: 'Merge or delete old contentrain/* branches before executing normalize.',\n } as unknown as ExtractionResult\n }\n\n // Execute — git transaction (always review mode for normalize)\n const branchName = buildBranchName('normalize', 'extract')\n const tx = await createTransaction(projectRoot, branchName, { workflowOverride: 'review' })\n const sourceMap: Array<{ model: string; locale: string; value: string; file: string; line: number }> = []\n const modelsCreated: string[] = []\n const modelsUpdated: string[] = []\n let entriesWritten = 0\n\n try {\n await tx.write(async (wt) => {\n for (const ext of extractions) {\n // Create or merge model\n const existing = await readModel(wt, ext.model)\n if (existing) {\n // Merge fields: add new, keep existing\n if (ext.fields) {\n const merged = { ...existing.fields, ...ext.fields }\n // Only overwrite if new fields were actually added\n const newFieldNames = Object.keys(ext.fields).filter(k => !(k in (existing.fields ?? {})))\n if (newFieldNames.length > 0) {\n existing.fields = merged\n await writeModel(wt, existing)\n modelsUpdated.push(ext.model)\n }\n }\n } else {\n // Create new model\n const newModel: ModelDefinition = {\n id: ext.model,\n name: ext.model.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),\n kind: ext.kind,\n domain: ext.domain,\n i18n: ext.i18n ?? true,\n fields: ext.fields,\n }\n await writeModel(wt, newModel)\n modelsCreated.push(ext.model)\n }\n\n // Write content entries\n const model = (await readModel(wt, ext.model))!\n const entries: ContentEntry[] = ext.entries.map(e => ({\n locale: e.locale,\n slug: e.slug,\n data: e.data,\n }))\n\n const wtConfig = await readConfig(wt) ?? config\n await writeContent(wt, model, entries, wtConfig)\n entriesWritten += entries.length\n\n // Track source map\n for (const entry of ext.entries) {\n // For dictionary entries with per-key source tracking\n if (ext.kind === 'dictionary' && entry.sources) {\n for (const s of entry.sources) {\n sourceMap.push({\n model: ext.model,\n locale: entry.locale ?? config.locales.default,\n value: s.value,\n file: s.file,\n line: s.line,\n })\n }\n } else if (entry.source) {\n sourceMap.push({\n model: ext.model,\n locale: entry.locale ?? config.locales.default,\n value: entry.source.value,\n file: entry.source.file,\n line: entry.source.line,\n })\n }\n }\n }\n\n // Write source map for reuse scope enforcement\n if (sourceMap.length > 0) {\n const sourcesByModel: Record<string, { source_files: string[]; entry_count: number }> = {}\n for (const s of sourceMap) {\n if (!sourcesByModel[s.model]) {\n sourcesByModel[s.model] = { source_files: [], entry_count: 0 }\n }\n const modelEntry = sourcesByModel[s.model]!\n if (!modelEntry.source_files.includes(s.file)) {\n modelEntry.source_files.push(s.file)\n }\n modelEntry.entry_count++\n }\n const sourcesJson = JSON.stringify({\n version: 1,\n created_at: new Date().toISOString(),\n models: sourcesByModel,\n }, null, 2) + '\\n'\n // Write to worktree only — merge brings it to main\n // Phase 2 (reuse) must wait for extract branch to be merged first\n await writeText(join(wt, '.contentrain', 'normalize-sources.json'), sourcesJson)\n }\n\n // Update context\n await writeContext(wt, {\n tool: 'contentrain_apply',\n model: extractions.map(e => e.model).join(','),\n locale: config.locales.default,\n entries: extractions.flatMap(e => e.entries.map(en => en.slug ?? 'entry')),\n })\n })\n\n const commitMsg = `[contentrain] normalize: extract ${entriesWritten} entries to ${extractions.length} models`\n await tx.commit(commitMsg)\n\n const gitResult = { branch: branchName, action: 'pending-review', commit: '' }\n try {\n const completed = await tx.complete()\n gitResult.action = completed.action\n gitResult.commit = completed.commit\n } catch {\n gitResult.action = 'pending-review'\n } finally {\n await tx.cleanup()\n }\n\n return {\n dry_run: false,\n results: {\n models_created: modelsCreated,\n models_updated: modelsUpdated,\n entries_written: entriesWritten,\n source_map: sourceMap,\n },\n git: gitResult,\n context_updated: true,\n next_steps: [\n 'Run contentrain_validate to check the extracted content',\n 'Run contentrain_submit to push the branch for review',\n 'After review, proceed with mode:reuse to patch source files',\n ],\n }\n } catch (error) {\n await tx.cleanup()\n throw error\n }\n}\n\n// ─── Reuse Mode ───\n\nexport async function applyReuse(\n projectRoot: string,\n input: ReuseInput,\n): Promise<ReuseResult> {\n const config = await readConfig(projectRoot)\n if (!config) throw new Error('Project not initialized. Run contentrain_init first.')\n\n const { scope, patches, dry_run } = input\n\n // Validate scope\n if (!scope.model && !scope.domain) {\n throw new Error('Scope required: provide model or domain. Whole-project patching is not allowed.')\n }\n\n // Validate patch count\n if (patches.length > MAX_PATCHES) {\n throw new Error(`Too many patches (${patches.length}). Maximum ${MAX_PATCHES} per operation. Split into multiple calls.`)\n }\n\n // Check content exists for scope (soft warning)\n if (scope.model) {\n const model = await readModel(projectRoot, scope.model)\n if (!model) {\n throw new Error(`Model \"${scope.model}\" not found. Run extract phase first.`)\n }\n }\n\n const scopeWarnings: string[] = []\n\n // Guardrail #1: Scope Real Enforcement\n // Step 1: Path safety — every patch must target a valid, patchable source file\n for (const patch of patches) {\n const pathError = validatePatchPath(patch.file)\n if (pathError) {\n throw new Error(`Invalid patch path: ${pathError}`)\n }\n }\n\n // Step 2: Semantic scope — verify scope model/domain exists and cross-check patch files\n if (scope.model || scope.domain) {\n const models = await listModels(projectRoot)\n const scopeModels = scope.model\n ? models.filter(m => m.id === scope.model)\n : scope.domain\n ? models.filter(m => m.domain === scope.domain)\n : models\n\n if (scopeModels.length === 0) {\n throw new Error(`No models found for scope ${scope.model ? `model=\"${scope.model}\"` : `domain=\"${scope.domain}\"`}`)\n }\n\n // Step 3: Verify patch files are source files (not content/config/meta files)\n // and belong to detectable source directories (not random locations)\n const { autoDetectSourceDirs } = await import('./scan-config.js')\n const sourceDirs = await autoDetectSourceDirs(projectRoot)\n\n // Build allowed file prefixes from source dirs\n const allowedPrefixes = sourceDirs.map(d => d === '.' ? '' : d + '/')\n\n for (const patch of patches) {\n const normalizedPath = patch.file.replace(/\\\\/g, '/')\n\n // Reject patches targeting .contentrain/ directory (content files should never be patched by reuse)\n if (normalizedPath.startsWith('.contentrain/') || normalizedPath.includes('/.contentrain/')) {\n throw new Error(`Cannot patch content/config files directly: \"${patch.file}\". Reuse patches source files only.`)\n }\n\n // If source dirs were detected (not just \".\"), verify patch files are within them\n if (sourceDirs.length > 0 && sourceDirs[0] !== '.') {\n const inSourceDir = allowedPrefixes.some(prefix =>\n prefix === '' || normalizedPath.startsWith(prefix),\n )\n if (!inSourceDir) {\n throw new Error(\n `Patch file \"${patch.file}\" is outside detected source directories (${sourceDirs.join(', ')}). ` +\n `Reuse patches must target source files within the project's source tree.`,\n )\n }\n }\n }\n\n // Step 4: Semantic source→model cross-check via normalize-sources.json\n // Source map is written by extract into the review branch worktree.\n // It only exists on base after extract branch is merged.\n // If missing: dry_run proceeds with warning, execute is blocked.\n if (scope.model || scope.domain) {\n const sourcesPath = join(projectRoot, '.contentrain', 'normalize-sources.json')\n const sourcesRaw = await readText(sourcesPath)\n\n if (!sourcesRaw) {\n // Source map not found — check if scoped model is a dictionary\n // Dictionaries store all keys in one entry, so per-file source tracking\n // is not available. Allow reuse with a warning instead of blocking.\n const scopedModel = scopeModels.find(m => m.id === scope.model)\n const isDictionary = scopedModel?.kind === 'dictionary'\n\n if (isDictionary) {\n scopeWarnings.push(\n 'normalize-sources.json not found. Dictionary models do not generate per-file source maps. ' +\n 'Scope enforcement is based on source-tree locality only.',\n )\n } else if (dry_run !== false) {\n scopeWarnings.push(\n 'normalize-sources.json not found. Semantic scope enforcement is unavailable. ' +\n 'Merge the extract branch first, then reuse will have full scope protection.',\n )\n } else {\n throw new Error(\n 'Cannot execute reuse: normalize-sources.json not found on base branch. ' +\n 'The extract branch must be merged before reuse can execute. ' +\n 'This ensures semantic scope enforcement protects against out-of-scope patching.',\n )\n }\n } else {\n const sourcesData = JSON.parse(sourcesRaw) as {\n models?: Record<string, { source_files?: string[] }>\n }\n\n // Collect all allowed source files for the scope\n let allowedSourceFiles: string[] = []\n let scopeLabel = ''\n\n if (scope.model) {\n const modelSources = sourcesData.models?.[scope.model]?.source_files\n if (modelSources) allowedSourceFiles = modelSources\n scopeLabel = `model \"${scope.model}\"`\n } else if (scope.domain) {\n const domainModelIds = scopeModels.map(m => m.id)\n for (const modelId of domainModelIds) {\n const modelSources = sourcesData.models?.[modelId]?.source_files\n if (modelSources) allowedSourceFiles.push(...modelSources)\n }\n allowedSourceFiles = [...new Set(allowedSourceFiles)]\n scopeLabel = `domain \"${scope.domain}\"`\n }\n\n if (allowedSourceFiles.length > 0) {\n const outOfScopePatches: string[] = []\n for (const patch of patches) {\n const normalizedPath = patch.file.replace(/\\\\/g, '/')\n if (!allowedSourceFiles.includes(normalizedPath)) {\n outOfScopePatches.push(patch.file)\n }\n }\n if (outOfScopePatches.length > 0) {\n throw new Error(\n `Scope enforcement: ${outOfScopePatches.length} patch file(s) are not associated with ${scopeLabel}. ` +\n `Out-of-scope files: ${outOfScopePatches.join(', ')}. ` +\n `Known source files: ${allowedSourceFiles.join(', ')}.`,\n )\n }\n }\n }\n }\n }\n\n // Group patches by file\n const patchesByFile = new Map<string, PatchEntry[]>()\n for (const patch of patches) {\n if (!patchesByFile.has(patch.file)) {\n patchesByFile.set(patch.file, [])\n }\n patchesByFile.get(patch.file)!.push(patch)\n }\n\n const filesToModify = [...patchesByFile.keys()]\n const importsToAdd = patches.filter(p => p.import_statement).length\n\n // Dry run — return preview only\n if (dry_run !== false) {\n return {\n dry_run: true,\n preview: {\n files_to_modify: filesToModify,\n patches_count: patches.length,\n imports_to_add: importsToAdd,\n },\n ...(scopeWarnings.length > 0 ? { scope_warnings: scopeWarnings } : {}),\n next_steps: [\n ...(scopeWarnings.length > 0 ? [`WARNING: ${scopeWarnings.length} patch file(s) not in extract source map — verify intent`] : []),\n 'Review the files and patches above',\n 'Call contentrain_apply with mode:reuse and dry_run:false to execute',\n ],\n }\n }\n\n // Branch health gate\n const reuseHealth = await checkBranchHealth(projectRoot)\n if (reuseHealth.blocked) {\n return {\n dry_run: false,\n error: `Branch blocked: ${reuseHealth.message}`,\n next_steps: ['Merge or delete old contentrain/* branches before executing reuse.'],\n }\n }\n\n // Execute — git transaction\n const scopeTarget = scope.model ?? scope.domain!\n const branchName = buildBranchName('normalize/reuse', scopeTarget)\n const tx = await createTransaction(projectRoot, branchName, { workflowOverride: 'review' })\n\n const filesModified: string[] = []\n let patchesApplied = 0\n let importsAdded = 0\n const patchesSkipped: Array<{ file: string; line: number; reason: string }> = []\n const frameworkWarnings: Array<{ file: string; warning: string }> = []\n const syntaxErrors: SyntaxError[] = []\n\n try {\n await tx.write(async (wt) => {\n for (const [relFile, filePatches] of patchesByFile) {\n const absPath = join(wt, relFile)\n\n if (!(await pathExists(absPath))) {\n for (const p of filePatches) {\n patchesSkipped.push({ file: relFile, line: p.line, reason: 'file not found' })\n }\n continue\n }\n\n const content = await readText(absPath)\n if (content === null) {\n for (const p of filePatches) {\n patchesSkipped.push({ file: relFile, line: p.line, reason: 'file unreadable' })\n }\n continue\n }\n\n // Sort patches by line DESC (bottom-up to avoid line shifts)\n const sorted = [...filePatches].toSorted((a, b) => b.line - a.line)\n\n const lines = content.split('\\n')\n let fileModified = false\n\n for (const patch of sorted) {\n // Determine replacement context before applying\n const targetLine = lines[patch.line - 1] ?? ''\n const isTagTextContext = targetLine.includes(`>${patch.old_value}<`)\n const replacementContext = isTagTextContext ? 'tag_text' as const : 'other' as const\n\n // Guardrail #2: Framework-aware validation — only warn for tag text replacements\n if (isTagTextContext) {\n const fwWarning = validateFrameworkExpression(relFile, patch.new_expression, replacementContext)\n if (fwWarning) {\n frameworkWarnings.push({ file: relFile, warning: fwWarning })\n }\n }\n\n const applied = applyPatchToLines(lines, patch)\n if (applied) {\n patchesApplied++\n fileModified = true\n } else {\n patchesSkipped.push({ file: relFile, line: patch.line, reason: 'old_value not found at or near specified line' })\n }\n }\n\n // Add imports (deduplicate)\n const importStatements = new Set(\n filePatches\n .filter(p => p.import_statement)\n .map(p => p.import_statement!),\n )\n\n if (importStatements.size > 0) {\n const added = addImportsToLines(lines, importStatements)\n importsAdded += added\n if (added > 0) fileModified = true\n }\n\n if (fileModified) {\n const newContent = lines.join('\\n')\n await writeText(absPath, newContent)\n filesModified.push(relFile)\n\n // Guardrail #5: Syntax check after patching\n const syntaxError = checkSyntax(relFile, newContent)\n if (syntaxError) {\n syntaxErrors.push({ file: relFile, error: syntaxError })\n }\n }\n }\n\n // Update context\n await writeContext(wt, {\n tool: 'contentrain_apply',\n model: scopeTarget,\n locale: config.locales.default,\n })\n })\n\n if (filesModified.length === 0) {\n await tx.cleanup()\n return {\n dry_run: false,\n results: {\n files_modified: [],\n patches_applied: 0,\n patches_skipped: patchesSkipped,\n imports_added: 0,\n framework_warnings: frameworkWarnings.length > 0 ? frameworkWarnings : undefined,\n },\n next_steps: ['No files were modified. Check patch definitions and try again.'],\n }\n }\n\n const commitMsg = `[contentrain] normalize: reuse ${scopeTarget} — patch ${filesModified.length} files (${patchesApplied} replacements)`\n await tx.commit(commitMsg)\n\n const gitResult = { branch: branchName, action: 'pending-review', commit: '' }\n try {\n const completed = await tx.complete()\n gitResult.action = completed.action\n gitResult.commit = completed.commit\n } catch {\n gitResult.action = 'pending-review'\n } finally {\n await tx.cleanup()\n }\n\n return {\n dry_run: false,\n results: {\n files_modified: filesModified,\n patches_applied: patchesApplied,\n patches_skipped: patchesSkipped,\n imports_added: importsAdded,\n framework_warnings: frameworkWarnings.length > 0 ? frameworkWarnings : undefined,\n syntax_errors: syntaxErrors.length > 0 ? syntaxErrors : undefined,\n },\n ...(scopeWarnings.length > 0 ? { scope_warnings: scopeWarnings } : {}),\n git: gitResult,\n next_steps: [\n 'Run contentrain_validate to verify the patched files',\n patchesSkipped.length > 0 ? `${patchesSkipped.length} patches were skipped — review and retry if needed` : '',\n syntaxErrors.length > 0 ? `WARNING: ${syntaxErrors.length} file(s) may have syntax errors after patching — review manually` : '',\n scopeWarnings.length > 0 ? `NOTE: ${scopeWarnings.length} patch file(s) not in extract source map` : '',\n 'Run contentrain_submit to push the branch for review',\n ].filter(Boolean),\n }\n } catch (error) {\n await tx.cleanup()\n throw error\n }\n}\n\n// ─── Patch Helpers ───\n\n/**\n * Apply a single patch to a lines array. Mutates lines in place.\n * Uses line hint for proximity matching — searches ±10 lines from hint.\n */\nfunction applyPatchToLines(lines: string[], patch: PatchEntry): boolean {\n const { line, old_value, new_expression } = patch\n const lineIdx = line - 1 // 0-based\n\n // Search range: ±10 lines from hint\n const searchStart = Math.max(0, lineIdx - 10)\n const searchEnd = Math.min(lines.length, lineIdx + 11)\n\n // First pass: exact line match\n if (lineIdx >= 0 && lineIdx < lines.length) {\n const replaced = replaceInLine(lines[lineIdx]!, old_value, new_expression)\n if (replaced !== null) {\n lines[lineIdx] = replaced\n return true\n }\n }\n\n // Second pass: proximity search\n for (let i = searchStart; i < searchEnd; i++) {\n if (i === lineIdx) continue // already tried\n const replaced = replaceInLine(lines[i]!, old_value, new_expression)\n if (replaced !== null) {\n lines[i] = replaced\n return true\n }\n }\n\n return false\n}\n\n/**\n * Replace old_value with new_expression in a single line.\n * Matches the string literal (quoted or unquoted tag text).\n * Returns the modified line, or null if not found.\n *\n * Guardrail #4: Safer patch matching — word boundary awareness and\n * ambiguity rejection for plain text fallback.\n */\nexport function replaceInLine(line: string, oldValue: string, newExpression: string): string | null {\n // Try exact match of the value in quoted strings\n // Match: \"old_value\", 'old_value', `old_value`\n for (const quote of ['\"', \"'\", '`']) {\n const quoted = `${quote}${oldValue}${quote}`\n if (line.includes(quoted)) {\n return line.replace(quoted, newExpression)\n }\n }\n\n // Try unquoted tag text match: >old_value<\n // The agent provides the complete new_expression with correct framework syntax:\n // JSX: {t('key')}\n // Vue: {{ $t('key') }}\n // Svelte: {$t('key')}\n // So we insert the expression as-is between > and <, without wrapping in braces.\n if (line.includes(`>${oldValue}<`)) {\n return line.replace(`>${oldValue}<`, `>${newExpression}<`)\n }\n\n // Guardrail #4: Safer plain text fallback\n // Only match if old_value appears at a word boundary and is unambiguous\n if (line.includes(oldValue)) {\n // Count occurrences — if multiple, it's ambiguous\n const occurrences = countOccurrences(line, oldValue)\n if (occurrences > 1) {\n return null // ambiguous — let proximity search handle it\n }\n\n // Word boundary check: don't replace \"Submit\" inside \"SubmitButton\"\n // Reject if the old_value is a substring of a larger word — check if\n // adjacent characters are word characters that extend the token.\n const idx = line.indexOf(oldValue)\n const charBefore = idx > 0 ? line[idx - 1]! : ''\n const charAfter = idx + oldValue.length < line.length ? line[idx + oldValue.length]! : ''\n\n const oldStartsWithWord = oldValue.length > 0 && isWordChar(oldValue[0]!)\n const oldEndsWithWord = oldValue.length > 0 && isWordChar(oldValue[oldValue.length - 1]!)\n\n // If old_value starts with a word char and char before is also word char, it's a partial match\n if (oldStartsWithWord && isWordChar(charBefore)) {\n return null\n }\n // If old_value ends with a word char and char after is also word char, it's a partial match\n if (oldEndsWithWord && isWordChar(charAfter)) {\n return null\n }\n\n return line.replace(oldValue, newExpression)\n }\n\n return null\n}\n\n/** Count non-overlapping occurrences of a substring */\nfunction countOccurrences(str: string, sub: string): number {\n let count = 0\n let pos = 0\n while (pos <= str.length - sub.length) {\n const idx = str.indexOf(sub, pos)\n if (idx === -1) break\n count++\n pos = idx + sub.length\n }\n return count\n}\n\n/** Check if a character is a word character (letter, digit, underscore) */\nfunction isWordChar(ch: string): boolean {\n return /\\w/.test(ch)\n}\n\n/**\n * Add import statements to the top of a file (after existing imports).\n * Deduplicates — won't add if the import already exists.\n * Returns number of imports actually added.\n */\nfunction addImportsToLines(lines: string[], imports: Set<string>): number {\n let added = 0\n const existingContent = lines.join('\\n')\n\n // Find the last import line position (handles multi-line imports)\n let lastImportIdx = -1\n let inMultiLineImport = false\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim()\n if (inMultiLineImport) {\n lastImportIdx = i\n if (trimmed.includes('}')) inMultiLineImport = false\n continue\n }\n if (trimmed.startsWith('import ') || trimmed.startsWith('import{')) {\n lastImportIdx = i\n // Check if this is a multi-line import (has { but no closing })\n if (trimmed.includes('{') && !trimmed.includes('}')) {\n inMultiLineImport = true\n }\n continue\n }\n // Stop searching after a non-import, non-empty, non-comment line following imports\n if (lastImportIdx >= 0 && trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && !trimmed.startsWith('*')) {\n break\n }\n }\n\n let insertAt: number\n if (lastImportIdx >= 0) {\n insertAt = lastImportIdx + 1\n } else {\n // No existing imports found — insert after shebang and directive lines\n insertAt = 0\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim()\n if (i === 0 && trimmed.startsWith('#!')) {\n insertAt = i + 1\n continue\n }\n if (trimmed === \"'use client'\" || trimmed === '\"use client\"'\n || trimmed === \"'use server'\" || trimmed === '\"use server\"'\n || trimmed === \"'use client';\" || trimmed === '\"use client\";'\n || trimmed === \"'use server';\" || trimmed === '\"use server\";') {\n insertAt = i + 1\n continue\n }\n if (insertAt > 0 && trimmed.length > 0) break\n if (insertAt === 0 && trimmed.length > 0) break\n }\n }\n const toInsert: string[] = []\n\n for (const imp of imports) {\n // Check if this import already exists (by checking the from clause)\n if (!existingContent.includes(imp)) {\n toInsert.push(imp)\n added++\n }\n }\n\n if (toInsert.length > 0) {\n lines.splice(insertAt, 0, ...toInsert)\n }\n\n return added\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import "../model-manager-kl78AAMg.mjs";
2
2
  import "../context-CUFulSi2.mjs";
3
3
  import "../branch-lifecycle-DB6sgeJi.mjs";
4
- import "../transaction-BhSvWfSV.mjs";
5
- import { a as detectFileFramework, c as validatePatchPath, i as checkSyntax, n as applyExtract, o as replaceInLine, r as applyReuse, s as validateFrameworkExpression, t as PATCHABLE_EXTENSIONS } from "../apply-manager-BzpUJEbA.mjs";
4
+ import "../transaction-_AiXqGuD.mjs";
5
+ import { a as detectFileFramework, c as validatePatchPath, i as checkSyntax, n as applyExtract, o as replaceInLine, r as applyReuse, s as validateFrameworkExpression, t as PATCHABLE_EXTENSIONS } from "../apply-manager-BDtHM8z-.mjs";
6
6
  export { PATCHABLE_EXTENSIONS, applyExtract, applyReuse, checkSyntax, detectFileFramework, replaceInLine, validateFrameworkExpression, validatePatchPath };
@@ -13,6 +13,7 @@ interface GitTransaction {
13
13
  complete(contextUpdate?: ContextUpdate): Promise<{
14
14
  action: 'auto-merged' | 'pending-review';
15
15
  commit: string;
16
+ warning?: string;
16
17
  }>;
17
18
  cleanup(): Promise<void>;
18
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.d.mts","names":[],"sources":["../../src/git/transaction.ts"],"mappings":";UAQiB,aAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,QAAA;EACA,MAAA;EACA,KAAA,CAAM,QAAA,GAAW,YAAA,aAAyB,OAAA,SAAgB,OAAA;EAC1D,MAAA,CAAO,OAAA,WAAkB,OAAA;EACzB,QAAA,CAAS,aAAA,GAAgB,aAAA,GAAgB,OAAA;IAAU,MAAA;IAA0C,MAAA;EAAA;EAC7F,OAAA,IAAW,OAAA;AAAA;AAAA,iBAGS,iBAAA,CACpB,WAAA,UACA,UAAA,UACA,OAAA;EAAY,gBAAA;AAAA,IACX,OAAA,CAAQ,cAAA;AAAA,iBAuJK,eAAA,CAAgB,KAAA,UAAe,MAAA,UAAgB,MAAA"}
1
+ {"version":3,"file":"transaction.d.mts","names":[],"sources":["../../src/git/transaction.ts"],"mappings":";UAQiB,aAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,QAAA;EACA,MAAA;EACA,KAAA,CAAM,QAAA,GAAW,YAAA,aAAyB,OAAA,SAAgB,OAAA;EAC1D,MAAA,CAAO,OAAA,WAAkB,OAAA;EACzB,QAAA,CAAS,aAAA,GAAgB,aAAA,GAAgB,OAAA;IAAU,MAAA;IAA0C,MAAA;IAAgB,OAAA;EAAA;EAC7G,OAAA,IAAW,OAAA;AAAA;AAAA,iBAGS,iBAAA,CACpB,WAAA,UACA,UAAA,UACA,OAAA;EAAY,gBAAA;AAAA,IACX,OAAA,CAAQ,cAAA;AAAA,iBAyKK,eAAA,CAAgB,KAAA,UAAe,MAAA,UAAgB,MAAA"}
@@ -1,4 +1,4 @@
1
1
  import "../model-manager-kl78AAMg.mjs";
2
2
  import "../context-CUFulSi2.mjs";
3
- import { n as createTransaction, t as buildBranchName } from "../transaction-BhSvWfSV.mjs";
3
+ import { n as createTransaction, t as buildBranchName } from "../transaction-_AiXqGuD.mjs";
4
4
  export { buildBranchName, createTransaction };
package/dist/index.mjs CHANGED
@@ -2,13 +2,13 @@
2
2
  import "./model-manager-kl78AAMg.mjs";
3
3
  import "./context-CUFulSi2.mjs";
4
4
  import "./branch-lifecycle-DB6sgeJi.mjs";
5
- import { t as createServer } from "./server-2cPZTC4X.mjs";
6
- import "./transaction-BhSvWfSV.mjs";
5
+ import { t as createServer } from "./server-QgivXkem.mjs";
6
+ import "./transaction-_AiXqGuD.mjs";
7
7
  import "./validator-CyckZAVr.mjs";
8
8
  import "./graph-builder-fPrDDwnu.mjs";
9
9
  import "./scanner-BdTiv8sw.mjs";
10
10
  import "./tsx-parser-CYVyBWjn.mjs";
11
- import "./apply-manager-BzpUJEbA.mjs";
11
+ import "./apply-manager-BDtHM8z-.mjs";
12
12
  import { resolve } from "node:path";
13
13
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
14
  //#region src/index.ts
@@ -6,11 +6,11 @@ import { t as readContext } from "./context-CUFulSi2.mjs";
6
6
  import { t as detectStack } from "./detect-C8XmJ4S6.mjs";
7
7
  import { n as cleanupMergedBranches, t as checkBranchHealth } from "./branch-lifecycle-DB6sgeJi.mjs";
8
8
  import { n as listTemplates, t as getTemplate } from "./templates-3HWi-PTu.mjs";
9
- import { n as createTransaction, t as buildBranchName } from "./transaction-BhSvWfSV.mjs";
9
+ import { n as createTransaction, t as buildBranchName } from "./transaction-_AiXqGuD.mjs";
10
10
  import { t as validateProject } from "./validator-CyckZAVr.mjs";
11
11
  import { t as buildGraph } from "./graph-builder-fPrDDwnu.mjs";
12
12
  import { n as scanCandidates, r as scanSummary } from "./scanner-BdTiv8sw.mjs";
13
- import { n as applyExtract, r as applyReuse } from "./apply-manager-BzpUJEbA.mjs";
13
+ import { n as applyExtract, r as applyReuse } from "./apply-manager-BDtHM8z-.mjs";
14
14
  import { join } from "node:path";
15
15
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
16
  import { z } from "zod";
@@ -1845,7 +1845,7 @@ function registerBulkTools(server, projectRoot) {
1845
1845
  }
1846
1846
  //#endregion
1847
1847
  //#region package.json
1848
- var version = "1.0.5";
1848
+ var version = "1.0.7";
1849
1849
  //#endregion
1850
1850
  //#region src/server.ts
1851
1851
  function createServer(projectRoot) {
@@ -1865,4 +1865,4 @@ function createServer(projectRoot) {
1865
1865
  //#endregion
1866
1866
  export { createServer as t };
1867
1867
 
1868
- //# sourceMappingURL=server-2cPZTC4X.mjs.map
1868
+ //# sourceMappingURL=server-QgivXkem.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-2cPZTC4X.mjs","names":["packageJson.version"],"sources":["../src/tools/context.ts","../src/tools/setup.ts","../src/tools/model.ts","../src/tools/content.ts","../src/tools/workflow.ts","../src/tools/normalize.ts","../src/tools/bulk.ts","../package.json","../src/server.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport { readConfig, readVocabulary } from '../core/config.js'\nimport { readContext } from '../core/context.js'\nimport { countEntries, listModels, readModel } from '../core/model-manager.js'\nimport { resolveContentDir, resolveJsonFilePath, resolveLocaleStrategy } from '../core/content-manager.js'\nimport { detectStack } from '../util/detect.js'\nimport { join } from 'node:path'\nimport { contentrainDir, pathExists } from '../util/fs.js'\nimport { checkBranchHealth, cleanupMergedBranches } from '../git/branch-lifecycle.js'\n\nexport function registerContextTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_status ───\n server.tool(\n 'contentrain_status',\n 'Get full project status (read-only). Returns config, models, context. Do NOT manually edit .contentrain/ based on this output.',\n {},\n async () => {\n const crDir = contentrainDir(projectRoot)\n const initialized = await pathExists(join(crDir, 'config.json'))\n\n if (!initialized) {\n const detectedStack = await detectStack(projectRoot)\n return {\n content: [{\n type: 'text' as const,\n text: JSON.stringify({\n initialized: false,\n detected_stack: detectedStack,\n suggestion: 'Run contentrain_init to set up .contentrain/ structure',\n next_steps: ['Run contentrain_init'],\n }, null, 2),\n }],\n }\n }\n\n const config = await readConfig(projectRoot)\n const models = await listModels(projectRoot)\n const context = await readContext(projectRoot)\n const vocabulary = await readVocabulary(projectRoot)\n\n const errors: string[] = []\n if (!config) errors.push('.contentrain/config.json missing')\n\n const result: Record<string, unknown> = {\n initialized: true,\n config: config ? {\n stack: config.stack,\n workflow: config.workflow,\n locales: config.locales,\n domains: config.domains,\n repository: config.repository,\n } : null,\n models,\n context: context ? {\n lastOperation: context.lastOperation,\n stats: context.stats,\n } : null,\n vocabulary_size: vocabulary ? Object.keys(vocabulary.terms).length : 0,\n }\n\n // Branch lifecycle: lazy cleanup + health check (run BEFORE validation summary)\n const hasGitRepo = await pathExists(join(projectRoot, '.git'))\n if (hasGitRepo) {\n try {\n const cleanup = await cleanupMergedBranches(projectRoot)\n const health = await checkBranchHealth(projectRoot)\n result['branches'] = {\n total: health.total,\n merged: health.merged,\n unmerged: health.unmerged,\n cleaned_up: cleanup.deleted,\n }\n if (health.message) {\n result['branch_warning'] = health.message\n }\n if (health.blocked) {\n errors.push(health.message!)\n }\n } catch {\n // Branch health check is best-effort — don't fail status\n }\n }\n\n if (errors.length > 0) {\n result['validation'] = { errors: errors.length, warnings: 0, summary: errors }\n }\n\n const nextSteps: string[] = []\n if (models.length === 0) nextSteps.push('Create models with contentrain_model_save')\n if (errors.length > 0) nextSteps.push(`Fix ${errors.length} validation error(s)`)\n if (nextSteps.length === 0) nextSteps.push('Use contentrain_describe to inspect a model')\n result['next_steps'] = nextSteps\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_describe ───\n server.tool(\n 'contentrain_describe',\n 'Get full schema of a single model (read-only). Do NOT manually create content files — use contentrain_content_save instead.',\n {\n model: z.string().describe('Model ID (e.g. \"blog-post\", \"hero\")'),\n include_sample: z.boolean().optional().default(false).describe('Include one sample entry'),\n locale: z.string().optional().describe('Locale for sample content (default: config default locale)'),\n },\n async ({ model: modelId, include_sample, locale }) => {\n const modelDef = await readModel(projectRoot, modelId)\n if (!modelDef) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${modelId}\" not found` }) }],\n isError: true,\n }\n }\n\n const config = await readConfig(projectRoot)\n const effectiveLocale = locale ?? config?.locales.default ?? 'en'\n const stats = await countEntries(projectRoot, modelDef)\n\n const result: Record<string, unknown> = {\n id: modelDef.id,\n name: modelDef.name,\n kind: modelDef.kind,\n domain: modelDef.domain,\n i18n: modelDef.i18n,\n description: modelDef.description,\n fields: modelDef.fields,\n stats: { total_entries: stats.total, locales: stats.locales },\n }\n\n if (include_sample) {\n const sample = await getSample(projectRoot, modelDef, effectiveLocale)\n if (sample) result['sample'] = sample\n }\n\n // Stack-aware import snippet\n const stack = config?.stack ?? 'other'\n result['import_snippet'] = generateImportSnippet(modelDef, stack, effectiveLocale)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_describe_format ───\n server.tool(\n 'contentrain_describe_format',\n 'Describes the Contentrain content file format for any language/platform. Returns a comprehensive specification of the file structure, JSON formats, markdown conventions, meta files, and locale strategies.',\n {},\n async () => {\n const formatSpec = {\n overview: 'Contentrain stores content as plain JSON and Markdown files in a .contentrain/ directory at the project root. All files are committed to git.',\n directory_structure: {\n description: 'Standard layout of .contentrain/ directory',\n layout: {\n '.contentrain/config.json': 'Project configuration (stack, workflow, locales, domains)',\n '.contentrain/context.json': 'Last operation metadata (written by MCP after every write)',\n '.contentrain/vocabulary.json': 'Shared vocabulary/terms (optional)',\n '.contentrain/models/{model-id}.json': 'Model schema definitions',\n '.contentrain/content/{domain}/{model-id}/': 'Content files per model',\n '.contentrain/meta/{model-id}/': 'Metadata files per model (status, source, timestamps)',\n },\n },\n model_kinds: {\n collection: {\n description: 'Multiple entries stored as an object-map keyed by entry ID',\n storage_format: 'JSON object-map: { \"entry-id-1\": { ...fields }, \"entry-id-2\": { ...fields } }',\n note: 'Keys are sorted alphabetically for canonical output. Entry IDs are alphanumeric (1-40 chars, hyphens/underscores allowed).',\n example: '{ \"abc123\": { \"title\": \"Hello\", \"slug\": \"hello\" }, \"def456\": { \"title\": \"World\", \"slug\": \"world\" } }',\n },\n singleton: {\n description: 'Single entry (e.g. site settings, hero section)',\n storage_format: 'JSON object with field key-value pairs: { \"title\": \"My Site\", \"description\": \"...\" }',\n },\n document: {\n description: 'Markdown files with YAML-like frontmatter',\n storage_format: 'Markdown file: ---\\\\nslug: my-post\\\\ntitle: My Post\\\\n---\\\\n\\\\nMarkdown body content here.',\n frontmatter_rules: [\n 'Delimited by --- on its own line',\n 'Key-value pairs: key: value',\n 'Arrays: key:\\\\n - item1\\\\n - item2',\n 'Inline arrays: key: [item1, item2]',\n 'Values auto-parsed: true/false -> boolean, integers -> number, quoted -> string',\n '\"body\" key is reserved for the markdown content below the frontmatter',\n ],\n },\n dictionary: {\n description: 'Flat key-value string map (e.g. i18n translation files)',\n storage_format: 'JSON object: { \"greeting\": \"Hello\", \"farewell\": \"Goodbye\" }',\n note: 'All values must be strings.',\n },\n },\n meta_files: {\n description: 'Metadata is stored separately from content in .contentrain/meta/{model-id}/',\n collection_meta: 'Object-map: { \"entry-id\": { \"status\": \"draft\", \"source\": \"agent\", \"updated_by\": \"contentrain-mcp\" } }',\n singleton_meta: 'Single object: { \"status\": \"published\", \"source\": \"human\", \"updated_by\": \"user@example.com\" }',\n fields: {\n status: 'Content lifecycle status: \"draft\" | \"in_review\" | \"published\" | \"rejected\" | \"archived\"',\n source: 'Who created the entry: \"agent\" | \"human\" | \"import\"',\n updated_by: 'Author identifier string',\n approved_by: 'Optional: approver identifier',\n version: 'Optional: version string',\n publish_at: 'Optional: ISO 8601 date for scheduled publishing',\n expire_at: 'Optional: ISO 8601 date for scheduled expiry (must be after publish_at)',\n },\n },\n locale_strategies: {\n description: 'How localized content files are organized. Set per-model via locale_strategy field.',\n strategies: {\n file: {\n description: 'Default. Each locale is a separate file in the model directory.',\n json_pattern: '{content-dir}/{locale}.json (e.g. en.json, tr.json)',\n md_pattern: '{content-dir}/{slug}/{locale}.md',\n },\n suffix: {\n description: 'Locale appended to filename.',\n json_pattern: '{content-dir}/{model-id}.{locale}.json',\n md_pattern: '{content-dir}/{slug}.{locale}.md',\n },\n directory: {\n description: 'Locale as subdirectory.',\n json_pattern: '{content-dir}/{locale}/{model-id}.json',\n md_pattern: '{content-dir}/{locale}/{slug}.md',\n },\n none: {\n description: 'No locale in path (single-language or externally managed).',\n json_pattern: '{content-dir}/{model-id}.json',\n md_pattern: '{content-dir}/{slug}.md',\n },\n },\n },\n i18n_disabled: {\n description: 'When a model has i18n:false, locale is ignored in file paths.',\n json_pattern: '{content-dir}/data.json (always \"data.json\", no locale suffix)',\n md_pattern: '{content-dir}/{slug}.md (no locale in path)',\n },\n canonical_serialization: {\n description: 'All JSON files use deterministic serialization for clean git diffs.',\n rules: [\n 'Object keys sorted alphabetically',\n '2-space indentation',\n 'Trailing newline at end of file',\n 'No trailing commas',\n 'UTF-8 encoding',\n ],\n },\n content_path_override: {\n description: 'Models can specify a custom content_path to store content outside .contentrain/content/.',\n example: 'A model with content_path: \"content/blog\" stores files at {project-root}/content/blog/ instead of .contentrain/content/{domain}/{model-id}/',\n note: 'This is useful for frameworks that expect content in specific directories (e.g. Nuxt Content, Astro).',\n },\n reading_content: {\n steps: [\n '1. Read .contentrain/config.json to get locales and workflow settings',\n '2. Read .contentrain/models/{model-id}.json to get the schema and model kind',\n '3. Determine content directory: model.content_path or .contentrain/content/{domain}/{model-id}/',\n '4. Determine file path using locale_strategy and i18n flag',\n '5. Parse JSON (collection/singleton/dictionary) or Markdown frontmatter (document)',\n '6. Optionally read .contentrain/meta/{model-id}/{locale}.json for status/metadata',\n ],\n },\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(formatSpec, null, 2) }],\n }\n },\n )\n}\n\nasync function getSample(\n projectRoot: string,\n model: import('@contentrain/types').ModelDefinition,\n locale: string,\n): Promise<unknown> {\n const { readJson, readDir } = await import('../util/fs.js')\n const cDir = resolveContentDir(projectRoot, model)\n\n if (model.kind === 'collection') {\n const data = await readJson<Record<string, Record<string, unknown>>>(resolveJsonFilePath(cDir, model, locale))\n if (!data) return null\n const firstKey = Object.keys(data)[0]\n return firstKey ? { id: firstKey, ...data[firstKey] } : null\n }\n\n if (model.kind === 'singleton' || model.kind === 'dictionary') {\n return readJson(resolveJsonFilePath(cDir, model, locale))\n }\n\n if (model.kind === 'document') {\n const strategy = resolveLocaleStrategy(model)\n\n if (!model.i18n) {\n const entry = (await readDir(cDir)).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'file') {\n const slug = (await readDir(cDir))[0]\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'suffix') {\n const suffix = `.${locale}.md`\n const entry = (await readDir(cDir)).find(file => file.endsWith(suffix))\n const slug = entry?.slice(0, -suffix.length)\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'directory') {\n const entry = (await readDir(join(cDir, locale))).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n const entry = (await readDir(cDir)).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n return null\n}\n\nfunction buildContentPath(\n model: import('@contentrain/types').ModelDefinition,\n locale: string,\n): string {\n const strategy = model.locale_strategy ?? 'file'\n const baseDir = model.content_path ?? `.contentrain/content/${model.domain}/${model.id}`\n\n if (model.kind === 'document') {\n // When i18n is disabled, documents are flat {slug}.md\n if (!model.i18n) return `${baseDir}/{slug}.md`\n\n // Documents use directories — show the base path pattern\n switch (strategy) {\n case 'suffix': return `${baseDir}/{slug}.${locale}.md`\n case 'directory': return `${baseDir}/${locale}/{slug}.md`\n case 'none': return `${baseDir}/{slug}.md`\n case 'file':\n default: return `${baseDir}/{slug}/${locale}.md`\n }\n }\n\n // JSON-based models (collection, singleton, dictionary)\n // When i18n is disabled, always data.json\n if (!model.i18n) return `${baseDir}/data.json`\n\n switch (strategy) {\n case 'suffix': return `${baseDir}/${model.id}.${locale}.json`\n case 'directory': return `${baseDir}/${locale}/${model.id}.json`\n case 'none': return `${baseDir}/${model.id}.json`\n case 'file':\n default: return `${baseDir}/${locale}.json`\n }\n}\n\nfunction generateImportSnippet(\n model: import('@contentrain/types').ModelDefinition,\n stack: string,\n locale: string,\n): Record<string, string> {\n const path = buildContentPath(model, locale)\n\n const snippets: Record<string, string> = {\n generic: `import data from '${path}'`,\n }\n\n switch (stack) {\n case 'nuxt':\n snippets['nuxt'] = model.kind === 'document'\n ? `const { data } = await useAsyncData(() => queryContent('${model.domain}/${model.id}').locale('${locale}').find())`\n : `const { data } = await useFetch('/api/content/${model.id}?locale=${locale}')`\n break\n case 'next':\n snippets['next'] = `import data from '@/${path}'`\n break\n case 'astro':\n snippets['astro'] = `import data from '~/${path}'`\n break\n }\n\n return snippets\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ContentrainConfig, Vocabulary } from '@contentrain/types'\nimport { z } from 'zod'\nimport { join } from 'node:path'\nimport { readFile, writeFile, appendFile } from 'node:fs/promises'\nimport { simpleGit } from 'simple-git'\nimport { detectStack } from '../util/detect.js'\nimport { contentrainDir, ensureDir, pathExists, writeJson } from '../util/fs.js'\nimport { readConfig, readVocabulary } from '../core/config.js'\nimport { writeModel } from '../core/model-manager.js'\nimport { writeContent, type ContentEntry } from '../core/content-manager.js'\nimport { getTemplate, listTemplates } from '../templates/index.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\n\nexport function registerSetupTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_init ───\n server.tool(\n 'contentrain_init',\n 'Initialize .contentrain/ structure. Changes are auto-committed to git — do NOT manually create .contentrain/ files.',\n {\n stack: z.string().optional().describe('Framework stack (nuxt, next, astro, svelte, react-vite, other). Auto-detected if omitted.'),\n locales: z.array(z.string()).optional().describe('Supported locales. Default: [\"en\"]'),\n domains: z.array(z.string()).optional().describe('Content domains. Default: auto-suggested'),\n },\n async ({ stack, locales, domains }) => {\n const crDir = contentrainDir(projectRoot)\n\n // Already initialized?\n if (await pathExists(join(crDir, 'config.json'))) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Already initialized',\n suggestion: 'Use contentrain_status to see current state',\n }) }],\n isError: true,\n }\n }\n\n // Detect stack\n const detectedStack = stack ?? await detectStack(projectRoot)\n const supportedLocales = locales ?? ['en']\n const suggestedDomains = domains ?? suggestDomains(projectRoot)\n\n // Ensure .git exists\n const hasGit = await pathExists(join(projectRoot, '.git'))\n if (!hasGit) {\n await simpleGit(projectRoot).init()\n }\n\n // Branch health gate\n const initHealth = await checkBranchHealth(projectRoot)\n if (initHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: initHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Create git transaction\n const branch = buildBranchName('new', 'init')\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n const wtCrDir = join(wt, '.contentrain')\n\n // Create directories with .gitkeep so git tracks them\n await Promise.all(['models', 'content', 'meta'].map(async (dir) => {\n await ensureDir(join(wtCrDir, dir))\n await writeFile(join(wtCrDir, dir, '.gitkeep'), '', 'utf-8')\n }))\n\n // Write config\n const config: ContentrainConfig = {\n version: 1,\n stack: detectedStack as ContentrainConfig['stack'],\n workflow: 'auto-merge',\n locales: {\n default: supportedLocales[0] ?? 'en',\n supported: supportedLocales,\n },\n domains: suggestedDomains,\n }\n await writeJson(join(wtCrDir, 'config.json'), config)\n\n // Write empty vocabulary\n const vocabulary: Vocabulary = { version: 1, terms: {} }\n await writeJson(join(wtCrDir, 'vocabulary.json'), vocabulary)\n\n // Write initial context\n await writeJson(join(wtCrDir, 'context.json'), {\n version: '1',\n lastOperation: {\n tool: 'contentrain_init',\n model: '',\n locale: supportedLocales[0] ?? 'en',\n timestamp: new Date().toISOString(),\n source: process.env['CONTENTRAIN_SOURCE'] === 'mcp-studio' ? 'mcp-studio' : 'mcp-local',\n },\n stats: { models: 0, entries: 0, locales: supportedLocales, lastSync: new Date().toISOString() },\n })\n\n // Update .gitignore\n await updateGitignore(wt)\n })\n\n await tx.commit('[contentrain] init: project setup')\n const gitResult = await tx.complete({\n tool: 'contentrain_init',\n model: '',\n locale: supportedLocales[0] ?? 'en',\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Project initialized and committed to git. Do NOT manually edit .contentrain/ files.',\n config_created: '.contentrain/config.json',\n detected_stack: detectedStack,\n detected_locales: supportedLocales,\n suggested_domains: suggestedDomains,\n files_created: [\n '.contentrain/config.json',\n '.contentrain/vocabulary.json',\n '.contentrain/context.json',\n ],\n directories_created: [\n '.contentrain/models/',\n '.contentrain/content/',\n '.contentrain/meta/',\n ],\n gitignore_updated: true,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n next_steps: [\n 'Create models with contentrain_model_save or contentrain_scaffold',\n 'Use contentrain_scan to find hardcoded strings',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Init failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_scaffold ───\n server.tool(\n 'contentrain_scaffold',\n `Template-based project setup. Available templates: ${listTemplates().join(', ')}. Changes are auto-committed to git.`,\n {\n template: z.string().describe('Template ID: blog, landing, docs, ecommerce, saas, i18n, mobile'),\n locales: z.array(z.string()).optional().describe('Override locales'),\n with_sample_content: z.boolean().optional().default(true).describe('Include sample content (default: true)'),\n },\n async ({ template: templateId, locales, with_sample_content }) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Project not initialized. Run contentrain_init first.',\n }) }],\n isError: true,\n }\n }\n\n const tmpl = getTemplate(templateId)\n if (!tmpl) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Unknown template: \"${templateId}\". Available: ${listTemplates().join(', ')}`,\n }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const scaffoldHealth = await checkBranchHealth(projectRoot)\n if (scaffoldHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: scaffoldHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const effectiveLocales = locales ?? config.locales.supported\n const defaultLocale = effectiveLocales[0] ?? 'en'\n\n const branch = buildBranchName('new', `scaffold-${templateId}`, defaultLocale)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n const modelsCreated: Array<{ id: string; kind: string; fields: number }> = []\n let contentCreated = 0\n let vocabAdded = 0\n\n await tx.write(async (wt) => {\n // Write models\n await Promise.all(tmpl.models.map(async (model) => {\n await writeModel(wt, model)\n modelsCreated.push({\n id: model.id,\n kind: model.kind,\n fields: model.fields ? Object.keys(model.fields).length : 0,\n })\n }))\n\n // Write sample content via content pipeline (handles content_path, locale_strategy, meta)\n if (with_sample_content && tmpl.sample_content) {\n contentCreated = await writeSampleContent(wt, tmpl, effectiveLocales, config)\n }\n\n // Merge vocabulary\n if (tmpl.vocabulary) {\n vocabAdded = await mergeVocabulary(wt, tmpl.vocabulary, effectiveLocales)\n }\n\n })\n\n await tx.commit(`[contentrain] scaffold: ${templateId} (${defaultLocale})`)\n const gitResult = await tx.complete({\n tool: 'contentrain_scaffold',\n model: tmpl.models.map(m => m.id).join(', '),\n locale: defaultLocale,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Scaffold applied and committed to git. Do NOT manually edit .contentrain/ files.',\n models_created: modelsCreated,\n content_created: contentCreated,\n vocabulary_terms_added: vocabAdded,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n next_steps: [\n 'Customize models with contentrain_model_save',\n 'Add content with contentrain_content_save',\n 'Run contentrain_submit when ready',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scaffold failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n}\n\nfunction suggestDomains(_projectRoot: string): string[] {\n return ['marketing', 'blog', 'system']\n}\n\nasync function updateGitignore(projectRoot: string): Promise<void> {\n const gitignorePath = join(projectRoot, '.gitignore')\n const cacheEntry = '.contentrain/.cache/'\n\n if (await pathExists(gitignorePath)) {\n const content = await readFile(gitignorePath, 'utf-8')\n if (!content.includes(cacheEntry)) {\n await appendFile(gitignorePath, `\\n# Contentrain cache\\n${cacheEntry}\\n`)\n }\n } else {\n await writeFile(gitignorePath, `# Contentrain cache\\n${cacheEntry}\\n`, 'utf-8')\n }\n}\n\nasync function writeSampleContent(\n worktreePath: string,\n tmpl: import('@contentrain/types').ScaffoldTemplate,\n locales: string[],\n config: import('@contentrain/types').ContentrainConfig,\n): Promise<number> {\n if (!tmpl.sample_content) return 0\n\n let count = 0\n\n for (const model of tmpl.models) {\n const sampleData = tmpl.sample_content[model.id]\n if (!sampleData) continue\n\n for (const [localeOrKey, data] of Object.entries(sampleData)) {\n const entryMap = data as Record<string, Record<string, unknown>>\n let targetLocale: string\n if (localeOrKey === 'data') {\n targetLocale = locales[0] ?? 'en'\n } else if (locales.includes(localeOrKey)) {\n targetLocale = localeOrKey\n } else {\n continue\n }\n\n // Convert template payload to ContentEntry[] based on model kind\n let entries: ContentEntry[]\n\n if (model.kind === 'singleton' || model.kind === 'dictionary') {\n // Singleton/dictionary: the entire locale object IS the data for one entry\n entries = [{ locale: targetLocale, data: entryMap as Record<string, unknown> }]\n } else if (model.kind === 'document') {\n // Document: keys are slugs, values are frontmatter objects\n entries = Object.entries(entryMap).map(([slug, fields]) => ({\n slug,\n locale: targetLocale,\n data: fields,\n }))\n } else {\n // Collection: keys are entry IDs, values are field objects\n entries = Object.entries(entryMap).map(([id, fields]) => ({\n id,\n locale: targetLocale,\n data: fields,\n }))\n }\n\n await writeContent(worktreePath, model, entries, config)\n count += entries.length\n }\n }\n\n return count\n}\n\nasync function mergeVocabulary(\n projectRoot: string,\n newTerms: Record<string, Record<string, string>>,\n _locales: string[],\n): Promise<number> {\n const vocabPath = join(contentrainDir(projectRoot), 'vocabulary.json')\n const existing = await readVocabulary(projectRoot)\n const vocab: Vocabulary = existing ?? { version: 1, terms: {} }\n\n let added = 0\n for (const [key, translations] of Object.entries(newTerms)) {\n if (!(key in vocab.terms)) {\n added++\n }\n vocab.terms[key] = { ...vocab.terms[key], ...translations }\n }\n\n await writeJson(vocabPath, vocab)\n return added\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ModelDefinition } from '@contentrain/types'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\n\nimport { resolveContentDir, resolveJsonFilePath, resolveMdFilePath } from '../core/content-manager.js'\nimport { checkReferences, deleteModel, readModel, writeModel, validateModelDefinition, fieldDefZodSchema } from '../core/model-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\n\n// Shared field definition schema — single source of truth with normalize extract\nconst fieldDefSchema = fieldDefZodSchema\n\nexport function registerModelTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_model_save ───\n server.tool(\n 'contentrain_model_save',\n 'Create or update a model definition. Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n id: z.string().describe('Model ID (kebab-case, e.g. \"blog-post\")'),\n name: z.string().describe('Human-readable name'),\n kind: z.enum(['singleton', 'collection', 'document', 'dictionary']).describe('Model kind'),\n domain: z.string().describe('Content domain (e.g. \"blog\", \"marketing\", \"system\")'),\n i18n: z.boolean().describe('Whether this model supports localization'),\n description: z.string().optional().describe('Model description'),\n fields: fieldDefSchema.optional().describe('Field definitions (not needed for dictionary)'),\n content_path: z.string().optional().describe('Framework-relative path for content files (e.g. \"content/blog\", \"locales\"). When set, content is written here instead of .contentrain/content/'),\n locale_strategy: z.enum(['file', 'suffix', 'directory', 'none']).optional().describe('How locale is encoded in file names. Default: \"file\"'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n // Validate\n const errors = validateModel(input)\n if (errors.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Validation failed', details: errors }) }],\n isError: true,\n }\n }\n\n // Reject invalid locale_strategy + i18n combinations\n if (input.locale_strategy === 'none' && input.i18n !== false) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'locale_strategy \"none\" requires i18n:false. The \"none\" strategy stores a single file without locale encoding, which is incompatible with multi-locale content. Use \"file\", \"suffix\", or \"directory\" for i18n models.',\n }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const existing = await readModel(projectRoot, input.id)\n const action = existing ? 'updated' : 'created'\n\n const model: ModelDefinition = {\n id: input.id,\n name: input.name,\n kind: input.kind,\n domain: input.domain,\n i18n: input.i18n,\n description: input.description,\n fields: input.fields as ModelDefinition['fields'],\n content_path: input.content_path,\n locale_strategy: input.locale_strategy,\n }\n\n const branch = buildBranchName('model', input.id)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n await writeModel(wt, model)\n })\n\n await tx.commit(`[contentrain] ${action}: ${input.id}`)\n const gitResult = await tx.complete({ tool: 'contentrain_model_save', model: input.id })\n\n const defaultLocale = config.locales.default\n // Build accurate content path using path resolvers\n const contentDir = resolveContentDir(projectRoot, model)\n const contentPath = model.content_path ?? `.contentrain/content/${input.domain}/${input.id}`\n let exampleFilePath: string\n if (model.kind === 'document') {\n exampleFilePath = resolveMdFilePath(contentDir, model, defaultLocale, '{slug}')\n } else {\n exampleFilePath = resolveJsonFilePath(contentDir, model, defaultLocale)\n }\n // Make the path relative for display\n const displayPath = exampleFilePath.replace(projectRoot + '/', '').replace(projectRoot, '')\n const importSnippet: Record<string, string> = {\n generic: `import data from '${displayPath}'`,\n }\n if (config.stack === 'nuxt') {\n importSnippet['nuxt'] = model.kind === 'document'\n ? `const { data } = await useAsyncData(() => queryContent('${model.domain}/${model.id}').locale('${defaultLocale}').find())`\n : `const { data } = await useFetch('/api/content/${model.id}?locale=${defaultLocale}')`\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Model saved and committed to git. Do NOT manually edit .contentrain/ files.',\n action,\n model: input.id,\n validation: { valid: true, errors: [] },\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n content_path: contentPath + '/',\n example_file: displayPath,\n import_snippet: importSnippet,\n next_steps: ['Add content with contentrain_content_save'],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model save failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_model_delete ───\n server.tool(\n 'contentrain_model_delete',\n 'Delete a model and its content/meta. Changes are auto-committed to git — do NOT manually edit .contentrain/ files.',\n {\n model: z.string().describe('Model ID to delete'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n async ({ model: modelId }) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n // Check model exists\n const existing = await readModel(projectRoot, modelId)\n if (!existing) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${modelId}\" not found` }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const deleteHealth = await checkBranchHealth(projectRoot)\n if (deleteHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Check references\n const refs = await checkReferences(projectRoot, modelId)\n if (refs.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n deleted: false,\n error: 'REFERENCED_MODEL',\n referenced_by: refs,\n next_steps: ['Remove relation fields from referencing models first'],\n }, null, 2) }],\n }\n }\n\n const branch = buildBranchName('model', modelId)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let filesRemoved: string[] = []\n\n await tx.write(async (wt) => {\n filesRemoved = await deleteModel(wt, modelId)\n })\n\n await tx.commit(`[contentrain] delete: ${modelId}`)\n const gitResult = await tx.complete({ tool: 'contentrain_model_delete', model: modelId })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Model deleted and committed to git. Do NOT manually edit .contentrain/ files.',\n deleted: true,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n files_removed: filesRemoved,\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n}\n\n// validateModel is now validateModelDefinition from model-manager.ts (shared)\nconst validateModel = validateModelDefinition\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\nimport { readModel } from '../core/model-manager.js'\nimport { writeContent, deleteContent, listContent } from '../core/content-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { validateProject } from '../core/validator.js'\n\nexport function registerContentTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_content_save ───\n server.tool(\n 'contentrain_content_save',\n 'Save content entries. Entry format varies by model kind: DICTIONARY — provide \"locale\" and \"data\" (flat key-value, all string values); \"id\" and \"slug\" are ignored; data keys are the identities. COLLECTION — provide \"locale\" and \"data\"; \"id\" is optional (auto-generated if omitted); \"slug\" is ignored. DOCUMENT — provide \"slug\" (required), \"locale\", and \"data\"; use the \"body\" key inside data for markdown content. SINGLETON — provide only \"locale\" and \"data\". Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n model: z.string().describe('Model ID'),\n entries: z.array(z.object({\n id: z.string().optional().describe('Entry ID (collection only, auto-generated if omitted)'),\n slug: z.string().optional().describe('Slug (document only)'),\n locale: z.string().optional().describe('Locale code (defaults to config default)'),\n data: z.record(z.string(), z.unknown()).describe('Content data. For documents, include \"body\" key for markdown content.'),\n publish_at: z.string().optional().describe('ISO 8601 date for scheduled publishing (stored in meta)'),\n expire_at: z.string().optional().describe('ISO 8601 date for scheduled expiry (stored in meta, must be after publish_at)'),\n })).describe('Content entries to save'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Validate locales and scheduling fields before starting transaction\n for (const entry of input.entries) {\n if (entry.locale && !config.locales.supported.includes(entry.locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Locale \"${entry.locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // Validate publish_at / expire_at\n if (entry.publish_at !== undefined) {\n const d = new Date(entry.publish_at)\n if (Number.isNaN(d.getTime())) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Invalid publish_at date: \"${entry.publish_at}\". Must be a valid ISO 8601 date string.`,\n }) }],\n isError: true,\n }\n }\n }\n if (entry.expire_at !== undefined) {\n const d = new Date(entry.expire_at)\n if (Number.isNaN(d.getTime())) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Invalid expire_at date: \"${entry.expire_at}\". Must be a valid ISO 8601 date string.`,\n }) }],\n isError: true,\n }\n }\n }\n if (entry.publish_at !== undefined && entry.expire_at !== undefined) {\n if (new Date(entry.expire_at) <= new Date(entry.publish_at)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `expire_at (\"${entry.expire_at}\") must be after publish_at (\"${entry.publish_at}\").`,\n }) }],\n isError: true,\n }\n }\n }\n\n // Merge scheduling fields into data so meta-manager picks them up\n if (entry.publish_at !== undefined) entry.data['publish_at'] = entry.publish_at\n if (entry.expire_at !== undefined) entry.data['expire_at'] = entry.expire_at\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('content', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let results: Awaited<ReturnType<typeof writeContent>>\n\n let entryIds: string[] = []\n\n await tx.write(async (wt) => {\n results = await writeContent(wt, model, input.entries, config)\n entryIds = results!.map(r => r.id ?? r.slug ?? r.locale).filter(Boolean) as string[]\n })\n\n await tx.commit(`[contentrain] content: ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_content_save',\n model: input.model,\n locale: input.entries[0]?.locale,\n entries: entryIds,\n })\n\n // Run real validation after save — don't fake it\n const validationResult = await validateProject(projectRoot, { model: input.model })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Content saved and committed to git. Do NOT manually edit .contentrain/ files.',\n results: results!,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n validation: {\n valid: validationResult.valid,\n errors: validationResult.issues.filter(i => i.severity === 'error').map(i => i.message),\n },\n context_updated: true,\n next_steps: [\n ...(!validationResult.valid ? ['WARNING: Content has validation errors — run contentrain_validate for details'] : []),\n ...(model.kind === 'collection'\n ? ['Use contentrain_content_list to verify', 'Add more entries or publish']\n : ['Use contentrain_content_list to verify']),\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Content save failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_content_delete ───\n server.tool(\n 'contentrain_content_delete',\n 'Delete content entries. For dictionaries, use \"keys\" to remove specific keys (omit to delete entire locale file). Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n model: z.string().describe('Model ID'),\n id: z.string().optional().describe('Entry ID (collection)'),\n slug: z.string().optional().describe('Slug (document)'),\n locale: z.string().optional().describe('Locale code'),\n keys: z.array(z.string()).optional().describe('Dictionary only: specific keys to remove. Omit to delete entire locale file.'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const deleteHealth = await checkBranchHealth(projectRoot)\n if (deleteHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('content', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let removed: string[] = []\n\n await tx.write(async (wt) => {\n removed = await deleteContent(wt, model, {\n id: input.id,\n slug: input.slug,\n locale: input.locale,\n keys: input.keys,\n })\n })\n\n await tx.commit(`[contentrain] delete content: ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_content_delete',\n model: input.model,\n locale: input.locale,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Content deleted and committed to git. Do NOT manually edit .contentrain/ files.',\n deleted: true,\n files_removed: removed,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_content_list ───\n server.tool(\n 'contentrain_content_list',\n 'List content entries (read-only). Returns data from .contentrain/ — do NOT manually create or modify content files.',\n {\n model: z.string().describe('Model ID'),\n locale: z.string().optional().describe('Locale code (defaults to config default)'),\n filter: z.record(z.string(), z.unknown()).optional().describe('Filter criteria (collection only)'),\n resolve: z.boolean().optional().describe('Resolve relation fields to actual data'),\n limit: z.number().optional().describe('Max entries to return'),\n offset: z.number().optional().describe('Skip N entries'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n try {\n const result = await listContent(projectRoot, model, {\n locale: input.locale,\n filter: input.filter as Record<string, unknown>,\n resolve: input.resolve,\n limit: input.limit,\n offset: input.offset,\n }, config)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `List failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport { simpleGit } from 'simple-git'\nimport { validateProject } from '../core/validator.js'\nimport { readConfig } from '../core/config.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth, cleanupMergedBranches } from '../git/branch-lifecycle.js'\n\nexport function registerWorkflowTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_validate ───\n server.tool(\n 'contentrain_validate',\n 'Validate project content against model schemas. Detects required field violations, type mismatches, broken relations, secret leaks, i18n parity issues, and more. If fix:true, auto-fixes structural issues (canonical sort, orphan meta, missing locale files) — do NOT manually edit .contentrain/ files.',\n {\n model: z.string().optional().describe('Model ID to validate (omit for all models)'),\n fix: z.boolean().optional().describe('Auto-fix structural issues (canonical sort, orphan meta, missing locale files). Default: false'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n try {\n let result: Awaited<ReturnType<typeof validateProject>> | undefined\n\n if (input.fix) {\n // Branch health gate for fix mode (creates a branch)\n const fixHealth = await checkBranchHealth(projectRoot)\n if (fixHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: fixHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before auto-fixing.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Use git transaction for fixes\n const branch = buildBranchName('fix', 'validate')\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n result = await validateProject(wt, { model: input.model, fix: true })\n })\n\n if (result!.fixed > 0) {\n await tx.commit(`[contentrain] validate: auto-fix ${result!.fixed} issue(s)`)\n const gitResult = await tx.complete({\n tool: 'contentrain_validate',\n model: input.model ?? '*',\n })\n\n const nextSteps: string[] = []\n if (result!.summary.errors > 0) nextSteps.push('Fix remaining errors manually')\n if (result!.summary.warnings > 0) nextSteps.push('Review warnings')\n nextSteps.push('Run contentrain_validate again to verify')\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: `Validation complete. ${result!.fixed} issue(s) auto-fixed and committed to git. Do NOT manually edit .contentrain/ files.`,\n ...result!,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } else {\n // Nothing to fix, cleanup the branch\n await tx.cleanup()\n }\n } catch (error) {\n await tx.cleanup()\n throw error\n } finally {\n await tx.cleanup()\n }\n }\n\n // No fix or nothing was fixed — run read-only validation\n if (!result) {\n result = await validateProject(projectRoot, { model: input.model, fix: false })\n }\n\n const nextSteps: string[] = []\n if (result.summary.errors > 0) nextSteps.push('Fix errors in content using contentrain_content_save')\n if (result.summary.warnings > 0) nextSteps.push('Review warnings')\n if (result.valid) nextSteps.push('Run contentrain_submit to push changes')\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'validated',\n message: result.valid\n ? 'All validation checks passed.'\n : `Validation found ${result.summary.errors} error(s) and ${result.summary.warnings} warning(s).`,\n ...result,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Validation failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_submit ───\n server.tool(\n 'contentrain_submit',\n 'Push contentrain/* branches to remote. MCP is push-only — PR creation is handled by the platform. Do NOT manually push or create PRs.',\n {\n branches: z.array(z.string()).optional().describe('Specific branch names to push (omit for all contentrain/* branches)'),\n message: z.string().optional().describe('Optional message for the push operation'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const git = simpleGit(projectRoot)\n const remoteName = process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n\n // Check remote exists\n let hasRemote = false\n try {\n const remotes = await git.getRemotes()\n hasRemote = remotes.some(r => r.name === remoteName)\n } catch {\n hasRemote = false\n }\n\n if (!hasRemote) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `No remote \"${remoteName}\" found. Configure a git remote first.`,\n next_steps: [`git remote add ${remoteName} <url>`],\n }) }],\n isError: true,\n }\n }\n\n try {\n // Determine the base branch for merge-status checks\n const baseBranch = config.repository?.default_branch\n ?? process.env['CONTENTRAIN_BRANCH']\n ?? ((await git.raw(['branch', '--show-current'])).trim() || 'main')\n\n // Get contentrain branches\n const branchSummary = await git.branchLocal()\n\n // Get branches NOT yet merged into base (only these need pushing)\n let unmergedRaw = ''\n try {\n unmergedRaw = await git.raw(['branch', '--no-merged', baseBranch])\n } catch {\n // If base branch doesn't exist yet, treat all as unmerged\n unmergedRaw = branchSummary.all.join('\\n')\n }\n const unmergedSet = new Set(\n unmergedRaw.split('\\n').map(b => b.replace(/^\\*?\\s+/, '').trim()).filter(Boolean),\n )\n\n let branchesToPush: string[]\n\n if (input.branches && input.branches.length > 0) {\n branchesToPush = input.branches.filter(b => branchSummary.all.includes(b))\n const missing = input.branches.filter(b => !branchSummary.all.includes(b))\n if (missing.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Branches not found: ${missing.join(', ')}`,\n }) }],\n isError: true,\n }\n }\n // Filter out already-merged branches from explicit list\n branchesToPush = branchesToPush.filter(b => unmergedSet.has(b))\n } else {\n branchesToPush = branchSummary.all.filter(b => b.startsWith('contentrain/') && unmergedSet.has(b))\n }\n\n if (branchesToPush.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'No unmerged contentrain/* branches found to push.',\n next_steps: ['Make changes first with contentrain_content_save or contentrain_model_save'],\n }) }],\n isError: true,\n }\n }\n\n // Push each branch\n const pushed: string[] = []\n const errors: Array<{ branch: string; error: string }> = []\n\n for (const branch of branchesToPush) {\n try {\n await git.push(remoteName, branch)\n pushed.push(branch)\n } catch (error) {\n errors.push({\n branch,\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n\n // Lazy cleanup: delete merged branches after push\n const cleanup = await cleanupMergedBranches(projectRoot)\n\n const nextSteps: string[] = []\n if (pushed.length > 0) nextSteps.push('Create PRs on your git platform for review')\n if (errors.length > 0) nextSteps.push('Fix push errors and retry')\n if (cleanup.remaining >= 50) nextSteps.push(`Warning: ${cleanup.remaining} active contentrain branches. Consider reviewing old branches.`)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: `Pushed ${pushed.length} branch(es) to ${remoteName}.`,\n pushed,\n errors: errors.length > 0 ? errors : undefined,\n remote: remoteName,\n cleanup: cleanup.deleted > 0 ? { deleted: cleanup.deleted, remaining: cleanup.remaining } : undefined,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { FieldDef } from '@contentrain/types'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\nimport { buildGraph } from '../core/graph-builder.js'\nimport { scanCandidates, scanSummary } from '../core/scanner.js'\nimport { applyExtract, applyReuse } from '../core/apply-manager.js'\nimport { fieldDefZodSchema } from '../core/model-manager.js'\n\nexport function registerNormalizeTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_scan ───\n server.tool(\n 'contentrain_scan',\n 'Scan project source code for content strings. Three modes: \"graph\" builds import/component graph for project intelligence, \"candidates\" extracts string literals with pre-filtering and pagination, \"summary\" provides quick overview stats. Read-only — no changes to disk or git. MCP finds strings deterministically; the agent decides what is content. Recommended workflow: start with \"summary\" or \"graph\" for orientation, then paginate through \"candidates\" to evaluate strings.',\n {\n mode: z.enum(['graph', 'candidates', 'summary']).optional().describe('Scan mode. Default: candidates'),\n paths: z.array(z.string()).optional().describe('Directories to scan (relative to project root). Default: auto-detect'),\n include: z.array(z.string()).optional().describe('File extensions to include. Default: .tsx, .jsx, .vue, .ts, .js, .mjs, .astro, .svelte'),\n exclude: z.array(z.string()).optional().describe('Additional directory names to exclude'),\n limit: z.number().optional().describe('Candidates mode: batch size. Default: 50'),\n offset: z.number().optional().describe('Candidates mode: pagination offset. Default: 0'),\n min_length: z.number().optional().describe('Candidates mode: minimum string length. Default: 2'),\n max_length: z.number().optional().describe('Candidates mode: maximum string length. Default: 500'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const mode = input.mode ?? 'candidates'\n\n try {\n switch (mode) {\n case 'graph': {\n const graph = await buildGraph(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'graph',\n ...graph,\n next_steps: [\n 'Use mode:candidates to scan specific files/directories for strings',\n 'Focus on pages and components with high string counts first',\n ],\n }, null, 2) }],\n }\n }\n\n case 'candidates': {\n const result = await scanCandidates(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n limit: input.limit,\n offset: input.offset,\n min_length: input.min_length,\n max_length: input.max_length,\n })\n\n const nextSteps: string[] = []\n if (result.stats.has_more) {\n nextSteps.push(`Use offset:${(input.offset ?? 0) + (input.limit ?? 50)} for next batch`)\n }\n nextSteps.push('Evaluate each candidate: is it user-facing content? Which domain/model?')\n if (result.duplicates.length > 0) {\n nextSteps.push('Consider deduplicating repeated strings into shared models')\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'candidates',\n ...result,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n }\n\n case 'summary': {\n const result = await scanSummary(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n min_length: input.min_length,\n max_length: input.max_length,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'summary',\n ...result,\n next_steps: [\n 'Use mode:graph for project structure analysis',\n 'Use mode:candidates to scan directories with most candidates',\n ],\n }, null, 2) }],\n }\n }\n\n default:\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown mode: ${mode}` }) }],\n isError: true,\n }\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scan failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_apply ───\n server.tool(\n 'contentrain_apply',\n 'Apply normalize operations. Two modes: \"extract\" writes agent-approved strings to Contentrain content files (source untouched), \"reuse\" patches source files with agent-provided replacement expressions. DRY RUN (default, dry_run:true): validates inputs, resolves conflicts, and returns a full preview — NO changes to disk or git. EXECUTE (dry_run:false): writes files to disk, commits to a branch, and requires branch health check to pass. Recommended workflow: always run dry_run first, review the preview, then call again with dry_run:false to execute. Normalize operations always use review workflow (never auto-merge).',\n {\n mode: z.enum(['extract', 'reuse']).describe('Apply mode: extract (content creation) or reuse (source patching)'),\n dry_run: z.boolean().optional().default(true).describe('Defaults to preview mode (dry_run:true). Set dry_run:false to execute after reviewing the preview.'),\n\n // Extract mode fields\n extractions: z.array(z.object({\n model: z.string().describe('Model ID (e.g. \"ui-texts\", \"hero-section\")'),\n kind: z.enum(['singleton', 'collection', 'dictionary', 'document']).describe('Model kind'),\n domain: z.string().describe('Content domain (e.g. \"marketing\", \"app\")'),\n i18n: z.boolean().optional().describe('Enable i18n. Default: true'),\n fields: fieldDefZodSchema.optional().describe('Field definitions — shared schema with model_save for full parity'),\n entries: z.array(z.object({\n locale: z.string().optional().describe('Locale. Default: project default'),\n slug: z.string().optional().describe('Document slug'),\n data: z.record(z.any()).describe('Content data'),\n source: z.object({\n file: z.string().describe('Source file path'),\n line: z.number().describe('Source line number'),\n value: z.string().describe('Original string value'),\n }).optional().describe('Source tracking for traceability'),\n sources: z.array(z.object({\n file: z.string().describe('Source file path'),\n line: z.number().describe('Source line number'),\n key: z.string().describe('Dictionary key this source maps to'),\n value: z.string().describe('Original string value'),\n })).optional().describe('Per-key source tracking for dictionary models'),\n })).describe('Content entries to create'),\n })).optional().describe('Extract mode: content extractions'),\n\n // Reuse mode fields\n scope: z.object({\n model: z.string().optional().describe('Target model ID'),\n domain: z.string().optional().describe('Target domain'),\n }).optional().describe('Reuse mode: scope (model or domain required)'),\n patches: z.array(z.object({\n file: z.string().describe('Relative file path to patch'),\n line: z.number().describe('Line number hint (±10 line search)'),\n old_value: z.string().describe('Original string to replace'),\n new_expression: z.string().describe('Replacement expression (agent-determined)'),\n import_statement: z.string().optional().describe('Import to add if needed'),\n })).optional().describe('Reuse mode: patches to apply (max 100)'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n try {\n switch (input.mode) {\n case 'extract': {\n if (!input.extractions || input.extractions.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Extract mode requires extractions array' }) }],\n isError: true,\n }\n }\n\n const result = await applyExtract(projectRoot, {\n extractions: input.extractions.map(e => ({\n ...e,\n fields: e.fields as Record<string, FieldDef> | undefined,\n })),\n dry_run: input.dry_run,\n })\n\n // Check for branch-blocked response\n if (result.error !== undefined) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'extract',\n ...result,\n }, null, 2) }],\n isError: true,\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'extract',\n ...result,\n }, null, 2) }],\n }\n }\n\n case 'reuse': {\n if (!input.scope) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Reuse mode requires scope (model or domain)' }) }],\n isError: true,\n }\n }\n if (!input.patches || input.patches.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Reuse mode requires patches array' }) }],\n isError: true,\n }\n }\n\n const result = await applyReuse(projectRoot, {\n scope: input.scope,\n patches: input.patches,\n dry_run: input.dry_run,\n })\n\n // Check for branch-blocked response\n if (result.error !== undefined) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'reuse',\n ...result,\n }, null, 2) }],\n isError: true,\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'reuse',\n ...result,\n }, null, 2) }],\n }\n }\n\n default:\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown mode: ${input.mode}` }) }],\n isError: true,\n }\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Apply failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { EntryMeta } from '@contentrain/types'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\nimport { readModel } from '../core/model-manager.js'\nimport { resolveContentDir, resolveJsonFilePath, deleteContent } from '../core/content-manager.js'\nimport { readMeta, writeMeta } from '../core/meta-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { readJson, writeJson } from '../util/fs.js'\n\nexport function registerBulkTools(server: McpServer, projectRoot: string): void {\n server.tool(\n 'contentrain_bulk',\n 'Batch operations on content entries. All operations are auto-committed to git.',\n {\n operation: z.enum(['copy_locale', 'update_status', 'delete_entries']),\n model: z.string().describe('Model ID'),\n source_locale: z.string().optional().describe('Source locale for copy_locale operation'),\n target_locale: z.string().optional().describe('Target locale for copy_locale operation'),\n entry_ids: z.array(z.string()).optional().describe('Entry IDs for update_status or delete_entries'),\n status: z.enum(['draft', 'in_review', 'published', 'rejected', 'archived']).optional().describe('New status for update_status'),\n confirm: z.boolean().optional().describe('Must be true for delete_entries'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n switch (input.operation) {\n case 'copy_locale': {\n if (!input.source_locale || !input.target_locale) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'copy_locale requires source_locale and target_locale' }) }],\n isError: true,\n }\n }\n if (!config.locales.supported.includes(input.source_locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Source locale \"${input.source_locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n if (!config.locales.supported.includes(input.target_locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Target locale \"${input.target_locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // Guard: copy_locale requires i18n model — non-i18n models use data.json for all locales\n if (!model.i18n) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model \"${input.model}\" has i18n disabled. copy_locale only works with i18n-enabled models. Enable i18n first with contentrain_model_save.`,\n }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection' && model.kind !== 'singleton' && model.kind !== 'dictionary') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'copy_locale is only supported for collection, singleton, and dictionary models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model, input.target_locale)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let copiedCount = 0\n\n await tx.write(async (wt) => {\n const cDir = resolveContentDir(wt, model)\n const sourceFile = resolveJsonFilePath(cDir, model, input.source_locale!)\n const targetFile = resolveJsonFilePath(cDir, model, input.target_locale!)\n\n const sourceData = await readJson<Record<string, unknown>>(sourceFile)\n if (!sourceData) {\n throw new Error(`No content found for locale \"${input.source_locale}\" in model \"${input.model}\"`)\n }\n\n await writeJson(targetFile, sourceData)\n\n if (model.kind === 'collection') {\n const entries = sourceData as Record<string, Record<string, unknown>>\n const entryIds = Object.keys(entries)\n const metaOps = entryIds.map(entryId =>\n writeMeta(wt, model, { locale: input.target_locale!, entryId }, {\n status: 'draft',\n source: 'agent',\n updated_by: 'contentrain-mcp',\n }),\n )\n await Promise.all(metaOps)\n copiedCount = entryIds.length\n } else {\n await writeMeta(wt, model, { locale: input.target_locale! }, {\n status: 'draft',\n source: 'agent',\n updated_by: 'contentrain-mcp',\n })\n copiedCount = 1\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: copy ${input.source_locale} → ${input.target_locale} for ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_bulk',\n model: input.model,\n locale: input.target_locale!,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'copy_locale',\n message: `Copied ${copiedCount} entries from ${input.source_locale} to ${input.target_locale}.`,\n copied: copiedCount,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `copy_locale failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'update_status': {\n if (!input.entry_ids || input.entry_ids.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status requires entry_ids' }) }],\n isError: true,\n }\n }\n if (!input.status) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status requires status' }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status with entry_ids is only supported for collection models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let updatedCount = 0\n const notFound: string[] = []\n\n await tx.write(async (wt) => {\n for (const locale of config.locales.supported) {\n const metaData = await readMeta(wt, model, { locale }) as Record<string, EntryMeta> | null\n\n const updateOps: Array<Promise<void>> = []\n for (const entryId of input.entry_ids!) {\n const existing = metaData?.[entryId]\n if (existing) {\n updateOps.push(writeMeta(wt, model, { locale, entryId }, {\n ...existing,\n status: input.status!,\n updated_by: 'contentrain-mcp',\n }))\n updatedCount++\n } else if (locale === config.locales.default) {\n notFound.push(entryId)\n }\n }\n await Promise.all(updateOps)\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: update status → ${input.status} for ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'update_status',\n message: `Updated ${updatedCount} meta entries to status \"${input.status}\".`,\n updated: updatedCount,\n not_found: notFound.length > 0 ? notFound : undefined,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `update_status failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'delete_entries': {\n if (!input.entry_ids || input.entry_ids.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries requires entry_ids' }) }],\n isError: true,\n }\n }\n if (input.confirm !== true) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries requires confirm:true' }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries with entry_ids is only supported for collection models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n const allRemoved: string[] = []\n\n await tx.write(async (wt) => {\n for (const entryId of input.entry_ids!) {\n const removed = await deleteContent(wt, model, { id: entryId })\n allRemoved.push(...removed)\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: delete ${input.entry_ids.length} entries from ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'delete_entries',\n message: `Deleted ${input.entry_ids.length} entries.`,\n deleted: input.entry_ids.length,\n files_removed: allRemoved,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `delete_entries failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n default: {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown operation: ${input.operation}` }) }],\n isError: true,\n }\n }\n }\n },\n )\n}\n","","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { registerContextTools } from './tools/context.js'\nimport { registerSetupTools } from './tools/setup.js'\nimport { registerModelTools } from './tools/model.js'\nimport { registerContentTools } from './tools/content.js'\nimport { registerWorkflowTools } from './tools/workflow.js'\nimport { registerNormalizeTools } from './tools/normalize.js'\nimport { registerBulkTools } from './tools/bulk.js'\nimport packageJson from '../package.json' with { type: 'json' }\n\nexport function createServer(projectRoot: string): McpServer {\n const server = new McpServer({\n name: 'contentrain-mcp',\n version: packageJson.version,\n })\n\n registerContextTools(server, projectRoot)\n registerSetupTools(server, projectRoot)\n registerModelTools(server, projectRoot)\n registerContentTools(server, projectRoot)\n registerWorkflowTools(server, projectRoot)\n registerNormalizeTools(server, projectRoot)\n registerBulkTools(server, projectRoot)\n\n return server\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"server-QgivXkem.mjs","names":["packageJson.version"],"sources":["../src/tools/context.ts","../src/tools/setup.ts","../src/tools/model.ts","../src/tools/content.ts","../src/tools/workflow.ts","../src/tools/normalize.ts","../src/tools/bulk.ts","../package.json","../src/server.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport { readConfig, readVocabulary } from '../core/config.js'\nimport { readContext } from '../core/context.js'\nimport { countEntries, listModels, readModel } from '../core/model-manager.js'\nimport { resolveContentDir, resolveJsonFilePath, resolveLocaleStrategy } from '../core/content-manager.js'\nimport { detectStack } from '../util/detect.js'\nimport { join } from 'node:path'\nimport { contentrainDir, pathExists } from '../util/fs.js'\nimport { checkBranchHealth, cleanupMergedBranches } from '../git/branch-lifecycle.js'\n\nexport function registerContextTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_status ───\n server.tool(\n 'contentrain_status',\n 'Get full project status (read-only). Returns config, models, context. Do NOT manually edit .contentrain/ based on this output.',\n {},\n async () => {\n const crDir = contentrainDir(projectRoot)\n const initialized = await pathExists(join(crDir, 'config.json'))\n\n if (!initialized) {\n const detectedStack = await detectStack(projectRoot)\n return {\n content: [{\n type: 'text' as const,\n text: JSON.stringify({\n initialized: false,\n detected_stack: detectedStack,\n suggestion: 'Run contentrain_init to set up .contentrain/ structure',\n next_steps: ['Run contentrain_init'],\n }, null, 2),\n }],\n }\n }\n\n const config = await readConfig(projectRoot)\n const models = await listModels(projectRoot)\n const context = await readContext(projectRoot)\n const vocabulary = await readVocabulary(projectRoot)\n\n const errors: string[] = []\n if (!config) errors.push('.contentrain/config.json missing')\n\n const result: Record<string, unknown> = {\n initialized: true,\n config: config ? {\n stack: config.stack,\n workflow: config.workflow,\n locales: config.locales,\n domains: config.domains,\n repository: config.repository,\n } : null,\n models,\n context: context ? {\n lastOperation: context.lastOperation,\n stats: context.stats,\n } : null,\n vocabulary_size: vocabulary ? Object.keys(vocabulary.terms).length : 0,\n }\n\n // Branch lifecycle: lazy cleanup + health check (run BEFORE validation summary)\n const hasGitRepo = await pathExists(join(projectRoot, '.git'))\n if (hasGitRepo) {\n try {\n const cleanup = await cleanupMergedBranches(projectRoot)\n const health = await checkBranchHealth(projectRoot)\n result['branches'] = {\n total: health.total,\n merged: health.merged,\n unmerged: health.unmerged,\n cleaned_up: cleanup.deleted,\n }\n if (health.message) {\n result['branch_warning'] = health.message\n }\n if (health.blocked) {\n errors.push(health.message!)\n }\n } catch {\n // Branch health check is best-effort — don't fail status\n }\n }\n\n if (errors.length > 0) {\n result['validation'] = { errors: errors.length, warnings: 0, summary: errors }\n }\n\n const nextSteps: string[] = []\n if (models.length === 0) nextSteps.push('Create models with contentrain_model_save')\n if (errors.length > 0) nextSteps.push(`Fix ${errors.length} validation error(s)`)\n if (nextSteps.length === 0) nextSteps.push('Use contentrain_describe to inspect a model')\n result['next_steps'] = nextSteps\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_describe ───\n server.tool(\n 'contentrain_describe',\n 'Get full schema of a single model (read-only). Do NOT manually create content files — use contentrain_content_save instead.',\n {\n model: z.string().describe('Model ID (e.g. \"blog-post\", \"hero\")'),\n include_sample: z.boolean().optional().default(false).describe('Include one sample entry'),\n locale: z.string().optional().describe('Locale for sample content (default: config default locale)'),\n },\n async ({ model: modelId, include_sample, locale }) => {\n const modelDef = await readModel(projectRoot, modelId)\n if (!modelDef) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${modelId}\" not found` }) }],\n isError: true,\n }\n }\n\n const config = await readConfig(projectRoot)\n const effectiveLocale = locale ?? config?.locales.default ?? 'en'\n const stats = await countEntries(projectRoot, modelDef)\n\n const result: Record<string, unknown> = {\n id: modelDef.id,\n name: modelDef.name,\n kind: modelDef.kind,\n domain: modelDef.domain,\n i18n: modelDef.i18n,\n description: modelDef.description,\n fields: modelDef.fields,\n stats: { total_entries: stats.total, locales: stats.locales },\n }\n\n if (include_sample) {\n const sample = await getSample(projectRoot, modelDef, effectiveLocale)\n if (sample) result['sample'] = sample\n }\n\n // Stack-aware import snippet\n const stack = config?.stack ?? 'other'\n result['import_snippet'] = generateImportSnippet(modelDef, stack, effectiveLocale)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_describe_format ───\n server.tool(\n 'contentrain_describe_format',\n 'Describes the Contentrain content file format for any language/platform. Returns a comprehensive specification of the file structure, JSON formats, markdown conventions, meta files, and locale strategies.',\n {},\n async () => {\n const formatSpec = {\n overview: 'Contentrain stores content as plain JSON and Markdown files in a .contentrain/ directory at the project root. All files are committed to git.',\n directory_structure: {\n description: 'Standard layout of .contentrain/ directory',\n layout: {\n '.contentrain/config.json': 'Project configuration (stack, workflow, locales, domains)',\n '.contentrain/context.json': 'Last operation metadata (written by MCP after every write)',\n '.contentrain/vocabulary.json': 'Shared vocabulary/terms (optional)',\n '.contentrain/models/{model-id}.json': 'Model schema definitions',\n '.contentrain/content/{domain}/{model-id}/': 'Content files per model',\n '.contentrain/meta/{model-id}/': 'Metadata files per model (status, source, timestamps)',\n },\n },\n model_kinds: {\n collection: {\n description: 'Multiple entries stored as an object-map keyed by entry ID',\n storage_format: 'JSON object-map: { \"entry-id-1\": { ...fields }, \"entry-id-2\": { ...fields } }',\n note: 'Keys are sorted alphabetically for canonical output. Entry IDs are alphanumeric (1-40 chars, hyphens/underscores allowed).',\n example: '{ \"abc123\": { \"title\": \"Hello\", \"slug\": \"hello\" }, \"def456\": { \"title\": \"World\", \"slug\": \"world\" } }',\n },\n singleton: {\n description: 'Single entry (e.g. site settings, hero section)',\n storage_format: 'JSON object with field key-value pairs: { \"title\": \"My Site\", \"description\": \"...\" }',\n },\n document: {\n description: 'Markdown files with YAML-like frontmatter',\n storage_format: 'Markdown file: ---\\\\nslug: my-post\\\\ntitle: My Post\\\\n---\\\\n\\\\nMarkdown body content here.',\n frontmatter_rules: [\n 'Delimited by --- on its own line',\n 'Key-value pairs: key: value',\n 'Arrays: key:\\\\n - item1\\\\n - item2',\n 'Inline arrays: key: [item1, item2]',\n 'Values auto-parsed: true/false -> boolean, integers -> number, quoted -> string',\n '\"body\" key is reserved for the markdown content below the frontmatter',\n ],\n },\n dictionary: {\n description: 'Flat key-value string map (e.g. i18n translation files)',\n storage_format: 'JSON object: { \"greeting\": \"Hello\", \"farewell\": \"Goodbye\" }',\n note: 'All values must be strings.',\n },\n },\n meta_files: {\n description: 'Metadata is stored separately from content in .contentrain/meta/{model-id}/',\n collection_meta: 'Object-map: { \"entry-id\": { \"status\": \"draft\", \"source\": \"agent\", \"updated_by\": \"contentrain-mcp\" } }',\n singleton_meta: 'Single object: { \"status\": \"published\", \"source\": \"human\", \"updated_by\": \"user@example.com\" }',\n fields: {\n status: 'Content lifecycle status: \"draft\" | \"in_review\" | \"published\" | \"rejected\" | \"archived\"',\n source: 'Who created the entry: \"agent\" | \"human\" | \"import\"',\n updated_by: 'Author identifier string',\n approved_by: 'Optional: approver identifier',\n version: 'Optional: version string',\n publish_at: 'Optional: ISO 8601 date for scheduled publishing',\n expire_at: 'Optional: ISO 8601 date for scheduled expiry (must be after publish_at)',\n },\n },\n locale_strategies: {\n description: 'How localized content files are organized. Set per-model via locale_strategy field.',\n strategies: {\n file: {\n description: 'Default. Each locale is a separate file in the model directory.',\n json_pattern: '{content-dir}/{locale}.json (e.g. en.json, tr.json)',\n md_pattern: '{content-dir}/{slug}/{locale}.md',\n },\n suffix: {\n description: 'Locale appended to filename.',\n json_pattern: '{content-dir}/{model-id}.{locale}.json',\n md_pattern: '{content-dir}/{slug}.{locale}.md',\n },\n directory: {\n description: 'Locale as subdirectory.',\n json_pattern: '{content-dir}/{locale}/{model-id}.json',\n md_pattern: '{content-dir}/{locale}/{slug}.md',\n },\n none: {\n description: 'No locale in path (single-language or externally managed).',\n json_pattern: '{content-dir}/{model-id}.json',\n md_pattern: '{content-dir}/{slug}.md',\n },\n },\n },\n i18n_disabled: {\n description: 'When a model has i18n:false, locale is ignored in file paths.',\n json_pattern: '{content-dir}/data.json (always \"data.json\", no locale suffix)',\n md_pattern: '{content-dir}/{slug}.md (no locale in path)',\n },\n canonical_serialization: {\n description: 'All JSON files use deterministic serialization for clean git diffs.',\n rules: [\n 'Object keys sorted alphabetically',\n '2-space indentation',\n 'Trailing newline at end of file',\n 'No trailing commas',\n 'UTF-8 encoding',\n ],\n },\n content_path_override: {\n description: 'Models can specify a custom content_path to store content outside .contentrain/content/.',\n example: 'A model with content_path: \"content/blog\" stores files at {project-root}/content/blog/ instead of .contentrain/content/{domain}/{model-id}/',\n note: 'This is useful for frameworks that expect content in specific directories (e.g. Nuxt Content, Astro).',\n },\n reading_content: {\n steps: [\n '1. Read .contentrain/config.json to get locales and workflow settings',\n '2. Read .contentrain/models/{model-id}.json to get the schema and model kind',\n '3. Determine content directory: model.content_path or .contentrain/content/{domain}/{model-id}/',\n '4. Determine file path using locale_strategy and i18n flag',\n '5. Parse JSON (collection/singleton/dictionary) or Markdown frontmatter (document)',\n '6. Optionally read .contentrain/meta/{model-id}/{locale}.json for status/metadata',\n ],\n },\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(formatSpec, null, 2) }],\n }\n },\n )\n}\n\nasync function getSample(\n projectRoot: string,\n model: import('@contentrain/types').ModelDefinition,\n locale: string,\n): Promise<unknown> {\n const { readJson, readDir } = await import('../util/fs.js')\n const cDir = resolveContentDir(projectRoot, model)\n\n if (model.kind === 'collection') {\n const data = await readJson<Record<string, Record<string, unknown>>>(resolveJsonFilePath(cDir, model, locale))\n if (!data) return null\n const firstKey = Object.keys(data)[0]\n return firstKey ? { id: firstKey, ...data[firstKey] } : null\n }\n\n if (model.kind === 'singleton' || model.kind === 'dictionary') {\n return readJson(resolveJsonFilePath(cDir, model, locale))\n }\n\n if (model.kind === 'document') {\n const strategy = resolveLocaleStrategy(model)\n\n if (!model.i18n) {\n const entry = (await readDir(cDir)).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'file') {\n const slug = (await readDir(cDir))[0]\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'suffix') {\n const suffix = `.${locale}.md`\n const entry = (await readDir(cDir)).find(file => file.endsWith(suffix))\n const slug = entry?.slice(0, -suffix.length)\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'directory') {\n const entry = (await readDir(join(cDir, locale))).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n const entry = (await readDir(cDir)).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n return null\n}\n\nfunction buildContentPath(\n model: import('@contentrain/types').ModelDefinition,\n locale: string,\n): string {\n const strategy = model.locale_strategy ?? 'file'\n const baseDir = model.content_path ?? `.contentrain/content/${model.domain}/${model.id}`\n\n if (model.kind === 'document') {\n // When i18n is disabled, documents are flat {slug}.md\n if (!model.i18n) return `${baseDir}/{slug}.md`\n\n // Documents use directories — show the base path pattern\n switch (strategy) {\n case 'suffix': return `${baseDir}/{slug}.${locale}.md`\n case 'directory': return `${baseDir}/${locale}/{slug}.md`\n case 'none': return `${baseDir}/{slug}.md`\n case 'file':\n default: return `${baseDir}/{slug}/${locale}.md`\n }\n }\n\n // JSON-based models (collection, singleton, dictionary)\n // When i18n is disabled, always data.json\n if (!model.i18n) return `${baseDir}/data.json`\n\n switch (strategy) {\n case 'suffix': return `${baseDir}/${model.id}.${locale}.json`\n case 'directory': return `${baseDir}/${locale}/${model.id}.json`\n case 'none': return `${baseDir}/${model.id}.json`\n case 'file':\n default: return `${baseDir}/${locale}.json`\n }\n}\n\nfunction generateImportSnippet(\n model: import('@contentrain/types').ModelDefinition,\n stack: string,\n locale: string,\n): Record<string, string> {\n const path = buildContentPath(model, locale)\n\n const snippets: Record<string, string> = {\n generic: `import data from '${path}'`,\n }\n\n switch (stack) {\n case 'nuxt':\n snippets['nuxt'] = model.kind === 'document'\n ? `const { data } = await useAsyncData(() => queryContent('${model.domain}/${model.id}').locale('${locale}').find())`\n : `const { data } = await useFetch('/api/content/${model.id}?locale=${locale}')`\n break\n case 'next':\n snippets['next'] = `import data from '@/${path}'`\n break\n case 'astro':\n snippets['astro'] = `import data from '~/${path}'`\n break\n }\n\n return snippets\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ContentrainConfig, Vocabulary } from '@contentrain/types'\nimport { z } from 'zod'\nimport { join } from 'node:path'\nimport { readFile, writeFile, appendFile } from 'node:fs/promises'\nimport { simpleGit } from 'simple-git'\nimport { detectStack } from '../util/detect.js'\nimport { contentrainDir, ensureDir, pathExists, writeJson } from '../util/fs.js'\nimport { readConfig, readVocabulary } from '../core/config.js'\nimport { writeModel } from '../core/model-manager.js'\nimport { writeContent, type ContentEntry } from '../core/content-manager.js'\nimport { getTemplate, listTemplates } from '../templates/index.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\n\nexport function registerSetupTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_init ───\n server.tool(\n 'contentrain_init',\n 'Initialize .contentrain/ structure. Changes are auto-committed to git — do NOT manually create .contentrain/ files.',\n {\n stack: z.string().optional().describe('Framework stack (nuxt, next, astro, svelte, react-vite, other). Auto-detected if omitted.'),\n locales: z.array(z.string()).optional().describe('Supported locales. Default: [\"en\"]'),\n domains: z.array(z.string()).optional().describe('Content domains. Default: auto-suggested'),\n },\n async ({ stack, locales, domains }) => {\n const crDir = contentrainDir(projectRoot)\n\n // Already initialized?\n if (await pathExists(join(crDir, 'config.json'))) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Already initialized',\n suggestion: 'Use contentrain_status to see current state',\n }) }],\n isError: true,\n }\n }\n\n // Detect stack\n const detectedStack = stack ?? await detectStack(projectRoot)\n const supportedLocales = locales ?? ['en']\n const suggestedDomains = domains ?? suggestDomains(projectRoot)\n\n // Ensure .git exists\n const hasGit = await pathExists(join(projectRoot, '.git'))\n if (!hasGit) {\n await simpleGit(projectRoot).init()\n }\n\n // Branch health gate\n const initHealth = await checkBranchHealth(projectRoot)\n if (initHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: initHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Create git transaction\n const branch = buildBranchName('new', 'init')\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n const wtCrDir = join(wt, '.contentrain')\n\n // Create directories with .gitkeep so git tracks them\n await Promise.all(['models', 'content', 'meta'].map(async (dir) => {\n await ensureDir(join(wtCrDir, dir))\n await writeFile(join(wtCrDir, dir, '.gitkeep'), '', 'utf-8')\n }))\n\n // Write config\n const config: ContentrainConfig = {\n version: 1,\n stack: detectedStack as ContentrainConfig['stack'],\n workflow: 'auto-merge',\n locales: {\n default: supportedLocales[0] ?? 'en',\n supported: supportedLocales,\n },\n domains: suggestedDomains,\n }\n await writeJson(join(wtCrDir, 'config.json'), config)\n\n // Write empty vocabulary\n const vocabulary: Vocabulary = { version: 1, terms: {} }\n await writeJson(join(wtCrDir, 'vocabulary.json'), vocabulary)\n\n // Write initial context\n await writeJson(join(wtCrDir, 'context.json'), {\n version: '1',\n lastOperation: {\n tool: 'contentrain_init',\n model: '',\n locale: supportedLocales[0] ?? 'en',\n timestamp: new Date().toISOString(),\n source: process.env['CONTENTRAIN_SOURCE'] === 'mcp-studio' ? 'mcp-studio' : 'mcp-local',\n },\n stats: { models: 0, entries: 0, locales: supportedLocales, lastSync: new Date().toISOString() },\n })\n\n // Update .gitignore\n await updateGitignore(wt)\n })\n\n await tx.commit('[contentrain] init: project setup')\n const gitResult = await tx.complete({\n tool: 'contentrain_init',\n model: '',\n locale: supportedLocales[0] ?? 'en',\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Project initialized and committed to git. Do NOT manually edit .contentrain/ files.',\n config_created: '.contentrain/config.json',\n detected_stack: detectedStack,\n detected_locales: supportedLocales,\n suggested_domains: suggestedDomains,\n files_created: [\n '.contentrain/config.json',\n '.contentrain/vocabulary.json',\n '.contentrain/context.json',\n ],\n directories_created: [\n '.contentrain/models/',\n '.contentrain/content/',\n '.contentrain/meta/',\n ],\n gitignore_updated: true,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n next_steps: [\n 'Create models with contentrain_model_save or contentrain_scaffold',\n 'Use contentrain_scan to find hardcoded strings',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Init failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_scaffold ───\n server.tool(\n 'contentrain_scaffold',\n `Template-based project setup. Available templates: ${listTemplates().join(', ')}. Changes are auto-committed to git.`,\n {\n template: z.string().describe('Template ID: blog, landing, docs, ecommerce, saas, i18n, mobile'),\n locales: z.array(z.string()).optional().describe('Override locales'),\n with_sample_content: z.boolean().optional().default(true).describe('Include sample content (default: true)'),\n },\n async ({ template: templateId, locales, with_sample_content }) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Project not initialized. Run contentrain_init first.',\n }) }],\n isError: true,\n }\n }\n\n const tmpl = getTemplate(templateId)\n if (!tmpl) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Unknown template: \"${templateId}\". Available: ${listTemplates().join(', ')}`,\n }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const scaffoldHealth = await checkBranchHealth(projectRoot)\n if (scaffoldHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: scaffoldHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const effectiveLocales = locales ?? config.locales.supported\n const defaultLocale = effectiveLocales[0] ?? 'en'\n\n const branch = buildBranchName('new', `scaffold-${templateId}`, defaultLocale)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n const modelsCreated: Array<{ id: string; kind: string; fields: number }> = []\n let contentCreated = 0\n let vocabAdded = 0\n\n await tx.write(async (wt) => {\n // Write models\n await Promise.all(tmpl.models.map(async (model) => {\n await writeModel(wt, model)\n modelsCreated.push({\n id: model.id,\n kind: model.kind,\n fields: model.fields ? Object.keys(model.fields).length : 0,\n })\n }))\n\n // Write sample content via content pipeline (handles content_path, locale_strategy, meta)\n if (with_sample_content && tmpl.sample_content) {\n contentCreated = await writeSampleContent(wt, tmpl, effectiveLocales, config)\n }\n\n // Merge vocabulary\n if (tmpl.vocabulary) {\n vocabAdded = await mergeVocabulary(wt, tmpl.vocabulary, effectiveLocales)\n }\n\n })\n\n await tx.commit(`[contentrain] scaffold: ${templateId} (${defaultLocale})`)\n const gitResult = await tx.complete({\n tool: 'contentrain_scaffold',\n model: tmpl.models.map(m => m.id).join(', '),\n locale: defaultLocale,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Scaffold applied and committed to git. Do NOT manually edit .contentrain/ files.',\n models_created: modelsCreated,\n content_created: contentCreated,\n vocabulary_terms_added: vocabAdded,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n next_steps: [\n 'Customize models with contentrain_model_save',\n 'Add content with contentrain_content_save',\n 'Run contentrain_submit when ready',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scaffold failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n}\n\nfunction suggestDomains(_projectRoot: string): string[] {\n return ['marketing', 'blog', 'system']\n}\n\nasync function updateGitignore(projectRoot: string): Promise<void> {\n const gitignorePath = join(projectRoot, '.gitignore')\n const cacheEntry = '.contentrain/.cache/'\n\n if (await pathExists(gitignorePath)) {\n const content = await readFile(gitignorePath, 'utf-8')\n if (!content.includes(cacheEntry)) {\n await appendFile(gitignorePath, `\\n# Contentrain cache\\n${cacheEntry}\\n`)\n }\n } else {\n await writeFile(gitignorePath, `# Contentrain cache\\n${cacheEntry}\\n`, 'utf-8')\n }\n}\n\nasync function writeSampleContent(\n worktreePath: string,\n tmpl: import('@contentrain/types').ScaffoldTemplate,\n locales: string[],\n config: import('@contentrain/types').ContentrainConfig,\n): Promise<number> {\n if (!tmpl.sample_content) return 0\n\n let count = 0\n\n for (const model of tmpl.models) {\n const sampleData = tmpl.sample_content[model.id]\n if (!sampleData) continue\n\n for (const [localeOrKey, data] of Object.entries(sampleData)) {\n const entryMap = data as Record<string, Record<string, unknown>>\n let targetLocale: string\n if (localeOrKey === 'data') {\n targetLocale = locales[0] ?? 'en'\n } else if (locales.includes(localeOrKey)) {\n targetLocale = localeOrKey\n } else {\n continue\n }\n\n // Convert template payload to ContentEntry[] based on model kind\n let entries: ContentEntry[]\n\n if (model.kind === 'singleton' || model.kind === 'dictionary') {\n // Singleton/dictionary: the entire locale object IS the data for one entry\n entries = [{ locale: targetLocale, data: entryMap as Record<string, unknown> }]\n } else if (model.kind === 'document') {\n // Document: keys are slugs, values are frontmatter objects\n entries = Object.entries(entryMap).map(([slug, fields]) => ({\n slug,\n locale: targetLocale,\n data: fields,\n }))\n } else {\n // Collection: keys are entry IDs, values are field objects\n entries = Object.entries(entryMap).map(([id, fields]) => ({\n id,\n locale: targetLocale,\n data: fields,\n }))\n }\n\n await writeContent(worktreePath, model, entries, config)\n count += entries.length\n }\n }\n\n return count\n}\n\nasync function mergeVocabulary(\n projectRoot: string,\n newTerms: Record<string, Record<string, string>>,\n _locales: string[],\n): Promise<number> {\n const vocabPath = join(contentrainDir(projectRoot), 'vocabulary.json')\n const existing = await readVocabulary(projectRoot)\n const vocab: Vocabulary = existing ?? { version: 1, terms: {} }\n\n let added = 0\n for (const [key, translations] of Object.entries(newTerms)) {\n if (!(key in vocab.terms)) {\n added++\n }\n vocab.terms[key] = { ...vocab.terms[key], ...translations }\n }\n\n await writeJson(vocabPath, vocab)\n return added\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ModelDefinition } from '@contentrain/types'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\n\nimport { resolveContentDir, resolveJsonFilePath, resolveMdFilePath } from '../core/content-manager.js'\nimport { checkReferences, deleteModel, readModel, writeModel, validateModelDefinition, fieldDefZodSchema } from '../core/model-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\n\n// Shared field definition schema — single source of truth with normalize extract\nconst fieldDefSchema = fieldDefZodSchema\n\nexport function registerModelTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_model_save ───\n server.tool(\n 'contentrain_model_save',\n 'Create or update a model definition. Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n id: z.string().describe('Model ID (kebab-case, e.g. \"blog-post\")'),\n name: z.string().describe('Human-readable name'),\n kind: z.enum(['singleton', 'collection', 'document', 'dictionary']).describe('Model kind'),\n domain: z.string().describe('Content domain (e.g. \"blog\", \"marketing\", \"system\")'),\n i18n: z.boolean().describe('Whether this model supports localization'),\n description: z.string().optional().describe('Model description'),\n fields: fieldDefSchema.optional().describe('Field definitions (not needed for dictionary)'),\n content_path: z.string().optional().describe('Framework-relative path for content files (e.g. \"content/blog\", \"locales\"). When set, content is written here instead of .contentrain/content/'),\n locale_strategy: z.enum(['file', 'suffix', 'directory', 'none']).optional().describe('How locale is encoded in file names. Default: \"file\"'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n // Validate\n const errors = validateModel(input)\n if (errors.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Validation failed', details: errors }) }],\n isError: true,\n }\n }\n\n // Reject invalid locale_strategy + i18n combinations\n if (input.locale_strategy === 'none' && input.i18n !== false) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'locale_strategy \"none\" requires i18n:false. The \"none\" strategy stores a single file without locale encoding, which is incompatible with multi-locale content. Use \"file\", \"suffix\", or \"directory\" for i18n models.',\n }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const existing = await readModel(projectRoot, input.id)\n const action = existing ? 'updated' : 'created'\n\n const model: ModelDefinition = {\n id: input.id,\n name: input.name,\n kind: input.kind,\n domain: input.domain,\n i18n: input.i18n,\n description: input.description,\n fields: input.fields as ModelDefinition['fields'],\n content_path: input.content_path,\n locale_strategy: input.locale_strategy,\n }\n\n const branch = buildBranchName('model', input.id)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n await writeModel(wt, model)\n })\n\n await tx.commit(`[contentrain] ${action}: ${input.id}`)\n const gitResult = await tx.complete({ tool: 'contentrain_model_save', model: input.id })\n\n const defaultLocale = config.locales.default\n // Build accurate content path using path resolvers\n const contentDir = resolveContentDir(projectRoot, model)\n const contentPath = model.content_path ?? `.contentrain/content/${input.domain}/${input.id}`\n let exampleFilePath: string\n if (model.kind === 'document') {\n exampleFilePath = resolveMdFilePath(contentDir, model, defaultLocale, '{slug}')\n } else {\n exampleFilePath = resolveJsonFilePath(contentDir, model, defaultLocale)\n }\n // Make the path relative for display\n const displayPath = exampleFilePath.replace(projectRoot + '/', '').replace(projectRoot, '')\n const importSnippet: Record<string, string> = {\n generic: `import data from '${displayPath}'`,\n }\n if (config.stack === 'nuxt') {\n importSnippet['nuxt'] = model.kind === 'document'\n ? `const { data } = await useAsyncData(() => queryContent('${model.domain}/${model.id}').locale('${defaultLocale}').find())`\n : `const { data } = await useFetch('/api/content/${model.id}?locale=${defaultLocale}')`\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Model saved and committed to git. Do NOT manually edit .contentrain/ files.',\n action,\n model: input.id,\n validation: { valid: true, errors: [] },\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n content_path: contentPath + '/',\n example_file: displayPath,\n import_snippet: importSnippet,\n next_steps: ['Add content with contentrain_content_save'],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model save failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_model_delete ───\n server.tool(\n 'contentrain_model_delete',\n 'Delete a model and its content/meta. Changes are auto-committed to git — do NOT manually edit .contentrain/ files.',\n {\n model: z.string().describe('Model ID to delete'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n async ({ model: modelId }) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n // Check model exists\n const existing = await readModel(projectRoot, modelId)\n if (!existing) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${modelId}\" not found` }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const deleteHealth = await checkBranchHealth(projectRoot)\n if (deleteHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Check references\n const refs = await checkReferences(projectRoot, modelId)\n if (refs.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n deleted: false,\n error: 'REFERENCED_MODEL',\n referenced_by: refs,\n next_steps: ['Remove relation fields from referencing models first'],\n }, null, 2) }],\n }\n }\n\n const branch = buildBranchName('model', modelId)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let filesRemoved: string[] = []\n\n await tx.write(async (wt) => {\n filesRemoved = await deleteModel(wt, modelId)\n })\n\n await tx.commit(`[contentrain] delete: ${modelId}`)\n const gitResult = await tx.complete({ tool: 'contentrain_model_delete', model: modelId })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Model deleted and committed to git. Do NOT manually edit .contentrain/ files.',\n deleted: true,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n files_removed: filesRemoved,\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n}\n\n// validateModel is now validateModelDefinition from model-manager.ts (shared)\nconst validateModel = validateModelDefinition\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\nimport { readModel } from '../core/model-manager.js'\nimport { writeContent, deleteContent, listContent } from '../core/content-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { validateProject } from '../core/validator.js'\n\nexport function registerContentTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_content_save ───\n server.tool(\n 'contentrain_content_save',\n 'Save content entries. Entry format varies by model kind: DICTIONARY — provide \"locale\" and \"data\" (flat key-value, all string values); \"id\" and \"slug\" are ignored; data keys are the identities. COLLECTION — provide \"locale\" and \"data\"; \"id\" is optional (auto-generated if omitted); \"slug\" is ignored. DOCUMENT — provide \"slug\" (required), \"locale\", and \"data\"; use the \"body\" key inside data for markdown content. SINGLETON — provide only \"locale\" and \"data\". Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n model: z.string().describe('Model ID'),\n entries: z.array(z.object({\n id: z.string().optional().describe('Entry ID (collection only, auto-generated if omitted)'),\n slug: z.string().optional().describe('Slug (document only)'),\n locale: z.string().optional().describe('Locale code (defaults to config default)'),\n data: z.record(z.string(), z.unknown()).describe('Content data. For documents, include \"body\" key for markdown content.'),\n publish_at: z.string().optional().describe('ISO 8601 date for scheduled publishing (stored in meta)'),\n expire_at: z.string().optional().describe('ISO 8601 date for scheduled expiry (stored in meta, must be after publish_at)'),\n })).describe('Content entries to save'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Validate locales and scheduling fields before starting transaction\n for (const entry of input.entries) {\n if (entry.locale && !config.locales.supported.includes(entry.locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Locale \"${entry.locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // Validate publish_at / expire_at\n if (entry.publish_at !== undefined) {\n const d = new Date(entry.publish_at)\n if (Number.isNaN(d.getTime())) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Invalid publish_at date: \"${entry.publish_at}\". Must be a valid ISO 8601 date string.`,\n }) }],\n isError: true,\n }\n }\n }\n if (entry.expire_at !== undefined) {\n const d = new Date(entry.expire_at)\n if (Number.isNaN(d.getTime())) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Invalid expire_at date: \"${entry.expire_at}\". Must be a valid ISO 8601 date string.`,\n }) }],\n isError: true,\n }\n }\n }\n if (entry.publish_at !== undefined && entry.expire_at !== undefined) {\n if (new Date(entry.expire_at) <= new Date(entry.publish_at)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `expire_at (\"${entry.expire_at}\") must be after publish_at (\"${entry.publish_at}\").`,\n }) }],\n isError: true,\n }\n }\n }\n\n // Merge scheduling fields into data so meta-manager picks them up\n if (entry.publish_at !== undefined) entry.data['publish_at'] = entry.publish_at\n if (entry.expire_at !== undefined) entry.data['expire_at'] = entry.expire_at\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('content', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let results: Awaited<ReturnType<typeof writeContent>>\n\n let entryIds: string[] = []\n\n await tx.write(async (wt) => {\n results = await writeContent(wt, model, input.entries, config)\n entryIds = results!.map(r => r.id ?? r.slug ?? r.locale).filter(Boolean) as string[]\n })\n\n await tx.commit(`[contentrain] content: ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_content_save',\n model: input.model,\n locale: input.entries[0]?.locale,\n entries: entryIds,\n })\n\n // Run real validation after save — don't fake it\n const validationResult = await validateProject(projectRoot, { model: input.model })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Content saved and committed to git. Do NOT manually edit .contentrain/ files.',\n results: results!,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n validation: {\n valid: validationResult.valid,\n errors: validationResult.issues.filter(i => i.severity === 'error').map(i => i.message),\n },\n context_updated: true,\n next_steps: [\n ...(!validationResult.valid ? ['WARNING: Content has validation errors — run contentrain_validate for details'] : []),\n ...(model.kind === 'collection'\n ? ['Use contentrain_content_list to verify', 'Add more entries or publish']\n : ['Use contentrain_content_list to verify']),\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Content save failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_content_delete ───\n server.tool(\n 'contentrain_content_delete',\n 'Delete content entries. For dictionaries, use \"keys\" to remove specific keys (omit to delete entire locale file). Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n model: z.string().describe('Model ID'),\n id: z.string().optional().describe('Entry ID (collection)'),\n slug: z.string().optional().describe('Slug (document)'),\n locale: z.string().optional().describe('Locale code'),\n keys: z.array(z.string()).optional().describe('Dictionary only: specific keys to remove. Omit to delete entire locale file.'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const deleteHealth = await checkBranchHealth(projectRoot)\n if (deleteHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('content', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let removed: string[] = []\n\n await tx.write(async (wt) => {\n removed = await deleteContent(wt, model, {\n id: input.id,\n slug: input.slug,\n locale: input.locale,\n keys: input.keys,\n })\n })\n\n await tx.commit(`[contentrain] delete content: ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_content_delete',\n model: input.model,\n locale: input.locale,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Content deleted and committed to git. Do NOT manually edit .contentrain/ files.',\n deleted: true,\n files_removed: removed,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_content_list ───\n server.tool(\n 'contentrain_content_list',\n 'List content entries (read-only). Returns data from .contentrain/ — do NOT manually create or modify content files.',\n {\n model: z.string().describe('Model ID'),\n locale: z.string().optional().describe('Locale code (defaults to config default)'),\n filter: z.record(z.string(), z.unknown()).optional().describe('Filter criteria (collection only)'),\n resolve: z.boolean().optional().describe('Resolve relation fields to actual data'),\n limit: z.number().optional().describe('Max entries to return'),\n offset: z.number().optional().describe('Skip N entries'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n try {\n const result = await listContent(projectRoot, model, {\n locale: input.locale,\n filter: input.filter as Record<string, unknown>,\n resolve: input.resolve,\n limit: input.limit,\n offset: input.offset,\n }, config)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `List failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport { simpleGit } from 'simple-git'\nimport { validateProject } from '../core/validator.js'\nimport { readConfig } from '../core/config.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth, cleanupMergedBranches } from '../git/branch-lifecycle.js'\n\nexport function registerWorkflowTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_validate ───\n server.tool(\n 'contentrain_validate',\n 'Validate project content against model schemas. Detects required field violations, type mismatches, broken relations, secret leaks, i18n parity issues, and more. If fix:true, auto-fixes structural issues (canonical sort, orphan meta, missing locale files) — do NOT manually edit .contentrain/ files.',\n {\n model: z.string().optional().describe('Model ID to validate (omit for all models)'),\n fix: z.boolean().optional().describe('Auto-fix structural issues (canonical sort, orphan meta, missing locale files). Default: false'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n try {\n let result: Awaited<ReturnType<typeof validateProject>> | undefined\n\n if (input.fix) {\n // Branch health gate for fix mode (creates a branch)\n const fixHealth = await checkBranchHealth(projectRoot)\n if (fixHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: fixHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before auto-fixing.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Use git transaction for fixes\n const branch = buildBranchName('fix', 'validate')\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n result = await validateProject(wt, { model: input.model, fix: true })\n })\n\n if (result!.fixed > 0) {\n await tx.commit(`[contentrain] validate: auto-fix ${result!.fixed} issue(s)`)\n const gitResult = await tx.complete({\n tool: 'contentrain_validate',\n model: input.model ?? '*',\n })\n\n const nextSteps: string[] = []\n if (result!.summary.errors > 0) nextSteps.push('Fix remaining errors manually')\n if (result!.summary.warnings > 0) nextSteps.push('Review warnings')\n nextSteps.push('Run contentrain_validate again to verify')\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: `Validation complete. ${result!.fixed} issue(s) auto-fixed and committed to git. Do NOT manually edit .contentrain/ files.`,\n ...result!,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } else {\n // Nothing to fix, cleanup the branch\n await tx.cleanup()\n }\n } catch (error) {\n await tx.cleanup()\n throw error\n } finally {\n await tx.cleanup()\n }\n }\n\n // No fix or nothing was fixed — run read-only validation\n if (!result) {\n result = await validateProject(projectRoot, { model: input.model, fix: false })\n }\n\n const nextSteps: string[] = []\n if (result.summary.errors > 0) nextSteps.push('Fix errors in content using contentrain_content_save')\n if (result.summary.warnings > 0) nextSteps.push('Review warnings')\n if (result.valid) nextSteps.push('Run contentrain_submit to push changes')\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'validated',\n message: result.valid\n ? 'All validation checks passed.'\n : `Validation found ${result.summary.errors} error(s) and ${result.summary.warnings} warning(s).`,\n ...result,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Validation failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_submit ───\n server.tool(\n 'contentrain_submit',\n 'Push contentrain/* branches to remote. MCP is push-only — PR creation is handled by the platform. Do NOT manually push or create PRs.',\n {\n branches: z.array(z.string()).optional().describe('Specific branch names to push (omit for all contentrain/* branches)'),\n message: z.string().optional().describe('Optional message for the push operation'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const git = simpleGit(projectRoot)\n const remoteName = process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n\n // Check remote exists\n let hasRemote = false\n try {\n const remotes = await git.getRemotes()\n hasRemote = remotes.some(r => r.name === remoteName)\n } catch {\n hasRemote = false\n }\n\n if (!hasRemote) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `No remote \"${remoteName}\" found. Configure a git remote first.`,\n next_steps: [`git remote add ${remoteName} <url>`],\n }) }],\n isError: true,\n }\n }\n\n try {\n // Determine the base branch for merge-status checks\n const baseBranch = config.repository?.default_branch\n ?? process.env['CONTENTRAIN_BRANCH']\n ?? ((await git.raw(['branch', '--show-current'])).trim() || 'main')\n\n // Get contentrain branches\n const branchSummary = await git.branchLocal()\n\n // Get branches NOT yet merged into base (only these need pushing)\n let unmergedRaw = ''\n try {\n unmergedRaw = await git.raw(['branch', '--no-merged', baseBranch])\n } catch {\n // If base branch doesn't exist yet, treat all as unmerged\n unmergedRaw = branchSummary.all.join('\\n')\n }\n const unmergedSet = new Set(\n unmergedRaw.split('\\n').map(b => b.replace(/^\\*?\\s+/, '').trim()).filter(Boolean),\n )\n\n let branchesToPush: string[]\n\n if (input.branches && input.branches.length > 0) {\n branchesToPush = input.branches.filter(b => branchSummary.all.includes(b))\n const missing = input.branches.filter(b => !branchSummary.all.includes(b))\n if (missing.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Branches not found: ${missing.join(', ')}`,\n }) }],\n isError: true,\n }\n }\n // Filter out already-merged branches from explicit list\n branchesToPush = branchesToPush.filter(b => unmergedSet.has(b))\n } else {\n branchesToPush = branchSummary.all.filter(b => b.startsWith('contentrain/') && unmergedSet.has(b))\n }\n\n if (branchesToPush.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'No unmerged contentrain/* branches found to push.',\n next_steps: ['Make changes first with contentrain_content_save or contentrain_model_save'],\n }) }],\n isError: true,\n }\n }\n\n // Push each branch\n const pushed: string[] = []\n const errors: Array<{ branch: string; error: string }> = []\n\n for (const branch of branchesToPush) {\n try {\n await git.push(remoteName, branch)\n pushed.push(branch)\n } catch (error) {\n errors.push({\n branch,\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n\n // Lazy cleanup: delete merged branches after push\n const cleanup = await cleanupMergedBranches(projectRoot)\n\n const nextSteps: string[] = []\n if (pushed.length > 0) nextSteps.push('Create PRs on your git platform for review')\n if (errors.length > 0) nextSteps.push('Fix push errors and retry')\n if (cleanup.remaining >= 50) nextSteps.push(`Warning: ${cleanup.remaining} active contentrain branches. Consider reviewing old branches.`)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: `Pushed ${pushed.length} branch(es) to ${remoteName}.`,\n pushed,\n errors: errors.length > 0 ? errors : undefined,\n remote: remoteName,\n cleanup: cleanup.deleted > 0 ? { deleted: cleanup.deleted, remaining: cleanup.remaining } : undefined,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { FieldDef } from '@contentrain/types'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\nimport { buildGraph } from '../core/graph-builder.js'\nimport { scanCandidates, scanSummary } from '../core/scanner.js'\nimport { applyExtract, applyReuse } from '../core/apply-manager.js'\nimport { fieldDefZodSchema } from '../core/model-manager.js'\n\nexport function registerNormalizeTools(server: McpServer, projectRoot: string): void {\n // ─── contentrain_scan ───\n server.tool(\n 'contentrain_scan',\n 'Scan project source code for content strings. Three modes: \"graph\" builds import/component graph for project intelligence, \"candidates\" extracts string literals with pre-filtering and pagination, \"summary\" provides quick overview stats. Read-only — no changes to disk or git. MCP finds strings deterministically; the agent decides what is content. Recommended workflow: start with \"summary\" or \"graph\" for orientation, then paginate through \"candidates\" to evaluate strings.',\n {\n mode: z.enum(['graph', 'candidates', 'summary']).optional().describe('Scan mode. Default: candidates'),\n paths: z.array(z.string()).optional().describe('Directories to scan (relative to project root). Default: auto-detect'),\n include: z.array(z.string()).optional().describe('File extensions to include. Default: .tsx, .jsx, .vue, .ts, .js, .mjs, .astro, .svelte'),\n exclude: z.array(z.string()).optional().describe('Additional directory names to exclude'),\n limit: z.number().optional().describe('Candidates mode: batch size. Default: 50'),\n offset: z.number().optional().describe('Candidates mode: pagination offset. Default: 0'),\n min_length: z.number().optional().describe('Candidates mode: minimum string length. Default: 2'),\n max_length: z.number().optional().describe('Candidates mode: maximum string length. Default: 500'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const mode = input.mode ?? 'candidates'\n\n try {\n switch (mode) {\n case 'graph': {\n const graph = await buildGraph(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'graph',\n ...graph,\n next_steps: [\n 'Use mode:candidates to scan specific files/directories for strings',\n 'Focus on pages and components with high string counts first',\n ],\n }, null, 2) }],\n }\n }\n\n case 'candidates': {\n const result = await scanCandidates(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n limit: input.limit,\n offset: input.offset,\n min_length: input.min_length,\n max_length: input.max_length,\n })\n\n const nextSteps: string[] = []\n if (result.stats.has_more) {\n nextSteps.push(`Use offset:${(input.offset ?? 0) + (input.limit ?? 50)} for next batch`)\n }\n nextSteps.push('Evaluate each candidate: is it user-facing content? Which domain/model?')\n if (result.duplicates.length > 0) {\n nextSteps.push('Consider deduplicating repeated strings into shared models')\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'candidates',\n ...result,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n }\n\n case 'summary': {\n const result = await scanSummary(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n min_length: input.min_length,\n max_length: input.max_length,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'summary',\n ...result,\n next_steps: [\n 'Use mode:graph for project structure analysis',\n 'Use mode:candidates to scan directories with most candidates',\n ],\n }, null, 2) }],\n }\n }\n\n default:\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown mode: ${mode}` }) }],\n isError: true,\n }\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scan failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_apply ───\n server.tool(\n 'contentrain_apply',\n 'Apply normalize operations. Two modes: \"extract\" writes agent-approved strings to Contentrain content files (source untouched), \"reuse\" patches source files with agent-provided replacement expressions. DRY RUN (default, dry_run:true): validates inputs, resolves conflicts, and returns a full preview — NO changes to disk or git. EXECUTE (dry_run:false): writes files to disk, commits to a branch, and requires branch health check to pass. Recommended workflow: always run dry_run first, review the preview, then call again with dry_run:false to execute. Normalize operations always use review workflow (never auto-merge).',\n {\n mode: z.enum(['extract', 'reuse']).describe('Apply mode: extract (content creation) or reuse (source patching)'),\n dry_run: z.boolean().optional().default(true).describe('Defaults to preview mode (dry_run:true). Set dry_run:false to execute after reviewing the preview.'),\n\n // Extract mode fields\n extractions: z.array(z.object({\n model: z.string().describe('Model ID (e.g. \"ui-texts\", \"hero-section\")'),\n kind: z.enum(['singleton', 'collection', 'dictionary', 'document']).describe('Model kind'),\n domain: z.string().describe('Content domain (e.g. \"marketing\", \"app\")'),\n i18n: z.boolean().optional().describe('Enable i18n. Default: true'),\n fields: fieldDefZodSchema.optional().describe('Field definitions — shared schema with model_save for full parity'),\n entries: z.array(z.object({\n locale: z.string().optional().describe('Locale. Default: project default'),\n slug: z.string().optional().describe('Document slug'),\n data: z.record(z.any()).describe('Content data'),\n source: z.object({\n file: z.string().describe('Source file path'),\n line: z.number().describe('Source line number'),\n value: z.string().describe('Original string value'),\n }).optional().describe('Source tracking for traceability'),\n sources: z.array(z.object({\n file: z.string().describe('Source file path'),\n line: z.number().describe('Source line number'),\n key: z.string().describe('Dictionary key this source maps to'),\n value: z.string().describe('Original string value'),\n })).optional().describe('Per-key source tracking for dictionary models'),\n })).describe('Content entries to create'),\n })).optional().describe('Extract mode: content extractions'),\n\n // Reuse mode fields\n scope: z.object({\n model: z.string().optional().describe('Target model ID'),\n domain: z.string().optional().describe('Target domain'),\n }).optional().describe('Reuse mode: scope (model or domain required)'),\n patches: z.array(z.object({\n file: z.string().describe('Relative file path to patch'),\n line: z.number().describe('Line number hint (±10 line search)'),\n old_value: z.string().describe('Original string to replace'),\n new_expression: z.string().describe('Replacement expression (agent-determined)'),\n import_statement: z.string().optional().describe('Import to add if needed'),\n })).optional().describe('Reuse mode: patches to apply (max 100)'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n try {\n switch (input.mode) {\n case 'extract': {\n if (!input.extractions || input.extractions.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Extract mode requires extractions array' }) }],\n isError: true,\n }\n }\n\n const result = await applyExtract(projectRoot, {\n extractions: input.extractions.map(e => ({\n ...e,\n fields: e.fields as Record<string, FieldDef> | undefined,\n })),\n dry_run: input.dry_run,\n })\n\n // Check for branch-blocked response\n if (result.error !== undefined) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'extract',\n ...result,\n }, null, 2) }],\n isError: true,\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'extract',\n ...result,\n }, null, 2) }],\n }\n }\n\n case 'reuse': {\n if (!input.scope) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Reuse mode requires scope (model or domain)' }) }],\n isError: true,\n }\n }\n if (!input.patches || input.patches.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Reuse mode requires patches array' }) }],\n isError: true,\n }\n }\n\n const result = await applyReuse(projectRoot, {\n scope: input.scope,\n patches: input.patches,\n dry_run: input.dry_run,\n })\n\n // Check for branch-blocked response\n if (result.error !== undefined) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'reuse',\n ...result,\n }, null, 2) }],\n isError: true,\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'reuse',\n ...result,\n }, null, 2) }],\n }\n }\n\n default:\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown mode: ${input.mode}` }) }],\n isError: true,\n }\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Apply failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { EntryMeta } from '@contentrain/types'\nimport { z } from 'zod'\nimport { readConfig } from '../core/config.js'\nimport { readModel } from '../core/model-manager.js'\nimport { resolveContentDir, resolveJsonFilePath, deleteContent } from '../core/content-manager.js'\nimport { readMeta, writeMeta } from '../core/meta-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { readJson, writeJson } from '../util/fs.js'\n\nexport function registerBulkTools(server: McpServer, projectRoot: string): void {\n server.tool(\n 'contentrain_bulk',\n 'Batch operations on content entries. All operations are auto-committed to git.',\n {\n operation: z.enum(['copy_locale', 'update_status', 'delete_entries']),\n model: z.string().describe('Model ID'),\n source_locale: z.string().optional().describe('Source locale for copy_locale operation'),\n target_locale: z.string().optional().describe('Target locale for copy_locale operation'),\n entry_ids: z.array(z.string()).optional().describe('Entry IDs for update_status or delete_entries'),\n status: z.enum(['draft', 'in_review', 'published', 'rejected', 'archived']).optional().describe('New status for update_status'),\n confirm: z.boolean().optional().describe('Must be true for delete_entries'),\n },\n async (input) => {\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n switch (input.operation) {\n case 'copy_locale': {\n if (!input.source_locale || !input.target_locale) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'copy_locale requires source_locale and target_locale' }) }],\n isError: true,\n }\n }\n if (!config.locales.supported.includes(input.source_locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Source locale \"${input.source_locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n if (!config.locales.supported.includes(input.target_locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Target locale \"${input.target_locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // Guard: copy_locale requires i18n model — non-i18n models use data.json for all locales\n if (!model.i18n) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model \"${input.model}\" has i18n disabled. copy_locale only works with i18n-enabled models. Enable i18n first with contentrain_model_save.`,\n }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection' && model.kind !== 'singleton' && model.kind !== 'dictionary') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'copy_locale is only supported for collection, singleton, and dictionary models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model, input.target_locale)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let copiedCount = 0\n\n await tx.write(async (wt) => {\n const cDir = resolveContentDir(wt, model)\n const sourceFile = resolveJsonFilePath(cDir, model, input.source_locale!)\n const targetFile = resolveJsonFilePath(cDir, model, input.target_locale!)\n\n const sourceData = await readJson<Record<string, unknown>>(sourceFile)\n if (!sourceData) {\n throw new Error(`No content found for locale \"${input.source_locale}\" in model \"${input.model}\"`)\n }\n\n await writeJson(targetFile, sourceData)\n\n if (model.kind === 'collection') {\n const entries = sourceData as Record<string, Record<string, unknown>>\n const entryIds = Object.keys(entries)\n const metaOps = entryIds.map(entryId =>\n writeMeta(wt, model, { locale: input.target_locale!, entryId }, {\n status: 'draft',\n source: 'agent',\n updated_by: 'contentrain-mcp',\n }),\n )\n await Promise.all(metaOps)\n copiedCount = entryIds.length\n } else {\n await writeMeta(wt, model, { locale: input.target_locale! }, {\n status: 'draft',\n source: 'agent',\n updated_by: 'contentrain-mcp',\n })\n copiedCount = 1\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: copy ${input.source_locale} → ${input.target_locale} for ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_bulk',\n model: input.model,\n locale: input.target_locale!,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'copy_locale',\n message: `Copied ${copiedCount} entries from ${input.source_locale} to ${input.target_locale}.`,\n copied: copiedCount,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `copy_locale failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'update_status': {\n if (!input.entry_ids || input.entry_ids.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status requires entry_ids' }) }],\n isError: true,\n }\n }\n if (!input.status) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status requires status' }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status with entry_ids is only supported for collection models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let updatedCount = 0\n const notFound: string[] = []\n\n await tx.write(async (wt) => {\n for (const locale of config.locales.supported) {\n const metaData = await readMeta(wt, model, { locale }) as Record<string, EntryMeta> | null\n\n const updateOps: Array<Promise<void>> = []\n for (const entryId of input.entry_ids!) {\n const existing = metaData?.[entryId]\n if (existing) {\n updateOps.push(writeMeta(wt, model, { locale, entryId }, {\n ...existing,\n status: input.status!,\n updated_by: 'contentrain-mcp',\n }))\n updatedCount++\n } else if (locale === config.locales.default) {\n notFound.push(entryId)\n }\n }\n await Promise.all(updateOps)\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: update status → ${input.status} for ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'update_status',\n message: `Updated ${updatedCount} meta entries to status \"${input.status}\".`,\n updated: updatedCount,\n not_found: notFound.length > 0 ? notFound : undefined,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `update_status failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'delete_entries': {\n if (!input.entry_ids || input.entry_ids.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries requires entry_ids' }) }],\n isError: true,\n }\n }\n if (input.confirm !== true) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries requires confirm:true' }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries with entry_ids is only supported for collection models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n const allRemoved: string[] = []\n\n await tx.write(async (wt) => {\n for (const entryId of input.entry_ids!) {\n const removed = await deleteContent(wt, model, { id: entryId })\n allRemoved.push(...removed)\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: delete ${input.entry_ids.length} entries from ${input.model}`)\n const gitResult = await tx.complete({\n tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'delete_entries',\n message: `Deleted ${input.entry_ids.length} entries.`,\n deleted: input.entry_ids.length,\n files_removed: allRemoved,\n git: { branch, action: gitResult.action, commit: gitResult.commit },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `delete_entries failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n default: {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown operation: ${input.operation}` }) }],\n isError: true,\n }\n }\n }\n },\n )\n}\n","","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { registerContextTools } from './tools/context.js'\nimport { registerSetupTools } from './tools/setup.js'\nimport { registerModelTools } from './tools/model.js'\nimport { registerContentTools } from './tools/content.js'\nimport { registerWorkflowTools } from './tools/workflow.js'\nimport { registerNormalizeTools } from './tools/normalize.js'\nimport { registerBulkTools } from './tools/bulk.js'\nimport packageJson from '../package.json' with { type: 'json' }\n\nexport function createServer(projectRoot: string): McpServer {\n const server = new McpServer({\n name: 'contentrain-mcp',\n version: packageJson.version,\n })\n\n registerContextTools(server, projectRoot)\n registerSetupTools(server, projectRoot)\n registerModelTools(server, projectRoot)\n registerContentTools(server, projectRoot)\n registerWorkflowTools(server, projectRoot)\n registerNormalizeTools(server, projectRoot)\n registerBulkTools(server, projectRoot)\n\n return server\n}\n"],"mappings":"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"}
package/dist/server.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import "./model-manager-kl78AAMg.mjs";
2
2
  import "./context-CUFulSi2.mjs";
3
3
  import "./branch-lifecycle-DB6sgeJi.mjs";
4
- import { t as createServer } from "./server-2cPZTC4X.mjs";
5
- import "./transaction-BhSvWfSV.mjs";
4
+ import { t as createServer } from "./server-QgivXkem.mjs";
5
+ import "./transaction-_AiXqGuD.mjs";
6
6
  import "./validator-CyckZAVr.mjs";
7
7
  import "./graph-builder-fPrDDwnu.mjs";
8
8
  import "./scanner-BdTiv8sw.mjs";
9
9
  import "./tsx-parser-CYVyBWjn.mjs";
10
- import "./apply-manager-BzpUJEbA.mjs";
10
+ import "./apply-manager-BDtHM8z-.mjs";
11
11
  export { createServer };
@@ -72,31 +72,46 @@ async function createTransaction(projectRoot, branchName, options) {
72
72
  if (hasRemote) try {
73
73
  await git.fetch(remoteName, baseBranch);
74
74
  } catch {}
75
- const status = await git.status();
76
- if (status.modified.length > 0 || status.not_added.length > 0 || status.created.length > 0) throw new Error("Working tree has uncommitted changes. Commit or stash them before completing the transaction.");
77
- await git.checkout(baseBranch);
75
+ const needsStash = (await git.status()).files.length > 0;
76
+ if (needsStash) await git.stash([
77
+ "push",
78
+ "--include-untracked",
79
+ "-m",
80
+ "contentrain-auto-stash"
81
+ ]);
82
+ let stashPopFailed = false;
78
83
  try {
79
- await git.merge([branch, "--no-edit"]);
80
- } catch {
84
+ await git.checkout(baseBranch);
81
85
  try {
82
- await git.merge(["--abort"]);
86
+ await git.merge([branch, "--no-edit"]);
87
+ } catch {
88
+ try {
89
+ await git.merge(["--abort"]);
90
+ } catch {}
91
+ throw new Error(`Merge conflict when merging branch "${branch}" into "${baseBranch}". The branch still exists with your changes intact. Resolve the conflict manually, or delete the branch and retry.`);
92
+ }
93
+ if (hasRemote) try {
94
+ await git.push(remoteName, baseBranch);
83
95
  } catch {}
84
- throw new Error(`Merge conflict when merging branch "${branch}" into "${baseBranch}". The branch still exists with your changes intact. Resolve the conflict manually, or delete the branch and retry.`);
96
+ if (contextUpdate) try {
97
+ await writeContext(projectRoot, contextUpdate);
98
+ await git.add(".contentrain/context.json");
99
+ await git.commit("[contentrain] update context", {
100
+ "--allow-empty": null,
101
+ "--author": `${authorName} <${authorEmail}>`
102
+ });
103
+ } catch {}
104
+ } finally {
105
+ if (needsStash) try {
106
+ await git.stash(["pop"]);
107
+ } catch {
108
+ stashPopFailed = true;
109
+ }
85
110
  }
86
- if (hasRemote) try {
87
- await git.push(remoteName, baseBranch);
88
- } catch {}
89
- if (contextUpdate) try {
90
- await writeContext(projectRoot, contextUpdate);
91
- await git.add(".contentrain/context.json");
92
- await git.commit("[contentrain] update context", {
93
- "--allow-empty": null,
94
- "--author": `${authorName} <${authorEmail}>`
95
- });
96
- } catch {}
97
111
  return {
98
112
  action: "auto-merged",
99
- commit: commitHash
113
+ commit: commitHash,
114
+ ...stashPopFailed ? { warning: "Your uncommitted changes were stashed but could not be restored automatically. Run `git stash pop` to recover them." } : {}
100
115
  };
101
116
  },
102
117
  async cleanup() {
@@ -125,4 +140,4 @@ function buildBranchName(scope, target, locale) {
125
140
  //#endregion
126
141
  export { createTransaction as n, buildBranchName as t };
127
142
 
128
- //# sourceMappingURL=transaction-BhSvWfSV.mjs.map
143
+ //# sourceMappingURL=transaction-_AiXqGuD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-_AiXqGuD.mjs","names":[],"sources":["../src/git/transaction.ts"],"sourcesContent":["import { simpleGit } from 'simple-git'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { randomUUID } from 'node:crypto'\nimport { readConfig } from '../core/config.js'\nimport { writeContext } from '../core/context.js'\nimport { branchTimestamp } from '../util/id.js'\n\nexport interface ContextUpdate {\n tool: string\n model: string\n locale?: string\n entries?: string[]\n}\n\nexport interface GitTransaction {\n worktree: string\n branch: string\n write(callback: (worktreePath: string) => Promise<void>): Promise<void>\n commit(message: string): Promise<string>\n complete(contextUpdate?: ContextUpdate): Promise<{ action: 'auto-merged' | 'pending-review'; commit: string; warning?: string }>\n cleanup(): Promise<void>\n}\n\nexport async function createTransaction(\n projectRoot: string,\n branchName: string,\n options?: { workflowOverride?: 'review' | 'auto-merge' },\n): Promise<GitTransaction> {\n const git = simpleGit(projectRoot)\n const config = await readConfig(projectRoot)\n const workflow = options?.workflowOverride ?? config?.workflow ?? 'auto-merge'\n\n const remoteName = process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n\n // Smart base branch detection: env → config → current branch → 'main'\n let baseBranch = process.env['CONTENTRAIN_BRANCH'] ?? config?.repository?.default_branch ?? ''\n if (!baseBranch) {\n try {\n baseBranch = (await git.raw(['branch', '--show-current'])).trim()\n } catch {\n // fallback below\n }\n }\n if (!baseBranch) {\n baseBranch = 'main'\n }\n\n // Detect remote\n let hasRemote = false\n try {\n const remotes = await git.getRemotes()\n hasRemote = remotes.some(r => r.name === remoteName)\n } catch {\n hasRemote = false\n }\n\n // Fetch latest if remote exists\n if (hasRemote) {\n try {\n await git.fetch(remoteName, baseBranch)\n } catch {\n // fetch may fail (no network), continue with local state\n }\n }\n\n const worktreePath = join(tmpdir(), `cr-${randomUUID()}`)\n const branch = branchName\n\n // Create worktree with new branch from base\n const baseRef = hasRemote ? `${remoteName}/${baseBranch}` : baseBranch\n await git.raw(['worktree', 'add', worktreePath, '-b', branch, baseRef])\n\n const wtGit = simpleGit(worktreePath)\n\n // Configure author\n const authorName = process.env['CONTENTRAIN_AUTHOR_NAME'] ?? 'Contentrain'\n const authorEmail = process.env['CONTENTRAIN_AUTHOR_EMAIL'] ?? 'mcp@contentrain.io'\n await wtGit.addConfig('user.name', authorName)\n await wtGit.addConfig('user.email', authorEmail)\n\n let commitHash = ''\n\n return {\n worktree: worktreePath,\n branch,\n\n async write(callback) {\n await callback(worktreePath)\n },\n\n async commit(message) {\n await wtGit.add('.')\n // Exclude context.json from branch commits — it contains timestamps\n // that cause merge conflicts on sequential operations. Context is\n // updated separately on the base branch after merge.\n try {\n await wtGit.raw(['reset', 'HEAD', '--', '.contentrain/context.json'])\n } catch {\n // context.json may not be staged — that's fine\n }\n const result = await wtGit.commit(message, { '--allow-empty': null })\n commitHash = result.commit || ''\n return commitHash\n },\n\n async complete(contextUpdate?: ContextUpdate) {\n if (workflow === 'review') {\n if (hasRemote) {\n await git.push(remoteName, branch)\n }\n return { action: 'pending-review', commit: commitHash }\n }\n\n // auto-merge: merge branch into base\n if (hasRemote) {\n try {\n await git.fetch(remoteName, baseBranch)\n } catch {\n // continue with local state\n }\n }\n\n // Stash dirty working tree so checkout + merge can proceed.\n // MCP writes to .contentrain/ while the developer works in src/ —\n // stash pop conflicts are extremely unlikely.\n const status = await git.status()\n const needsStash = status.files.length > 0\n if (needsStash) {\n await git.stash(['push', '--include-untracked', '-m', 'contentrain-auto-stash'])\n }\n\n let stashPopFailed = false\n try {\n await git.checkout(baseBranch)\n\n try {\n await git.merge([branch, '--no-edit'])\n } catch {\n // Merge conflict — abort and let the user resolve manually.\n try {\n await git.merge(['--abort'])\n } catch {\n // abort may fail if not in merge state\n }\n throw new Error(\n `Merge conflict when merging branch \"${branch}\" into \"${baseBranch}\". `\n + `The branch still exists with your changes intact. `\n + `Resolve the conflict manually, or delete the branch and retry.`,\n )\n }\n\n if (hasRemote) {\n try {\n await git.push(remoteName, baseBranch)\n } catch {\n // push may fail, branch is merged locally\n }\n }\n\n // Update context.json on base branch after successful merge\n if (contextUpdate) {\n try {\n await writeContext(projectRoot, contextUpdate)\n await git.add('.contentrain/context.json')\n await git.commit('[contentrain] update context', { '--allow-empty': null, '--author': `${authorName} <${authorEmail}>` })\n } catch {\n // Context update is best-effort — don't fail the transaction\n }\n }\n } finally {\n if (needsStash) {\n try {\n await git.stash(['pop'])\n } catch {\n stashPopFailed = true\n }\n }\n }\n\n return {\n action: 'auto-merged' as const,\n commit: commitHash,\n ...(stashPopFailed ? { warning: 'Your uncommitted changes were stashed but could not be restored automatically. Run `git stash pop` to recover them.' } : {}),\n }\n },\n\n async cleanup() {\n try {\n await git.raw(['worktree', 'remove', worktreePath, '--force'])\n } catch {\n // worktree may already be cleaned up\n }\n },\n }\n}\n\nexport function buildBranchName(scope: string, target: string, locale?: string): string {\n const ts = branchTimestamp()\n const parts = ['contentrain', scope, target]\n if (locale) parts.push(locale)\n parts.push(ts)\n return parts.join('/')\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contentrain/mcp",
3
- "version": "1.0.5",
4
- "mcpName": "io.github.contentrain/contentrain",
3
+ "version": "1.0.7",
4
+ "mcpName": "io.github.Contentrain/contentrain",
5
5
  "license": "MIT",
6
6
  "description": "Local-first MCP server for AI-generated content governance — 13 deterministic tools for any platform",
7
7
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"file":"transaction-BhSvWfSV.mjs","names":[],"sources":["../src/git/transaction.ts"],"sourcesContent":["import { simpleGit } from 'simple-git'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { randomUUID } from 'node:crypto'\nimport { readConfig } from '../core/config.js'\nimport { writeContext } from '../core/context.js'\nimport { branchTimestamp } from '../util/id.js'\n\nexport interface ContextUpdate {\n tool: string\n model: string\n locale?: string\n entries?: string[]\n}\n\nexport interface GitTransaction {\n worktree: string\n branch: string\n write(callback: (worktreePath: string) => Promise<void>): Promise<void>\n commit(message: string): Promise<string>\n complete(contextUpdate?: ContextUpdate): Promise<{ action: 'auto-merged' | 'pending-review'; commit: string }>\n cleanup(): Promise<void>\n}\n\nexport async function createTransaction(\n projectRoot: string,\n branchName: string,\n options?: { workflowOverride?: 'review' | 'auto-merge' },\n): Promise<GitTransaction> {\n const git = simpleGit(projectRoot)\n const config = await readConfig(projectRoot)\n const workflow = options?.workflowOverride ?? config?.workflow ?? 'auto-merge'\n\n const remoteName = process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n\n // Smart base branch detection: env → config → current branch → 'main'\n let baseBranch = process.env['CONTENTRAIN_BRANCH'] ?? config?.repository?.default_branch ?? ''\n if (!baseBranch) {\n try {\n baseBranch = (await git.raw(['branch', '--show-current'])).trim()\n } catch {\n // fallback below\n }\n }\n if (!baseBranch) {\n baseBranch = 'main'\n }\n\n // Detect remote\n let hasRemote = false\n try {\n const remotes = await git.getRemotes()\n hasRemote = remotes.some(r => r.name === remoteName)\n } catch {\n hasRemote = false\n }\n\n // Fetch latest if remote exists\n if (hasRemote) {\n try {\n await git.fetch(remoteName, baseBranch)\n } catch {\n // fetch may fail (no network), continue with local state\n }\n }\n\n const worktreePath = join(tmpdir(), `cr-${randomUUID()}`)\n const branch = branchName\n\n // Create worktree with new branch from base\n const baseRef = hasRemote ? `${remoteName}/${baseBranch}` : baseBranch\n await git.raw(['worktree', 'add', worktreePath, '-b', branch, baseRef])\n\n const wtGit = simpleGit(worktreePath)\n\n // Configure author\n const authorName = process.env['CONTENTRAIN_AUTHOR_NAME'] ?? 'Contentrain'\n const authorEmail = process.env['CONTENTRAIN_AUTHOR_EMAIL'] ?? 'mcp@contentrain.io'\n await wtGit.addConfig('user.name', authorName)\n await wtGit.addConfig('user.email', authorEmail)\n\n let commitHash = ''\n\n return {\n worktree: worktreePath,\n branch,\n\n async write(callback) {\n await callback(worktreePath)\n },\n\n async commit(message) {\n await wtGit.add('.')\n // Exclude context.json from branch commits — it contains timestamps\n // that cause merge conflicts on sequential operations. Context is\n // updated separately on the base branch after merge.\n try {\n await wtGit.raw(['reset', 'HEAD', '--', '.contentrain/context.json'])\n } catch {\n // context.json may not be staged — that's fine\n }\n const result = await wtGit.commit(message, { '--allow-empty': null })\n commitHash = result.commit || ''\n return commitHash\n },\n\n async complete(contextUpdate?: ContextUpdate) {\n if (workflow === 'review') {\n if (hasRemote) {\n await git.push(remoteName, branch)\n }\n return { action: 'pending-review', commit: commitHash }\n }\n\n // auto-merge: merge branch into base\n if (hasRemote) {\n try {\n await git.fetch(remoteName, baseBranch)\n } catch {\n // continue with local state\n }\n }\n\n // Ensure working tree is clean before checkout\n const status = await git.status()\n if (status.modified.length > 0 || status.not_added.length > 0 || status.created.length > 0) {\n throw new Error('Working tree has uncommitted changes. Commit or stash them before completing the transaction.')\n }\n\n await git.checkout(baseBranch)\n\n try {\n await git.merge([branch, '--no-edit'])\n } catch {\n // Merge conflict — abort and let the user resolve manually.\n try {\n await git.merge(['--abort'])\n } catch {\n // abort may fail if not in merge state\n }\n throw new Error(\n `Merge conflict when merging branch \"${branch}\" into \"${baseBranch}\". `\n + `The branch still exists with your changes intact. `\n + `Resolve the conflict manually, or delete the branch and retry.`,\n )\n }\n\n if (hasRemote) {\n try {\n await git.push(remoteName, baseBranch)\n } catch {\n // push may fail, branch is merged locally\n }\n }\n\n // Update context.json on base branch after successful merge\n if (contextUpdate) {\n try {\n await writeContext(projectRoot, contextUpdate)\n await git.add('.contentrain/context.json')\n await git.commit('[contentrain] update context', { '--allow-empty': null, '--author': `${authorName} <${authorEmail}>` })\n } catch {\n // Context update is best-effort — don't fail the transaction\n }\n }\n\n return { action: 'auto-merged', commit: commitHash }\n },\n\n async cleanup() {\n try {\n await git.raw(['worktree', 'remove', worktreePath, '--force'])\n } catch {\n // worktree may already be cleaned up\n }\n },\n }\n}\n\nexport function buildBranchName(scope: string, target: string, locale?: string): string {\n const ts = branchTimestamp()\n const parts = ['contentrain', scope, target]\n if (locale) parts.push(locale)\n parts.push(ts)\n return parts.join('/')\n}\n"],"mappings":"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"}