@contentrain/mcp 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +29 -20
  2. package/dist/{apply-manager-Cf5OWOJ7.mjs → apply-manager-BkPMfpsN.mjs} +8 -3
  3. package/dist/apply-manager-BkPMfpsN.mjs.map +1 -0
  4. package/dist/{astro-parser-RofKmKQi.mjs → astro-parser-BJ-54JGe.mjs} +3 -3
  5. package/dist/{astro-parser-RofKmKQi.mjs.map → astro-parser-BJ-54JGe.mjs.map} +1 -1
  6. package/dist/core/apply-manager.d.mts.map +1 -1
  7. package/dist/core/apply-manager.mjs +2 -2
  8. package/dist/core/scanner.d.mts +3 -9
  9. package/dist/core/scanner.d.mts.map +1 -1
  10. package/dist/core/scanner.mjs +3 -3
  11. package/dist/git/transaction.d.mts +6 -1
  12. package/dist/git/transaction.d.mts.map +1 -1
  13. package/dist/git/transaction.mjs +2 -2
  14. package/dist/index.mjs +5 -5
  15. package/dist/{scanner-DPrbU-oK.mjs → scanner-CQOxARUU.mjs} +275 -308
  16. package/dist/scanner-CQOxARUU.mjs.map +1 -0
  17. package/dist/{server-CjK63H6X.mjs → server-DfWRpzdP.mjs} +172 -20
  18. package/dist/server-DfWRpzdP.mjs.map +1 -0
  19. package/dist/server.mjs +5 -5
  20. package/dist/{svelte-parser-COTNG92w.mjs → svelte-parser-1bBcMx3T.mjs} +16 -6
  21. package/dist/{svelte-parser-COTNG92w.mjs.map → svelte-parser-1bBcMx3T.mjs.map} +1 -1
  22. package/dist/{transaction-B0V0_SE2.mjs → transaction-C_Pvux78.mjs} +102 -2
  23. package/dist/{transaction-B0V0_SE2.mjs.map → transaction-C_Pvux78.mjs.map} +1 -1
  24. package/dist/tsx-parser-BFLv292Y.mjs +2 -0
  25. package/dist/{tsx-parser-CYVyBWjn.mjs → tsx-parser-BWiMGAIR.mjs} +3 -2
  26. package/dist/tsx-parser-BWiMGAIR.mjs.map +1 -0
  27. package/dist/{vue-parser-CjS4YAoQ.mjs → vue-parser-DTPTN3Yb.mjs} +16 -5
  28. package/dist/{vue-parser-CjS4YAoQ.mjs.map → vue-parser-DTPTN3Yb.mjs.map} +1 -1
  29. package/package.json +2 -2
  30. package/dist/apply-manager-Cf5OWOJ7.mjs.map +0 -1
  31. package/dist/scanner-DPrbU-oK.mjs.map +0 -1
  32. package/dist/server-CjK63H6X.mjs.map +0 -1
  33. package/dist/tsx-parser-Bf8Firrq.mjs +0 -2
  34. package/dist/tsx-parser-CYVyBWjn.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-DfWRpzdP.mjs","names":["packageJson.version"],"sources":["../src/tools/annotations.ts","../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 { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'\n\n/**\n * Centralized tool annotations registry.\n * MCP clients use these hints to distinguish read-only vs. write vs. destructive tools.\n */\nexport const TOOL_ANNOTATIONS: Record<string, ToolAnnotations> = {\n // ─── Context (read-only) ───\n contentrain_status: {\n title: 'Project Status',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n },\n contentrain_describe: {\n title: 'Describe Model',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n },\n contentrain_describe_format: {\n title: 'Describe Format',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n },\n\n // ─── Setup (write + git) ───\n contentrain_init: {\n title: 'Initialize Project',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n },\n contentrain_scaffold: {\n title: 'Scaffold Template',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n },\n\n // ─── Model (write + git) ───\n contentrain_model_save: {\n title: 'Save Model',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n contentrain_model_delete: {\n title: 'Delete Model',\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: false,\n },\n\n // ─── Content (mixed) ───\n contentrain_content_save: {\n title: 'Save Content',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n contentrain_content_delete: {\n title: 'Delete Content',\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: false,\n },\n contentrain_content_list: {\n title: 'List Content',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n },\n\n // ─── Workflow (mixed) ───\n contentrain_validate: {\n title: 'Validate Project',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n contentrain_submit: {\n title: 'Submit Branches',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n },\n contentrain_merge: {\n title: 'Merge Branch',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n },\n\n // ─── Normalize (mixed) ───\n contentrain_scan: {\n title: 'Scan Source Code',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n },\n contentrain_apply: {\n title: 'Apply Normalize',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n },\n\n // ─── Bulk (write + git) ───\n contentrain_bulk: {\n title: 'Bulk Operations',\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n },\n}\n","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'\nimport { TOOL_ANNOTATIONS } from './annotations.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 TOOL_ANNOTATIONS['contentrain_status']!,\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 TOOL_ANNOTATIONS['contentrain_describe']!,\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 TOOL_ANNOTATIONS['contentrain_describe_format']!,\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, ensureContentBranch } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { TOOL_ANNOTATIONS } from './annotations.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 TOOL_ANNOTATIONS['contentrain_init']!,\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 tool: 'contentrain_init',\n model: '',\n locale: supportedLocales[0] ?? 'en',\n })\n const gitResult = await tx.complete()\n\n // Ensure the contentrain branch exists after init\n await ensureContentBranch(projectRoot)\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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 TOOL_ANNOTATIONS['contentrain_scaffold']!,\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 tool: 'contentrain_scaffold',\n model: tmpl.models.map(m => m.id).join(', '),\n locale: defaultLocale,\n })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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'\nimport { TOOL_ANNOTATIONS } from './annotations.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 TOOL_ANNOTATIONS['contentrain_model_save']!,\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}`, { tool: 'contentrain_model_save', model: input.id })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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 TOOL_ANNOTATIONS['contentrain_model_delete']!,\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}`, { tool: 'contentrain_model_delete', model: modelId })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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'\nimport { TOOL_ANNOTATIONS } from './annotations.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 TOOL_ANNOTATIONS['contentrain_content_save']!,\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 tool: 'contentrain_content_save',\n model: input.model,\n locale: input.entries[0]?.locale,\n entries: entryIds,\n })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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 TOOL_ANNOTATIONS['contentrain_content_delete']!,\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 tool: 'contentrain_content_delete',\n model: input.model,\n locale: input.locale,\n })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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 TOOL_ANNOTATIONS['contentrain_content_list']!,\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 { CONTENTRAIN_BRANCH } from '@contentrain/types'\nimport { z } from 'zod'\nimport { simpleGit } from 'simple-git'\nimport { validateProject } from '../core/validator.js'\nimport { readConfig } from '../core/config.js'\nimport { createTransaction, buildBranchName, mergeBranch } from '../git/transaction.js'\nimport { checkBranchHealth, cleanupMergedBranches } from '../git/branch-lifecycle.js'\nimport { TOOL_ANNOTATIONS } from './annotations.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 TOOL_ANNOTATIONS['contentrain_validate']!,\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 tool: 'contentrain_validate',\n model: input.model ?? '*',\n })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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 TOOL_ANNOTATIONS['contentrain_submit']!,\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('cr/') && 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 cr/* 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 // Best-effort: push the contentrain branch itself\n try {\n await git.push(remoteName, CONTENTRAIN_BRANCH)\n } catch {\n // contentrain branch push is best-effort\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 (pushed.length > 0) nextSteps.push('For merge: use contentrain_merge or direct user to http://localhost:3333/branches')\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 // ─── contentrain_merge ───\n server.tool(\n 'contentrain_merge',\n 'Merge a review-mode branch into contentrain. Local git operation — no external platform needed. Merges the feature branch into the contentrain branch, advances the base branch via update-ref, and selectively syncs .contentrain/ files to the working tree.',\n {\n branch: z.string().describe('Branch name to merge (e.g. cr/normalize/extract/...)'),\n confirm: z.literal(true).describe('Must be true to confirm the merge'),\n },\n TOOL_ANNOTATIONS['contentrain_merge']!,\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 // Verify branch exists\n const git = simpleGit(projectRoot)\n const branches = await git.branchLocal()\n if (!branches.all.includes(input.branch)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Branch \"${input.branch}\" not found locally.`,\n next_steps: ['Check branch name with git branch -l', 'Use contentrain_submit to push first if needed'],\n }) }],\n isError: true,\n }\n }\n\n const result = await mergeBranch(projectRoot, input.branch)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'merged',\n action: result.action,\n commit: result.commit,\n sync: result.sync,\n next_steps: [\n 'Run `npx contentrain generate` to update SDK client',\n 'Run contentrain_validate to verify content integrity',\n result.sync.skipped.length > 0\n ? `${result.sync.skipped.length} file(s) skipped due to local changes — resolve manually`\n : '',\n ].filter(Boolean),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Merge 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'\nimport { TOOL_ANNOTATIONS } from './annotations.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 TOOL_ANNOTATIONS['contentrain_scan']!,\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 TOOL_ANNOTATIONS['contentrain_apply']!,\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'\nimport { TOOL_ANNOTATIONS } from './annotations.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 TOOL_ANNOTATIONS['contentrain_bulk']!,\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 tool: 'contentrain_bulk',\n model: input.model,\n locale: input.target_locale!,\n })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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 tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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 tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n const gitResult = await tx.complete()\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, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\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
@@ -6,13 +6,13 @@ import "./model-manager-DW_fOGRM.mjs";
6
6
  import "./context-DIDt_TxX.mjs";
7
7
  import "./detect-DinOQAmK.mjs";
8
8
  import "./branch-lifecycle-wGJg25LT.mjs";
9
- import { t as createServer } from "./server-CjK63H6X.mjs";
9
+ import { t as createServer } from "./server-DfWRpzdP.mjs";
10
10
  import "./id-DV_T9Ic8.mjs";
11
- import "./transaction-B0V0_SE2.mjs";
11
+ import "./transaction-C_Pvux78.mjs";
12
12
  import "./validator-SQYUV2JP.mjs";
13
13
  import "./scan-config-QgnJ1-52.mjs";
14
14
  import "./graph-builder-tM7zj-SX.mjs";
15
- import "./scanner-DPrbU-oK.mjs";
16
- import "./tsx-parser-CYVyBWjn.mjs";
17
- import "./apply-manager-Cf5OWOJ7.mjs";
15
+ import "./scanner-CQOxARUU.mjs";
16
+ import "./tsx-parser-BWiMGAIR.mjs";
17
+ import "./apply-manager-BkPMfpsN.mjs";
18
18
  export { createServer };
@@ -13,7 +13,7 @@ let _tsxParser = null;
13
13
  async function loadTsxParser() {
14
14
  if (_tsxParser) return _tsxParser;
15
15
  try {
16
- _tsxParser = (await import("./tsx-parser-Bf8Firrq.mjs")).parseTsx;
16
+ _tsxParser = (await import("./tsx-parser-BFLv292Y.mjs")).parseTsx;
17
17
  return _tsxParser;
18
18
  } catch {
19
19
  return null;
@@ -173,8 +173,16 @@ function processAttribute(attr, parentTag, content, results) {
173
173
  });
174
174
  }
175
175
  }
176
- function parseScriptBlock(scriptContent, scriptStartOffset, fullContent, fileName, parseTsx) {
177
- const scriptResults = parseTsx(scriptContent, fileName);
176
+ /**
177
+ * Resolve script filename with correct extension for TypeScript parser.
178
+ * Svelte files with <script lang="ts"> need ScriptKind.TS, not ScriptKind.JS.
179
+ */
180
+ function resolveScriptFileName(svelteFileName, lang) {
181
+ if (lang === "ts" || lang === "typescript") return svelteFileName.replace(/\.svelte$/, ".ts");
182
+ return svelteFileName.replace(/\.svelte$/, ".js");
183
+ }
184
+ function parseScriptBlock(scriptContent, scriptStartOffset, fullContent, fileName, parseTsx, lang) {
185
+ const scriptResults = parseTsx(scriptContent, resolveScriptFileName(fileName, lang));
178
186
  const scriptStartLine = getLineAndColumn(fullContent, scriptStartOffset).line;
179
187
  return scriptResults.map((r) => {
180
188
  r.line = r.line + scriptStartLine - 1;
@@ -201,7 +209,8 @@ async function parseSvelte(content, fileName) {
201
209
  const scriptContentMatch = scriptSource.match(/<script[^>]*>([\s\S]*?)<\/script>/);
202
210
  if (scriptContentMatch?.[1]) {
203
211
  const scriptContentStr = scriptContentMatch[1];
204
- const scriptResults = parseScriptBlock(scriptContentStr, scriptStart + (scriptSource.indexOf(">") + 1), content, fileName, tsxParser);
212
+ const scriptLang = scriptSource.match(/<script[^>]*\slang=["'](\w+)["']/)?.[1];
213
+ const scriptResults = parseScriptBlock(scriptContentStr, scriptStart + (scriptSource.indexOf(">") + 1), content, fileName, tsxParser, scriptLang);
205
214
  results.push(...scriptResults);
206
215
  }
207
216
  }
@@ -212,7 +221,8 @@ async function parseSvelte(content, fileName) {
212
221
  const moduleContentMatch = moduleSource.match(/<script[^>]*>([\s\S]*?)<\/script>/);
213
222
  if (moduleContentMatch?.[1]) {
214
223
  const moduleContentStr = moduleContentMatch[1];
215
- const moduleResults = parseScriptBlock(moduleContentStr, moduleStart + (moduleSource.indexOf(">") + 1), content, fileName, tsxParser);
224
+ const moduleLang = moduleSource.match(/<script[^>]*\slang=["'](\w+)["']/)?.[1];
225
+ const moduleResults = parseScriptBlock(moduleContentStr, moduleStart + (moduleSource.indexOf(">") + 1), content, fileName, tsxParser, moduleLang);
216
226
  results.push(...moduleResults);
217
227
  }
218
228
  }
@@ -222,4 +232,4 @@ async function parseSvelte(content, fileName) {
222
232
  //#endregion
223
233
  export { parseSvelte };
224
234
 
225
- //# sourceMappingURL=svelte-parser-COTNG92w.mjs.map
235
+ //# sourceMappingURL=svelte-parser-1bBcMx3T.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"svelte-parser-COTNG92w.mjs","names":[],"sources":["../src/core/ast-scanner/svelte-parser.ts"],"sourcesContent":["// ─── Svelte SFC Parser for Scanner v2 ───\n// Parses .svelte Single File Components using svelte/compiler.\n// Extracts ALL strings with structural context metadata.\n// Scanner does NOT classify — agent does. When in doubt, INCLUDE.\n\nimport type { ExtractedString } from './types.js'\n\n// ─── Lazy-loaded svelte/compiler ───\n\ninterface _SvelteLoc {\n start: number\n end: number\n line: number\n column: number\n}\n\n// Svelte AST node types from svelte/compiler parse()\ninterface SvelteBaseNode {\n type: string\n start: number\n end: number\n}\n\ninterface SvelteFragment extends SvelteBaseNode {\n type: 'Fragment'\n children: SvelteNode[]\n}\n\ninterface SvelteElement extends SvelteBaseNode {\n type: 'Element' | 'InlineComponent' | 'SlotTemplate' | 'Slot' | 'Head' | 'Title' | 'Window' | 'Document' | 'Body'\n name: string\n attributes: SvelteAttribute[]\n children: SvelteNode[]\n}\n\ninterface SvelteText extends SvelteBaseNode {\n type: 'Text'\n data: string\n raw: string\n}\n\ninterface SvelteAttribute extends SvelteBaseNode {\n type: 'Attribute'\n name: string\n value: SvelteAttributeValue[]\n}\n\ninterface SvelteAttributeText extends SvelteBaseNode {\n type: 'Text'\n data: string\n raw: string\n}\n\ntype SvelteAttributeValue = SvelteAttributeText | SvelteMustacheTag | SvelteBaseNode\n\ninterface _SvelteSpread extends SvelteBaseNode {\n type: 'Spread'\n}\n\ninterface SvelteMustacheTag extends SvelteBaseNode {\n type: 'MustacheTag'\n expression: SvelteBaseNode\n}\n\ninterface SvelteIfBlock extends SvelteBaseNode {\n type: 'IfBlock'\n expression: SvelteBaseNode\n children: SvelteNode[]\n else?: SvelteElseBlock\n}\n\ninterface SvelteElseBlock extends SvelteBaseNode {\n type: 'ElseBlock'\n children: SvelteNode[]\n}\n\ninterface SvelteEachBlock extends SvelteBaseNode {\n type: 'EachBlock'\n expression: SvelteBaseNode\n children: SvelteNode[]\n else?: SvelteElseBlock\n}\n\ninterface SvelteAwaitBlock extends SvelteBaseNode {\n type: 'AwaitBlock'\n pending: SvelteFragment | null\n then: SvelteFragment | null\n catch: SvelteFragment | null\n}\n\ninterface SvelteKeyBlock extends SvelteBaseNode {\n type: 'KeyBlock'\n children: SvelteNode[]\n}\n\ninterface SvelteRawMustacheTag extends SvelteBaseNode {\n type: 'RawMustacheTag'\n expression: SvelteBaseNode\n}\n\ninterface SvelteDirective extends SvelteBaseNode {\n type: 'EventHandler' | 'Binding' | 'Action' | 'Class' | 'StyleDirective' | 'Transition' | 'Animation' | 'Let' | 'Ref'\n name: string\n}\n\ninterface SvelteScript extends SvelteBaseNode {\n type: 'Script'\n content: string\n context?: string // \"module\" for <script context=\"module\">\n}\n\ninterface SvelteStyle extends SvelteBaseNode {\n type: 'Style'\n}\n\ntype SvelteNode =\n | SvelteFragment\n | SvelteElement\n | SvelteText\n | SvelteMustacheTag\n | SvelteIfBlock\n | SvelteEachBlock\n | SvelteAwaitBlock\n | SvelteKeyBlock\n | SvelteRawMustacheTag\n | SvelteBaseNode\n\ninterface SvelteAst {\n html: SvelteFragment\n instance?: SvelteScript\n module?: SvelteScript\n css?: SvelteStyle\n}\n\ninterface SvelteCompiler {\n parse: (source: string, options?: { filename?: string }) => SvelteAst\n}\n\nlet _compiler: SvelteCompiler | null = null\n\nasync function loadCompiler(): Promise<SvelteCompiler> {\n if (_compiler) return _compiler\n try {\n const mod = await import('svelte/compiler')\n _compiler = mod as unknown as SvelteCompiler\n return _compiler\n } catch {\n throw new Error(\n 'svelte is required to parse .svelte files. '\n + 'Install it with: pnpm add -D svelte',\n )\n }\n}\n\n// ─── tsx-parser delegation ───\n\ntype TsxParserFn = (content: string, fileName: string) => ExtractedString[]\n\nlet _tsxParser: TsxParserFn | null = null\n\nasync function loadTsxParser(): Promise<TsxParserFn | null> {\n if (_tsxParser) return _tsxParser\n try {\n const mod = await import('./tsx-parser.js')\n _tsxParser = mod.parseTsx\n return _tsxParser\n } catch {\n return null\n }\n}\n\n// ─── Constants ───\n\nconst SURROUNDING_MAX = 120\n\n/** Attributes whose values are CSS, not content */\nconst CSS_ATTRIBUTES = new Set(['class', 'style'])\n\n/** Directive types that contain code, not content */\nconst CODE_DIRECTIVE_TYPES = new Set([\n 'EventHandler', // on:click\n 'Binding', // bind:value\n 'Action', // use:action\n 'Class', // class:name\n 'StyleDirective', // style:color\n 'Transition', // transition:fade\n 'Animation', // animate:flip\n 'Let', // let:item\n 'Ref', // ref (legacy)\n])\n\n// ─── Helpers ───\n\nfunction getLineAndColumn(content: string, offset: number): { line: number; column: number } {\n let line = 1\n let lastNewline = -1\n\n for (let i = 0; i < offset && i < content.length; i++) {\n if (content[i] === '\\n') {\n line++\n lastNewline = i\n }\n }\n\n return { line, column: offset - lastNewline }\n}\n\nfunction getSurroundingByLine(content: string, line: number): string {\n const lines = content.split('\\n')\n const idx = line - 1\n const start = Math.max(0, idx - 1)\n const end = Math.min(lines.length - 1, idx + 1)\n\n const parts: string[] = []\n for (let i = start; i <= end; i++) {\n const l = lines[i]\n if (l !== undefined) {\n parts.push(l)\n }\n }\n\n const joined = parts.join('\\n')\n if (joined.length > SURROUNDING_MAX) {\n return joined.slice(0, SURROUNDING_MAX)\n }\n return joined\n}\n\n// ─── Template AST Walker ───\n\nfunction walkTemplate(\n node: SvelteNode,\n content: string,\n results: ExtractedString[],\n parentTag: string = '',\n): void {\n const nodeType = node.type\n\n switch (nodeType) {\n case 'Fragment': {\n const fragment = node as SvelteFragment\n for (const child of fragment.children) {\n walkTemplate(child, content, results, parentTag)\n }\n break\n }\n\n case 'Text': {\n const textNode = node as SvelteText\n const trimmed = textNode.data.trim()\n if (trimmed.length > 0 && /\\S/.test(trimmed)) {\n const pos = getLineAndColumn(content, textNode.start)\n results.push({\n value: trimmed,\n line: pos.line,\n column: pos.column,\n context: 'template_text',\n scope: 'template',\n parent: parentTag,\n surrounding: getSurroundingByLine(content, pos.line),\n })\n }\n break\n }\n\n case 'Element':\n case 'InlineComponent':\n case 'SlotTemplate':\n case 'Slot':\n case 'Head':\n case 'Title':\n case 'Window':\n case 'Document':\n case 'Body': {\n const el = node as SvelteElement\n const tag = el.name\n\n // Process attributes\n for (const attr of el.attributes) {\n processAttribute(attr, tag, content, results)\n }\n\n // Recurse into children\n for (const child of el.children) {\n walkTemplate(child, content, results, tag)\n }\n break\n }\n\n case 'IfBlock': {\n const ifBlock = node as SvelteIfBlock\n // Skip the expression (code) — walk children for content\n for (const child of ifBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n // Walk else branch\n if (ifBlock.else) {\n walkTemplate(ifBlock.else, content, results, parentTag)\n }\n break\n }\n\n case 'ElseBlock': {\n const elseBlock = node as SvelteElseBlock\n for (const child of elseBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n break\n }\n\n case 'EachBlock': {\n const eachBlock = node as SvelteEachBlock\n // Skip the expression — walk children for content\n for (const child of eachBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n if (eachBlock.else) {\n walkTemplate(eachBlock.else, content, results, parentTag)\n }\n break\n }\n\n case 'AwaitBlock': {\n const awaitBlock = node as SvelteAwaitBlock\n if (awaitBlock.pending) {\n walkTemplate(awaitBlock.pending, content, results, parentTag)\n }\n if (awaitBlock.then) {\n walkTemplate(awaitBlock.then, content, results, parentTag)\n }\n if (awaitBlock.catch) {\n walkTemplate(awaitBlock.catch, content, results, parentTag)\n }\n break\n }\n\n case 'KeyBlock': {\n const keyBlock = node as SvelteKeyBlock\n for (const child of keyBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n break\n }\n\n case 'MustacheTag':\n case 'RawMustacheTag': {\n // {expression} or {@html expression} — code expressions, skip\n break\n }\n\n default: {\n // For unknown node types, try to walk children\n const unknownNode = node as SvelteBaseNode & { children?: SvelteNode[] }\n if (unknownNode.children) {\n for (const child of unknownNode.children) {\n walkTemplate(child, content, results, parentTag)\n }\n }\n break\n }\n }\n}\n\nfunction processAttribute(\n attr: SvelteAttribute | SvelteDirective | SvelteBaseNode,\n parentTag: string,\n content: string,\n results: ExtractedString[],\n): void {\n // Skip directive types (EventHandler, Binding, etc.) — they contain code\n if (CODE_DIRECTIVE_TYPES.has(attr.type)) return\n\n // Only process regular Attribute nodes\n if (attr.type !== 'Attribute') return\n\n const attribute = attr as SvelteAttribute\n const attrName = attribute.name\n\n // Skip if no value or empty value array\n if (!attribute.value || attribute.value.length === 0) return\n\n // Process each value segment (attribute values can be arrays in Svelte)\n for (const valuePart of attribute.value) {\n if (valuePart.type !== 'Text') continue\n\n const textValue = (valuePart as SvelteAttributeText).data\n if (!textValue || textValue.trim().length === 0) continue\n\n const pos = getLineAndColumn(content, valuePart.start)\n\n // CSS attributes get css_class context\n if (CSS_ATTRIBUTES.has(attrName)) {\n results.push({\n value: textValue,\n line: pos.line,\n column: pos.column,\n context: 'css_class',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(content, pos.line),\n })\n continue\n }\n\n results.push({\n value: textValue,\n line: pos.line,\n column: pos.column,\n context: 'template_attribute',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(content, pos.line),\n })\n }\n}\n\n// ─── Script Block Parsing ───\n\nfunction parseScriptBlock(\n scriptContent: string,\n scriptStartOffset: number,\n fullContent: string,\n fileName: string,\n parseTsx: TsxParserFn,\n): ExtractedString[] {\n const scriptResults = parseTsx(scriptContent, fileName)\n const scriptStartPos = getLineAndColumn(fullContent, scriptStartOffset)\n const scriptStartLine = scriptStartPos.line\n\n return scriptResults.map(r => {\n r.line = r.line + scriptStartLine - 1\n r.scope = 'script'\n return r\n })\n}\n\n// ─── Main Export ───\n\nexport async function parseSvelte(content: string, fileName: string): Promise<ExtractedString[]> {\n const compiler = await loadCompiler()\n const results: ExtractedString[] = []\n\n let ast: SvelteAst\n try {\n ast = compiler.parse(content, { filename: fileName })\n } catch {\n // If Svelte parsing fails, return empty — malformed files shouldn't block scanning\n return []\n }\n\n // ─── Template (html) ───\n if (ast.html) {\n walkTemplate(ast.html, content, results)\n }\n\n // ─── Script Block ───\n const tsxParser = await loadTsxParser()\n if (tsxParser) {\n if (ast.instance) {\n // <script> block — extract its content from source\n const scriptStart = ast.instance.start\n const scriptEnd = ast.instance.end\n\n // Find the content between <script> tags\n const scriptSource = content.slice(scriptStart, scriptEnd)\n const scriptContentMatch = scriptSource.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/)\n if (scriptContentMatch?.[1]) {\n const scriptContentStr = scriptContentMatch[1]\n // Calculate offset of script content within the file\n const scriptTagEnd = scriptSource.indexOf('>') + 1\n const contentOffset = scriptStart + scriptTagEnd\n const scriptResults = parseScriptBlock(\n scriptContentStr,\n contentOffset,\n content,\n fileName,\n tsxParser,\n )\n results.push(...scriptResults)\n }\n }\n\n if (ast.module) {\n // <script context=\"module\"> block\n const moduleStart = ast.module.start\n const moduleEnd = ast.module.end\n const moduleSource = content.slice(moduleStart, moduleEnd)\n const moduleContentMatch = moduleSource.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/)\n if (moduleContentMatch?.[1]) {\n const moduleContentStr = moduleContentMatch[1]\n const scriptTagEnd = moduleSource.indexOf('>') + 1\n const contentOffset = moduleStart + scriptTagEnd\n const moduleResults = parseScriptBlock(\n moduleContentStr,\n contentOffset,\n content,\n fileName,\n tsxParser,\n )\n results.push(...moduleResults)\n }\n }\n }\n\n // Style blocks are intentionally skipped — no content strings in CSS\n\n return results\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"svelte-parser-1bBcMx3T.mjs","names":[],"sources":["../src/core/ast-scanner/svelte-parser.ts"],"sourcesContent":["// ─── Svelte SFC Parser for Scanner v2 ───\n// Parses .svelte Single File Components using svelte/compiler.\n// Extracts ALL strings with structural context metadata.\n// Scanner does NOT classify — agent does. When in doubt, INCLUDE.\n\nimport type { ExtractedString } from './types.js'\n\n// ─── Lazy-loaded svelte/compiler ───\n\ninterface _SvelteLoc {\n start: number\n end: number\n line: number\n column: number\n}\n\n// Svelte AST node types from svelte/compiler parse()\ninterface SvelteBaseNode {\n type: string\n start: number\n end: number\n}\n\ninterface SvelteFragment extends SvelteBaseNode {\n type: 'Fragment'\n children: SvelteNode[]\n}\n\ninterface SvelteElement extends SvelteBaseNode {\n type: 'Element' | 'InlineComponent' | 'SlotTemplate' | 'Slot' | 'Head' | 'Title' | 'Window' | 'Document' | 'Body'\n name: string\n attributes: SvelteAttribute[]\n children: SvelteNode[]\n}\n\ninterface SvelteText extends SvelteBaseNode {\n type: 'Text'\n data: string\n raw: string\n}\n\ninterface SvelteAttribute extends SvelteBaseNode {\n type: 'Attribute'\n name: string\n value: SvelteAttributeValue[]\n}\n\ninterface SvelteAttributeText extends SvelteBaseNode {\n type: 'Text'\n data: string\n raw: string\n}\n\ntype SvelteAttributeValue = SvelteAttributeText | SvelteMustacheTag | SvelteBaseNode\n\ninterface _SvelteSpread extends SvelteBaseNode {\n type: 'Spread'\n}\n\ninterface SvelteMustacheTag extends SvelteBaseNode {\n type: 'MustacheTag'\n expression: SvelteBaseNode\n}\n\ninterface SvelteIfBlock extends SvelteBaseNode {\n type: 'IfBlock'\n expression: SvelteBaseNode\n children: SvelteNode[]\n else?: SvelteElseBlock\n}\n\ninterface SvelteElseBlock extends SvelteBaseNode {\n type: 'ElseBlock'\n children: SvelteNode[]\n}\n\ninterface SvelteEachBlock extends SvelteBaseNode {\n type: 'EachBlock'\n expression: SvelteBaseNode\n children: SvelteNode[]\n else?: SvelteElseBlock\n}\n\ninterface SvelteAwaitBlock extends SvelteBaseNode {\n type: 'AwaitBlock'\n pending: SvelteFragment | null\n then: SvelteFragment | null\n catch: SvelteFragment | null\n}\n\ninterface SvelteKeyBlock extends SvelteBaseNode {\n type: 'KeyBlock'\n children: SvelteNode[]\n}\n\ninterface SvelteRawMustacheTag extends SvelteBaseNode {\n type: 'RawMustacheTag'\n expression: SvelteBaseNode\n}\n\ninterface SvelteDirective extends SvelteBaseNode {\n type: 'EventHandler' | 'Binding' | 'Action' | 'Class' | 'StyleDirective' | 'Transition' | 'Animation' | 'Let' | 'Ref'\n name: string\n}\n\ninterface SvelteScript extends SvelteBaseNode {\n type: 'Script'\n content: string\n context?: string // \"module\" for <script context=\"module\">\n}\n\ninterface SvelteStyle extends SvelteBaseNode {\n type: 'Style'\n}\n\ntype SvelteNode =\n | SvelteFragment\n | SvelteElement\n | SvelteText\n | SvelteMustacheTag\n | SvelteIfBlock\n | SvelteEachBlock\n | SvelteAwaitBlock\n | SvelteKeyBlock\n | SvelteRawMustacheTag\n | SvelteBaseNode\n\ninterface SvelteAst {\n html: SvelteFragment\n instance?: SvelteScript\n module?: SvelteScript\n css?: SvelteStyle\n}\n\ninterface SvelteCompiler {\n parse: (source: string, options?: { filename?: string }) => SvelteAst\n}\n\nlet _compiler: SvelteCompiler | null = null\n\nasync function loadCompiler(): Promise<SvelteCompiler> {\n if (_compiler) return _compiler\n try {\n const mod = await import('svelte/compiler')\n _compiler = mod as unknown as SvelteCompiler\n return _compiler\n } catch {\n throw new Error(\n 'svelte is required to parse .svelte files. '\n + 'Install it with: pnpm add -D svelte',\n )\n }\n}\n\n// ─── tsx-parser delegation ───\n\ntype TsxParserFn = (content: string, fileName: string) => ExtractedString[]\n\nlet _tsxParser: TsxParserFn | null = null\n\nasync function loadTsxParser(): Promise<TsxParserFn | null> {\n if (_tsxParser) return _tsxParser\n try {\n const mod = await import('./tsx-parser.js')\n _tsxParser = mod.parseTsx\n return _tsxParser\n } catch {\n return null\n }\n}\n\n// ─── Constants ───\n\nconst SURROUNDING_MAX = 120\n\n/** Attributes whose values are CSS, not content */\nconst CSS_ATTRIBUTES = new Set(['class', 'style'])\n\n/** Directive types that contain code, not content */\nconst CODE_DIRECTIVE_TYPES = new Set([\n 'EventHandler', // on:click\n 'Binding', // bind:value\n 'Action', // use:action\n 'Class', // class:name\n 'StyleDirective', // style:color\n 'Transition', // transition:fade\n 'Animation', // animate:flip\n 'Let', // let:item\n 'Ref', // ref (legacy)\n])\n\n// ─── Helpers ───\n\nfunction getLineAndColumn(content: string, offset: number): { line: number; column: number } {\n let line = 1\n let lastNewline = -1\n\n for (let i = 0; i < offset && i < content.length; i++) {\n if (content[i] === '\\n') {\n line++\n lastNewline = i\n }\n }\n\n return { line, column: offset - lastNewline }\n}\n\nfunction getSurroundingByLine(content: string, line: number): string {\n const lines = content.split('\\n')\n const idx = line - 1\n const start = Math.max(0, idx - 1)\n const end = Math.min(lines.length - 1, idx + 1)\n\n const parts: string[] = []\n for (let i = start; i <= end; i++) {\n const l = lines[i]\n if (l !== undefined) {\n parts.push(l)\n }\n }\n\n const joined = parts.join('\\n')\n if (joined.length > SURROUNDING_MAX) {\n return joined.slice(0, SURROUNDING_MAX)\n }\n return joined\n}\n\n// ─── Template AST Walker ───\n\nfunction walkTemplate(\n node: SvelteNode,\n content: string,\n results: ExtractedString[],\n parentTag: string = '',\n): void {\n const nodeType = node.type\n\n switch (nodeType) {\n case 'Fragment': {\n const fragment = node as SvelteFragment\n for (const child of fragment.children) {\n walkTemplate(child, content, results, parentTag)\n }\n break\n }\n\n case 'Text': {\n const textNode = node as SvelteText\n const trimmed = textNode.data.trim()\n if (trimmed.length > 0 && /\\S/.test(trimmed)) {\n const pos = getLineAndColumn(content, textNode.start)\n results.push({\n value: trimmed,\n line: pos.line,\n column: pos.column,\n context: 'template_text',\n scope: 'template',\n parent: parentTag,\n surrounding: getSurroundingByLine(content, pos.line),\n })\n }\n break\n }\n\n case 'Element':\n case 'InlineComponent':\n case 'SlotTemplate':\n case 'Slot':\n case 'Head':\n case 'Title':\n case 'Window':\n case 'Document':\n case 'Body': {\n const el = node as SvelteElement\n const tag = el.name\n\n // Process attributes\n for (const attr of el.attributes) {\n processAttribute(attr, tag, content, results)\n }\n\n // Recurse into children\n for (const child of el.children) {\n walkTemplate(child, content, results, tag)\n }\n break\n }\n\n case 'IfBlock': {\n const ifBlock = node as SvelteIfBlock\n // Skip the expression (code) — walk children for content\n for (const child of ifBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n // Walk else branch\n if (ifBlock.else) {\n walkTemplate(ifBlock.else, content, results, parentTag)\n }\n break\n }\n\n case 'ElseBlock': {\n const elseBlock = node as SvelteElseBlock\n for (const child of elseBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n break\n }\n\n case 'EachBlock': {\n const eachBlock = node as SvelteEachBlock\n // Skip the expression — walk children for content\n for (const child of eachBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n if (eachBlock.else) {\n walkTemplate(eachBlock.else, content, results, parentTag)\n }\n break\n }\n\n case 'AwaitBlock': {\n const awaitBlock = node as SvelteAwaitBlock\n if (awaitBlock.pending) {\n walkTemplate(awaitBlock.pending, content, results, parentTag)\n }\n if (awaitBlock.then) {\n walkTemplate(awaitBlock.then, content, results, parentTag)\n }\n if (awaitBlock.catch) {\n walkTemplate(awaitBlock.catch, content, results, parentTag)\n }\n break\n }\n\n case 'KeyBlock': {\n const keyBlock = node as SvelteKeyBlock\n for (const child of keyBlock.children) {\n walkTemplate(child, content, results, parentTag)\n }\n break\n }\n\n case 'MustacheTag':\n case 'RawMustacheTag': {\n // {expression} or {@html expression} — code expressions, skip\n break\n }\n\n default: {\n // For unknown node types, try to walk children\n const unknownNode = node as SvelteBaseNode & { children?: SvelteNode[] }\n if (unknownNode.children) {\n for (const child of unknownNode.children) {\n walkTemplate(child, content, results, parentTag)\n }\n }\n break\n }\n }\n}\n\nfunction processAttribute(\n attr: SvelteAttribute | SvelteDirective | SvelteBaseNode,\n parentTag: string,\n content: string,\n results: ExtractedString[],\n): void {\n // Skip directive types (EventHandler, Binding, etc.) — they contain code\n if (CODE_DIRECTIVE_TYPES.has(attr.type)) return\n\n // Only process regular Attribute nodes\n if (attr.type !== 'Attribute') return\n\n const attribute = attr as SvelteAttribute\n const attrName = attribute.name\n\n // Skip if no value or empty value array\n if (!attribute.value || attribute.value.length === 0) return\n\n // Process each value segment (attribute values can be arrays in Svelte)\n for (const valuePart of attribute.value) {\n if (valuePart.type !== 'Text') continue\n\n const textValue = (valuePart as SvelteAttributeText).data\n if (!textValue || textValue.trim().length === 0) continue\n\n const pos = getLineAndColumn(content, valuePart.start)\n\n // CSS attributes get css_class context\n if (CSS_ATTRIBUTES.has(attrName)) {\n results.push({\n value: textValue,\n line: pos.line,\n column: pos.column,\n context: 'css_class',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(content, pos.line),\n })\n continue\n }\n\n results.push({\n value: textValue,\n line: pos.line,\n column: pos.column,\n context: 'template_attribute',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(content, pos.line),\n })\n }\n}\n\n// ─── Script Block Parsing ───\n\n/**\n * Resolve script filename with correct extension for TypeScript parser.\n * Svelte files with <script lang=\"ts\"> need ScriptKind.TS, not ScriptKind.JS.\n */\nfunction resolveScriptFileName(svelteFileName: string, lang?: string): string {\n if (lang === 'ts' || lang === 'typescript') return svelteFileName.replace(/\\.svelte$/, '.ts')\n return svelteFileName.replace(/\\.svelte$/, '.js')\n}\n\nfunction parseScriptBlock(\n scriptContent: string,\n scriptStartOffset: number,\n fullContent: string,\n fileName: string,\n parseTsx: TsxParserFn,\n lang?: string,\n): ExtractedString[] {\n const resolvedFileName = resolveScriptFileName(fileName, lang)\n const scriptResults = parseTsx(scriptContent, resolvedFileName)\n const scriptStartPos = getLineAndColumn(fullContent, scriptStartOffset)\n const scriptStartLine = scriptStartPos.line\n\n return scriptResults.map(r => {\n r.line = r.line + scriptStartLine - 1\n r.scope = 'script'\n return r\n })\n}\n\n// ─── Main Export ───\n\nexport async function parseSvelte(content: string, fileName: string): Promise<ExtractedString[]> {\n const compiler = await loadCompiler()\n const results: ExtractedString[] = []\n\n let ast: SvelteAst\n try {\n ast = compiler.parse(content, { filename: fileName })\n } catch {\n // If Svelte parsing fails, return empty — malformed files shouldn't block scanning\n return []\n }\n\n // ─── Template (html) ───\n if (ast.html) {\n walkTemplate(ast.html, content, results)\n }\n\n // ─── Script Block ───\n const tsxParser = await loadTsxParser()\n if (tsxParser) {\n if (ast.instance) {\n // <script> block — extract its content from source\n const scriptStart = ast.instance.start\n const scriptEnd = ast.instance.end\n\n // Find the content between <script> tags\n const scriptSource = content.slice(scriptStart, scriptEnd)\n const scriptContentMatch = scriptSource.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/)\n if (scriptContentMatch?.[1]) {\n const scriptContentStr = scriptContentMatch[1]\n // Extract lang attribute from <script lang=\"ts\">\n const langMatch = scriptSource.match(/<script[^>]*\\slang=[\"'](\\w+)[\"']/)\n const scriptLang = langMatch?.[1]\n // Calculate offset of script content within the file\n const scriptTagEnd = scriptSource.indexOf('>') + 1\n const contentOffset = scriptStart + scriptTagEnd\n const scriptResults = parseScriptBlock(\n scriptContentStr,\n contentOffset,\n content,\n fileName,\n tsxParser,\n scriptLang,\n )\n results.push(...scriptResults)\n }\n }\n\n if (ast.module) {\n // <script context=\"module\"> block\n const moduleStart = ast.module.start\n const moduleEnd = ast.module.end\n const moduleSource = content.slice(moduleStart, moduleEnd)\n const moduleContentMatch = moduleSource.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/)\n if (moduleContentMatch?.[1]) {\n const moduleContentStr = moduleContentMatch[1]\n const moduleLangMatch = moduleSource.match(/<script[^>]*\\slang=[\"'](\\w+)[\"']/)\n const moduleLang = moduleLangMatch?.[1]\n const scriptTagEnd = moduleSource.indexOf('>') + 1\n const contentOffset = moduleStart + scriptTagEnd\n const moduleResults = parseScriptBlock(\n moduleContentStr,\n contentOffset,\n content,\n fileName,\n tsxParser,\n moduleLang,\n )\n results.push(...moduleResults)\n }\n }\n }\n\n // Style blocks are intentionally skipped — no content strings in CSS\n\n return results\n}\n"],"mappings":"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"}