@contentrain/mcp 2.3.1 → 3.0.1

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 (162) hide show
  1. package/README.md +1 -1
  2. package/dist/{annotations-D3tlsF38.mjs → annotations-C-tQVmot.mjs} +15 -1
  3. package/dist/annotations-C-tQVmot.mjs.map +1 -0
  4. package/dist/{apply-manager-SLCRLHN_.mjs → apply-manager-BybvnUVC.mjs} +28 -18
  5. package/dist/apply-manager-BybvnUVC.mjs.map +1 -0
  6. package/dist/{astro-parser-LNjEspnf.mjs → astro-parser-Cb9uuBpW.mjs} +3 -3
  7. package/dist/{astro-parser-LNjEspnf.mjs.map → astro-parser-Cb9uuBpW.mjs.map} +1 -1
  8. package/dist/{availability-sWWRz2W0.mjs → availability-Dgcmvv99.mjs} +1 -1
  9. package/dist/{availability-sWWRz2W0.mjs.map → availability-Dgcmvv99.mjs.map} +1 -1
  10. package/dist/{branch-lifecycle-BAfgSQBv.mjs → branch-lifecycle-B6PXZ2N7.mjs} +11 -61
  11. package/dist/branch-lifecycle-B6PXZ2N7.mjs.map +1 -0
  12. package/dist/{branch-lifecycle-D4W0WIs4.d.mts → branch-lifecycle-DhzHfzs3.d.mts} +1 -1
  13. package/dist/{branch-lifecycle-D4W0WIs4.d.mts.map → branch-lifecycle-DhzHfzs3.d.mts.map} +1 -1
  14. package/dist/{compiler-CNzm2Y2I.mjs → compiler-B6hNQeCC.mjs} +2 -2
  15. package/dist/{compiler-CNzm2Y2I.mjs.map → compiler-B6hNQeCC.mjs.map} +1 -1
  16. package/dist/{compiler-sfc.cjs-y0B0L3MH.mjs → compiler-sfc.cjs-8yY7b5bx.mjs} +2 -2
  17. package/dist/{compiler-sfc.cjs-y0B0L3MH.mjs.map → compiler-sfc.cjs-8yY7b5bx.mjs.map} +1 -1
  18. package/dist/{config-oxxgznz7.mjs → config-VlRKRwAS.mjs} +1 -1
  19. package/dist/{config-oxxgznz7.mjs.map → config-VlRKRwAS.mjs.map} +1 -1
  20. package/dist/{content-manager-DWo3G64y.d.mts → content-manager-Dn4tqDw1.d.mts} +10 -7
  21. package/dist/content-manager-Dn4tqDw1.d.mts.map +1 -0
  22. package/dist/{context-DjglDPvj.mjs → context-C3x6I3cE.mjs} +4 -4
  23. package/dist/{context-DjglDPvj.mjs.map → context-C3x6I3cE.mjs.map} +1 -1
  24. package/dist/core/apply-manager.d.mts +8 -0
  25. package/dist/core/apply-manager.d.mts.map +1 -1
  26. package/dist/core/apply-manager.mjs +10 -9
  27. package/dist/core/config.d.mts +1 -1
  28. package/dist/core/config.mjs +1 -1
  29. package/dist/core/content-manager.d.mts +2 -2
  30. package/dist/core/content-manager.mjs +2 -2
  31. package/dist/core/context.d.mts +1 -1
  32. package/dist/core/context.mjs +5 -5
  33. package/dist/core/contracts/index.d.mts +2 -2
  34. package/dist/core/doctor.mjs +7 -6
  35. package/dist/core/graph-builder.mjs +2 -2
  36. package/dist/core/meta-manager.d.mts +8 -9
  37. package/dist/core/meta-manager.d.mts.map +1 -1
  38. package/dist/core/meta-manager.mjs +2 -2
  39. package/dist/core/model-manager.d.mts +39 -3
  40. package/dist/core/model-manager.d.mts.map +1 -1
  41. package/dist/core/model-manager.mjs +3 -3
  42. package/dist/core/ops/index.d.mts +40 -3
  43. package/dist/core/ops/index.d.mts.map +1 -1
  44. package/dist/core/ops/index.mjs +4 -4
  45. package/dist/core/overlay-reader.d.mts +1 -1
  46. package/dist/core/overlay-reader.mjs +1 -1
  47. package/dist/core/scan-config.mjs +1 -1
  48. package/dist/core/scanner.mjs +3 -3
  49. package/dist/core/validator/index.d.mts +1 -1
  50. package/dist/core/validator/index.d.mts.map +1 -1
  51. package/dist/core/validator/index.mjs +4 -4
  52. package/dist/{detect-CdDU4Ew_.mjs → detect-B0oXbQaP.mjs} +1 -1
  53. package/dist/{detect-CdDU4Ew_.mjs.map → detect-B0oXbQaP.mjs.map} +1 -1
  54. package/dist/{doctor-BwJ_nmqS.mjs → doctor-DNPuGgcg.mjs} +28 -15
  55. package/dist/doctor-DNPuGgcg.mjs.map +1 -0
  56. package/dist/{errors-e0YdjooK.mjs → errors-CvBS12e7.mjs} +1 -1
  57. package/dist/{errors-e0YdjooK.mjs.map → errors-CvBS12e7.mjs.map} +1 -1
  58. package/dist/fs-DLbVB-Ek.mjs.map +1 -1
  59. package/dist/git/branch-lifecycle.d.mts +1 -1
  60. package/dist/git/branch-lifecycle.mjs +3 -2
  61. package/dist/git/identity.d.mts +42 -0
  62. package/dist/git/identity.d.mts.map +1 -0
  63. package/dist/git/identity.mjs +2 -0
  64. package/dist/git/transaction.d.mts +1 -1
  65. package/dist/git/transaction.d.mts.map +1 -1
  66. package/dist/git/transaction.mjs +9 -8
  67. package/dist/{graph-builder-DK4Mh8Tn.mjs → graph-builder-BEocwW13.mjs} +2 -2
  68. package/dist/{graph-builder-DK4Mh8Tn.mjs.map → graph-builder-BEocwW13.mjs.map} +1 -1
  69. package/dist/{id-DV_T9Ic8.mjs → id-BjhaIxTL.mjs} +1 -1
  70. package/dist/{id-DV_T9Ic8.mjs.map → id-BjhaIxTL.mjs.map} +1 -1
  71. package/dist/identity-D8QoGxhT.mjs +125 -0
  72. package/dist/identity-D8QoGxhT.mjs.map +1 -0
  73. package/dist/{index-w8QHThNS.d.mts → index-BWbTJlXm.d.mts} +2 -2
  74. package/dist/index.mjs +21 -20
  75. package/dist/index.mjs.map +1 -1
  76. package/dist/{local-8dJ5NIWL.mjs → local-Db5X9mjZ.mjs} +25 -13
  77. package/dist/local-Db5X9mjZ.mjs.map +1 -0
  78. package/dist/{meta-manager-CJUiTgP2.mjs → meta-manager-B_Se183X.mjs} +29 -5
  79. package/dist/meta-manager-B_Se183X.mjs.map +1 -0
  80. package/dist/{model-manager-DP2CZiMT.mjs → model-manager-DgyKMjLN.mjs} +169 -25
  81. package/dist/model-manager-DgyKMjLN.mjs.map +1 -0
  82. package/dist/{node-BxujauDw.mjs → node-DUGiIkv1.mjs} +1 -1
  83. package/dist/{node-BxujauDw.mjs.map → node-DUGiIkv1.mjs.map} +1 -1
  84. package/dist/{ops-CGKIKwDc.mjs → ops-BzbThweu.mjs} +165 -26
  85. package/dist/ops-BzbThweu.mjs.map +1 -0
  86. package/dist/{overlay-reader-DNaVsgiS.mjs → overlay-reader-CkU_pGXZ.mjs} +1 -1
  87. package/dist/{overlay-reader-DNaVsgiS.mjs.map → overlay-reader-CkU_pGXZ.mjs.map} +1 -1
  88. package/dist/{paths-enT2coeX.mjs → paths-CdPPWpbG.mjs} +1 -1
  89. package/dist/{paths-enT2coeX.mjs.map → paths-CdPPWpbG.mjs.map} +1 -1
  90. package/dist/providers/github/index.d.mts +1 -1
  91. package/dist/providers/github/index.mjs +2 -2
  92. package/dist/providers/gitlab/index.d.mts +1 -1
  93. package/dist/providers/gitlab/index.mjs +2 -2
  94. package/dist/providers/local/index.d.mts +7 -1
  95. package/dist/providers/local/index.d.mts.map +1 -1
  96. package/dist/providers/local/index.mjs +11 -10
  97. package/dist/{scan-config-BlNLRCMx.mjs → scan-config-Dx49nCqk.mjs} +1 -1
  98. package/dist/{scan-config-BlNLRCMx.mjs.map → scan-config-Dx49nCqk.mjs.map} +1 -1
  99. package/dist/{scanner-CGWhmpDz.mjs → scanner-BSvXjfkp.mjs} +6 -6
  100. package/dist/{scanner-CGWhmpDz.mjs.map → scanner-BSvXjfkp.mjs.map} +1 -1
  101. package/dist/serialization-tmgfFBOh.mjs +2 -0
  102. package/dist/server/http/index.d.mts +1 -1
  103. package/dist/server/http/index.mjs +21 -20
  104. package/dist/server/http/index.mjs.map +1 -1
  105. package/dist/{server-DLauBiu2.d.mts → server-CLPNnhYL.d.mts} +2 -2
  106. package/dist/{server-DLauBiu2.d.mts.map → server-CLPNnhYL.d.mts.map} +1 -1
  107. package/dist/{server-CUZnuHQx.mjs → server-D-zJWbdI.mjs} +313 -115
  108. package/dist/server-D-zJWbdI.mjs.map +1 -0
  109. package/dist/server.d.mts +1 -1
  110. package/dist/server.mjs +21 -20
  111. package/dist/{source-map-generator-Bvbu-kM5.mjs → source-map-generator-DhjiQdV3.mjs} +1 -1
  112. package/dist/{source-map-generator-Bvbu-kM5.mjs.map → source-map-generator-DhjiQdV3.mjs.map} +1 -1
  113. package/dist/{svelte-parser-DaFK9iY0.mjs → svelte-parser-CwySu9yn.mjs} +3 -3
  114. package/dist/{svelte-parser-DaFK9iY0.mjs.map → svelte-parser-CwySu9yn.mjs.map} +1 -1
  115. package/dist/templates/index.mjs +1 -1
  116. package/dist/{templates-D6EllixQ.mjs → templates-CAkTIRkE.mjs} +430 -109
  117. package/dist/templates-CAkTIRkE.mjs.map +1 -0
  118. package/dist/testing/memory-provider.d.mts +52 -0
  119. package/dist/testing/memory-provider.d.mts.map +1 -0
  120. package/dist/testing/memory-provider.mjs +205 -0
  121. package/dist/testing/memory-provider.mjs.map +1 -0
  122. package/dist/tools/annotations.d.mts.map +1 -1
  123. package/dist/tools/annotations.mjs +1 -1
  124. package/dist/tools/availability.mjs +1 -1
  125. package/dist/{transaction-1SPznNt3.mjs → transaction-BW9ektWu.mjs} +13 -12
  126. package/dist/transaction-BW9ektWu.mjs.map +1 -0
  127. package/dist/{tsx-parser-md1N0Niu.mjs → tsx-parser-BvsK6lyG.mjs} +1 -1
  128. package/dist/{tsx-parser-md1N0Niu.mjs.map → tsx-parser-BvsK6lyG.mjs.map} +1 -1
  129. package/dist/tsx-parser-Byc7HlJ8.mjs +2 -0
  130. package/dist/util/detect.mjs +1 -1
  131. package/dist/util/fs.d.mts +1 -1
  132. package/dist/util/fs.d.mts.map +1 -1
  133. package/dist/util/id.mjs +1 -1
  134. package/dist/{validator-ChiYk6ap.mjs → validator-0sJ9vh1j.mjs} +65 -8
  135. package/dist/validator-0sJ9vh1j.mjs.map +1 -0
  136. package/dist/{vue-parser-CHxDohd8.mjs → vue-parser-auedZiJG.mjs} +3 -3
  137. package/dist/{vue-parser-CHxDohd8.mjs.map → vue-parser-auedZiJG.mjs.map} +1 -1
  138. package/package.json +13 -5
  139. package/testing/conformance/01-new-collection-entry/expected/.contentrain/meta/blog/en.json +1 -0
  140. package/testing/conformance/01-new-collection-entry/expected/.contentrain/models/blog.json +6 -5
  141. package/testing/conformance/01-new-collection-entry/setup/.contentrain/models/blog.json +6 -5
  142. package/testing/conformance/02-new-singleton/expected/.contentrain/meta/hero/en.json +1 -0
  143. package/testing/conformance/02-new-singleton/expected/.contentrain/models/hero.json +6 -5
  144. package/testing/conformance/02-new-singleton/setup/.contentrain/models/hero.json +6 -5
  145. package/testing/conformance/03-new-dictionary/expected/.contentrain/meta/ui-strings/en.json +1 -0
  146. package/testing/conformance/03-new-dictionary/expected/.contentrain/models/ui-strings.json +4 -3
  147. package/testing/conformance/03-new-dictionary/setup/.contentrain/models/ui-strings.json +4 -3
  148. package/dist/annotations-D3tlsF38.mjs.map +0 -1
  149. package/dist/apply-manager-SLCRLHN_.mjs.map +0 -1
  150. package/dist/branch-lifecycle-BAfgSQBv.mjs.map +0 -1
  151. package/dist/content-manager-DWo3G64y.d.mts.map +0 -1
  152. package/dist/doctor-BwJ_nmqS.mjs.map +0 -1
  153. package/dist/local-8dJ5NIWL.mjs.map +0 -1
  154. package/dist/meta-manager-CJUiTgP2.mjs.map +0 -1
  155. package/dist/model-manager-DP2CZiMT.mjs.map +0 -1
  156. package/dist/ops-CGKIKwDc.mjs.map +0 -1
  157. package/dist/serialization-B1CEzR4H.mjs +0 -2
  158. package/dist/server-CUZnuHQx.mjs.map +0 -1
  159. package/dist/templates-D6EllixQ.mjs.map +0 -1
  160. package/dist/transaction-1SPznNt3.mjs.map +0 -1
  161. package/dist/tsx-parser-C3XsIrwU.mjs +0 -2
  162. package/dist/validator-ChiYk6ap.mjs.map +0 -1
@@ -1,29 +1,30 @@
1
- import { i as planContentDelete, n as planModelDelete, r as planContentSave, t as planModelSave } from "./ops-CGKIKwDc.mjs";
1
+ import { a as planContentSave, i as planModelDelete, n as planVocabularySave, o as planContentDelete, r as planModelSave, t as planVocabularyDelete } from "./ops-BzbThweu.mjs";
2
2
  import { n as contentFilePath, t as contentDirPath } from "./paths-CmVw5Cw2.mjs";
3
3
  import { a as readJson, n as ensureDir, r as pathExists, s as writeJson, t as contentrainDir } from "./fs-DLbVB-Ek.mjs";
4
- import { a as writeMetaEntries, i as writeMeta, r as readMeta } from "./meta-manager-CJUiTgP2.mjs";
5
- import { n as readVocabulary, t as readConfig } from "./config-oxxgznz7.mjs";
6
- import { S as writeContent, _ as resolveMdFilePath, a as fieldDefZodSchema, c as validateModelDefinition, d as listContent, g as resolveLocaleStrategy, h as resolveJsonFilePath, l as writeModel, m as resolveContentDir, n as checkReferences, o as listModels, r as countEntries, s as readModel, u as deleteContent } from "./model-manager-DP2CZiMT.mjs";
7
- import { n as readContext } from "./context-DjglDPvj.mjs";
8
- import { a as deleteRemoteBranch, c as pruneMergedRemoteBranches, i as cleanupMergedBranches, n as checkBranchHealth, s as listRemoteCrBranches } from "./branch-lifecycle-BAfgSQBv.mjs";
9
- import { i as mergeBranch, n as createTransaction, r as ensureContentBranch, t as buildBranchName } from "./transaction-1SPznNt3.mjs";
10
- import { n as isMerged, t as LocalProvider } from "./local-8dJ5NIWL.mjs";
11
- import { n as isToolAvailable, t as TOOL_REQUIREMENTS } from "./availability-sWWRz2W0.mjs";
12
- import { t as detectStack } from "./detect-CdDU4Ew_.mjs";
13
- import { t as TOOL_ANNOTATIONS } from "./annotations-D3tlsF38.mjs";
14
- import { n as listTemplates, t as getTemplate } from "./templates-D6EllixQ.mjs";
15
- import { n as mapProviderError } from "./errors-e0YdjooK.mjs";
16
- import { t as validateProject } from "./validator-ChiYk6ap.mjs";
17
- import { t as OverlayReader } from "./overlay-reader-DNaVsgiS.mjs";
18
- import { t as buildGraph } from "./graph-builder-DK4Mh8Tn.mjs";
19
- import { n as scanSummary, t as scanCandidates } from "./scanner-CGWhmpDz.mjs";
20
- import { n as applyExtract, r as applyReuse } from "./apply-manager-SLCRLHN_.mjs";
21
- import { t as runDoctor } from "./doctor-BwJ_nmqS.mjs";
4
+ import { a as writeMeta, i as readMeta, o as writeMetaEntries, r as mergeEntryMeta, t as applyStatusChange } from "./meta-manager-B_Se183X.mjs";
5
+ import { r as createGit } from "./identity-D8QoGxhT.mjs";
6
+ import { n as readVocabulary, t as readConfig } from "./config-VlRKRwAS.mjs";
7
+ import { a as deleteRemoteBranch, c as pruneMergedRemoteBranches, i as cleanupMergedBranches, n as checkBranchHealth, s as listRemoteCrBranches } from "./branch-lifecycle-B6PXZ2N7.mjs";
8
+ import { T as writeContent, _ as resolveContentDir, b as resolveMdFilePath, c as listModels, d as validateModelDefinition, f as writeModel, i as countEntries, l as readModel, m as listContent, o as fieldDefZodSchema, p as deleteContent, r as checkReferences, v as resolveJsonFilePath, y as resolveLocaleStrategy } from "./model-manager-DgyKMjLN.mjs";
9
+ import { n as readContext } from "./context-C3x6I3cE.mjs";
10
+ import { i as mergeBranch, n as createTransaction, r as ensureContentBranch, t as buildBranchName } from "./transaction-BW9ektWu.mjs";
11
+ import { n as isMerged, t as LocalProvider } from "./local-Db5X9mjZ.mjs";
12
+ import { n as isToolAvailable, t as TOOL_REQUIREMENTS } from "./availability-Dgcmvv99.mjs";
13
+ import { t as detectStack } from "./detect-B0oXbQaP.mjs";
14
+ import { t as TOOL_ANNOTATIONS } from "./annotations-C-tQVmot.mjs";
15
+ import { n as listTemplates, t as getTemplate } from "./templates-CAkTIRkE.mjs";
16
+ import { n as mapProviderError } from "./errors-CvBS12e7.mjs";
17
+ import { t as validateProject } from "./validator-0sJ9vh1j.mjs";
18
+ import { t as OverlayReader } from "./overlay-reader-CkU_pGXZ.mjs";
19
+ import { t as buildGraph } from "./graph-builder-BEocwW13.mjs";
20
+ import { n as scanSummary, t as scanCandidates } from "./scanner-BSvXjfkp.mjs";
21
+ import { n as applyExtract, r as applyReuse } from "./apply-manager-BybvnUVC.mjs";
22
+ import { t as runDoctor } from "./doctor-DNPuGgcg.mjs";
22
23
  import { join } from "node:path";
23
24
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24
25
  import { CONTENTRAIN_BRANCH } from "@contentrain/types";
25
26
  import { appendFile, readFile, writeFile } from "node:fs/promises";
26
- import { simpleGit } from "simple-git";
27
+ import "simple-git";
27
28
  import { z } from "zod";
28
29
  //#region src/tools/context.ts
29
30
  function registerContextTools(server, provider, projectRoot) {
@@ -127,6 +128,7 @@ function registerContextTools(server, provider, projectRoot) {
127
128
  kind: modelDef.kind,
128
129
  domain: modelDef.domain,
129
130
  i18n: modelDef.i18n,
131
+ title_field: modelDef.title_field,
130
132
  description: modelDef.description,
131
133
  fields: modelDef.fields,
132
134
  stats: {
@@ -167,6 +169,24 @@ function registerContextTools(server, provider, projectRoot) {
167
169
  ".contentrain/meta/{model-id}/": "Metadata files per model (status, source, timestamps)"
168
170
  }
169
171
  },
172
+ model_definition: {
173
+ description: "Shape of .contentrain/models/{model-id}.json. Keys are written in this order.",
174
+ required: {
175
+ id: "Kebab-case model ID, matching the file name.",
176
+ name: "Human-readable display name.",
177
+ kind: "One of: singleton, collection, document, dictionary.",
178
+ domain: "Organizational group; maps to the content subdirectory.",
179
+ i18n: "Whether the model stores content per locale.",
180
+ title_field: "Name of the field shown as an entry title in listings, pickers and relation references. Must name a field declared on this model whose type is one of: string, text, slug, email, url, code, markdown, richtext. Dictionary models have no fields — they use the reserved value \"key\", meaning the entry key is the title."
181
+ },
182
+ optional: {
183
+ description: "Model description for documentation and agent context.",
184
+ fields: "Field definitions. Dictionary models must NOT declare fields.",
185
+ content_path: "Framework-relative path for content files, replacing .contentrain/content/.",
186
+ locale_strategy: "How locale is encoded in file names: file (default), suffix, directory, none."
187
+ },
188
+ note: "title_field is declared, never inferred. A consumer that guesses the title from field order or length picks the icon over the headline — set it explicitly on create AND keep it correct when renaming or removing fields."
189
+ },
170
190
  model_kinds: {
171
191
  collection: {
172
192
  description: "Multiple entries stored as an object-map keyed by entry ID",
@@ -269,12 +289,27 @@ function registerContextTools(server, provider, projectRoot) {
269
289
  file: ".contentrain/vocabulary.json",
270
290
  format: {
271
291
  version: "number",
272
- terms: "Record<category, Record<slug, translation_value>>"
292
+ terms: "Record<term-slug, Record<locale, translation>>"
293
+ },
294
+ example: {
295
+ version: 1,
296
+ terms: {
297
+ "sign-in": {
298
+ en: "Sign in",
299
+ tr: "Giriş yap"
300
+ },
301
+ "add-to-cart": {
302
+ en: "Add to cart",
303
+ tr: "Sepete ekle"
304
+ }
305
+ }
273
306
  },
307
+ note: "OUTER key is the term (kebab-case, locale-independent); INNER key is a locale code. One term, one entry, all its translations inside. Grouping by locale at the top level is the common mistake and produces a vocabulary that matches nothing.",
274
308
  usage: [
275
309
  "Check vocabulary before creating dictionary entries — reuse canonical terms",
276
310
  "If a new term is needed, consider adding it to vocabulary first",
277
- "Vocabulary applies across ALL dictionary models and ALL locales"
311
+ "Vocabulary applies across ALL dictionary models and ALL locales",
312
+ "Write it with contentrain_vocabulary_save — never edit the file by hand"
278
313
  ]
279
314
  }
280
315
  }, null, 2)
@@ -471,7 +506,7 @@ function registerSetupTools(server, _provider, projectRoot) {
471
506
  const supportedLocales = locales ?? ["en"];
472
507
  const suggestedDomains = domains ?? suggestDomains(projectRoot);
473
508
  const hasGit = await pathExists(join(projectRoot, ".git"));
474
- const git = simpleGit(projectRoot);
509
+ const git = createGit(projectRoot);
475
510
  if (!hasGit) await git.init();
476
511
  if (!await git.raw([
477
512
  "rev-list",
@@ -631,6 +666,20 @@ function registerSetupTools(server, _provider, projectRoot) {
631
666
  const modelsCreated = [];
632
667
  let contentCreated = 0;
633
668
  let vocabAdded = 0;
669
+ const templateErrors = tmpl.models.flatMap((m) => validateModelDefinition(m).errors.map((e) => `[${m.id}] ${e}`));
670
+ if (templateErrors.length > 0) {
671
+ await tx.cleanup();
672
+ return {
673
+ content: [{
674
+ type: "text",
675
+ text: JSON.stringify({
676
+ error: `Scaffold template "${templateId}" is invalid`,
677
+ details: templateErrors
678
+ }, null, 2)
679
+ }],
680
+ isError: true
681
+ };
682
+ }
634
683
  await tx.write(async (wt) => {
635
684
  await Promise.all(tmpl.models.map(async (model) => {
636
685
  await writeModel(wt, model);
@@ -752,12 +801,24 @@ async function mergeVocabulary(projectRoot, newTerms, _locales) {
752
801
  //#endregion
753
802
  //#region src/tools/commit-plan.ts
754
803
  /**
804
+ * Context payload describing the operation, threaded into the local
805
+ * transaction so it can regenerate `.contentrain/context.json` on the
806
+ * contentrain branch after merge. Kept as a loose object so tool-specific
807
+ * payloads can add optional fields (`locale`, `entries`) without churning
808
+ * the helper's signature. Remote providers ignore it — see
809
+ * `commitThroughProvider` below.
810
+ */
811
+ const COMMIT_AUTHOR = {
812
+ name: "Contentrain",
813
+ email: "ai@contentrain.io"
814
+ };
815
+ /**
755
816
  * Commit a plan's changes through whichever provider is wired into the
756
817
  * tool handler. Encapsulates the LocalProvider vs remote RepoProvider
757
818
  * dispatch that every write-side tool (content_save, content_delete,
758
819
  * model_save, model_delete) used to inline:
759
820
  *
760
- * - **LocalProvider** — goes through the worktree-backed transaction.
821
+ * - **localWorktree providers** — go through the worktree-backed transaction.
761
822
  * `context` payload is threaded as an extra write-through and the
762
823
  * transaction layer decides `auto-merged` vs `pending-review` based on
763
824
  * the project's configured workflow. Selective-sync result is surfaced
@@ -780,16 +841,17 @@ async function mergeVocabulary(projectRoot, newTerms, _locales) {
780
841
  */
781
842
  async function commitThroughProvider(provider, input) {
782
843
  const { branch, changes, message, contextPayload } = input;
783
- if (provider instanceof LocalProvider) {
844
+ if (provider.capabilities.localWorktree) {
784
845
  const result = await provider.applyPlan({
785
846
  branch,
786
847
  changes,
787
848
  message,
849
+ author: COMMIT_AUTHOR,
788
850
  context: contextPayload
789
851
  });
790
852
  return {
791
853
  commitSha: result.sha,
792
- workflowAction: result.workflowAction,
854
+ workflowAction: result.workflowAction ?? "pending-review",
793
855
  sync: result.sync
794
856
  };
795
857
  }
@@ -799,10 +861,7 @@ async function commitThroughProvider(provider, input) {
799
861
  branch,
800
862
  changes: allChanges,
801
863
  message,
802
- author: {
803
- name: "Contentrain",
804
- email: "ai@contentrain.io"
805
- },
864
+ author: COMMIT_AUTHOR,
806
865
  base: CONTENTRAIN_BRANCH
807
866
  })).sha,
808
867
  workflowAction: "pending-review"
@@ -823,6 +882,7 @@ function registerModelTools(server, provider, projectRoot) {
823
882
  ]).describe("Model kind"),
824
883
  domain: z.string().describe("Content domain (e.g. \"blog\", \"marketing\", \"system\")"),
825
884
  i18n: z.boolean().describe("Whether this model supports localization"),
885
+ title_field: z.string().describe("Name of the field shown as an entry's title in listings, pickers and relation references. Must name a field on this model whose type is string, text, slug, email, url, code, markdown or richtext. Dictionary models have no fields — use \"key\"."),
826
886
  description: z.string().optional().describe("Model description"),
827
887
  fields: fieldDefSchema.optional().describe("Field definitions (not needed for dictionary)"),
828
888
  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/"),
@@ -859,26 +919,25 @@ function registerModelTools(server, provider, projectRoot) {
859
919
  }],
860
920
  isError: true
861
921
  };
862
- if (provider instanceof LocalProvider) {
863
- const health = await checkBranchHealth(provider.projectRoot);
864
- if (health.blocked) return {
865
- content: [{
866
- type: "text",
867
- text: JSON.stringify({
868
- error: health.message,
869
- action: "blocked",
870
- hint: "Merge or delete old contentrain/* branches before creating new ones."
871
- }, null, 2)
872
- }],
873
- isError: true
874
- };
875
- }
922
+ const readiness = await provider.checkWriteReadiness?.();
923
+ if (readiness?.blocked) return {
924
+ content: [{
925
+ type: "text",
926
+ text: JSON.stringify({
927
+ error: readiness.message,
928
+ action: "blocked",
929
+ hint: "Merge or delete old contentrain/* branches before creating new ones."
930
+ }, null, 2)
931
+ }],
932
+ isError: true
933
+ };
876
934
  const model = {
877
935
  id: input.id,
878
936
  name: input.name,
879
937
  kind: input.kind,
880
938
  domain: input.domain,
881
939
  i18n: input.i18n,
940
+ title_field: input.title_field,
882
941
  description: input.description,
883
942
  fields: input.fields,
884
943
  content_path: input.content_path,
@@ -969,20 +1028,18 @@ function registerModelTools(server, provider, projectRoot) {
969
1028
  }],
970
1029
  isError: true
971
1030
  };
972
- if (provider instanceof LocalProvider) {
973
- const deleteHealth = await checkBranchHealth(provider.projectRoot);
974
- if (deleteHealth.blocked) return {
975
- content: [{
976
- type: "text",
977
- text: JSON.stringify({
978
- error: deleteHealth.message,
979
- action: "blocked",
980
- hint: "Merge or delete old contentrain/* branches before creating new ones."
981
- }, null, 2)
982
- }],
983
- isError: true
984
- };
985
- }
1031
+ const deleteReadiness = await provider.checkWriteReadiness?.();
1032
+ if (deleteReadiness?.blocked) return {
1033
+ content: [{
1034
+ type: "text",
1035
+ text: JSON.stringify({
1036
+ error: deleteReadiness.message,
1037
+ action: "blocked",
1038
+ hint: "Merge or delete old contentrain/* branches before creating new ones."
1039
+ }, null, 2)
1040
+ }],
1041
+ isError: true
1042
+ };
986
1043
  const refs = await checkReferences(provider, modelId);
987
1044
  if (refs.length > 0) return { content: [{
988
1045
  type: "text",
@@ -1111,20 +1168,18 @@ function registerContentTools(server, provider, projectRoot) {
1111
1168
  if (entry.publish_at !== void 0) entry.data["publish_at"] = entry.publish_at;
1112
1169
  if (entry.expire_at !== void 0) entry.data["expire_at"] = entry.expire_at;
1113
1170
  }
1114
- if (provider instanceof LocalProvider) {
1115
- const health = await checkBranchHealth(provider.projectRoot);
1116
- if (health.blocked) return {
1117
- content: [{
1118
- type: "text",
1119
- text: JSON.stringify({
1120
- error: health.message,
1121
- action: "blocked",
1122
- hint: "Merge or delete old contentrain/* branches before creating new ones."
1123
- }, null, 2)
1124
- }],
1125
- isError: true
1126
- };
1127
- }
1171
+ const readiness = await provider.checkWriteReadiness?.();
1172
+ if (readiness?.blocked) return {
1173
+ content: [{
1174
+ type: "text",
1175
+ text: JSON.stringify({
1176
+ error: readiness.message,
1177
+ action: "blocked",
1178
+ hint: "Merge or delete old contentrain/* branches before creating new ones."
1179
+ }, null, 2)
1180
+ }],
1181
+ isError: true
1182
+ };
1128
1183
  const vocabulary = await readVocabulary(provider);
1129
1184
  let plan;
1130
1185
  try {
@@ -1244,20 +1299,18 @@ function registerContentTools(server, provider, projectRoot) {
1244
1299
  }],
1245
1300
  isError: true
1246
1301
  };
1247
- if (provider instanceof LocalProvider) {
1248
- const deleteHealth = await checkBranchHealth(provider.projectRoot);
1249
- if (deleteHealth.blocked) return {
1250
- content: [{
1251
- type: "text",
1252
- text: JSON.stringify({
1253
- error: deleteHealth.message,
1254
- action: "blocked",
1255
- hint: "Merge or delete old contentrain/* branches before creating new ones."
1256
- }, null, 2)
1257
- }],
1258
- isError: true
1259
- };
1260
- }
1302
+ const deleteReadiness = await provider.checkWriteReadiness?.();
1303
+ if (deleteReadiness?.blocked) return {
1304
+ content: [{
1305
+ type: "text",
1306
+ text: JSON.stringify({
1307
+ error: deleteReadiness.message,
1308
+ action: "blocked",
1309
+ hint: "Merge or delete old contentrain/* branches before creating new ones."
1310
+ }, null, 2)
1311
+ }],
1312
+ isError: true
1313
+ };
1261
1314
  let deletePlan;
1262
1315
  try {
1263
1316
  deletePlan = await planContentDelete(provider, {
@@ -1401,6 +1454,162 @@ function touchesSavedEntries(issue, results) {
1401
1454
  });
1402
1455
  }
1403
1456
  //#endregion
1457
+ //#region src/tools/vocabulary.ts
1458
+ /**
1459
+ * Vocabulary write tools.
1460
+ *
1461
+ * The vocabulary is the one piece of `.contentrain/` that had no tool. The
1462
+ * rules forbid hand-editing that directory, so the only way to add a canonical
1463
+ * term was to break the rule — which a field report duly did, having no
1464
+ * alternative.
1465
+ *
1466
+ * These go through the same plan → commit path as content and models, so a
1467
+ * vocabulary change lands on a `cr/*` branch and obeys the project's workflow
1468
+ * like everything else.
1469
+ */
1470
+ function registerVocabularyTools(server, provider) {
1471
+ server.tool("contentrain_vocabulary_save", "Add or update canonical vocabulary terms. Terms nest as { \"term-slug\": { \"en\": \"…\", \"tr\": \"…\" } } — the OUTER key is the term, the INNER key is a locale. Merges with the existing vocabulary: a term you omit is untouched. Changes are auto-committed to git — do NOT manually edit .contentrain/vocabulary.json.", { terms: z.record(z.string(), z.record(z.string(), z.string())).describe("Terms keyed by kebab-case term slug, each holding its translations keyed by locale code. Example: { \"sign-in\": { \"en\": \"Sign in\", \"tr\": \"Giriş yap\" } }") }, TOOL_ANNOTATIONS["contentrain_vocabulary_save"], async (input) => {
1472
+ if (!await readConfig(provider)) return {
1473
+ content: [{
1474
+ type: "text",
1475
+ text: JSON.stringify({ error: "Project not initialized. Run contentrain_init first." })
1476
+ }],
1477
+ isError: true
1478
+ };
1479
+ const readiness = await provider.checkWriteReadiness?.();
1480
+ if (readiness?.blocked) return {
1481
+ content: [{
1482
+ type: "text",
1483
+ text: JSON.stringify({
1484
+ error: readiness.message,
1485
+ action: "blocked",
1486
+ hint: "Merge or delete old contentrain/* branches before creating new ones."
1487
+ }, null, 2)
1488
+ }],
1489
+ isError: true
1490
+ };
1491
+ let plan;
1492
+ try {
1493
+ plan = await planVocabularySave(provider, { terms: input.terms });
1494
+ } catch (error) {
1495
+ return {
1496
+ content: [{
1497
+ type: "text",
1498
+ text: JSON.stringify({
1499
+ error: error instanceof Error ? error.message : String(error),
1500
+ hint: "Terms nest as { \"term-slug\": { \"en\": \"…\" } }. Grouping by locale at the top level is the usual mistake."
1501
+ }, null, 2)
1502
+ }],
1503
+ isError: true
1504
+ };
1505
+ }
1506
+ const termCount = plan.result.added.length + plan.result.updated.length;
1507
+ try {
1508
+ const commit = await commitThroughProvider(provider, {
1509
+ branch: buildBranchName("vocabulary", "save"),
1510
+ changes: plan.changes,
1511
+ message: `[contentrain] vocabulary: ${termCount} term(s)`,
1512
+ contextPayload: {
1513
+ tool: "contentrain_vocabulary_save",
1514
+ model: "vocabulary"
1515
+ }
1516
+ });
1517
+ return { content: [{
1518
+ type: "text",
1519
+ text: JSON.stringify({
1520
+ status: "committed",
1521
+ added: plan.result.added,
1522
+ updated: plan.result.updated,
1523
+ total_terms: plan.result.total,
1524
+ commit: commit.commitSha,
1525
+ workflow_action: commit.workflowAction,
1526
+ ...plan.advisories.length > 0 ? { advisories: plan.advisories } : {}
1527
+ }, null, 2)
1528
+ }] };
1529
+ } catch (error) {
1530
+ return {
1531
+ content: [{
1532
+ type: "text",
1533
+ text: JSON.stringify({ ...normalizeOperationError(error, "vocabulary_save") })
1534
+ }],
1535
+ isError: true
1536
+ };
1537
+ }
1538
+ });
1539
+ server.tool("contentrain_vocabulary_delete", "Remove canonical vocabulary terms by slug. Content already using a term is not touched — the vocabulary only advises. Changes are auto-committed to git.", {
1540
+ terms: z.array(z.string()).min(1).describe("Term slugs to remove"),
1541
+ confirm: z.boolean().describe("Must be true — this deletes canonical terms")
1542
+ }, TOOL_ANNOTATIONS["contentrain_vocabulary_delete"], async (input) => {
1543
+ if (!input.confirm) return {
1544
+ content: [{
1545
+ type: "text",
1546
+ text: JSON.stringify({ error: "Deleting vocabulary terms requires confirm: true" })
1547
+ }],
1548
+ isError: true
1549
+ };
1550
+ if (!await readConfig(provider)) return {
1551
+ content: [{
1552
+ type: "text",
1553
+ text: JSON.stringify({ error: "Project not initialized. Run contentrain_init first." })
1554
+ }],
1555
+ isError: true
1556
+ };
1557
+ const plan = await planVocabularyDelete(provider, { terms: input.terms });
1558
+ if (plan.changes.length === 0) return { content: [{
1559
+ type: "text",
1560
+ text: JSON.stringify({
1561
+ status: "no-op",
1562
+ deleted: [],
1563
+ missing: plan.result.missing,
1564
+ total_terms: plan.result.total,
1565
+ ...plan.advisories.length > 0 ? { advisories: plan.advisories } : {}
1566
+ }, null, 2)
1567
+ }] };
1568
+ const readiness = await provider.checkWriteReadiness?.();
1569
+ if (readiness?.blocked) return {
1570
+ content: [{
1571
+ type: "text",
1572
+ text: JSON.stringify({
1573
+ error: readiness.message,
1574
+ action: "blocked"
1575
+ }, null, 2)
1576
+ }],
1577
+ isError: true
1578
+ };
1579
+ try {
1580
+ const commit = await commitThroughProvider(provider, {
1581
+ branch: buildBranchName("vocabulary", "delete"),
1582
+ changes: plan.changes,
1583
+ message: `[contentrain] vocabulary: remove ${plan.result.deleted.length} term(s)`,
1584
+ contextPayload: {
1585
+ tool: "contentrain_vocabulary_delete",
1586
+ model: "vocabulary"
1587
+ }
1588
+ });
1589
+ return { content: [{
1590
+ type: "text",
1591
+ text: JSON.stringify({
1592
+ status: "committed",
1593
+ deleted: plan.result.deleted,
1594
+ missing: plan.result.missing,
1595
+ total_terms: plan.result.total,
1596
+ commit: commit.commitSha,
1597
+ workflow_action: commit.workflowAction,
1598
+ ...plan.advisories.length > 0 ? { advisories: plan.advisories } : {}
1599
+ }, null, 2)
1600
+ }] };
1601
+ } catch (error) {
1602
+ return {
1603
+ content: [{
1604
+ type: "text",
1605
+ text: JSON.stringify({ ...normalizeOperationError(error, "vocabulary_delete") })
1606
+ }],
1607
+ isError: true
1608
+ };
1609
+ }
1610
+ });
1611
+ }
1612
+ //#endregion
1404
1613
  //#region src/tools/workflow.ts
1405
1614
  /**
1406
1615
  * Resolve a merge target from either an exact branch name or a
@@ -1446,9 +1655,9 @@ async function resolveMergeBranch(git, input) {
1446
1655
  }))).toSorted((left, right) => right.t - left.t)[0].b };
1447
1656
  }
1448
1657
  function registerWorkflowTools(server, provider, projectRoot) {
1449
- server.tool("contentrain_validate", "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, stray non-i18n meta layout) — do NOT manually edit .contentrain/ files.", {
1658
+ server.tool("contentrain_validate", "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, stray non-i18n meta layout) and backfills a missing model title_field — do NOT manually edit .contentrain/ files.", {
1450
1659
  model: z.string().optional().describe("Model ID to validate (omit for all models)"),
1451
- fix: z.boolean().optional().describe("Auto-fix structural issues (canonical sort, orphan meta, missing locale files, stray non-i18n meta layout). Default: false")
1660
+ fix: z.boolean().optional().describe("Auto-fix structural issues (canonical sort, orphan meta, missing locale files, stray non-i18n meta layout) and backfill a missing model title_field. Each backfilled title_field is reported as a notice naming the rule that chose it. Default: false")
1452
1661
  }, TOOL_ANNOTATIONS["contentrain_validate"], async (input) => {
1453
1662
  if (input.fix && !projectRoot) return capabilityError("contentrain_validate", "localWorktree");
1454
1663
  if (!await readConfig(provider)) return {
@@ -1559,7 +1768,7 @@ function registerWorkflowTools(server, provider, projectRoot) {
1559
1768
  }],
1560
1769
  isError: true
1561
1770
  };
1562
- const git = simpleGit(projectRoot);
1771
+ const git = createGit(projectRoot);
1563
1772
  const remoteName = process.env["CONTENTRAIN_REMOTE"] ?? "origin";
1564
1773
  let hasRemote = false;
1565
1774
  try {
@@ -1687,7 +1896,7 @@ function registerWorkflowTools(server, provider, projectRoot) {
1687
1896
  isError: true
1688
1897
  };
1689
1898
  try {
1690
- const resolved = await resolveMergeBranch(simpleGit(projectRoot), input);
1899
+ const resolved = await resolveMergeBranch(createGit(projectRoot), input);
1691
1900
  if ("error" in resolved) return {
1692
1901
  content: [{
1693
1902
  type: "text",
@@ -1734,7 +1943,7 @@ function registerWorkflowTools(server, provider, projectRoot) {
1734
1943
  }, TOOL_ANNOTATIONS["contentrain_branch_list"], async (input) => {
1735
1944
  if (!provider.capabilities.localWorktree || !projectRoot) return capabilityError("contentrain_branch_list", "localWorktree");
1736
1945
  try {
1737
- const git = simpleGit(projectRoot);
1946
+ const git = createGit(projectRoot);
1738
1947
  const summary = await git.branchLocal();
1739
1948
  const crBranches = summary.all.filter((b) => b.startsWith("cr/") && b !== CONTENTRAIN_BRANCH);
1740
1949
  const remoteList = input.remote ? await listRemoteCrBranches(projectRoot) : void 0;
@@ -1796,7 +2005,7 @@ function registerWorkflowTools(server, provider, projectRoot) {
1796
2005
  isError: true
1797
2006
  };
1798
2007
  try {
1799
- const git = simpleGit(projectRoot);
2008
+ const git = createGit(projectRoot);
1800
2009
  if (!(await git.branchLocal()).all.includes(input.branch)) {
1801
2010
  const remoteOnly = await deleteRemoteBranch(projectRoot, input.branch);
1802
2011
  if (remoteOnly.deleted) return { content: [{
@@ -1964,6 +2173,7 @@ function registerNormalizeTools(server, provider, projectRoot) {
1964
2173
  ]).describe("Model kind"),
1965
2174
  domain: z.string().describe("Content domain (e.g. \"marketing\", \"app\")"),
1966
2175
  i18n: z.boolean().optional().describe("Enable i18n. Default: true"),
2176
+ title_field: z.string().optional().describe("Field shown as an entry title. Inferred from the extracted fields when omitted; the dry run reports what was chosen. Dictionary models: \"key\"."),
1967
2177
  fields: fieldDefZodSchema.optional().describe("Field definitions — shared schema with model_save for full parity"),
1968
2178
  entries: z.array(z.object({
1969
2179
  locale: z.string().optional().describe("Locale. Default: project default"),
@@ -2198,11 +2408,7 @@ function registerBulkTools(server, _provider, projectRoot) {
2198
2408
  if (model.kind === "collection") {
2199
2409
  const entries = sourceData;
2200
2410
  const updates = {};
2201
- for (const entryId of Object.keys(entries)) updates[entryId] = {
2202
- status: "draft",
2203
- source: "agent",
2204
- updated_by: "contentrain-mcp"
2205
- };
2411
+ for (const entryId of Object.keys(entries)) updates[entryId] = mergeEntryMeta(void 0);
2206
2412
  copiedCount = (await writeMetaEntries(wt, model, {
2207
2413
  locale: input.target_locale,
2208
2414
  defaultLocale: config.locales.default
@@ -2211,11 +2417,7 @@ function registerBulkTools(server, _provider, projectRoot) {
2211
2417
  await writeMeta(wt, model, {
2212
2418
  locale: input.target_locale,
2213
2419
  defaultLocale: config.locales.default
2214
- }, {
2215
- status: "draft",
2216
- source: "agent",
2217
- updated_by: "contentrain-mcp"
2218
- });
2420
+ }, mergeEntryMeta(void 0));
2219
2421
  copiedCount = 1;
2220
2422
  }
2221
2423
  });
@@ -2307,11 +2509,7 @@ function registerBulkTools(server, _provider, projectRoot) {
2307
2509
  for (const entryId of input.entry_ids) {
2308
2510
  const existing = metaData?.[entryId];
2309
2511
  if (!existing) continue;
2310
- updates[entryId] = {
2311
- ...existing,
2312
- status: input.status,
2313
- updated_by: "contentrain-mcp"
2314
- };
2512
+ updates[entryId] = applyStatusChange(existing, input.status);
2315
2513
  foundIds.add(entryId);
2316
2514
  }
2317
2515
  const written = await writeMetaEntries(wt, model, {
@@ -2328,11 +2526,7 @@ function registerBulkTools(server, _provider, projectRoot) {
2328
2526
  await writeMeta(wt, model, {
2329
2527
  locale,
2330
2528
  defaultLocale: config.locales.default
2331
- }, {
2332
- ...existing,
2333
- status: input.status,
2334
- updated_by: "contentrain-mcp"
2335
- });
2529
+ }, applyStatusChange(existing, input.status));
2336
2530
  updatedPerLocale[locale] = [model.id];
2337
2531
  }
2338
2532
  });
@@ -2612,7 +2806,7 @@ function registerMediaTools(server, provider, _projectRoot) {
2612
2806
  }
2613
2807
  //#endregion
2614
2808
  //#region package.json
2615
- var version = "2.3.1";
2809
+ var version = "3.0.1";
2616
2810
  //#endregion
2617
2811
  //#region src/server.ts
2618
2812
  /**
@@ -2649,6 +2843,7 @@ function createServer(input) {
2649
2843
  registerSetupTools(server, provider, projectRoot);
2650
2844
  registerModelTools(server, provider, projectRoot);
2651
2845
  registerContentTools(server, provider, projectRoot);
2846
+ registerVocabularyTools(server, provider);
2652
2847
  registerWorkflowTools(server, provider, projectRoot);
2653
2848
  registerNormalizeTools(server, provider, projectRoot);
2654
2849
  registerBulkTools(server, provider, projectRoot);
@@ -2665,7 +2860,10 @@ function resolveServerContext(input) {
2665
2860
  const instructions = input.instructions ?? "Contentrain is git-native content governance: models define structure; content is canonical JSON/Markdown on a dedicated branch. Start with contentrain_status; call contentrain_describe_format before creating models or content, and contentrain_describe before editing a model's entries. Every write lands on an isolated cr/* branch and content_save validates before committing. Destructive tools require confirm:true; preview contentrain_apply with dry_run:true first. Content decisions stay with the agent.";
2666
2861
  if (input.provider) {
2667
2862
  let projectRoot = input.projectRoot;
2668
- if (!projectRoot && input.provider instanceof LocalProvider) projectRoot = input.provider.projectRoot;
2863
+ if (!projectRoot) {
2864
+ const maybeRoot = input.provider.projectRoot;
2865
+ if (typeof maybeRoot === "string") projectRoot = maybeRoot;
2866
+ }
2669
2867
  return {
2670
2868
  provider: input.provider,
2671
2869
  projectRoot,
@@ -2682,4 +2880,4 @@ function resolveServerContext(input) {
2682
2880
  //#endregion
2683
2881
  export { createServer as n, DEFAULT_INSTRUCTIONS as t };
2684
2882
 
2685
- //# sourceMappingURL=server-CUZnuHQx.mjs.map
2883
+ //# sourceMappingURL=server-D-zJWbdI.mjs.map