@contentrain/mcp 3.6.7 → 3.7.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 (111) hide show
  1. package/README.md +1 -0
  2. package/dist/{annotations-CXHPnS-M.mjs → annotations-BYkjoj2d.mjs} +1 -1
  3. package/dist/{annotations-CXHPnS-M.mjs.map → annotations-BYkjoj2d.mjs.map} +1 -1
  4. package/dist/{apply-manager-llmSyK5K.mjs → apply-manager-B-VMXKiW.mjs} +3 -3
  5. package/dist/{apply-manager-llmSyK5K.mjs.map → apply-manager-B-VMXKiW.mjs.map} +1 -1
  6. package/dist/{astro-parser-dhFgc2wA.mjs → astro-parser-DsGdiUnu.mjs} +3 -3
  7. package/dist/{astro-parser-dhFgc2wA.mjs.map → astro-parser-DsGdiUnu.mjs.map} +1 -1
  8. package/dist/{availability-DYHNoP0_.mjs → availability-CO8Xfx3w.mjs} +1 -1
  9. package/dist/{availability-DYHNoP0_.mjs.map → availability-CO8Xfx3w.mjs.map} +1 -1
  10. package/dist/base-branch-Cw9DWvCC.mjs +121 -0
  11. package/dist/base-branch-Cw9DWvCC.mjs.map +1 -0
  12. package/dist/{branch-lifecycle-f4gI_eGo.mjs → branch-lifecycle-CcsSyfSw.mjs} +3 -68
  13. package/dist/branch-lifecycle-CcsSyfSw.mjs.map +1 -0
  14. package/dist/{branch-lifecycle-DGlgjnC7.d.mts → branch-lifecycle-CirYfTXu.d.mts} +1 -1
  15. package/dist/{branch-lifecycle-DGlgjnC7.d.mts.map → branch-lifecycle-CirYfTXu.d.mts.map} +1 -1
  16. package/dist/{compiler-CR9F0wZZ.mjs → compiler-CKelkozt.mjs} +2 -2
  17. package/dist/{compiler-CR9F0wZZ.mjs.map → compiler-CKelkozt.mjs.map} +1 -1
  18. package/dist/{compiler-sfc.cjs-Cpexs92i.mjs → compiler-sfc.cjs-BH5AEf2z.mjs} +2 -2
  19. package/dist/{compiler-sfc.cjs-Cpexs92i.mjs.map → compiler-sfc.cjs-BH5AEf2z.mjs.map} +1 -1
  20. package/dist/{content-manager-BqaMkMVk.d.mts → content-manager-wKsAkhly.d.mts} +2 -2
  21. package/dist/{content-manager-BqaMkMVk.d.mts.map → content-manager-wKsAkhly.d.mts.map} +1 -1
  22. package/dist/core/apply-manager.mjs +5 -4
  23. package/dist/core/config.d.mts +1 -1
  24. package/dist/core/content-manager.d.mts +1 -1
  25. package/dist/core/context.d.mts +1 -1
  26. package/dist/core/contracts/index.d.mts +1 -1
  27. package/dist/core/doctor.mjs +4 -3
  28. package/dist/core/graph-builder.mjs +2 -2
  29. package/dist/core/model-manager.d.mts +1 -1
  30. package/dist/core/ops/index.d.mts +3 -3
  31. package/dist/core/overlay-reader.d.mts +1 -1
  32. package/dist/core/scan-config.mjs +1 -1
  33. package/dist/core/scanner.mjs +3 -3
  34. package/dist/core/validator/index.d.mts +1 -1
  35. package/dist/{detect-DUYFv3-O.mjs → detect-B1QHYA0u.mjs} +1 -1
  36. package/dist/{detect-DUYFv3-O.mjs.map → detect-B1QHYA0u.mjs.map} +1 -1
  37. package/dist/{doctor-fIG4TtsO.mjs → doctor-Du4MVG5w.mjs} +3 -3
  38. package/dist/{doctor-fIG4TtsO.mjs.map → doctor-Du4MVG5w.mjs.map} +1 -1
  39. package/dist/{errors-DLkAWnzQ.mjs → errors-B0NcpPIp.mjs} +1 -1
  40. package/dist/{errors-DLkAWnzQ.mjs.map → errors-B0NcpPIp.mjs.map} +1 -1
  41. package/dist/{errors-BmLE-JSC.mjs → errors-De8Noahk.mjs} +2 -2
  42. package/dist/{errors-BmLE-JSC.mjs.map → errors-De8Noahk.mjs.map} +1 -1
  43. package/dist/git/base-branch.d.mts +61 -0
  44. package/dist/git/base-branch.d.mts.map +1 -0
  45. package/dist/git/base-branch.mjs +2 -0
  46. package/dist/git/branch-lifecycle.d.mts +1 -1
  47. package/dist/git/branch-lifecycle.mjs +2 -1
  48. package/dist/git/errors.mjs +1 -1
  49. package/dist/git/reconcile.d.mts +1 -1
  50. package/dist/git/reconcile.mjs +5 -4
  51. package/dist/git/transaction.d.mts +1 -1
  52. package/dist/git/transaction.mjs +4 -3
  53. package/dist/{graph-builder-CNswWNtb.mjs → graph-builder-BPhi-1KH.mjs} +2 -2
  54. package/dist/{graph-builder-CNswWNtb.mjs.map → graph-builder-BPhi-1KH.mjs.map} +1 -1
  55. package/dist/{id-CjGMPP1k.mjs → id-jrHWdtqn.mjs} +1 -1
  56. package/dist/{id-CjGMPP1k.mjs.map → id-jrHWdtqn.mjs.map} +1 -1
  57. package/dist/{index-DKZNUqpP.d.mts → index-CkhXTFdk.d.mts} +2 -2
  58. package/dist/{index-DKZNUqpP.d.mts.map → index-CkhXTFdk.d.mts.map} +1 -1
  59. package/dist/index.mjs +16 -15
  60. package/dist/index.mjs.map +1 -1
  61. package/dist/{local-DkDTrGt_.mjs → local-BySg4Ib-.mjs} +4 -3
  62. package/dist/{local-DkDTrGt_.mjs.map → local-BySg4Ib-.mjs.map} +1 -1
  63. package/dist/{node-CpLm3qr6.mjs → node-hrF4BrH2.mjs} +1 -1
  64. package/dist/{node-CpLm3qr6.mjs.map → node-hrF4BrH2.mjs.map} +1 -1
  65. package/dist/{paths-C6_C1gLp.mjs → paths-Dz5v-z00.mjs} +1 -1
  66. package/dist/{paths-C6_C1gLp.mjs.map → paths-Dz5v-z00.mjs.map} +1 -1
  67. package/dist/providers/github/index.d.mts +1 -1
  68. package/dist/providers/github/index.mjs +2 -2
  69. package/dist/providers/gitlab/index.d.mts +1 -1
  70. package/dist/providers/gitlab/index.mjs +2 -2
  71. package/dist/providers/local/index.d.mts +1 -1
  72. package/dist/providers/local/index.mjs +5 -4
  73. package/dist/{reconcile-Cq60cldB.mjs → reconcile-BWbckvbv.mjs} +4 -3
  74. package/dist/{reconcile-Cq60cldB.mjs.map → reconcile-BWbckvbv.mjs.map} +1 -1
  75. package/dist/{scan-config-DkRKu_ON.mjs → scan-config-S5fkzNhL.mjs} +1 -1
  76. package/dist/{scan-config-DkRKu_ON.mjs.map → scan-config-S5fkzNhL.mjs.map} +1 -1
  77. package/dist/{scanner-BLo3ssiF.mjs → scanner-0sc_pARB.mjs} +6 -6
  78. package/dist/{scanner-BLo3ssiF.mjs.map → scanner-0sc_pARB.mjs.map} +1 -1
  79. package/dist/server/http/index.d.mts +1 -1
  80. package/dist/server/http/index.mjs +16 -15
  81. package/dist/server/http/index.mjs.map +1 -1
  82. package/dist/{server-CmVCy0_b.mjs → server-BdGsBjPT.mjs} +20 -16
  83. package/dist/server-BdGsBjPT.mjs.map +1 -0
  84. package/dist/{server-CUtmhlno.d.mts → server-D1c-79hA.d.mts} +2 -2
  85. package/dist/{server-CUtmhlno.d.mts.map → server-D1c-79hA.d.mts.map} +1 -1
  86. package/dist/server.d.mts +1 -1
  87. package/dist/server.mjs +16 -15
  88. package/dist/{source-map-generator-Clb2Eqse.mjs → source-map-generator-DVDSWUZ1.mjs} +1 -1
  89. package/dist/{source-map-generator-Clb2Eqse.mjs.map → source-map-generator-DVDSWUZ1.mjs.map} +1 -1
  90. package/dist/{svelte-parser-CBN7Jxb7.mjs → svelte-parser-CVUIcakb.mjs} +3 -3
  91. package/dist/{svelte-parser-CBN7Jxb7.mjs.map → svelte-parser-CVUIcakb.mjs.map} +1 -1
  92. package/dist/templates/index.mjs +1 -1
  93. package/dist/{templates-BZbohJc8.mjs → templates-Dn-iZyzp.mjs} +1 -1
  94. package/dist/{templates-BZbohJc8.mjs.map → templates-Dn-iZyzp.mjs.map} +1 -1
  95. package/dist/testing/memory-provider.d.mts +1 -1
  96. package/dist/tools/annotations.mjs +1 -1
  97. package/dist/tools/availability.mjs +1 -1
  98. package/dist/{transaction-BWJt1KQb.mjs → transaction-DfrvGIfk.mjs} +4 -3
  99. package/dist/{transaction-BWJt1KQb.mjs.map → transaction-DfrvGIfk.mjs.map} +1 -1
  100. package/dist/{tsx-parser-go5V9zoc.mjs → tsx-parser-DgFdpsea.mjs} +1 -1
  101. package/dist/{tsx-parser-go5V9zoc.mjs.map → tsx-parser-DgFdpsea.mjs.map} +1 -1
  102. package/dist/tsx-parser-v8C-7cZv.mjs +2 -0
  103. package/dist/util/detect.mjs +1 -1
  104. package/dist/util/id.mjs +1 -1
  105. package/dist/{vue-parser-5Mp8w-mb.mjs → vue-parser-DvliI6UE.mjs} +3 -3
  106. package/dist/{vue-parser-5Mp8w-mb.mjs.map → vue-parser-DvliI6UE.mjs.map} +1 -1
  107. package/package.json +8 -4
  108. package/dist/branch-lifecycle-f4gI_eGo.mjs.map +0 -1
  109. package/dist/server-CmVCy0_b.mjs.map +0 -1
  110. package/dist/tsx-parser-DOQpJVgK.mjs +0 -2
  111. /package/dist/{index-A5iw3yDk.d.mts → index-PCQQBpIH.d.mts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"vue-parser-5Mp8w-mb.mjs","names":[],"sources":["../src/core/ast-scanner/vue-parser.ts"],"sourcesContent":["// ─── Vue SFC Parser for Scanner v2 ───\n// Parses .vue Single File Components using @vue/compiler-sfc.\n// Extracts ALL strings with structural context metadata.\n// Scanner does NOT classify — agent does. When in doubt, INCLUDE.\n\n// ─── Types ───\n\nimport type { ExtractedString } from './types.js'\nexport type { ExtractedString }\n\n// ─── Lazy-loaded @vue/compiler-sfc ───\n\ninterface VueCompilerSFC {\n parse: (source: string, options?: { filename?: string }) => {\n descriptor: {\n template: {\n content: string\n loc: { start: { line: number; column: number; offset: number } }\n ast?: VueTemplateNode\n } | null\n script: {\n content: string\n loc: { start: { line: number; column: number; offset: number } }\n lang?: string\n } | null\n scriptSetup: {\n content: string\n loc: { start: { line: number; column: number; offset: number } }\n lang?: string\n } | null\n }\n }\n compileTemplate: (options: {\n source: string\n filename: string\n id: string\n }) => {\n ast?: VueTemplateNode\n }\n}\n\n// Vue template AST node types\nconst NodeTypes = {\n ROOT: 0,\n ELEMENT: 1,\n TEXT: 2,\n COMMENT: 3,\n SIMPLE_EXPRESSION: 4,\n INTERPOLATION: 5,\n ATTRIBUTE: 6,\n DIRECTIVE: 7,\n COMPOUND_EXPRESSION: 8,\n IF: 9,\n IF_BRANCH: 10,\n FOR: 11,\n TEXT_CALL: 12,\n VNODE_CALL: 13,\n JS_CALL_EXPRESSION: 14,\n} as const\n\ninterface VueTemplateLoc {\n start: { line: number; column: number; offset: number }\n end: { line: number; column: number; offset: number }\n source: string\n}\n\ninterface VueTemplateNode {\n type: number\n loc: VueTemplateLoc\n children?: VueTemplateNode[]\n tag?: string // for ELEMENT\n props?: VueTemplateProp[]\n content?: VueTemplateNode | string // for INTERPOLATION, TEXT, SIMPLE_EXPRESSION\n branches?: VueTemplateNode[] // for IF\n}\n\ninterface VueTemplateProp {\n type: number // ATTRIBUTE (6) or DIRECTIVE (7)\n name: string\n loc: VueTemplateLoc\n value?: {\n type: number\n content: string\n loc: VueTemplateLoc\n }\n exp?: {\n type: number\n content: string\n loc: VueTemplateLoc\n }\n arg?: {\n type: number\n content: string\n loc: VueTemplateLoc\n }\n}\n\nlet _compiler: VueCompilerSFC | null = null\n\nasync function loadCompiler(): Promise<VueCompilerSFC> {\n if (_compiler) return _compiler\n try {\n // Dynamic import — @vue/compiler-sfc is optional\n const mod = await import('@vue/compiler-sfc')\n _compiler = mod as unknown as VueCompilerSFC\n return _compiler\n } catch {\n throw new Error(\n '@vue/compiler-sfc is required to parse .vue files. '\n + 'Install it with: pnpm add -D @vue/compiler-sfc',\n )\n }\n}\n\n// ─── Directives that contain code expressions (not user content) ───\n\nconst CODE_DIRECTIVES = new Set([\n 'if', 'else-if', 'else', 'show',\n 'for',\n 'on', // @click, v-on:click\n 'model',\n 'memo',\n 'once',\n 'pre',\n 'cloak',\n 'is',\n 'slot',\n 'key',\n])\n\n// Directive args that are code/binding expressions, not content\nconst CODE_DIRECTIVE_ARGS = new Set([\n 'class', 'style', // :class, :style → CSS bindings\n 'key',\n 'ref',\n 'is',\n])\n\n// Static attributes whose values are CSS, not content\nconst CSS_ATTRIBUTES = new Set([\n 'class', 'style',\n])\n\n// ─── Surrounding text helper ───\n\nconst SURROUNDING_MAX = 120\n\nfunction _getSurrounding(content: string, offset: number): string {\n const lines = content.split('\\n')\n let charCount = 0\n for (let i = 0; i < lines.length; i++) {\n const lineLen = (lines[i]?.length ?? 0) + 1 // +1 for newline\n if (charCount + lineLen > offset) {\n return (lines[i] ?? '').slice(0, SURROUNDING_MAX)\n }\n charCount += lineLen\n }\n return ''\n}\n\nfunction getSurroundingByLine(content: string, line: number): string {\n const lines = content.split('\\n')\n const idx = line - 1\n if (idx >= 0 && idx < lines.length) {\n return (lines[idx] ?? '').slice(0, SURROUNDING_MAX)\n }\n return ''\n}\n\n// ─── Template AST Walker ───\n\nfunction walkTemplate(\n node: VueTemplateNode,\n templateLineOffset: number,\n sfcContent: string,\n results: ExtractedString[],\n parentTag: string = '',\n): void {\n switch (node.type) {\n case NodeTypes.TEXT: {\n // Static text between tags\n const text = typeof node.content === 'string'\n ? node.content\n : (node.loc?.source ?? '')\n const trimmed = text.trim()\n if (trimmed.length > 0 && /\\S/.test(trimmed)) {\n results.push({\n value: trimmed,\n line: node.loc.start.line + templateLineOffset - 1,\n column: node.loc.start.column,\n context: 'template_text',\n scope: 'template',\n parent: parentTag,\n surrounding: getSurroundingByLine(sfcContent, node.loc.start.line + templateLineOffset - 1),\n })\n }\n break\n }\n\n case NodeTypes.INTERPOLATION: {\n // {{ expression }} — extract string literals from within\n if (node.content && typeof node.content !== 'string' && node.content.type === NodeTypes.SIMPLE_EXPRESSION) {\n const expr = typeof node.content.content === 'string'\n ? node.content.content\n : ''\n // Only extract if the expression itself is a string literal\n // e.g., {{ 'Hello' }} or {{ \"World\" }}\n const stringMatch = expr.match(/^(['\"`])(.+)\\1$/)\n if (stringMatch?.[2]) {\n results.push({\n value: stringMatch[2],\n line: node.loc.start.line + templateLineOffset - 1,\n column: node.loc.start.column,\n context: 'template_text',\n scope: 'template',\n parent: parentTag,\n surrounding: getSurroundingByLine(sfcContent, node.loc.start.line + templateLineOffset - 1),\n })\n }\n // If it's a variable reference like {{ greeting }}, skip — that's code\n }\n break\n }\n\n case NodeTypes.ELEMENT: {\n const tag = node.tag ?? ''\n\n // Process props/attributes\n if (node.props) {\n for (const prop of node.props) {\n processProp(prop, tag, templateLineOffset, sfcContent, results)\n }\n }\n\n // Recurse into children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, tag)\n }\n }\n break\n }\n\n case NodeTypes.IF: {\n // v-if creates branches — walk each branch's children\n if (node.branches) {\n for (const branch of node.branches) {\n if (branch.children) {\n for (const child of branch.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n }\n }\n break\n }\n\n case NodeTypes.IF_BRANCH: {\n // Walk children of if branch\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n case NodeTypes.FOR: {\n // v-for node — walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n case NodeTypes.ROOT: {\n // Root node — walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n case NodeTypes.COMPOUND_EXPRESSION: {\n // Compound expression — walk children\n if (node.children) {\n for (const child of node.children) {\n if (typeof child !== 'string') {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n }\n break\n }\n\n case NodeTypes.TEXT_CALL: {\n // Text call node (wrapper for text in v-if, etc.) — walk content\n if (node.content && typeof node.content !== 'string') {\n walkTemplate(node.content, templateLineOffset, sfcContent, results, parentTag)\n }\n // Also walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n default: {\n // For any unknown node type, try to walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n }\n}\n\nfunction processProp(\n prop: VueTemplateProp,\n parentTag: string,\n templateLineOffset: number,\n sfcContent: string,\n results: ExtractedString[],\n): void {\n if (prop.type === NodeTypes.ATTRIBUTE) {\n // Static attribute: title=\"Hello\"\n if (!prop.value) return\n\n const attrName = prop.name\n const attrValue = prop.value.content\n\n if (!attrValue || attrValue.trim().length === 0) return\n\n // CSS attributes get css_class context\n if (CSS_ATTRIBUTES.has(attrName)) {\n results.push({\n value: attrValue,\n line: prop.value.loc.start.line + templateLineOffset - 1,\n column: prop.value.loc.start.column,\n context: 'css_class',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(sfcContent, prop.value.loc.start.line + templateLineOffset - 1),\n })\n return\n }\n\n results.push({\n value: attrValue,\n line: prop.value.loc.start.line + templateLineOffset - 1,\n column: prop.value.loc.start.column,\n context: 'template_attribute',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(sfcContent, prop.value.loc.start.line + templateLineOffset - 1),\n })\n } else if (prop.type === NodeTypes.DIRECTIVE) {\n // Dynamic directive: :title=\"expr\", v-bind:title=\"expr\", @click=\"handler\"\n const directiveName = prop.name // 'bind', 'on', 'if', 'for', etc.\n\n // Skip code directives entirely (v-if, v-for, @click, etc.)\n if (CODE_DIRECTIVES.has(directiveName)) return\n\n // For v-bind (:attr=\"expr\"), check if the arg is a code binding\n if (directiveName === 'bind' && prop.arg) {\n const argName = typeof prop.arg.content === 'string' ? prop.arg.content : ''\n if (CODE_DIRECTIVE_ARGS.has(argName)) return\n }\n\n // Extract string literals from directive expressions\n if (prop.exp) {\n const expr = typeof prop.exp.content === 'string' ? prop.exp.content : ''\n const argName = prop.arg && typeof prop.arg.content === 'string' ? prop.arg.content : ''\n\n // Check if expression is a simple string literal: 'text' or \"text\"\n const stringMatch = expr.match(/^(['\"`])(.+)\\1$/)\n if (stringMatch?.[2]) {\n results.push({\n value: stringMatch[2],\n line: prop.exp.loc.start.line + templateLineOffset - 1,\n column: prop.exp.loc.start.column,\n context: 'template_attribute',\n scope: 'template',\n parent: parentTag,\n parentProperty: argName || directiveName,\n surrounding: getSurroundingByLine(sfcContent, prop.exp.loc.start.line + templateLineOffset - 1),\n })\n }\n // If it's a variable or complex expression, skip — scanner doesn't interpret code\n }\n }\n}\n\n// ─── Script Block Parsing ───\n\n// tsx-parser is being built in parallel; define the interface we expect\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 // tsx-parser not yet available — fall back to no script parsing\n return null\n }\n}\n\n/**\n * Map script block lang to a file extension that tsx-parser's getScriptKind understands.\n * Without this, .vue files default to ScriptKind.JS — causing TypeScript type annotations\n * (e.g. defineEmits<{ 'update:modelValue': [...] }>) to be misclassified as object literals.\n */\nfunction resolveScriptFileName(vueFileName: string, lang?: string): string {\n if (lang === 'tsx') return vueFileName.replace(/\\.vue$/, '.tsx')\n if (lang === 'ts') return vueFileName.replace(/\\.vue$/, '.ts')\n if (lang === 'jsx') return vueFileName.replace(/\\.vue$/, '.jsx')\n return vueFileName.replace(/\\.vue$/, '.js')\n}\n\nfunction parseScriptBlock(\n scriptContent: string,\n scriptStartLine: number,\n fileName: string,\n parseTsx: TsxParserFn,\n lang?: string,\n): ExtractedString[] {\n // Resolve filename with correct extension for TypeScript parser\n const resolvedFileName = resolveScriptFileName(fileName, lang)\n const scriptResults = parseTsx(scriptContent, resolvedFileName)\n\n // Adjust line numbers by script block offset\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 parseVue(content: string, fileName: string): Promise<ExtractedString[]> {\n const compiler = await loadCompiler()\n const results: ExtractedString[] = []\n\n const { descriptor } = compiler.parse(content, { filename: fileName })\n\n // ─── Template Block ───\n if (descriptor.template) {\n const templateLineOffset = descriptor.template.loc.start.line\n const templateAst = descriptor.template.ast\n\n if (templateAst) {\n walkTemplate(templateAst, templateLineOffset, content, results)\n } else {\n // Fallback: compile template to get AST\n try {\n const compiled = compiler.compileTemplate({\n source: descriptor.template.content,\n filename: fileName,\n id: 'ast-scanner',\n })\n if (compiled.ast) {\n walkTemplate(compiled.ast, templateLineOffset, content, results)\n }\n } catch {\n // If template compilation fails, we skip template extraction\n // This is acceptable — malformed templates shouldn't block scanning\n }\n }\n }\n\n // ─── Script Block ───\n const scriptBlock = descriptor.scriptSetup ?? descriptor.script\n if (scriptBlock) {\n const parseTsx = await loadTsxParser()\n if (parseTsx) {\n const scriptStartLine = scriptBlock.loc.start.line\n const scriptResults = parseScriptBlock(\n scriptBlock.content,\n scriptStartLine,\n fileName,\n parseTsx,\n scriptBlock.lang,\n )\n results.push(...scriptResults)\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":"vue-parser-DvliI6UE.mjs","names":[],"sources":["../src/core/ast-scanner/vue-parser.ts"],"sourcesContent":["// ─── Vue SFC Parser for Scanner v2 ───\n// Parses .vue Single File Components using @vue/compiler-sfc.\n// Extracts ALL strings with structural context metadata.\n// Scanner does NOT classify — agent does. When in doubt, INCLUDE.\n\n// ─── Types ───\n\nimport type { ExtractedString } from './types.js'\nexport type { ExtractedString }\n\n// ─── Lazy-loaded @vue/compiler-sfc ───\n\ninterface VueCompilerSFC {\n parse: (source: string, options?: { filename?: string }) => {\n descriptor: {\n template: {\n content: string\n loc: { start: { line: number; column: number; offset: number } }\n ast?: VueTemplateNode\n } | null\n script: {\n content: string\n loc: { start: { line: number; column: number; offset: number } }\n lang?: string\n } | null\n scriptSetup: {\n content: string\n loc: { start: { line: number; column: number; offset: number } }\n lang?: string\n } | null\n }\n }\n compileTemplate: (options: {\n source: string\n filename: string\n id: string\n }) => {\n ast?: VueTemplateNode\n }\n}\n\n// Vue template AST node types\nconst NodeTypes = {\n ROOT: 0,\n ELEMENT: 1,\n TEXT: 2,\n COMMENT: 3,\n SIMPLE_EXPRESSION: 4,\n INTERPOLATION: 5,\n ATTRIBUTE: 6,\n DIRECTIVE: 7,\n COMPOUND_EXPRESSION: 8,\n IF: 9,\n IF_BRANCH: 10,\n FOR: 11,\n TEXT_CALL: 12,\n VNODE_CALL: 13,\n JS_CALL_EXPRESSION: 14,\n} as const\n\ninterface VueTemplateLoc {\n start: { line: number; column: number; offset: number }\n end: { line: number; column: number; offset: number }\n source: string\n}\n\ninterface VueTemplateNode {\n type: number\n loc: VueTemplateLoc\n children?: VueTemplateNode[]\n tag?: string // for ELEMENT\n props?: VueTemplateProp[]\n content?: VueTemplateNode | string // for INTERPOLATION, TEXT, SIMPLE_EXPRESSION\n branches?: VueTemplateNode[] // for IF\n}\n\ninterface VueTemplateProp {\n type: number // ATTRIBUTE (6) or DIRECTIVE (7)\n name: string\n loc: VueTemplateLoc\n value?: {\n type: number\n content: string\n loc: VueTemplateLoc\n }\n exp?: {\n type: number\n content: string\n loc: VueTemplateLoc\n }\n arg?: {\n type: number\n content: string\n loc: VueTemplateLoc\n }\n}\n\nlet _compiler: VueCompilerSFC | null = null\n\nasync function loadCompiler(): Promise<VueCompilerSFC> {\n if (_compiler) return _compiler\n try {\n // Dynamic import — @vue/compiler-sfc is optional\n const mod = await import('@vue/compiler-sfc')\n _compiler = mod as unknown as VueCompilerSFC\n return _compiler\n } catch {\n throw new Error(\n '@vue/compiler-sfc is required to parse .vue files. '\n + 'Install it with: pnpm add -D @vue/compiler-sfc',\n )\n }\n}\n\n// ─── Directives that contain code expressions (not user content) ───\n\nconst CODE_DIRECTIVES = new Set([\n 'if', 'else-if', 'else', 'show',\n 'for',\n 'on', // @click, v-on:click\n 'model',\n 'memo',\n 'once',\n 'pre',\n 'cloak',\n 'is',\n 'slot',\n 'key',\n])\n\n// Directive args that are code/binding expressions, not content\nconst CODE_DIRECTIVE_ARGS = new Set([\n 'class', 'style', // :class, :style → CSS bindings\n 'key',\n 'ref',\n 'is',\n])\n\n// Static attributes whose values are CSS, not content\nconst CSS_ATTRIBUTES = new Set([\n 'class', 'style',\n])\n\n// ─── Surrounding text helper ───\n\nconst SURROUNDING_MAX = 120\n\nfunction _getSurrounding(content: string, offset: number): string {\n const lines = content.split('\\n')\n let charCount = 0\n for (let i = 0; i < lines.length; i++) {\n const lineLen = (lines[i]?.length ?? 0) + 1 // +1 for newline\n if (charCount + lineLen > offset) {\n return (lines[i] ?? '').slice(0, SURROUNDING_MAX)\n }\n charCount += lineLen\n }\n return ''\n}\n\nfunction getSurroundingByLine(content: string, line: number): string {\n const lines = content.split('\\n')\n const idx = line - 1\n if (idx >= 0 && idx < lines.length) {\n return (lines[idx] ?? '').slice(0, SURROUNDING_MAX)\n }\n return ''\n}\n\n// ─── Template AST Walker ───\n\nfunction walkTemplate(\n node: VueTemplateNode,\n templateLineOffset: number,\n sfcContent: string,\n results: ExtractedString[],\n parentTag: string = '',\n): void {\n switch (node.type) {\n case NodeTypes.TEXT: {\n // Static text between tags\n const text = typeof node.content === 'string'\n ? node.content\n : (node.loc?.source ?? '')\n const trimmed = text.trim()\n if (trimmed.length > 0 && /\\S/.test(trimmed)) {\n results.push({\n value: trimmed,\n line: node.loc.start.line + templateLineOffset - 1,\n column: node.loc.start.column,\n context: 'template_text',\n scope: 'template',\n parent: parentTag,\n surrounding: getSurroundingByLine(sfcContent, node.loc.start.line + templateLineOffset - 1),\n })\n }\n break\n }\n\n case NodeTypes.INTERPOLATION: {\n // {{ expression }} — extract string literals from within\n if (node.content && typeof node.content !== 'string' && node.content.type === NodeTypes.SIMPLE_EXPRESSION) {\n const expr = typeof node.content.content === 'string'\n ? node.content.content\n : ''\n // Only extract if the expression itself is a string literal\n // e.g., {{ 'Hello' }} or {{ \"World\" }}\n const stringMatch = expr.match(/^(['\"`])(.+)\\1$/)\n if (stringMatch?.[2]) {\n results.push({\n value: stringMatch[2],\n line: node.loc.start.line + templateLineOffset - 1,\n column: node.loc.start.column,\n context: 'template_text',\n scope: 'template',\n parent: parentTag,\n surrounding: getSurroundingByLine(sfcContent, node.loc.start.line + templateLineOffset - 1),\n })\n }\n // If it's a variable reference like {{ greeting }}, skip — that's code\n }\n break\n }\n\n case NodeTypes.ELEMENT: {\n const tag = node.tag ?? ''\n\n // Process props/attributes\n if (node.props) {\n for (const prop of node.props) {\n processProp(prop, tag, templateLineOffset, sfcContent, results)\n }\n }\n\n // Recurse into children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, tag)\n }\n }\n break\n }\n\n case NodeTypes.IF: {\n // v-if creates branches — walk each branch's children\n if (node.branches) {\n for (const branch of node.branches) {\n if (branch.children) {\n for (const child of branch.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n }\n }\n break\n }\n\n case NodeTypes.IF_BRANCH: {\n // Walk children of if branch\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n case NodeTypes.FOR: {\n // v-for node — walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n case NodeTypes.ROOT: {\n // Root node — walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n case NodeTypes.COMPOUND_EXPRESSION: {\n // Compound expression — walk children\n if (node.children) {\n for (const child of node.children) {\n if (typeof child !== 'string') {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n }\n break\n }\n\n case NodeTypes.TEXT_CALL: {\n // Text call node (wrapper for text in v-if, etc.) — walk content\n if (node.content && typeof node.content !== 'string') {\n walkTemplate(node.content, templateLineOffset, sfcContent, results, parentTag)\n }\n // Also walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n\n default: {\n // For any unknown node type, try to walk children\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, templateLineOffset, sfcContent, results, parentTag)\n }\n }\n break\n }\n }\n}\n\nfunction processProp(\n prop: VueTemplateProp,\n parentTag: string,\n templateLineOffset: number,\n sfcContent: string,\n results: ExtractedString[],\n): void {\n if (prop.type === NodeTypes.ATTRIBUTE) {\n // Static attribute: title=\"Hello\"\n if (!prop.value) return\n\n const attrName = prop.name\n const attrValue = prop.value.content\n\n if (!attrValue || attrValue.trim().length === 0) return\n\n // CSS attributes get css_class context\n if (CSS_ATTRIBUTES.has(attrName)) {\n results.push({\n value: attrValue,\n line: prop.value.loc.start.line + templateLineOffset - 1,\n column: prop.value.loc.start.column,\n context: 'css_class',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(sfcContent, prop.value.loc.start.line + templateLineOffset - 1),\n })\n return\n }\n\n results.push({\n value: attrValue,\n line: prop.value.loc.start.line + templateLineOffset - 1,\n column: prop.value.loc.start.column,\n context: 'template_attribute',\n scope: 'template',\n parent: parentTag,\n parentProperty: attrName,\n surrounding: getSurroundingByLine(sfcContent, prop.value.loc.start.line + templateLineOffset - 1),\n })\n } else if (prop.type === NodeTypes.DIRECTIVE) {\n // Dynamic directive: :title=\"expr\", v-bind:title=\"expr\", @click=\"handler\"\n const directiveName = prop.name // 'bind', 'on', 'if', 'for', etc.\n\n // Skip code directives entirely (v-if, v-for, @click, etc.)\n if (CODE_DIRECTIVES.has(directiveName)) return\n\n // For v-bind (:attr=\"expr\"), check if the arg is a code binding\n if (directiveName === 'bind' && prop.arg) {\n const argName = typeof prop.arg.content === 'string' ? prop.arg.content : ''\n if (CODE_DIRECTIVE_ARGS.has(argName)) return\n }\n\n // Extract string literals from directive expressions\n if (prop.exp) {\n const expr = typeof prop.exp.content === 'string' ? prop.exp.content : ''\n const argName = prop.arg && typeof prop.arg.content === 'string' ? prop.arg.content : ''\n\n // Check if expression is a simple string literal: 'text' or \"text\"\n const stringMatch = expr.match(/^(['\"`])(.+)\\1$/)\n if (stringMatch?.[2]) {\n results.push({\n value: stringMatch[2],\n line: prop.exp.loc.start.line + templateLineOffset - 1,\n column: prop.exp.loc.start.column,\n context: 'template_attribute',\n scope: 'template',\n parent: parentTag,\n parentProperty: argName || directiveName,\n surrounding: getSurroundingByLine(sfcContent, prop.exp.loc.start.line + templateLineOffset - 1),\n })\n }\n // If it's a variable or complex expression, skip — scanner doesn't interpret code\n }\n }\n}\n\n// ─── Script Block Parsing ───\n\n// tsx-parser is being built in parallel; define the interface we expect\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 // tsx-parser not yet available — fall back to no script parsing\n return null\n }\n}\n\n/**\n * Map script block lang to a file extension that tsx-parser's getScriptKind understands.\n * Without this, .vue files default to ScriptKind.JS — causing TypeScript type annotations\n * (e.g. defineEmits<{ 'update:modelValue': [...] }>) to be misclassified as object literals.\n */\nfunction resolveScriptFileName(vueFileName: string, lang?: string): string {\n if (lang === 'tsx') return vueFileName.replace(/\\.vue$/, '.tsx')\n if (lang === 'ts') return vueFileName.replace(/\\.vue$/, '.ts')\n if (lang === 'jsx') return vueFileName.replace(/\\.vue$/, '.jsx')\n return vueFileName.replace(/\\.vue$/, '.js')\n}\n\nfunction parseScriptBlock(\n scriptContent: string,\n scriptStartLine: number,\n fileName: string,\n parseTsx: TsxParserFn,\n lang?: string,\n): ExtractedString[] {\n // Resolve filename with correct extension for TypeScript parser\n const resolvedFileName = resolveScriptFileName(fileName, lang)\n const scriptResults = parseTsx(scriptContent, resolvedFileName)\n\n // Adjust line numbers by script block offset\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 parseVue(content: string, fileName: string): Promise<ExtractedString[]> {\n const compiler = await loadCompiler()\n const results: ExtractedString[] = []\n\n const { descriptor } = compiler.parse(content, { filename: fileName })\n\n // ─── Template Block ───\n if (descriptor.template) {\n const templateLineOffset = descriptor.template.loc.start.line\n const templateAst = descriptor.template.ast\n\n if (templateAst) {\n walkTemplate(templateAst, templateLineOffset, content, results)\n } else {\n // Fallback: compile template to get AST\n try {\n const compiled = compiler.compileTemplate({\n source: descriptor.template.content,\n filename: fileName,\n id: 'ast-scanner',\n })\n if (compiled.ast) {\n walkTemplate(compiled.ast, templateLineOffset, content, results)\n }\n } catch {\n // If template compilation fails, we skip template extraction\n // This is acceptable — malformed templates shouldn't block scanning\n }\n }\n }\n\n // ─── Script Block ───\n const scriptBlock = descriptor.scriptSetup ?? descriptor.script\n if (scriptBlock) {\n const parseTsx = await loadTsxParser()\n if (parseTsx) {\n const scriptStartLine = scriptBlock.loc.start.line\n const scriptResults = parseScriptBlock(\n scriptBlock.content,\n scriptStartLine,\n fileName,\n parseTsx,\n scriptBlock.lang,\n )\n results.push(...scriptResults)\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentrain/mcp",
3
- "version": "3.6.7",
3
+ "version": "3.7.0",
4
4
  "mcpName": "io.github.Contentrain/contentrain",
5
5
  "license": "MIT",
6
6
  "description": "Local-first MCP server for AI-generated content governance — 27 deterministic tools (22 core + 5 media), stdio + HTTP transports, Local / GitHub / GitLab providers",
@@ -126,6 +126,10 @@
126
126
  "types": "./dist/git/branch-lifecycle.d.mts",
127
127
  "import": "./dist/git/branch-lifecycle.mjs"
128
128
  },
129
+ "./git/base-branch": {
130
+ "types": "./dist/git/base-branch.d.mts",
131
+ "import": "./dist/git/base-branch.mjs"
132
+ },
129
133
  "./git/errors": {
130
134
  "types": "./dist/git/errors.d.mts",
131
135
  "import": "./dist/git/errors.mjs"
@@ -178,7 +182,7 @@
178
182
  "simple-git": "^3.36.0",
179
183
  "typescript": "^5.7.0",
180
184
  "zod": "^3.24.0",
181
- "@contentrain/types": "1.24.0"
185
+ "@contentrain/types": "1.26.0"
182
186
  },
183
187
  "optionalDependencies": {
184
188
  "@astrojs/compiler": "^2.10.0",
@@ -208,8 +212,8 @@
208
212
  "vitest": "^3.0.0"
209
213
  },
210
214
  "scripts": {
211
- "build": "tsdown src/index.ts src/server.ts src/core/config.ts src/core/context.ts src/core/model-manager.ts src/core/content-manager.ts src/core/meta-manager.ts src/core/validator/index.ts src/core/scanner.ts src/core/scan-config.ts src/core/doctor.ts src/core/graph-builder.ts src/core/apply-manager.ts src/util/detect.ts src/util/fs.ts src/util/id.ts src/git/identity.ts src/git/transaction.ts src/git/branch-lifecycle.ts src/git/errors.ts src/git/reconcile.ts src/tools/annotations.ts src/tools/availability.ts src/testing/conformance.ts src/testing/memory-provider.ts src/templates/index.ts src/core/contracts/index.ts src/core/ops/index.ts src/core/migration/index.ts src/core/overlay-reader.ts src/providers/local/index.ts src/providers/github/index.ts src/providers/gitlab/index.ts src/server/http/index.ts --format esm --dts --external typescript",
212
- "dev": "tsdown src/index.ts src/server.ts src/core/config.ts src/core/context.ts src/core/model-manager.ts src/core/content-manager.ts src/core/meta-manager.ts src/core/validator/index.ts src/core/scanner.ts src/core/scan-config.ts src/core/doctor.ts src/core/graph-builder.ts src/core/apply-manager.ts src/util/detect.ts src/util/fs.ts src/util/id.ts src/git/identity.ts src/git/transaction.ts src/git/branch-lifecycle.ts src/git/errors.ts src/git/reconcile.ts src/tools/annotations.ts src/tools/availability.ts src/testing/conformance.ts src/testing/memory-provider.ts src/templates/index.ts src/core/contracts/index.ts src/core/ops/index.ts src/core/migration/index.ts src/core/overlay-reader.ts src/providers/local/index.ts src/providers/github/index.ts src/providers/gitlab/index.ts src/server/http/index.ts --format esm --dts --external typescript --watch",
215
+ "build": "tsdown src/index.ts src/server.ts src/core/config.ts src/core/context.ts src/core/model-manager.ts src/core/content-manager.ts src/core/meta-manager.ts src/core/validator/index.ts src/core/scanner.ts src/core/scan-config.ts src/core/doctor.ts src/core/graph-builder.ts src/core/apply-manager.ts src/util/detect.ts src/util/fs.ts src/util/id.ts src/git/identity.ts src/git/transaction.ts src/git/branch-lifecycle.ts src/git/base-branch.ts src/git/errors.ts src/git/reconcile.ts src/tools/annotations.ts src/tools/availability.ts src/testing/conformance.ts src/testing/memory-provider.ts src/templates/index.ts src/core/contracts/index.ts src/core/ops/index.ts src/core/migration/index.ts src/core/overlay-reader.ts src/providers/local/index.ts src/providers/github/index.ts src/providers/gitlab/index.ts src/server/http/index.ts --format esm --dts --external typescript",
216
+ "dev": "tsdown src/index.ts src/server.ts src/core/config.ts src/core/context.ts src/core/model-manager.ts src/core/content-manager.ts src/core/meta-manager.ts src/core/validator/index.ts src/core/scanner.ts src/core/scan-config.ts src/core/doctor.ts src/core/graph-builder.ts src/core/apply-manager.ts src/util/detect.ts src/util/fs.ts src/util/id.ts src/git/identity.ts src/git/transaction.ts src/git/branch-lifecycle.ts src/git/base-branch.ts src/git/errors.ts src/git/reconcile.ts src/tools/annotations.ts src/tools/availability.ts src/testing/conformance.ts src/testing/memory-provider.ts src/templates/index.ts src/core/contracts/index.ts src/core/ops/index.ts src/core/migration/index.ts src/core/overlay-reader.ts src/providers/local/index.ts src/providers/github/index.ts src/providers/gitlab/index.ts src/server/http/index.ts --format esm --dts --external typescript --watch",
213
217
  "test": "vitest run",
214
218
  "typecheck": "tsc --noEmit",
215
219
  "clean": "rm -rf dist"
@@ -1 +0,0 @@
1
- {"version":3,"file":"branch-lifecycle-f4gI_eGo.mjs","names":[],"sources":["../src/git/base-branch.ts","../src/git/branch-lifecycle.ts"],"sourcesContent":["import type { SimpleGit } from 'simple-git'\nimport type { ContentrainConfig } from '@contentrain/types'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\n\n/**\n * Resolve the base branch — the branch a local auto-merge advances with\n * `update-ref`, pushes, and merges into `contentrain` before every write.\n *\n * Precedence, the same at every call site:\n * 1. `CONTENTRAIN_BRANCH` env — a per-process override;\n * 2. `config.repository.default_branch`;\n * 3. the remote's default (`refs/remotes/<remote>/HEAD`), when that branch\n * exists locally;\n * 4. a local `main`, then a local `master`;\n * 5. the checked-out branch — only when none of the above exists;\n * 6. `'main'`.\n *\n * The checked-out branch used to be step 3. That made a developer's feature\n * branch the base of every content write run while it was checked out: its\n * code commits were merged into `contentrain`, the feature branch itself was\n * advanced and pushed, and the code reached the real default branch on the\n * next write by anyone else (#227). The checked-out branch is where the\n * developer happens to be, not where content belongs.\n *\n * `currentBranch` may be passed when the caller already has it, to save a\n * spawn; it is only consulted as the last resort.\n */\nexport async function resolveBaseBranch(\n git: SimpleGit,\n config: ContentrainConfig | null | undefined,\n options: { remoteName?: string, currentBranch?: string } = {},\n): Promise<string> {\n const fromEnv = process.env['CONTENTRAIN_BRANCH']?.trim()\n if (fromEnv) return fromEnv\n const fromConfig = config?.repository?.default_branch?.trim()\n if (fromConfig) return fromConfig\n\n const remoteName = options.remoteName ?? process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n const remoteHead = `refs/remotes/${remoteName}/HEAD`\n\n // One spawn answers \"which of main/master exist\" and \"where does the\n // remote HEAD point\". for-each-ref prints nothing for a missing pattern.\n const refs = new Map<string, string>()\n try {\n const raw = await git.raw([\n 'for-each-ref', '--format=%(refname) %(symref)',\n 'refs/heads/main', 'refs/heads/master', remoteHead,\n ])\n for (const line of raw.split('\\n')) {\n const [name, symref = ''] = line.trim().split(' ')\n if (name) refs.set(name, symref)\n }\n } catch {\n // Not a repository or no refs yet — fall through to the checked-out branch.\n }\n\n const remoteDefault = refs.get(remoteHead)?.replace(`refs/remotes/${remoteName}/`, '')\n if (remoteDefault && remoteDefault !== CONTENTRAIN_BRANCH && await localBranchExists(git, remoteDefault, refs)) {\n return remoteDefault\n }\n if (refs.has('refs/heads/main')) return 'main'\n if (refs.has('refs/heads/master')) return 'master'\n\n const current = options.currentBranch\n ?? (await git.raw(['branch', '--show-current']).catch(() => '')).trim()\n if (current && current !== CONTENTRAIN_BRANCH) return current\n return 'main'\n}\n\nasync function localBranchExists(git: SimpleGit, branch: string, known: Map<string, string>): Promise<boolean> {\n if (known.has(`refs/heads/${branch}`)) return true\n try {\n return (await git.raw(['for-each-ref', '--format=%(refname)', `refs/heads/${branch}`])).trim() !== ''\n } catch {\n return false\n }\n}\n","import { type SimpleGit } from 'simple-git'\nimport { createGit } from '../git/identity.js'\nimport { CONTENTRAIN_BRANCH, type ContentrainConfig } from '@contentrain/types'\nimport { readConfig } from '../core/config.js'\nimport { NETWORK_UNSAFE } from './identity.js'\nimport { resolveBaseBranch } from './base-branch.js'\n\nexport interface CleanupResult {\n deleted: number\n remaining: number\n deletedBranches: string[]\n}\n\nexport interface BranchHealthCheck {\n total: number\n merged: number\n unmerged: number\n warning: boolean\n blocked: boolean\n message?: string\n}\n\n/**\n * Lists all local contentrain/* branches, deletes those already merged\n * into the base branch, and returns the count of remaining unmerged ones.\n */\nexport async function cleanupMergedBranches(projectRoot: string): Promise<CleanupResult> {\n const git = createGit(projectRoot)\n const config = await readConfig(projectRoot)\n\n // Determine base branch\n const baseBranch = await resolveBaseBranch(git, config)\n\n // Get all local branches (exclude the dedicated contentrain branch itself)\n const branchSummary = await git.branchLocal()\n const contentrainBranches = branchSummary.all\n .filter(b => b.startsWith('cr/'))\n .filter(b => b !== CONTENTRAIN_BRANCH)\n\n if (contentrainBranches.length === 0) {\n return { deleted: 0, remaining: 0, deletedBranches: [] }\n }\n\n // Check merged into the dedicated contentrain branch, falling back to baseBranch\n let mergedSet: Set<string>\n try {\n mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, CONTENTRAIN_BRANCH)\n } catch {\n // Contentrain branch may not exist yet (pre-init); fall back to baseBranch\n try {\n mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, baseBranch)\n } catch {\n // Base branch may not exist either; nothing is merged\n return { deleted: 0, remaining: contentrainBranches.length, deletedBranches: [] }\n }\n }\n\n const mergedContentrain = contentrainBranches.filter(b => mergedSet.has(b))\n const deletedBranches: string[] = []\n\n // Determine retention period (days). Default: 30\n const retentionDays = config?.branchRetention ?? 30\n const retentionMs = retentionDays * 24 * 60 * 60 * 1000\n const now = Date.now()\n\n // Delete merged branches only if older than retention period\n for (const branch of mergedContentrain) {\n try {\n const timestampRaw = (await git.raw(['log', '-1', '--format=%ct', branch])).trim()\n const commitTimestamp = Number(timestampRaw) * 1000\n if (now - commitTimestamp < retentionMs) {\n continue // Branch is within retention period — keep it\n }\n // -D, not -d: classifyMergedBranches already proved merged-ness (including\n // patch-id equivalence after a base-history rewrite, which -d's\n // ancestry-only check would refuse).\n await git.raw(['branch', '-D', branch])\n deletedBranches.push(branch)\n } catch {\n // Branch may be checked out, locked, or log failed — skip\n }\n }\n\n const remaining = contentrainBranches.length - deletedBranches.length\n return { deleted: deletedBranches.length, remaining, deletedBranches }\n}\n\n/**\n * Check branch health: count contentrain/* branches and return warning/blocked status.\n * - 50+ branches: warning\n * - 80+ branches: blocked\n */\nexport async function checkBranchHealth(projectRoot: string): Promise<BranchHealthCheck> {\n const git = createGit(projectRoot)\n const config = await readConfig(projectRoot)\n\n const baseBranch = await resolveBaseBranch(git, config)\n\n const branchSummary = await git.branchLocal()\n const contentrainBranches = branchSummary.all\n .filter(b => b.startsWith('cr/'))\n .filter(b => b !== CONTENTRAIN_BRANCH)\n const total = contentrainBranches.length\n\n const warnLimit = config?.branchWarnLimit ?? 50\n const blockLimit = config?.branchBlockLimit ?? 80\n\n // Count merged into contentrain branch, falling back to baseBranch.\n // This gate runs before EVERY write: the patch-id fallback (which rescues\n // rewrite-orphaned merged branches from being counted as unmerged) only\n // matters once the ancestry-unmerged count could trip the warning, so\n // below `warnLimit` the check costs exactly one `branch --merged`.\n let mergedCount = 0\n const classifyOpts = { fallbackThreshold: warnLimit }\n try {\n const mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, CONTENTRAIN_BRANCH, classifyOpts)\n mergedCount = mergedSet.size\n } catch {\n // Contentrain branch may not exist yet (pre-init); fall back to baseBranch\n try {\n const mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, baseBranch, classifyOpts)\n mergedCount = mergedSet.size\n } catch {\n // ignore — neither branch exists\n }\n }\n const unmerged = total - mergedCount\n const warning = unmerged >= warnLimit\n const blocked = unmerged >= blockLimit\n\n let message: string | undefined\n if (blocked) {\n message = `BLOCKED: ${unmerged} active contentrain branches (limit: ${blockLimit}). Run cleanup or merge/delete old branches before creating new ones.`\n } else if (warning) {\n message = `WARNING: ${unmerged} active contentrain branches. Consider merging or deleting old branches (warning at ${warnLimit}, blocked at ${blockLimit}).`\n }\n\n return { total, merged: mergedCount, unmerged, warning, blocked, message }\n}\n\nexport interface BranchDiffResult {\n /** The feature branch the diff was computed from. */\n branch: string\n /** The base ref the diff was computed against. Defaults to the `contentrain` branch. */\n base: string\n /** `git diff --stat` output — human-readable summary. */\n stat: string\n /** Raw unified diff. */\n patch: string\n /** Number of files touched in the diff. */\n filesChanged: number\n}\n\n/**\n * Compute the diff between a feature branch and its base.\n *\n * Defaults `base` to `CONTENTRAIN_BRANCH` — the singleton content-\n * tracking branch every feature branch forks from. Passing the repo's\n * default branch (e.g. `main`) is almost always a bug: when\n * `contentrain` is ahead of `main`, the diff picks up unrelated\n * historical content changes that the feature branch did not produce.\n *\n * Used by `contentrain serve` (branch detail view), the `contentrain\n * diff` CLI command, and any Studio-side driver that needs to preview\n * a feature branch before approving it.\n */\nexport async function branchDiff(\n projectRoot: string,\n opts: { branch: string, base?: string },\n): Promise<BranchDiffResult> {\n const git = createGit(projectRoot)\n const base = opts.base ?? CONTENTRAIN_BRANCH\n const range = `${base}...${opts.branch}`\n\n const [stat, patch, summary] = await Promise.all([\n git.diff([range, '--stat']),\n git.diff([range]),\n git.diffSummary([range]),\n ])\n\n return {\n branch: opts.branch,\n base,\n stat,\n patch,\n filesChanged: summary.changed,\n }\n}\n\n// ─── Merged-state classification (ancestry + patch-id fallback) ───\n\n/**\n * Merged-verdict caches.\n *\n * - `pairVerdictCache` — keyed by `(tipSha, intoTipSha, cap)`: the\n * relationship between two FIXED commits never changes, so entries are\n * permanently valid.\n * - `mergedTipCache` — keyed by `(tipSha, into NAME)`: once a tip is merged\n * into a branch, it stays merged as that branch advances (merged-ness is\n * monotonic), so positives survive `contentrain` moving forward. This is\n * what keeps the per-write branch-health gate cheap in long-lived\n * processes (MCP server, `contentrain serve`).\n */\nconst pairVerdictCache = new Map<string, boolean>()\nconst mergedTipCache = new Map<string, true>()\nconst MERGED_CACHE_LIMIT = 10_000\nconst DEFAULT_MAX_CHERRY_COMMITS = 200\n/** Concurrent `git cherry` subprocesses during classification. */\nconst CHERRY_CONCURRENCY = 8\n\nasync function isTipMerged(\n git: SimpleGit,\n tip: string,\n intoTip: string,\n maxCherryCommits: number,\n intoName?: string,\n): Promise<boolean> {\n const tipKey = intoName ? `${tip}→${intoName}` : undefined\n if (tipKey && mergedTipCache.has(tipKey)) return true\n const pairKey = `${tip}:${intoTip}:${maxCherryCommits}`\n const cached = pairVerdictCache.get(pairKey)\n if (cached !== undefined) return cached\n\n let merged = false\n try {\n // ONE subprocess answers both questions: `git cherry intoTip tip` lists\n // tip's commits missing from intoTip by ancestry — empty output means\n // ancestor (merged); otherwise a line without `+` is patch-id-equivalent\n // to a commit already in intoTip (survives base-history rewrites).\n // Deliberately NOT `merge-base --is-ancestor`: that plumbing signals via\n // exit code with EMPTY stderr, and simple-git reports exit-code-only\n // failures as success, silently inverting the check. Bounded by\n // maxCherryCommits so rewrite-orphaned deep histories cannot stall the\n // hot pre-write gate.\n const lines = (await git.raw(['cherry', intoTip, tip]))\n .split('\\n').map(line => line.trim()).filter(Boolean)\n if (lines.length === 0) {\n merged = true\n } else if (lines.length <= maxCherryCommits) {\n merged = !lines.some(line => line.startsWith('+'))\n }\n } catch {\n merged = false\n }\n\n if (pairVerdictCache.size >= MERGED_CACHE_LIMIT) pairVerdictCache.clear()\n pairVerdictCache.set(pairKey, merged)\n if (merged && tipKey) {\n if (mergedTipCache.size >= MERGED_CACHE_LIMIT) mergedTipCache.clear()\n mergedTipCache.set(tipKey, true)\n }\n return merged\n}\n\n/**\n * Robust merged check for a single ref: ancestry fast-path, then a bounded\n * `git cherry` (patch-id) fallback that survives base-history rewrites.\n * Returns false when either ref cannot be resolved.\n */\nexport async function isRefMerged(\n git: SimpleGit,\n ref: string,\n into: string,\n opts?: { maxCherryCommits?: number },\n): Promise<boolean> {\n let tip: string | undefined\n let intoTip: string | undefined\n try {\n const lines = (await git.raw(['rev-parse', ref, into])).trim().split('\\n')\n tip = lines[0]?.trim()\n intoTip = lines[1]?.trim()\n } catch {\n return false\n }\n if (!tip || !intoTip) return false\n return isTipMerged(git, tip, intoTip, opts?.maxCherryCommits ?? DEFAULT_MAX_CHERRY_COMMITS, into)\n}\n\n/**\n * Classify which of the given local branches are merged into `into`\n * (default: the contentrain branch). One `git branch --merged` call covers\n * the ancestry-merged majority; only the remainder pays the patch-id\n * fallback (bounded concurrency, verdicts cached).\n *\n * `opts.fallbackThreshold` skips the patch-id fallback entirely when fewer\n * than that many branches are ancestry-unmerged — the fallback can only\n * LOWER the unmerged count, so callers that merely compare the count\n * against a limit (the hot pre-write gate) pay nothing in the normal case.\n *\n * Throws when `into` does not resolve — callers use this to fall back to\n * the base branch (mirrors the previous `branch --merged` semantics).\n */\nexport async function classifyMergedBranches(\n projectRoot: string,\n branches: string[],\n into: string = CONTENTRAIN_BRANCH,\n opts?: { fallbackThreshold?: number },\n): Promise<Set<string>> {\n const git = createGit(projectRoot)\n if (branches.length === 0) {\n // Preserve the \"throws when into is missing\" contract even for empty input.\n await git.raw(['branch', '--merged', into])\n return new Set()\n }\n const mergedRaw = await git.raw(['branch', '--merged', into])\n const ancestryMerged = new Set(\n mergedRaw.split('\\n').map(b => b.replace(/^\\*?\\s+/, '').trim()).filter(Boolean),\n )\n\n const merged = new Set<string>()\n const rest: string[] = []\n for (const branch of branches) {\n if (ancestryMerged.has(branch)) merged.add(branch)\n else rest.push(branch)\n }\n if (rest.length === 0 || rest.length < (opts?.fallbackThreshold ?? 0)) return merged\n\n // Resolve all remaining tips + into in ONE subprocess, then run the\n // (mostly cached) patch-id fallback in bounded-concurrency chunks. Each\n // task gets its OWN simple-git instance — a shared instance serializes\n // its command queue, which turns 80 branches into 80 sequential spawns.\n let tips: string[]\n try {\n tips = (await git.raw(['rev-parse', into, ...rest])).trim().split('\\n').map(s => s.trim())\n } catch {\n return merged\n }\n const intoTip = tips[0]\n if (!intoTip) return merged\n for (let i = 0; i < rest.length; i += CHERRY_CONCURRENCY) {\n const chunk = rest.slice(i, i + CHERRY_CONCURRENCY)\n const verdicts = await Promise.all(chunk.map((branch, j) => {\n const tip = tips[i + j + 1]\n return tip\n ? isTipMerged(createGit(projectRoot), tip, intoTip, DEFAULT_MAX_CHERRY_COMMITS, into)\n : Promise.resolve(false)\n }))\n for (const [j, verdict] of verdicts.entries()) {\n if (verdict) merged.add(chunk[j]!)\n }\n }\n return merged\n}\n\n// ─── Remote cr/* branch lifecycle ───\n\nconst REMOTE_PUSH_TIMEOUT_MS = 10_000\nconst REMOTE_LIST_TIMEOUT_MS = 5_000\n\nfunction contentrainRemoteName(): string {\n return process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n}\n\n/**\n * simple-git instance hardened for network operations: `timeout.block` kills\n * the child after N ms without output (hung SSH passphrase prompts), and\n * `GIT_TERMINAL_PROMPT=0` refuses interactive HTTPS credential prompts.\n *\n * This is the one place that legitimately inherits the real environment — push\n * needs the host's askpass/SSH/proxy setup to authenticate. Because `.env()` is\n * therefore unavoidable, `unsafe` opts out of the block-unsafe guard categories\n * that inherited variables (EDITOR, GIT_ASKPASS, …) would otherwise trip; see\n * NETWORK_UNSAFE. Arg-injection protections stay intact.\n */\nexport function networkGit(projectRoot: string, timeoutMs: number): SimpleGit {\n return createGit(undefined, { baseDir: projectRoot, timeout: { block: timeoutMs }, unsafe: NETWORK_UNSAFE })\n .env({ ...process.env, GIT_TERMINAL_PROMPT: '0' })\n}\n\nasync function resolveRemote(git: SimpleGit): Promise<string | null> {\n try {\n const remotes = await git.getRemotes()\n const name = contentrainRemoteName()\n return remotes.some(r => r.name === name) ? name : null\n } catch {\n return null\n }\n}\n\nexport interface RemoteDeleteResult {\n deleted: boolean\n /** Why nothing was deleted, when that is expected (not a failure). */\n skipped?: 'disabled' | 'no-remote' | 'not-found' | 'protected'\n /** A real failure (offline, auth, protected ref) — surfaced, never thrown. */\n warning?: string\n}\n\n/**\n * Best-effort delete of a cr/* branch on the configured remote. Never\n * throws: expected conditions land in `skipped`, real failures in\n * `warning`. Gated by `config.remoteBranchCleanup` (default: on).\n *\n * Pass `opts.config` when the caller already read it (avoids a re-read);\n * `null` means \"no config\" and applies the default gate.\n */\nexport async function deleteRemoteBranch(\n projectRoot: string,\n branch: string,\n opts?: { config?: ContentrainConfig | null, timeoutMs?: number },\n): Promise<RemoteDeleteResult> {\n if (!branch.startsWith('cr/') || branch === CONTENTRAIN_BRANCH) {\n return { deleted: false, skipped: 'protected' }\n }\n const config = opts?.config === undefined ? await readConfig(projectRoot) : opts.config\n if (!(config?.remoteBranchCleanup ?? true)) {\n return { deleted: false, skipped: 'disabled' }\n }\n const git = networkGit(projectRoot, opts?.timeoutMs ?? REMOTE_PUSH_TIMEOUT_MS)\n const remote = await resolveRemote(git)\n if (!remote) return { deleted: false, skipped: 'no-remote' }\n try {\n await git.push([remote, '--delete', branch])\n return { deleted: true }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/remote ref does not exist|couldn't find remote ref/i.test(message)) {\n return { deleted: false, skipped: 'not-found' }\n }\n return { deleted: false, warning: `Could not delete \"${branch}\" on ${remote}: ${message}` }\n }\n}\n\nexport interface RemoteBranchList {\n remote: string\n branches: { name: string, sha: string }[]\n /** ls-remote failed (offline/timeout) — branches is empty, not authoritative. */\n error?: string\n}\n\n/**\n * Authoritative list of cr/* branches on the configured remote via\n * `ls-remote --heads` (no fetch, no stale remote-tracking refs). Returns\n * null when no remote is configured. Never throws.\n */\nexport async function listRemoteCrBranches(\n projectRoot: string,\n opts?: { timeoutMs?: number },\n): Promise<RemoteBranchList | null> {\n const git = networkGit(projectRoot, opts?.timeoutMs ?? REMOTE_LIST_TIMEOUT_MS)\n const remote = await resolveRemote(git)\n if (!remote) return null\n try {\n const raw = await git.raw(['ls-remote', '--heads', remote, 'refs/heads/cr/*'])\n const branches = raw.split('\\n')\n .map(line => line.trim())\n .filter(Boolean)\n .map((line) => {\n const [sha, ref] = line.split('\\t')\n return { sha: sha?.trim() ?? '', name: ref?.trim().replace(/^refs\\/heads\\//, '') ?? '' }\n })\n .filter(b => b.name.startsWith('cr/') && b.name !== CONTENTRAIN_BRANCH)\n return { remote, branches }\n } catch (error) {\n return { remote, branches: [], error: error instanceof Error ? error.message : String(error) }\n }\n}\n\nexport interface RemotePruneResult {\n /** Branches removed from the remote (in dryRun mode: the candidates). */\n deleted: string[]\n kept: string[]\n errors: string[]\n skipped?: 'disabled' | 'no-remote' | 'offline'\n}\n\nconst PRUNE_PUSH_CHUNK = 50\n\n/**\n * Delete already-merged cr/* branches on the remote in batches. Merged-state\n * uses the same ancestry + patch-id classification as the local cleanup, so\n * branches leaked before a base-history rewrite are still recognised.\n * Ignores `branchRetention` — a merged remote copy only produces phantom\n * reviews. Never throws; gated by `config.remoteBranchCleanup`.\n */\nexport async function pruneMergedRemoteBranches(\n projectRoot: string,\n opts?: { config?: ContentrainConfig | null, max?: number, dryRun?: boolean, timeoutMs?: number },\n): Promise<RemotePruneResult> {\n const config = opts?.config === undefined ? await readConfig(projectRoot) : opts.config\n if (!(config?.remoteBranchCleanup ?? true)) {\n return { deleted: [], kept: [], errors: [], skipped: 'disabled' }\n }\n\n const listed = await listRemoteCrBranches(projectRoot, { timeoutMs: opts?.timeoutMs })\n if (!listed) return { deleted: [], kept: [], errors: [], skipped: 'no-remote' }\n if (listed.error) return { deleted: [], kept: [], errors: [listed.error], skipped: 'offline' }\n if (listed.branches.length === 0) return { deleted: [], kept: [], errors: [] }\n\n const git = networkGit(projectRoot, opts?.timeoutMs ?? REMOTE_PUSH_TIMEOUT_MS)\n\n // Remote tips may predate this clone (leaked long ago, and our fetches are\n // single-refspec) — one scoped fetch backfills any missing objects.\n // `cat-file -t` (not `-e`): -e signals via exit code with empty stderr,\n // which simple-git reports as success.\n const presence = await Promise.all(listed.branches.map(b =>\n git.raw(['cat-file', '-t', b.sha]).then(() => true).catch(() => false),\n ))\n if (presence.some(present => !present)) {\n try {\n await git.fetch(listed.remote, `+refs/heads/cr/*:refs/remotes/${listed.remote}/cr/*`)\n } catch {\n // Best-effort: branches whose objects stay unresolvable are kept below.\n }\n }\n\n const kept: string[] = []\n const candidates: string[] = []\n const verdicts = await Promise.all(listed.branches.map(b => isRefMerged(git, b.sha, CONTENTRAIN_BRANCH)))\n for (const [i, merged] of verdicts.entries()) {\n const name = listed.branches[i]!.name\n if (merged) candidates.push(name)\n else kept.push(name)\n }\n\n const limit = opts?.max ?? Number.POSITIVE_INFINITY\n const toDelete = candidates.slice(0, limit)\n kept.push(...candidates.slice(toDelete.length))\n\n if (opts?.dryRun) return { deleted: toDelete, kept, errors: [] }\n\n const deleted: string[] = []\n const errors: string[] = []\n for (let i = 0; i < toDelete.length; i += PRUNE_PUSH_CHUNK) {\n const chunk = toDelete.slice(i, i + PRUNE_PUSH_CHUNK)\n try {\n await git.push([listed.remote, '--delete', ...chunk])\n deleted.push(...chunk)\n } catch {\n // One missing ref fails the whole multi-refspec push — retry per branch.\n for (const branch of chunk) {\n try {\n await git.push([listed.remote, '--delete', branch])\n deleted.push(branch)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/remote ref does not exist|couldn't find remote ref/i.test(message)) {\n deleted.push(branch) // already gone — net effect is pruned\n } else {\n errors.push(`${branch}: ${message}`)\n }\n }\n }\n }\n }\n return { deleted, kept, errors }\n}\n\nexport interface ContentBranchRelation {\n /** Commits on the content branch that the base branch lacks. */\n ahead: number\n /** Commits on the base branch that the content branch lacks. */\n behind: number\n relation: 'in_sync' | 'content_ahead' | 'base_ahead' | 'diverged'\n}\n\n/**\n * How the content-tracking branch relates to the base branch, counted in\n * BOTH directions. One-directional \"ahead\" counting reported a diverged base\n * as \"in sync\" — the state that blocks every auto-merge advance is exactly\n * the one it could not see. Shared by CLI `status` and `contentrain_status`.\n *\n * `base_ahead` self-heals on the next write (the pre-fork sync fast-forwards\n * contentrain); `diverged` blocks the base advance until the branches are\n * reconciled.\n */\nexport async function contentBranchRelation(\n git: SimpleGit,\n baseBranch: string,\n): Promise<ContentBranchRelation> {\n const [aheadRaw, behindRaw] = await Promise.all([\n git.raw(['rev-list', '--count', `${baseBranch}..${CONTENTRAIN_BRANCH}`]),\n git.raw(['rev-list', '--count', `${CONTENTRAIN_BRANCH}..${baseBranch}`]),\n ])\n const ahead = Number.parseInt(aheadRaw.trim(), 10) || 0\n const behind = Number.parseInt(behindRaw.trim(), 10) || 0\n const relation = ahead > 0 && behind > 0\n ? 'diverged' as const\n : ahead > 0\n ? 'content_ahead' as const\n : behind > 0\n ? 'base_ahead' as const\n : 'in_sync' as const\n return { ahead, behind, relation }\n}\n"],"mappings":"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"}