@contentrain/mcp 3.9.32 → 3.10.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 (62) hide show
  1. package/README.md +18 -1
  2. package/dist/{apply-manager-BenQNb_b.mjs → apply-manager-B-O3eAbz.mjs} +16 -8
  3. package/dist/apply-manager-B-O3eAbz.mjs.map +1 -0
  4. package/dist/{branch-lifecycle-CirYfTXu.d.mts → branch-lifecycle-4iZax7a7.d.mts} +3 -3
  5. package/dist/{branch-lifecycle-CirYfTXu.d.mts.map → branch-lifecycle-4iZax7a7.d.mts.map} +1 -1
  6. package/dist/{branch-lifecycle-CcsSyfSw.mjs → branch-lifecycle-D4DxPgQg.mjs} +53 -5
  7. package/dist/branch-lifecycle-D4DxPgQg.mjs.map +1 -0
  8. package/dist/{config-BLwZtwlI.mjs → config-BnNYu128.mjs} +3 -2
  9. package/dist/config-BnNYu128.mjs.map +1 -0
  10. package/dist/content-manager-2g0pkLFh.d.mts.map +1 -1
  11. package/dist/{context-Cp8b8Nxm.mjs → context-D9HXnnGj.mjs} +5 -5
  12. package/dist/{context-Cp8b8Nxm.mjs.map → context-D9HXnnGj.mjs.map} +1 -1
  13. package/dist/core/apply-manager.d.mts.map +1 -1
  14. package/dist/core/apply-manager.mjs +6 -6
  15. package/dist/core/config.d.mts.map +1 -1
  16. package/dist/core/config.mjs +1 -1
  17. package/dist/core/content-manager.mjs +1 -1
  18. package/dist/core/context.mjs +3 -3
  19. package/dist/core/doctor.mjs +4 -4
  20. package/dist/core/model-manager.mjs +1 -1
  21. package/dist/core/ops/index.mjs +6 -6
  22. package/dist/core/validator/index.d.mts.map +1 -1
  23. package/dist/core/validator/index.mjs +3 -3
  24. package/dist/{doctor-Qabcb-Z1.mjs → doctor-Br1NqEl2.mjs} +4 -4
  25. package/dist/{doctor-Qabcb-Z1.mjs.map → doctor-Br1NqEl2.mjs.map} +1 -1
  26. package/dist/git/branch-lifecycle.d.mts +1 -1
  27. package/dist/git/branch-lifecycle.mjs +2 -2
  28. package/dist/git/reconcile.d.mts.map +1 -1
  29. package/dist/git/reconcile.mjs +8 -8
  30. package/dist/git/transaction.d.mts +7 -3
  31. package/dist/git/transaction.d.mts.map +1 -1
  32. package/dist/git/transaction.mjs +6 -6
  33. package/dist/index.mjs +13 -13
  34. package/dist/{local-BQVdC9MM.mjs → local-CBtRYFH4.mjs} +7 -7
  35. package/dist/{local-BQVdC9MM.mjs.map → local-CBtRYFH4.mjs.map} +1 -1
  36. package/dist/{model-manager-BYF2z7qC.mjs → model-manager-D0VztE8M.mjs} +8 -5
  37. package/dist/model-manager-D0VztE8M.mjs.map +1 -0
  38. package/dist/{ops-CQ_IIedh.mjs → ops-CVI308jA.mjs} +9 -4
  39. package/dist/ops-CVI308jA.mjs.map +1 -0
  40. package/dist/providers/local/index.mjs +9 -9
  41. package/dist/{reconcile-CRAukgae.mjs → reconcile-DSflMfhj.mjs} +8 -7
  42. package/dist/reconcile-DSflMfhj.mjs.map +1 -0
  43. package/dist/{reconcile-C7AaUfCl.mjs → reconcile-J-iorvSa.mjs} +5 -4
  44. package/dist/reconcile-J-iorvSa.mjs.map +1 -0
  45. package/dist/server/http/index.mjs +13 -13
  46. package/dist/{server-D8oYrulE.mjs → server-CJbmjX48.mjs} +43 -14
  47. package/dist/server-CJbmjX48.mjs.map +1 -0
  48. package/dist/server.mjs +13 -13
  49. package/dist/{transaction-CcjGiVYg.mjs → transaction-DnrUATJF.mjs} +14 -10
  50. package/dist/transaction-DnrUATJF.mjs.map +1 -0
  51. package/dist/{validator-Bz3jnSnb.mjs → validator-S19GHR4V.mjs} +16 -5
  52. package/dist/{validator-Bz3jnSnb.mjs.map → validator-S19GHR4V.mjs.map} +1 -1
  53. package/package.json +2 -2
  54. package/dist/apply-manager-BenQNb_b.mjs.map +0 -1
  55. package/dist/branch-lifecycle-CcsSyfSw.mjs.map +0 -1
  56. package/dist/config-BLwZtwlI.mjs.map +0 -1
  57. package/dist/model-manager-BYF2z7qC.mjs.map +0 -1
  58. package/dist/ops-CQ_IIedh.mjs.map +0 -1
  59. package/dist/reconcile-C7AaUfCl.mjs.map +0 -1
  60. package/dist/reconcile-CRAukgae.mjs.map +0 -1
  61. package/dist/server-D8oYrulE.mjs.map +0 -1
  62. package/dist/transaction-CcjGiVYg.mjs.map +0 -1
@@ -1,12 +1,12 @@
1
- import "../context-Cp8b8Nxm.mjs";
2
- import "../model-manager-BYF2z7qC.mjs";
1
+ import "../context-D9HXnnGj.mjs";
2
+ import "../model-manager-D0VztE8M.mjs";
3
3
  import "../fs-BFTuXbbw.mjs";
4
4
  import "../meta-manager-CSpKtjnP.mjs";
5
- import "../config-BLwZtwlI.mjs";
5
+ import "../config-BnNYu128.mjs";
6
6
  import "../identity-CxAB9Qb8.mjs";
7
7
  import "../base-branch-Cw9DWvCC.mjs";
8
- import "../branch-lifecycle-CcsSyfSw.mjs";
8
+ import "../branch-lifecycle-D4DxPgQg.mjs";
9
9
  import "../id-jrHWdtqn.mjs";
10
- import "../transaction-CcjGiVYg.mjs";
11
- import { a as detectFileFramework, c as validatePatchPath, i as checkSyntax, n as applyExtract, o as replaceInLine, r as applyReuse, s as validateFrameworkExpression, t as PATCHABLE_EXTENSIONS } from "../apply-manager-BenQNb_b.mjs";
10
+ import "../transaction-DnrUATJF.mjs";
11
+ import { a as detectFileFramework, c as validatePatchPath, i as checkSyntax, n as applyExtract, o as replaceInLine, r as applyReuse, s as validateFrameworkExpression, t as PATCHABLE_EXTENSIONS } from "../apply-manager-B-O3eAbz.mjs";
12
12
  export { PATCHABLE_EXTENSIONS, applyExtract, applyReuse, checkSyntax, detectFileFramework, replaceInLine, validateFrameworkExpression, validatePatchPath };
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.mts","names":[],"sources":["../../src/core/config.ts"],"mappings":";;;;;;AA2CA;;;;;iBAAgB,UAAA,CAAW,WAAA,WAAsB,OAAA,CAAQ,iBAAA;AAAA,iBACzC,UAAA,CAAW,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,iBAAA;;;AAAxD;iBAagB,cAAA,CAAe,WAAA,WAAsB,OAAA,CAAQ,UAAA;AAAA,iBAC7C,cAAA,CAAe,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA"}
1
+ {"version":3,"file":"config.d.mts","names":[],"sources":["../../src/core/config.ts"],"mappings":";;;;;;AA4CA;;;;;iBAAgB,UAAA,CAAW,WAAA,WAAsB,OAAA,CAAQ,iBAAA;AAAA,iBACzC,UAAA,CAAW,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,iBAAA;;;AAAxD;iBAagB,cAAA,CAAe,WAAA,WAAsB,OAAA,CAAQ,UAAA;AAAA,iBAC7C,cAAA,CAAe,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA"}
@@ -1,3 +1,3 @@
1
1
  import "../fs-BFTuXbbw.mjs";
2
- import { n as readVocabulary, t as readConfig } from "../config-BLwZtwlI.mjs";
2
+ import { n as readVocabulary, t as readConfig } from "../config-BnNYu128.mjs";
3
3
  export { readConfig, readVocabulary };
@@ -1,4 +1,4 @@
1
- import { C as resolveMdFilePath, D as validateSlug, E as validateLocale, O as writeContent, S as resolveLocaleStrategy, T as validateEntryId, _ as listContent, b as resolveContentDir, g as deleteContent, v as parseFrontmatter, w as serializeFrontmatter, x as resolveJsonFilePath, y as readContent } from "../model-manager-BYF2z7qC.mjs";
1
+ import { C as resolveMdFilePath, D as validateSlug, E as validateLocale, O as writeContent, S as resolveLocaleStrategy, T as validateEntryId, _ as listContent, b as resolveContentDir, g as deleteContent, v as parseFrontmatter, w as serializeFrontmatter, x as resolveJsonFilePath, y as readContent } from "../model-manager-D0VztE8M.mjs";
2
2
  import "../fs-BFTuXbbw.mjs";
3
3
  import "../meta-manager-CSpKtjnP.mjs";
4
4
  export { deleteContent, listContent, parseFrontmatter, readContent, resolveContentDir, resolveJsonFilePath, resolveLocaleStrategy, resolveMdFilePath, serializeFrontmatter, validateEntryId, validateLocale, validateSlug, writeContent };
@@ -1,6 +1,6 @@
1
- import { n as readContext, r as writeContext, t as buildContextChange } from "../context-Cp8b8Nxm.mjs";
2
- import "../model-manager-BYF2z7qC.mjs";
1
+ import { n as readContext, r as writeContext, t as buildContextChange } from "../context-D9HXnnGj.mjs";
2
+ import "../model-manager-D0VztE8M.mjs";
3
3
  import "../fs-BFTuXbbw.mjs";
4
4
  import "../meta-manager-CSpKtjnP.mjs";
5
- import "../config-BLwZtwlI.mjs";
5
+ import "../config-BnNYu128.mjs";
6
6
  export { buildContextChange, readContext, writeContext };
@@ -1,10 +1,10 @@
1
- import "../model-manager-BYF2z7qC.mjs";
1
+ import "../model-manager-D0VztE8M.mjs";
2
2
  import "../fs-BFTuXbbw.mjs";
3
3
  import "../meta-manager-CSpKtjnP.mjs";
4
- import "../config-BLwZtwlI.mjs";
4
+ import "../config-BnNYu128.mjs";
5
5
  import "../identity-CxAB9Qb8.mjs";
6
6
  import "../base-branch-Cw9DWvCC.mjs";
7
- import "../branch-lifecycle-CcsSyfSw.mjs";
7
+ import "../branch-lifecycle-D4DxPgQg.mjs";
8
8
  import "../scan-config-S5fkzNhL.mjs";
9
- import { t as runDoctor } from "../doctor-Qabcb-Z1.mjs";
9
+ import { t as runDoctor } from "../doctor-Br1NqEl2.mjs";
10
10
  export { runDoctor };
@@ -1,4 +1,4 @@
1
- import { a as countEntries, c as inferTitleField, d as readModel, f as titleFieldIssues, h as writeModel, i as collectFieldPaths, l as legacyFieldNames, m as validateModelDefinition, n as MODEL_FIELD_ORDER, o as deleteModel, p as titleFieldOptions, r as checkReferences, s as fieldDefZodSchema, t as FIELD_TYPE_ENUM, u as listModels } from "../model-manager-BYF2z7qC.mjs";
1
+ import { a as countEntries, c as inferTitleField, d as readModel, f as titleFieldIssues, h as writeModel, i as collectFieldPaths, l as legacyFieldNames, m as validateModelDefinition, n as MODEL_FIELD_ORDER, o as deleteModel, p as titleFieldOptions, r as checkReferences, s as fieldDefZodSchema, t as FIELD_TYPE_ENUM, u as listModels } from "../model-manager-D0VztE8M.mjs";
2
2
  import "../fs-BFTuXbbw.mjs";
3
3
  import "../meta-manager-CSpKtjnP.mjs";
4
4
  export { FIELD_TYPE_ENUM, MODEL_FIELD_ORDER, checkReferences, collectFieldPaths, countEntries, deleteModel, fieldDefZodSchema, inferTitleField, legacyFieldNames, listModels, readModel, titleFieldIssues, titleFieldOptions, validateModelDefinition, writeModel };
@@ -1,9 +1,9 @@
1
- import { n as applyChangesToWorktree, t as planReconcile } from "../../reconcile-C7AaUfCl.mjs";
2
- import "../../context-Cp8b8Nxm.mjs";
3
- import { A as contentDirPath, M as documentFilePath, N as metaFilePath, j as contentFilePath } from "../../model-manager-BYF2z7qC.mjs";
4
- import { a as planModelDelete, i as planModelSave, n as planVocabularySave, o as planContentSave, r as bindRef, s as planContentDelete, t as planVocabularyDelete } from "../../ops-CQ_IIedh.mjs";
1
+ import { n as applyChangesToWorktree, t as planReconcile } from "../../reconcile-J-iorvSa.mjs";
2
+ import "../../context-D9HXnnGj.mjs";
3
+ import { A as contentDirPath, M as documentFilePath, N as metaFilePath, j as contentFilePath } from "../../model-manager-D0VztE8M.mjs";
4
+ import { a as planModelDelete, i as planModelSave, n as planVocabularySave, o as planContentSave, r as bindRef, s as planContentDelete, t as planVocabularyDelete } from "../../ops-CVI308jA.mjs";
5
5
  import "../../fs-BFTuXbbw.mjs";
6
6
  import "../../meta-manager-CSpKtjnP.mjs";
7
- import "../../config-BLwZtwlI.mjs";
8
- import "../../validator-Bz3jnSnb.mjs";
7
+ import "../../config-BnNYu128.mjs";
8
+ import "../../validator-S19GHR4V.mjs";
9
9
  export { applyChangesToWorktree, bindRef, contentDirPath, contentFilePath, documentFilePath, metaFilePath, planContentDelete, planContentSave, planModelDelete, planModelSave, planReconcile, planVocabularyDelete, planVocabularySave };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/core/validator/entry.ts","../../../src/core/validator/relation-integrity.ts","../../../src/core/validator/project.ts","../../../src/core/validator/schedule.ts"],"mappings":";;;;;;;AAUA;;;;UAAiB,iBAAA;EAMN;EAJT,UAAA,GAAa,MAAA,SAAe,MAAA;EAIJ;EAFxB,cAAA;EAFa;EAIb,MAAA,GAAS,eAAA;AAAA;;;;;AAgBX;;;;;;;;;iBAAgB,eAAA,CACd,IAAA,EAAM,MAAA,mBACN,MAAA,EAAQ,MAAA,SAAe,QAAA,GACvB,OAAA,UACA,MAAA,UACA,OAAA,WACA,GAAA,GAAM,iBAAA,GACL,gBAAA;;;;;;AA7BH;;;;;;;;;UCIiB,cAAA;EACf,MAAA;EACA,OAAA,GAAU,MAAA;AAAA;;KAIA,iBAAA,IACV,aAAA,UACA,YAAA,aACG,OAAA,CAAQ,MAAA;AAAA,UAEI,6BAAA;EDOD;ECLd,QAAA;;;;;;EAMA,aAAA,IACE,aAAA,UACA,YAAA,aACG,OAAA,CAAQ,cAAA;AAAA;;;;;;;;;;;;iBAcO,sBAAA,CACpB,IAAA,EAAM,MAAA,mBACN,MAAA,EAAQ,MAAA,SAAe,QAAA,GACvB,OAAA,UACA,MAAA,UACA,OAAA,sBACA,WAAA,EAAa,iBAAA,EACb,IAAA,GAAM,6BAAA,GACL,OAAA,CAAQ,eAAA;;;UCxCM,eAAA;EACf,KAAA;EACA,GAAA;AAAA;AAAA,UAGe,cAAA;EACf,KAAA;EACA,OAAA;IACE,MAAA;IACA,QAAA;IACA,OAAA;IACA,cAAA;IACA,eAAA;EAAA;EAEF,MAAA,EAAQ,eAAA;EACR,KAAA;AAAA;;;AFDF;;;;;;;;;;;;;iBEkgCsB,eAAA,CAAgB,WAAA,UAAqB,OAAA,GAAU,eAAA,GAAkB,OAAA,CAAQ,cAAA;AAAA,iBACzE,eAAA,CAAgB,MAAA,EAAQ,UAAA,EAAY,OAAA,GAAU,eAAA,GAAkB,OAAA,CAAQ,cAAA;;;;;;AFzhC9F;;;;;iBGAgB,sBAAA,CACd,IAAA,EAAM,SAAA,EACN,GAAA;EAAO,KAAA;EAAe,MAAA;EAAgB,KAAA;EAAgB,IAAA;AAAA,GACtD,MAAA,EAAQ,eAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/core/validator/entry.ts","../../../src/core/validator/relation-integrity.ts","../../../src/core/validator/project.ts","../../../src/core/validator/schedule.ts"],"mappings":";;;;;;;AAUA;;;;UAAiB,iBAAA;EAMN;EAJT,UAAA,GAAa,MAAA,SAAe,MAAA;EAIJ;EAFxB,cAAA;EAFa;EAIb,MAAA,GAAS,eAAA;AAAA;;;;;AAgBX;;;;;;;;;iBAAgB,eAAA,CACd,IAAA,EAAM,MAAA,mBACN,MAAA,EAAQ,MAAA,SAAe,QAAA,GACvB,OAAA,UACA,MAAA,UACA,OAAA,WACA,GAAA,GAAM,iBAAA,GACL,gBAAA;;;;;;AA7BH;;;;;;;;;UCIiB,cAAA;EACf,MAAA;EACA,OAAA,GAAU,MAAA;AAAA;;KAIA,iBAAA,IACV,aAAA,UACA,YAAA,aACG,OAAA,CAAQ,MAAA;AAAA,UAEI,6BAAA;EDOD;ECLd,QAAA;;;;;;EAMA,aAAA,IACE,aAAA,UACA,YAAA,aACG,OAAA,CAAQ,cAAA;AAAA;;;;;;;;;;;;iBAcO,sBAAA,CACpB,IAAA,EAAM,MAAA,mBACN,MAAA,EAAQ,MAAA,SAAe,QAAA,GACvB,OAAA,UACA,MAAA,UACA,OAAA,sBACA,WAAA,EAAa,iBAAA,EACb,IAAA,GAAM,6BAAA,GACL,OAAA,CAAQ,eAAA;;;UCxCM,eAAA;EACf,KAAA;EACA,GAAA;AAAA;AAAA,UAGe,cAAA;EACf,KAAA;EACA,OAAA;IACE,MAAA;IACA,QAAA;IACA,OAAA;IACA,cAAA;IACA,eAAA;EAAA;EAEF,MAAA,EAAQ,eAAA;EACR,KAAA;AAAA;;;AFDF;;;;;;;;;;;;;iBE6gCsB,eAAA,CAAgB,WAAA,UAAqB,OAAA,GAAU,eAAA,GAAkB,OAAA,CAAQ,cAAA;AAAA,iBACzE,eAAA,CAAgB,MAAA,EAAQ,UAAA,EAAY,OAAA,GAAU,eAAA,GAAkB,OAAA,CAAQ,cAAA;;;;;;AFpiC9F;;;;;iBGAgB,sBAAA,CACd,IAAA,EAAM,SAAA,EACN,GAAA;EAAO,KAAA;EAAe,MAAA;EAAgB,KAAA;EAAgB,IAAA;AAAA,GACtD,MAAA,EAAQ,eAAA"}
@@ -1,6 +1,6 @@
1
- import "../../model-manager-BYF2z7qC.mjs";
1
+ import "../../model-manager-D0VztE8M.mjs";
2
2
  import "../../fs-BFTuXbbw.mjs";
3
3
  import "../../meta-manager-CSpKtjnP.mjs";
4
- import "../../config-BLwZtwlI.mjs";
5
- import { i as validateContent, n as validateScheduleFields, r as checkRelationIntegrity, t as validateProject } from "../../validator-Bz3jnSnb.mjs";
4
+ import "../../config-BnNYu128.mjs";
5
+ import { i as validateContent, n as validateScheduleFields, r as checkRelationIntegrity, t as validateProject } from "../../validator-S19GHR4V.mjs";
6
6
  export { checkRelationIntegrity, validateContent, validateProject, validateScheduleFields };
@@ -1,8 +1,8 @@
1
- import { S as resolveLocaleStrategy, b as resolveContentDir, d as readModel, m as validateModelDefinition, u as listModels, x as resolveJsonFilePath } from "./model-manager-BYF2z7qC.mjs";
1
+ import { S as resolveLocaleStrategy, b as resolveContentDir, d as readModel, m as validateModelDefinition, u as listModels, x as resolveJsonFilePath } from "./model-manager-D0VztE8M.mjs";
2
2
  import { a as readJson, i as readDir, o as readText, r as pathExists, t as contentrainDir } from "./fs-BFTuXbbw.mjs";
3
- import { t as readConfig } from "./config-BLwZtwlI.mjs";
3
+ import { t as readConfig } from "./config-BnNYu128.mjs";
4
4
  import { r as createGit } from "./identity-CxAB9Qb8.mjs";
5
- import { c as listRemoteCrBranches, n as checkBranchHealth } from "./branch-lifecycle-CcsSyfSw.mjs";
5
+ import { c as listRemoteCrBranches, n as checkBranchHealth } from "./branch-lifecycle-D4DxPgQg.mjs";
6
6
  import { i as autoDetectSourceDirs, o as discoverFiles } from "./scan-config-S5fkzNhL.mjs";
7
7
  import { join } from "node:path";
8
8
  import { readdir, stat } from "node:fs/promises";
@@ -393,4 +393,4 @@ async function analyzeMissingLocaleKeys(projectRoot, config) {
393
393
  //#endregion
394
394
  export { runDoctor as t };
395
395
 
396
- //# sourceMappingURL=doctor-Qabcb-Z1.mjs.map
396
+ //# sourceMappingURL=doctor-Br1NqEl2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-Qabcb-Z1.mjs","names":[],"sources":["../src/core/doctor.ts"],"sourcesContent":["import { join } from 'node:path'\nimport { createGit } from '../git/identity.js'\nimport { readdir, stat } from 'node:fs/promises'\nimport type { Dirent } from 'node:fs'\nimport type { ContentrainConfig, ModelDefinition } from '@contentrain/types'\nimport { readConfig } from './config.js'\nimport { listModels, readModel, validateModelDefinition } from './model-manager.js'\nimport { resolveContentDir, resolveJsonFilePath, resolveLocaleStrategy } from './content-manager.js'\nimport { autoDetectSourceDirs, discoverFiles } from './scan-config.js'\nimport { checkBranchHealth, listRemoteCrBranches } from '../git/branch-lifecycle.js'\nimport { contentrainDir, pathExists, readDir, readJson, readText } from '../util/fs.js'\n\n/**\n * Doctor — project health report.\n *\n * The public entry point is `runDoctor(projectRoot, { usage? })`. It is\n * inherently local-filesystem work (Node version, git install, file\n * mtimes, orphan directory detection), so the MCP tool surface gates\n * it behind the `localWorktree` capability — same pattern as\n * `contentrain_setup` and normalize.\n *\n * The report is structured JSON so three consumers can share it:\n *\n * - The `contentrain doctor` CLI command pretty-prints the checks.\n * - The Serve UI `/api/doctor` route returns the report to the\n * Dashboard's Doctor panel.\n * - Automation (CI, Studio) gets a deterministic JSON shape it can\n * assert against.\n *\n * Usage analysis (`--usage`) is a heavier, opt-in branch — it scans\n * every source file in the repo for content-key references. Kept\n * behind the flag so the default doctor run stays fast.\n */\n\nexport type CheckSeverity = 'error' | 'warning' | 'info'\n\nexport interface DoctorCheck {\n name: string\n pass: boolean\n detail: string\n /**\n * `error` — default for failing checks. Blocks a clean bill of health.\n * `warning` — failing-but-not-blocking (e.g. pending branches above\n * threshold, stale SDK client).\n * `info` — passed check; pure informational.\n */\n severity?: CheckSeverity\n}\n\nexport interface UnusedKeyEntry {\n model: string\n kind: string\n key: string\n locale: string\n}\n\nexport interface DuplicateValueEntry {\n model: string\n locale: string\n value: string\n keys: string[]\n}\n\nexport interface MissingLocaleEntry {\n model: string\n key: string\n missingIn: string\n}\n\nexport interface DoctorUsageAnalysis {\n unusedKeys: UnusedKeyEntry[]\n duplicateValues: DuplicateValueEntry[]\n missingLocaleKeys: MissingLocaleEntry[]\n}\n\nexport interface DoctorReport {\n checks: DoctorCheck[]\n summary: {\n total: number\n passed: number\n failed: number\n warnings: number\n }\n /** Present only when `options.usage === true`. */\n usage?: DoctorUsageAnalysis\n}\n\nexport interface RunDoctorOptions {\n /** Run heavier `--usage` analysis (unused keys, duplicates, locale gaps). */\n usage?: boolean\n}\n\nexport async function runDoctor(\n projectRoot: string,\n options: RunDoctorOptions = {},\n): Promise<DoctorReport> {\n const checks: DoctorCheck[] = []\n\n // ─── 1. Git installed ───\n try {\n const git = createGit(projectRoot)\n const version = await git.version()\n checks.push({\n name: 'Git',\n pass: true,\n detail: `v${version.major}.${version.minor}.${version.patch}`,\n })\n } catch {\n checks.push({ name: 'Git', pass: false, detail: 'Not installed or not in PATH', severity: 'error' })\n }\n\n // ─── 2. Git repo initialized ───\n const hasGit = await pathExists(join(projectRoot, '.git'))\n checks.push({\n name: 'Git repository',\n pass: hasGit,\n detail: hasGit ? projectRoot : 'No .git directory found',\n severity: hasGit ? undefined : 'error',\n })\n\n // ─── 3. Node version ───\n const nodeVersion = process.versions.node\n const [major] = nodeVersion.split('.').map(Number)\n const nodePass = (major ?? 0) >= 22\n checks.push({\n name: 'Node.js',\n pass: nodePass,\n detail: `v${nodeVersion}${nodePass ? '' : ' (requires ≥22)'}`,\n severity: nodePass ? undefined : 'error',\n })\n\n // ─── 4. .contentrain/ structure ───\n const crDir = contentrainDir(projectRoot)\n const hasCrDir = await pathExists(crDir)\n const hasConfig = await pathExists(join(crDir, 'config.json'))\n const hasModels = await pathExists(join(crDir, 'models'))\n const hasContent = await pathExists(join(crDir, 'content'))\n const structurePass = hasCrDir && hasConfig && hasModels && hasContent\n\n checks.push({\n name: '.contentrain/ structure',\n pass: structurePass,\n detail: !hasCrDir\n ? 'Not initialized — run `contentrain init`'\n : [\n hasConfig ? null : 'missing config.json',\n hasModels ? null : 'missing models/',\n hasContent ? null : 'missing content/',\n ].filter(Boolean).join(', ') || 'OK',\n severity: structurePass ? undefined : 'error',\n })\n\n // ─── 5. Config parseable ───\n let config: ContentrainConfig | null = null\n if (hasConfig) {\n config = await readConfig(projectRoot)\n checks.push({\n name: 'Config',\n pass: config !== null,\n detail: config\n ? `stack: ${config.stack}, locales: ${config.locales.supported.join(', ')}`\n : 'Failed to parse config.json',\n severity: config ? undefined : 'error',\n })\n }\n\n // ─── 6. Models parse AND satisfy the schema contract ───\n //\n // This check used to report \"all valid\" on the strength of JSON.parse alone,\n // which was a claim it had not earned. Model reads are unvalidated casts, so a\n // definition can be structurally wrong — a missing title_field, a field\n // constraint that cannot hold — and still load without a word. Doctor reports;\n // `contentrain validate --fix` is what repairs.\n if (hasCrDir) {\n try {\n // Enumerate the files, not `listModels`. `listModels` drops anything that\n // fails to parse, so routing this check through it made the \"failed to\n // parse\" branch unreachable — a corrupt model file was invisible to the\n // check whose job is to find it.\n const modelsDir = join(crDir, 'models')\n const files = (await readDir(modelsDir)).filter(f => f.endsWith('.json'))\n const parsed = await Promise.all(\n files.map(f => readJson<ModelDefinition>(join(modelsDir, f))),\n )\n const unparseable = files.filter((_, i) => parsed[i] === null)\n const invalid = parsed\n .filter((m): m is ModelDefinition => m !== null)\n .filter(m => validateModelDefinition(m).errors.length > 0)\n .map(m => m.id)\n const models = parsed.filter((m): m is ModelDefinition => m !== null)\n\n const problems = [\n ...(unparseable.length > 0 ? [`${unparseable.length} failed to parse (${unparseable.join(', ')})`] : []),\n ...(invalid.length > 0 ? [`${invalid.length} invalid (${invalid.join(', ')}) — run: contentrain validate --fix`] : []),\n ]\n checks.push({\n name: 'Models',\n pass: problems.length === 0,\n detail: `${models.length} model(s)${problems.length === 0 ? ', all valid' : `, ${problems.join('; ')}`}`,\n severity: problems.length === 0 ? undefined : 'error',\n })\n } catch {\n checks.push({ name: 'Models', pass: false, detail: 'Failed to read models', severity: 'error' })\n }\n }\n\n // ─── 7. Orphan content ───\n if (hasCrDir) {\n const orphans = await findOrphanContent(projectRoot)\n checks.push({\n name: 'Orphan content',\n pass: orphans.length === 0,\n detail: orphans.length === 0 ? 'None' : `Found: ${orphans.join(', ')}`,\n severity: orphans.length === 0 ? undefined : 'warning',\n })\n }\n\n // ─── 8. Stale contentrain branches ───\n if (hasGit) {\n try {\n const health = await checkBranchHealth(projectRoot)\n checks.push({\n name: 'Pending branches',\n pass: !health.blocked && !health.warning,\n detail: health.message\n ?? (health.unmerged === 0 ? 'None' : `${health.unmerged} active cr/* branch(es)`),\n severity: health.blocked ? 'error' : health.warning ? 'warning' : undefined,\n })\n } catch {\n checks.push({ name: 'Pending branches', pass: true, detail: 'Could not check' })\n }\n\n // ─── 8b. Remote cr/* branches ───\n // Authoritative ls-remote count (local branch pressure cannot see the\n // remote pile). Best-effort: skipped entirely without a remote, and an\n // unreachable remote is informational — doctor never fails offline.\n const remoteList = await listRemoteCrBranches(projectRoot, { timeoutMs: 5000 })\n if (remoteList) {\n if (remoteList.error) {\n checks.push({\n name: 'Remote branches',\n pass: true,\n detail: `Could not check ${remoteList.remote} (offline?)`,\n severity: 'info',\n })\n } else {\n const count = remoteList.branches.length\n const warnLimit = config?.branchWarnLimit ?? 50\n const warn = count >= warnLimit\n checks.push({\n name: 'Remote branches',\n pass: !warn,\n detail: count === 0\n ? `None on ${remoteList.remote}`\n : `${count} cr/* branch(es) on ${remoteList.remote}${warn ? ' — run `contentrain prune` to remove merged leftovers' : ''}`,\n severity: warn ? 'warning' : undefined,\n })\n }\n }\n }\n\n // ─── 9. SDK client freshness ───\n const clientDir = join(crDir, 'client')\n const modelsDir = join(crDir, 'models')\n if (await pathExists(clientDir) && await pathExists(modelsDir)) {\n try {\n const [clientMtime, modelsMtime] = await Promise.all([\n newestFileMtime(clientDir),\n newestFileMtime(modelsDir),\n ])\n if (clientMtime === null || modelsMtime === null) {\n checks.push({ name: 'SDK client', pass: true, detail: 'Could not check' })\n } else {\n const fresh = clientMtime >= modelsMtime\n checks.push({\n name: 'SDK client',\n pass: fresh,\n detail: fresh ? 'Up to date' : 'Stale — run `contentrain generate`',\n severity: fresh ? undefined : 'warning',\n })\n }\n } catch {\n checks.push({ name: 'SDK client', pass: true, detail: 'Could not check' })\n }\n }\n\n // ─── 10–12. Usage analysis (optional) ───\n let usage: DoctorUsageAnalysis | undefined\n if (options.usage && hasCrDir && config) {\n const [unusedKeys, duplicateValues, missingLocaleKeys] = await Promise.all([\n analyzeUnusedKeys(projectRoot, config),\n analyzeDuplicateValues(projectRoot, config),\n analyzeMissingLocaleKeys(projectRoot, config),\n ])\n usage = { unusedKeys, duplicateValues, missingLocaleKeys }\n\n checks.push({\n name: 'Unused dictionary keys',\n pass: unusedKeys.length === 0,\n detail: unusedKeys.length === 0\n ? 'All dictionary keys referenced in source'\n : `${unusedKeys.length} dictionary key(s) not referenced in source code`,\n severity: unusedKeys.length === 0 ? undefined : 'warning',\n })\n\n checks.push({\n name: 'Duplicate dictionary values',\n pass: duplicateValues.length === 0,\n detail: duplicateValues.length === 0\n ? 'No duplicate values'\n : `${duplicateValues.length} value(s) mapped to multiple keys`,\n severity: duplicateValues.length === 0 ? undefined : 'warning',\n })\n\n checks.push({\n name: 'Locale key coverage',\n pass: missingLocaleKeys.length === 0,\n detail: missingLocaleKeys.length === 0\n ? 'All locales have matching keys'\n : `${missingLocaleKeys.length} key(s) missing in some locales`,\n severity: missingLocaleKeys.length === 0 ? undefined : 'warning',\n })\n }\n\n const passed = checks.filter(c => c.pass).length\n const failed = checks.length - passed\n const warnings = checks.filter(c => !c.pass && c.severity === 'warning').length\n\n const report: DoctorReport = {\n checks,\n summary: { total: checks.length, passed, failed, warnings },\n }\n if (usage) report.usage = usage\n return report\n}\n\n/**\n * Newest mtime among the files under `dir`, recursively — null if it holds none.\n *\n * Stat the files, never the directory. A directory's mtime only moves when an\n * entry is added, removed, or renamed inside it: `generate` rewrites the client\n * files in place, so `.contentrain/client` never moves after the first run,\n * while a selective sync recreates model files via `git checkout`, which does\n * move `.contentrain/models`. Comparing the two directories therefore reported\n * \"stale\" permanently after any model save.\n */\nasync function newestFileMtime(dir: string): Promise<number | null> {\n const entries: Dirent[] = await readdir(dir, { withFileTypes: true }).catch(() => [])\n const mtimes = await Promise.all(entries.map(async (entry) => {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) return newestFileMtime(full)\n return stat(full).then(s => s.mtimeMs, () => null)\n }))\n const known = mtimes.filter((m): m is number => m !== null)\n return known.length > 0 ? Math.max(...known) : null\n}\n\nasync function findOrphanContent(projectRoot: string): Promise<string[]> {\n const crDir = contentrainDir(projectRoot)\n const models = await listModels(projectRoot)\n const orphans: string[] = []\n\n const knownContentDirs = new Set<string>()\n for (const m of models) {\n const full = await readModel(projectRoot, m.id)\n const modelForPath = full\n ? {\n ...full,\n content_path: full.content_path ?? (m as { content_path?: string }).content_path,\n }\n : {\n id: m.id,\n name: m.id,\n kind: m.kind,\n domain: m.domain,\n i18n: m.i18n,\n fields: {},\n content_path: (m as { content_path?: string }).content_path,\n }\n knownContentDirs.add(resolveContentDir(projectRoot, modelForPath))\n }\n\n const contentDir = join(crDir, 'content')\n if (await pathExists(contentDir)) {\n const domains = await readDir(contentDir)\n for (const domain of domains) {\n const domainDir = join(contentDir, domain)\n const entries = await readDir(domainDir)\n for (const entry of entries) {\n if (entry === '.gitkeep') continue\n const entryDir = join(domainDir, entry)\n if (!knownContentDirs.has(entryDir)) {\n orphans.push(`${domain}/${entry}`)\n }\n }\n }\n }\n\n for (const dir of knownContentDirs) {\n if (dir.startsWith(contentDir)) continue\n if (!await pathExists(dir)) {\n orphans.push(`(missing custom path) ${dir}`)\n continue\n }\n await readDir(dir)\n }\n\n return orphans\n}\n\nasync function analyzeUnusedKeys(\n projectRoot: string,\n config: ContentrainConfig,\n): Promise<UnusedKeyEntry[]> {\n const sourceDirs = await autoDetectSourceDirs(projectRoot)\n const files = await discoverFiles(projectRoot, { paths: sourceDirs })\n if (files.length === 0) return []\n\n const chunks = await Promise.all(\n files.map(async (relPath) => {\n const content = await readText(join(projectRoot, relPath))\n return content ?? ''\n }),\n )\n const allSource = chunks.join('\\n')\n\n // Dictionaries only.\n //\n // A dictionary key IS the reference — source code writes `t('nav.home')`, so\n // a key absent from source is genuinely dead. Collection entry IDs and\n // document slugs are not: entries are fetched by query, and a hex id like\n // `0a1b2c3d4e5f` will never appear in a source file. Checking them produced\n // a report that was mostly noise — of 134 warnings on a real project, ~120\n // were collection and document rows, and the real findings were lost among\n // them.\n const models = (await listModels(projectRoot)).filter(m => m.kind === 'dictionary')\n const defaultLocale = config.locales.default\n const unused: UnusedKeyEntry[] = []\n\n for (const m of models) {\n const fullModel = await readModel(projectRoot, m.id)\n if (!fullModel) continue\n\n const keys = await extractContentKeys(projectRoot, fullModel, defaultLocale)\n for (const key of keys) {\n if (!allSource.includes(key)) {\n unused.push({ model: m.id, kind: m.kind, key, locale: defaultLocale })\n }\n }\n }\n\n return unused\n}\n\n/**\n * Keys of a model that source code could plausibly reference by name.\n *\n * Only dictionaries have any: their keys are semantic addresses that appear\n * verbatim in source. Every other kind is queried, so its ids and slugs are\n * data, not identifiers, and looking for them in source finds nothing and\n * means nothing.\n */\nasync function extractContentKeys(\n projectRoot: string,\n model: ModelDefinition,\n locale: string,\n): Promise<string[]> {\n const cDir = resolveContentDir(projectRoot, model)\n if (!await pathExists(cDir)) return []\n\n switch (model.kind) {\n case 'dictionary': {\n const filePath = resolveJsonFilePath(cDir, model, locale)\n const data = await readJson<Record<string, string>>(filePath)\n return data ? Object.keys(data) : []\n }\n case 'collection': {\n const filePath = resolveJsonFilePath(cDir, model, locale)\n const data = await readJson<Record<string, Record<string, unknown>>>(filePath)\n return data ? Object.keys(data) : []\n }\n case 'document': {\n const strategy = resolveLocaleStrategy(model)\n const slugs: string[] = []\n if (!model.i18n) {\n const files = await readDir(cDir)\n for (const f of files) if (f.endsWith('.md')) slugs.push(f.replace('.md', ''))\n } else if (strategy === 'file') {\n const dirs = await readDir(cDir)\n for (const d of dirs) if (!d.startsWith('.')) slugs.push(d)\n } else if (strategy === 'suffix') {\n const files = await readDir(cDir)\n const suffix = `.${locale}.md`\n for (const f of files) if (f.endsWith(suffix)) slugs.push(f.slice(0, -suffix.length))\n } else if (strategy === 'directory') {\n const localeDir = join(cDir, locale)\n if (await pathExists(localeDir)) {\n const files = await readDir(localeDir)\n for (const f of files) if (f.endsWith('.md')) slugs.push(f.replace('.md', ''))\n }\n } else {\n const files = await readDir(cDir)\n for (const f of files) if (f.endsWith('.md')) slugs.push(f.replace('.md', ''))\n }\n return slugs\n }\n case 'singleton':\n return []\n default:\n return []\n }\n}\n\nasync function analyzeDuplicateValues(\n projectRoot: string,\n config: ContentrainConfig,\n): Promise<DuplicateValueEntry[]> {\n const models = await listModels(projectRoot)\n const result: DuplicateValueEntry[] = []\n\n for (const m of models) {\n if (m.kind !== 'dictionary') continue\n const fullModel = await readModel(projectRoot, m.id)\n if (!fullModel) continue\n\n const cDir = resolveContentDir(projectRoot, fullModel)\n for (const locale of config.locales.supported) {\n const filePath = resolveJsonFilePath(cDir, fullModel, locale)\n const data = await readJson<Record<string, string>>(filePath)\n if (!data) continue\n\n const valueToKeys = new Map<string, string[]>()\n for (const [key, value] of Object.entries(data)) {\n const arr = valueToKeys.get(value)\n if (arr) arr.push(key)\n else valueToKeys.set(value, [key])\n }\n\n for (const [value, keys] of valueToKeys) {\n if (keys.length > 1) {\n result.push({ model: m.id, locale, value, keys })\n }\n }\n }\n }\n\n return result\n}\n\nasync function analyzeMissingLocaleKeys(\n projectRoot: string,\n config: ContentrainConfig,\n): Promise<MissingLocaleEntry[]> {\n if (config.locales.supported.length < 2) return []\n\n const models = await listModels(projectRoot)\n const result: MissingLocaleEntry[] = []\n const defaultLocale = config.locales.default\n const otherLocales = config.locales.supported.filter(l => l !== defaultLocale)\n\n for (const m of models) {\n if (m.kind !== 'dictionary' && m.kind !== 'collection') continue\n if (!m.i18n) continue\n\n const fullModel = await readModel(projectRoot, m.id)\n if (!fullModel) continue\n\n const cDir = resolveContentDir(projectRoot, fullModel)\n const defaultPath = resolveJsonFilePath(cDir, fullModel, defaultLocale)\n const defaultData = await readJson<Record<string, unknown>>(defaultPath)\n if (!defaultData) continue\n const defaultKeys = new Set(Object.keys(defaultData))\n\n for (const locale of otherLocales) {\n const localePath = resolveJsonFilePath(cDir, fullModel, locale)\n const localeData = await readJson<Record<string, unknown>>(localePath)\n const localeKeys = localeData ? new Set(Object.keys(localeData)) : new Set<string>()\n\n for (const key of defaultKeys) {\n if (!localeKeys.has(key)) {\n result.push({ model: m.id, key, missingIn: locale })\n }\n }\n }\n }\n\n return result\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"doctor-Br1NqEl2.mjs","names":[],"sources":["../src/core/doctor.ts"],"sourcesContent":["import { join } from 'node:path'\nimport { createGit } from '../git/identity.js'\nimport { readdir, stat } from 'node:fs/promises'\nimport type { Dirent } from 'node:fs'\nimport type { ContentrainConfig, ModelDefinition } from '@contentrain/types'\nimport { readConfig } from './config.js'\nimport { listModels, readModel, validateModelDefinition } from './model-manager.js'\nimport { resolveContentDir, resolveJsonFilePath, resolveLocaleStrategy } from './content-manager.js'\nimport { autoDetectSourceDirs, discoverFiles } from './scan-config.js'\nimport { checkBranchHealth, listRemoteCrBranches } from '../git/branch-lifecycle.js'\nimport { contentrainDir, pathExists, readDir, readJson, readText } from '../util/fs.js'\n\n/**\n * Doctor — project health report.\n *\n * The public entry point is `runDoctor(projectRoot, { usage? })`. It is\n * inherently local-filesystem work (Node version, git install, file\n * mtimes, orphan directory detection), so the MCP tool surface gates\n * it behind the `localWorktree` capability — same pattern as\n * `contentrain_setup` and normalize.\n *\n * The report is structured JSON so three consumers can share it:\n *\n * - The `contentrain doctor` CLI command pretty-prints the checks.\n * - The Serve UI `/api/doctor` route returns the report to the\n * Dashboard's Doctor panel.\n * - Automation (CI, Studio) gets a deterministic JSON shape it can\n * assert against.\n *\n * Usage analysis (`--usage`) is a heavier, opt-in branch — it scans\n * every source file in the repo for content-key references. Kept\n * behind the flag so the default doctor run stays fast.\n */\n\nexport type CheckSeverity = 'error' | 'warning' | 'info'\n\nexport interface DoctorCheck {\n name: string\n pass: boolean\n detail: string\n /**\n * `error` — default for failing checks. Blocks a clean bill of health.\n * `warning` — failing-but-not-blocking (e.g. pending branches above\n * threshold, stale SDK client).\n * `info` — passed check; pure informational.\n */\n severity?: CheckSeverity\n}\n\nexport interface UnusedKeyEntry {\n model: string\n kind: string\n key: string\n locale: string\n}\n\nexport interface DuplicateValueEntry {\n model: string\n locale: string\n value: string\n keys: string[]\n}\n\nexport interface MissingLocaleEntry {\n model: string\n key: string\n missingIn: string\n}\n\nexport interface DoctorUsageAnalysis {\n unusedKeys: UnusedKeyEntry[]\n duplicateValues: DuplicateValueEntry[]\n missingLocaleKeys: MissingLocaleEntry[]\n}\n\nexport interface DoctorReport {\n checks: DoctorCheck[]\n summary: {\n total: number\n passed: number\n failed: number\n warnings: number\n }\n /** Present only when `options.usage === true`. */\n usage?: DoctorUsageAnalysis\n}\n\nexport interface RunDoctorOptions {\n /** Run heavier `--usage` analysis (unused keys, duplicates, locale gaps). */\n usage?: boolean\n}\n\nexport async function runDoctor(\n projectRoot: string,\n options: RunDoctorOptions = {},\n): Promise<DoctorReport> {\n const checks: DoctorCheck[] = []\n\n // ─── 1. Git installed ───\n try {\n const git = createGit(projectRoot)\n const version = await git.version()\n checks.push({\n name: 'Git',\n pass: true,\n detail: `v${version.major}.${version.minor}.${version.patch}`,\n })\n } catch {\n checks.push({ name: 'Git', pass: false, detail: 'Not installed or not in PATH', severity: 'error' })\n }\n\n // ─── 2. Git repo initialized ───\n const hasGit = await pathExists(join(projectRoot, '.git'))\n checks.push({\n name: 'Git repository',\n pass: hasGit,\n detail: hasGit ? projectRoot : 'No .git directory found',\n severity: hasGit ? undefined : 'error',\n })\n\n // ─── 3. Node version ───\n const nodeVersion = process.versions.node\n const [major] = nodeVersion.split('.').map(Number)\n const nodePass = (major ?? 0) >= 22\n checks.push({\n name: 'Node.js',\n pass: nodePass,\n detail: `v${nodeVersion}${nodePass ? '' : ' (requires ≥22)'}`,\n severity: nodePass ? undefined : 'error',\n })\n\n // ─── 4. .contentrain/ structure ───\n const crDir = contentrainDir(projectRoot)\n const hasCrDir = await pathExists(crDir)\n const hasConfig = await pathExists(join(crDir, 'config.json'))\n const hasModels = await pathExists(join(crDir, 'models'))\n const hasContent = await pathExists(join(crDir, 'content'))\n const structurePass = hasCrDir && hasConfig && hasModels && hasContent\n\n checks.push({\n name: '.contentrain/ structure',\n pass: structurePass,\n detail: !hasCrDir\n ? 'Not initialized — run `contentrain init`'\n : [\n hasConfig ? null : 'missing config.json',\n hasModels ? null : 'missing models/',\n hasContent ? null : 'missing content/',\n ].filter(Boolean).join(', ') || 'OK',\n severity: structurePass ? undefined : 'error',\n })\n\n // ─── 5. Config parseable ───\n let config: ContentrainConfig | null = null\n if (hasConfig) {\n config = await readConfig(projectRoot)\n checks.push({\n name: 'Config',\n pass: config !== null,\n detail: config\n ? `stack: ${config.stack}, locales: ${config.locales.supported.join(', ')}`\n : 'Failed to parse config.json',\n severity: config ? undefined : 'error',\n })\n }\n\n // ─── 6. Models parse AND satisfy the schema contract ───\n //\n // This check used to report \"all valid\" on the strength of JSON.parse alone,\n // which was a claim it had not earned. Model reads are unvalidated casts, so a\n // definition can be structurally wrong — a missing title_field, a field\n // constraint that cannot hold — and still load without a word. Doctor reports;\n // `contentrain validate --fix` is what repairs.\n if (hasCrDir) {\n try {\n // Enumerate the files, not `listModels`. `listModels` drops anything that\n // fails to parse, so routing this check through it made the \"failed to\n // parse\" branch unreachable — a corrupt model file was invisible to the\n // check whose job is to find it.\n const modelsDir = join(crDir, 'models')\n const files = (await readDir(modelsDir)).filter(f => f.endsWith('.json'))\n const parsed = await Promise.all(\n files.map(f => readJson<ModelDefinition>(join(modelsDir, f))),\n )\n const unparseable = files.filter((_, i) => parsed[i] === null)\n const invalid = parsed\n .filter((m): m is ModelDefinition => m !== null)\n .filter(m => validateModelDefinition(m).errors.length > 0)\n .map(m => m.id)\n const models = parsed.filter((m): m is ModelDefinition => m !== null)\n\n const problems = [\n ...(unparseable.length > 0 ? [`${unparseable.length} failed to parse (${unparseable.join(', ')})`] : []),\n ...(invalid.length > 0 ? [`${invalid.length} invalid (${invalid.join(', ')}) — run: contentrain validate --fix`] : []),\n ]\n checks.push({\n name: 'Models',\n pass: problems.length === 0,\n detail: `${models.length} model(s)${problems.length === 0 ? ', all valid' : `, ${problems.join('; ')}`}`,\n severity: problems.length === 0 ? undefined : 'error',\n })\n } catch {\n checks.push({ name: 'Models', pass: false, detail: 'Failed to read models', severity: 'error' })\n }\n }\n\n // ─── 7. Orphan content ───\n if (hasCrDir) {\n const orphans = await findOrphanContent(projectRoot)\n checks.push({\n name: 'Orphan content',\n pass: orphans.length === 0,\n detail: orphans.length === 0 ? 'None' : `Found: ${orphans.join(', ')}`,\n severity: orphans.length === 0 ? undefined : 'warning',\n })\n }\n\n // ─── 8. Stale contentrain branches ───\n if (hasGit) {\n try {\n const health = await checkBranchHealth(projectRoot)\n checks.push({\n name: 'Pending branches',\n pass: !health.blocked && !health.warning,\n detail: health.message\n ?? (health.unmerged === 0 ? 'None' : `${health.unmerged} active cr/* branch(es)`),\n severity: health.blocked ? 'error' : health.warning ? 'warning' : undefined,\n })\n } catch {\n checks.push({ name: 'Pending branches', pass: true, detail: 'Could not check' })\n }\n\n // ─── 8b. Remote cr/* branches ───\n // Authoritative ls-remote count (local branch pressure cannot see the\n // remote pile). Best-effort: skipped entirely without a remote, and an\n // unreachable remote is informational — doctor never fails offline.\n const remoteList = await listRemoteCrBranches(projectRoot, { timeoutMs: 5000 })\n if (remoteList) {\n if (remoteList.error) {\n checks.push({\n name: 'Remote branches',\n pass: true,\n detail: `Could not check ${remoteList.remote} (offline?)`,\n severity: 'info',\n })\n } else {\n const count = remoteList.branches.length\n const warnLimit = config?.branchWarnLimit ?? 50\n const warn = count >= warnLimit\n checks.push({\n name: 'Remote branches',\n pass: !warn,\n detail: count === 0\n ? `None on ${remoteList.remote}`\n : `${count} cr/* branch(es) on ${remoteList.remote}${warn ? ' — run `contentrain prune` to remove merged leftovers' : ''}`,\n severity: warn ? 'warning' : undefined,\n })\n }\n }\n }\n\n // ─── 9. SDK client freshness ───\n const clientDir = join(crDir, 'client')\n const modelsDir = join(crDir, 'models')\n if (await pathExists(clientDir) && await pathExists(modelsDir)) {\n try {\n const [clientMtime, modelsMtime] = await Promise.all([\n newestFileMtime(clientDir),\n newestFileMtime(modelsDir),\n ])\n if (clientMtime === null || modelsMtime === null) {\n checks.push({ name: 'SDK client', pass: true, detail: 'Could not check' })\n } else {\n const fresh = clientMtime >= modelsMtime\n checks.push({\n name: 'SDK client',\n pass: fresh,\n detail: fresh ? 'Up to date' : 'Stale — run `contentrain generate`',\n severity: fresh ? undefined : 'warning',\n })\n }\n } catch {\n checks.push({ name: 'SDK client', pass: true, detail: 'Could not check' })\n }\n }\n\n // ─── 10–12. Usage analysis (optional) ───\n let usage: DoctorUsageAnalysis | undefined\n if (options.usage && hasCrDir && config) {\n const [unusedKeys, duplicateValues, missingLocaleKeys] = await Promise.all([\n analyzeUnusedKeys(projectRoot, config),\n analyzeDuplicateValues(projectRoot, config),\n analyzeMissingLocaleKeys(projectRoot, config),\n ])\n usage = { unusedKeys, duplicateValues, missingLocaleKeys }\n\n checks.push({\n name: 'Unused dictionary keys',\n pass: unusedKeys.length === 0,\n detail: unusedKeys.length === 0\n ? 'All dictionary keys referenced in source'\n : `${unusedKeys.length} dictionary key(s) not referenced in source code`,\n severity: unusedKeys.length === 0 ? undefined : 'warning',\n })\n\n checks.push({\n name: 'Duplicate dictionary values',\n pass: duplicateValues.length === 0,\n detail: duplicateValues.length === 0\n ? 'No duplicate values'\n : `${duplicateValues.length} value(s) mapped to multiple keys`,\n severity: duplicateValues.length === 0 ? undefined : 'warning',\n })\n\n checks.push({\n name: 'Locale key coverage',\n pass: missingLocaleKeys.length === 0,\n detail: missingLocaleKeys.length === 0\n ? 'All locales have matching keys'\n : `${missingLocaleKeys.length} key(s) missing in some locales`,\n severity: missingLocaleKeys.length === 0 ? undefined : 'warning',\n })\n }\n\n const passed = checks.filter(c => c.pass).length\n const failed = checks.length - passed\n const warnings = checks.filter(c => !c.pass && c.severity === 'warning').length\n\n const report: DoctorReport = {\n checks,\n summary: { total: checks.length, passed, failed, warnings },\n }\n if (usage) report.usage = usage\n return report\n}\n\n/**\n * Newest mtime among the files under `dir`, recursively — null if it holds none.\n *\n * Stat the files, never the directory. A directory's mtime only moves when an\n * entry is added, removed, or renamed inside it: `generate` rewrites the client\n * files in place, so `.contentrain/client` never moves after the first run,\n * while a selective sync recreates model files via `git checkout`, which does\n * move `.contentrain/models`. Comparing the two directories therefore reported\n * \"stale\" permanently after any model save.\n */\nasync function newestFileMtime(dir: string): Promise<number | null> {\n const entries: Dirent[] = await readdir(dir, { withFileTypes: true }).catch(() => [])\n const mtimes = await Promise.all(entries.map(async (entry) => {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) return newestFileMtime(full)\n return stat(full).then(s => s.mtimeMs, () => null)\n }))\n const known = mtimes.filter((m): m is number => m !== null)\n return known.length > 0 ? Math.max(...known) : null\n}\n\nasync function findOrphanContent(projectRoot: string): Promise<string[]> {\n const crDir = contentrainDir(projectRoot)\n const models = await listModels(projectRoot)\n const orphans: string[] = []\n\n const knownContentDirs = new Set<string>()\n for (const m of models) {\n const full = await readModel(projectRoot, m.id)\n const modelForPath = full\n ? {\n ...full,\n content_path: full.content_path ?? (m as { content_path?: string }).content_path,\n }\n : {\n id: m.id,\n name: m.id,\n kind: m.kind,\n domain: m.domain,\n i18n: m.i18n,\n fields: {},\n content_path: (m as { content_path?: string }).content_path,\n }\n knownContentDirs.add(resolveContentDir(projectRoot, modelForPath))\n }\n\n const contentDir = join(crDir, 'content')\n if (await pathExists(contentDir)) {\n const domains = await readDir(contentDir)\n for (const domain of domains) {\n const domainDir = join(contentDir, domain)\n const entries = await readDir(domainDir)\n for (const entry of entries) {\n if (entry === '.gitkeep') continue\n const entryDir = join(domainDir, entry)\n if (!knownContentDirs.has(entryDir)) {\n orphans.push(`${domain}/${entry}`)\n }\n }\n }\n }\n\n for (const dir of knownContentDirs) {\n if (dir.startsWith(contentDir)) continue\n if (!await pathExists(dir)) {\n orphans.push(`(missing custom path) ${dir}`)\n continue\n }\n await readDir(dir)\n }\n\n return orphans\n}\n\nasync function analyzeUnusedKeys(\n projectRoot: string,\n config: ContentrainConfig,\n): Promise<UnusedKeyEntry[]> {\n const sourceDirs = await autoDetectSourceDirs(projectRoot)\n const files = await discoverFiles(projectRoot, { paths: sourceDirs })\n if (files.length === 0) return []\n\n const chunks = await Promise.all(\n files.map(async (relPath) => {\n const content = await readText(join(projectRoot, relPath))\n return content ?? ''\n }),\n )\n const allSource = chunks.join('\\n')\n\n // Dictionaries only.\n //\n // A dictionary key IS the reference — source code writes `t('nav.home')`, so\n // a key absent from source is genuinely dead. Collection entry IDs and\n // document slugs are not: entries are fetched by query, and a hex id like\n // `0a1b2c3d4e5f` will never appear in a source file. Checking them produced\n // a report that was mostly noise — of 134 warnings on a real project, ~120\n // were collection and document rows, and the real findings were lost among\n // them.\n const models = (await listModels(projectRoot)).filter(m => m.kind === 'dictionary')\n const defaultLocale = config.locales.default\n const unused: UnusedKeyEntry[] = []\n\n for (const m of models) {\n const fullModel = await readModel(projectRoot, m.id)\n if (!fullModel) continue\n\n const keys = await extractContentKeys(projectRoot, fullModel, defaultLocale)\n for (const key of keys) {\n if (!allSource.includes(key)) {\n unused.push({ model: m.id, kind: m.kind, key, locale: defaultLocale })\n }\n }\n }\n\n return unused\n}\n\n/**\n * Keys of a model that source code could plausibly reference by name.\n *\n * Only dictionaries have any: their keys are semantic addresses that appear\n * verbatim in source. Every other kind is queried, so its ids and slugs are\n * data, not identifiers, and looking for them in source finds nothing and\n * means nothing.\n */\nasync function extractContentKeys(\n projectRoot: string,\n model: ModelDefinition,\n locale: string,\n): Promise<string[]> {\n const cDir = resolveContentDir(projectRoot, model)\n if (!await pathExists(cDir)) return []\n\n switch (model.kind) {\n case 'dictionary': {\n const filePath = resolveJsonFilePath(cDir, model, locale)\n const data = await readJson<Record<string, string>>(filePath)\n return data ? Object.keys(data) : []\n }\n case 'collection': {\n const filePath = resolveJsonFilePath(cDir, model, locale)\n const data = await readJson<Record<string, Record<string, unknown>>>(filePath)\n return data ? Object.keys(data) : []\n }\n case 'document': {\n const strategy = resolveLocaleStrategy(model)\n const slugs: string[] = []\n if (!model.i18n) {\n const files = await readDir(cDir)\n for (const f of files) if (f.endsWith('.md')) slugs.push(f.replace('.md', ''))\n } else if (strategy === 'file') {\n const dirs = await readDir(cDir)\n for (const d of dirs) if (!d.startsWith('.')) slugs.push(d)\n } else if (strategy === 'suffix') {\n const files = await readDir(cDir)\n const suffix = `.${locale}.md`\n for (const f of files) if (f.endsWith(suffix)) slugs.push(f.slice(0, -suffix.length))\n } else if (strategy === 'directory') {\n const localeDir = join(cDir, locale)\n if (await pathExists(localeDir)) {\n const files = await readDir(localeDir)\n for (const f of files) if (f.endsWith('.md')) slugs.push(f.replace('.md', ''))\n }\n } else {\n const files = await readDir(cDir)\n for (const f of files) if (f.endsWith('.md')) slugs.push(f.replace('.md', ''))\n }\n return slugs\n }\n case 'singleton':\n return []\n default:\n return []\n }\n}\n\nasync function analyzeDuplicateValues(\n projectRoot: string,\n config: ContentrainConfig,\n): Promise<DuplicateValueEntry[]> {\n const models = await listModels(projectRoot)\n const result: DuplicateValueEntry[] = []\n\n for (const m of models) {\n if (m.kind !== 'dictionary') continue\n const fullModel = await readModel(projectRoot, m.id)\n if (!fullModel) continue\n\n const cDir = resolveContentDir(projectRoot, fullModel)\n for (const locale of config.locales.supported) {\n const filePath = resolveJsonFilePath(cDir, fullModel, locale)\n const data = await readJson<Record<string, string>>(filePath)\n if (!data) continue\n\n const valueToKeys = new Map<string, string[]>()\n for (const [key, value] of Object.entries(data)) {\n const arr = valueToKeys.get(value)\n if (arr) arr.push(key)\n else valueToKeys.set(value, [key])\n }\n\n for (const [value, keys] of valueToKeys) {\n if (keys.length > 1) {\n result.push({ model: m.id, locale, value, keys })\n }\n }\n }\n }\n\n return result\n}\n\nasync function analyzeMissingLocaleKeys(\n projectRoot: string,\n config: ContentrainConfig,\n): Promise<MissingLocaleEntry[]> {\n if (config.locales.supported.length < 2) return []\n\n const models = await listModels(projectRoot)\n const result: MissingLocaleEntry[] = []\n const defaultLocale = config.locales.default\n const otherLocales = config.locales.supported.filter(l => l !== defaultLocale)\n\n for (const m of models) {\n if (m.kind !== 'dictionary' && m.kind !== 'collection') continue\n if (!m.i18n) continue\n\n const fullModel = await readModel(projectRoot, m.id)\n if (!fullModel) continue\n\n const cDir = resolveContentDir(projectRoot, fullModel)\n const defaultPath = resolveJsonFilePath(cDir, fullModel, defaultLocale)\n const defaultData = await readJson<Record<string, unknown>>(defaultPath)\n if (!defaultData) continue\n const defaultKeys = new Set(Object.keys(defaultData))\n\n for (const locale of otherLocales) {\n const localePath = resolveJsonFilePath(cDir, fullModel, locale)\n const localeData = await readJson<Record<string, unknown>>(localePath)\n const localeKeys = localeData ? new Set(Object.keys(localeData)) : new Set<string>()\n\n for (const key of defaultKeys) {\n if (!localeKeys.has(key)) {\n result.push({ model: m.id, key, missingIn: locale })\n }\n }\n }\n }\n\n return result\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { _ as pruneMergedRemoteBranches, a as RemoteBranchList, c as branchDiff, d as cleanupMergedBranches, f as contentBranchRelation, g as networkGit, h as listRemoteCrBranches, i as ContentBranchRelation, l as checkBranchHealth, m as isRefMerged, n as BranchHealthCheck, o as RemoteDeleteResult, p as deleteRemoteBranch, r as CleanupResult, s as RemotePruneResult, t as BranchDiffResult, u as classifyMergedBranches } from "../branch-lifecycle-CirYfTXu.mjs";
1
+ import { _ as pruneMergedRemoteBranches, a as RemoteBranchList, c as branchDiff, d as cleanupMergedBranches, f as contentBranchRelation, g as networkGit, h as listRemoteCrBranches, i as ContentBranchRelation, l as checkBranchHealth, m as isRefMerged, n as BranchHealthCheck, o as RemoteDeleteResult, p as deleteRemoteBranch, r as CleanupResult, s as RemotePruneResult, t as BranchDiffResult, u as classifyMergedBranches } from "../branch-lifecycle-4iZax7a7.mjs";
2
2
  export { BranchDiffResult, BranchHealthCheck, CleanupResult, ContentBranchRelation, RemoteBranchList, RemoteDeleteResult, RemotePruneResult, branchDiff, checkBranchHealth, classifyMergedBranches, cleanupMergedBranches, contentBranchRelation, deleteRemoteBranch, isRefMerged, listRemoteCrBranches, networkGit, pruneMergedRemoteBranches };
@@ -1,6 +1,6 @@
1
1
  import "../fs-BFTuXbbw.mjs";
2
- import "../config-BLwZtwlI.mjs";
2
+ import "../config-BnNYu128.mjs";
3
3
  import "../identity-CxAB9Qb8.mjs";
4
4
  import "../base-branch-Cw9DWvCC.mjs";
5
- import { a as contentBranchRelation, c as listRemoteCrBranches, i as cleanupMergedBranches, l as networkGit, n as checkBranchHealth, o as deleteRemoteBranch, r as classifyMergedBranches, s as isRefMerged, t as branchDiff, u as pruneMergedRemoteBranches } from "../branch-lifecycle-CcsSyfSw.mjs";
5
+ import { a as contentBranchRelation, c as listRemoteCrBranches, i as cleanupMergedBranches, l as networkGit, n as checkBranchHealth, o as deleteRemoteBranch, r as classifyMergedBranches, s as isRefMerged, t as branchDiff, u as pruneMergedRemoteBranches } from "../branch-lifecycle-D4DxPgQg.mjs";
6
6
  export { branchDiff, checkBranchHealth, classifyMergedBranches, cleanupMergedBranches, contentBranchRelation, deleteRemoteBranch, isRefMerged, listRemoteCrBranches, networkGit, pruneMergedRemoteBranches };
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.d.mts","names":[],"sources":["../../src/git/reconcile.ts"],"mappings":";;;;UAqBiB,wBAAA;;EAEf,MAAA;EAFe;EAIf,WAAA,GAAc,kBAAA;;EAEd,MAAA,GAAS,aAAA;AAAA;AAAA,UAGM,uBAAA;EALD;;;;;AAKhB;;EAQE,MAAA;EACA,IAAA,EAAM,aAAA;EACN,IAAA;EACA,UAAA;EACA,MAAA;EACA,YAAA,GAAe,WAAA;EACf,IAAA,GAAO,UAAA;EACP,WAAA,GAAc,UAAA;AAAA;;;;;;;;;;;;;AAmBhB;;;;iBAAsB,iBAAA,CACpB,WAAA,UACA,OAAA,GAAS,wBAAA,GACR,OAAA,CAAQ,uBAAA"}
1
+ {"version":3,"file":"reconcile.d.mts","names":[],"sources":["../../src/git/reconcile.ts"],"mappings":";;;;UAsBiB,wBAAA;;EAEf,MAAA;EAFe;EAIf,WAAA,GAAc,kBAAA;;EAEd,MAAA,GAAS,aAAA;AAAA;AAAA,UAGM,uBAAA;EALD;;;;;AAKhB;;EAQE,MAAA;EACA,IAAA,EAAM,aAAA;EACN,IAAA;EACA,UAAA;EACA,MAAA;EACA,YAAA,GAAe,WAAA;EACf,IAAA,GAAO,UAAA;EACP,WAAA,GAAc,UAAA;AAAA;;;;;;;;;;;;;AAmBhB;;;;iBAAsB,iBAAA,CACpB,WAAA,UACA,OAAA,GAAS,wBAAA,GACR,OAAA,CAAQ,uBAAA"}
@@ -1,14 +1,14 @@
1
- import "../reconcile-C7AaUfCl.mjs";
2
- import "../context-Cp8b8Nxm.mjs";
3
- import "../model-manager-BYF2z7qC.mjs";
1
+ import "../reconcile-J-iorvSa.mjs";
2
+ import "../context-D9HXnnGj.mjs";
3
+ import "../model-manager-D0VztE8M.mjs";
4
4
  import "../fs-BFTuXbbw.mjs";
5
5
  import "../meta-manager-CSpKtjnP.mjs";
6
- import "../config-BLwZtwlI.mjs";
7
- import "../validator-Bz3jnSnb.mjs";
6
+ import "../config-BnNYu128.mjs";
7
+ import "../validator-S19GHR4V.mjs";
8
8
  import "../identity-CxAB9Qb8.mjs";
9
9
  import "../base-branch-Cw9DWvCC.mjs";
10
- import "../branch-lifecycle-CcsSyfSw.mjs";
10
+ import "../branch-lifecycle-D4DxPgQg.mjs";
11
11
  import "../id-jrHWdtqn.mjs";
12
- import "../transaction-CcjGiVYg.mjs";
13
- import { t as reconcileBranches } from "../reconcile-CRAukgae.mjs";
12
+ import "../transaction-DnrUATJF.mjs";
13
+ import { t as reconcileBranches } from "../reconcile-DSflMfhj.mjs";
14
14
  export { reconcileBranches };
@@ -1,7 +1,11 @@
1
- import { o as RemoteDeleteResult } from "../branch-lifecycle-CirYfTXu.mjs";
2
- import { BaseAdvance, RemotePush, SyncResult, WorkflowMode } from "@contentrain/types";
1
+ import { o as RemoteDeleteResult } from "../branch-lifecycle-4iZax7a7.mjs";
2
+ import { BaseAdvance, ContentrainConfig, RemotePush, SyncResult, WorkflowMode } from "@contentrain/types";
3
3
  import { SimpleGit } from "simple-git";
4
4
 
5
+ //#region src/git/push-policy.d.ts
6
+ /** What a result says when a write stayed local because pushing is off. */
7
+ declare const LOCAL_MODE_NOTE = "not pushed (local mode)";
8
+ //#endregion
5
9
  //#region src/git/transaction.d.ts
6
10
  /**
7
11
  * Block-timeout for fetch/push inside transactions. These calls inherit the
@@ -82,5 +86,5 @@ declare function isAncestor(git: SimpleGit, ancestor: string, descendant: string
82
86
  */
83
87
  declare function pushContentBranches(projectRoot: string, worktreePath: string, wtGit: SimpleGit, remoteName: string, baseBranch: string, baseAdvanced: boolean): Promise<RemotePush>;
84
88
  //#endregion
85
- export { CompleteResult, ContextUpdate, GitTransaction, MergeBranchResult, NETWORK_TIMEOUT_MS, buildBranchName, createTransaction, ensureContentBranch, isAncestor, mergeBranch, pushContentBranches, selectiveSync };
89
+ export { CompleteResult, ContextUpdate, GitTransaction, LOCAL_MODE_NOTE, MergeBranchResult, NETWORK_TIMEOUT_MS, buildBranchName, createTransaction, ensureContentBranch, isAncestor, mergeBranch, pushContentBranches, selectiveSync };
86
90
  //# sourceMappingURL=transaction.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.d.mts","names":[],"sources":["../../src/git/transaction.ts"],"mappings":";;;;;;;AAqBA;;;cAAa,kBAAA;AAAA,UAEI,aAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,OAAA;AAAA;;;;;;AAWF;;;UAAiB,cAAA;EACf,MAAA;EACA,MAAA;EACA,IAAA,GAAO,UAAA;EACP,OAAA;EAHA;EAKA,YAAA,GAAe,WAAA;EAHf;EAKA,WAAA,GAAc,UAAA;AAAA;;UAIC,iBAAA;EACf,MAAA;EACA,MAAA;EACA,IAAA,EAAM,UAAA;EACN,YAAA,EAAc,WAAA;EACd,WAAA,EAAa,UAAA;EACb,OAAA;EACA,MAAA,GAAS,kBAAA;AAAA;AAAA,UAGM,cAAA;EACf,QAAA;EACA,MAAA;EAL2B;EAO3B,UAAA;EAbA;EAeA,cAAA;EACA,KAAA,CAAM,QAAA,GAAW,YAAA,aAAyB,OAAA,SAAgB,OAAA;EAC1D,MAAA,CAAO,OAAA,UAAiB,aAAA,GAAgB,aAAA,GAAgB,OAAA;EACxD,QAAA,IAAY,OAAA,CAAQ,cAAA;EACpB,OAAA,IAAW,OAAA;AAAA;AAAA,iBAGS,mBAAA,CAAoB,WAAA,WAAsB,OAAA;AAAA,iBA+B1C,aAAA,CACpB,WAAA,UACA,aAAA,UACA,cAAA,UACA,gBAAA,WACA,sBAAA,GAAyB,GAAA,WACxB,OAAA,CAAQ,UAAA;AAAA,iBAqGW,iBAAA,CACpB,WAAA,UACA,UAAA,UACA,OAAA;EAAY,gBAAA,GAAmB,YAAA;AAAA,IAC9B,OAAA,CAAQ,cAAA;AAAA,iBAoPW,WAAA,CACpB,WAAA,UACA,UAAA,WACC,OAAA,CAAQ,iBAAA;AAAA,iBA2JK,eAAA,CAAgB,KAAA,UAAe,MAAA,UAAgB,MAAA;;;;;;;iBAgEzC,UAAA,CAAW,GAAA,EAAK,SAAA,EAAW,QAAA,UAAkB,UAAA,WAAqB,OAAA;;;;;;;;;;;;iBAoBlE,mBAAA,CACpB,WAAA,UACA,YAAA,UACA,KAAA,EAAO,SAAA,EACP,UAAA,UACA,UAAA,UACA,YAAA,YACC,OAAA,CAAQ,UAAA"}
1
+ {"version":3,"file":"transaction.d.mts","names":[],"sources":["../../src/git/push-policy.ts","../../src/git/transaction.ts"],"mappings":";;;;;;cAKa,eAAA;;;;AAAb;;;;cCoBa,kBAAA;AAAA,UAEI,aAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,OAAA;AAAA;;;AAJF;;;;;;UAeiB,cAAA;EACf,MAAA;EACA,MAAA;EACA,IAAA,GAAO,UAAA;EACP,OAAA;EAJ6B;EAM7B,YAAA,GAAe,WAAA;EAHR;EAKP,WAAA,GAAc,UAAA;AAAA;;UAIC,iBAAA;EACf,MAAA;EACA,MAAA;EACA,IAAA,EAAM,UAAA;EACN,YAAA,EAAc,WAAA;EACd,WAAA,EAAa,UAAA;EACb,OAAA;EACA,MAAA,GAAS,kBAAA;AAAA;AAAA,UAGM,cAAA;EACf,QAAA;EACA,MAAA;EAZe;EAcf,UAAA;;EAEA,cAAA;EACA,KAAA,CAAM,QAAA,GAAW,YAAA,aAAyB,OAAA,SAAgB,OAAA;EAC1D,MAAA,CAAO,OAAA,UAAiB,aAAA,GAAgB,aAAA,GAAgB,OAAA;EACxD,QAAA,IAAY,OAAA,CAAQ,cAAA;EACpB,OAAA,IAAW,OAAA;AAAA;AAAA,iBAGS,mBAAA,CAAoB,WAAA,WAAsB,OAAA;AAAA,iBAgC1C,aAAA,CACpB,WAAA,UACA,aAAA,UACA,cAAA,UACA,gBAAA,WACA,sBAAA,GAAyB,GAAA,WACxB,OAAA,CAAQ,UAAA;AAAA,iBAqGW,iBAAA,CACpB,WAAA,UACA,UAAA,UACA,OAAA;EAAY,gBAAA,GAAmB,YAAA;AAAA,IAC9B,OAAA,CAAQ,cAAA;AAAA,iBAqPW,WAAA,CACpB,WAAA,UACA,UAAA,WACC,OAAA,CAAQ,iBAAA;AAAA,iBA4JK,eAAA,CAAgB,KAAA,UAAe,MAAA,UAAgB,MAAA;;;;;;;iBAgEzC,UAAA,CAAW,GAAA,EAAK,SAAA,EAAW,QAAA,UAAkB,UAAA,WAAqB,OAAA;;;;;;;;;;;;iBAoBlE,mBAAA,CACpB,WAAA,UACA,YAAA,UACA,KAAA,EAAO,SAAA,EACP,UAAA,UACA,UAAA,UACA,YAAA,YACC,OAAA,CAAQ,UAAA"}
@@ -1,11 +1,11 @@
1
- import "../context-Cp8b8Nxm.mjs";
2
- import "../model-manager-BYF2z7qC.mjs";
1
+ import "../context-D9HXnnGj.mjs";
2
+ import "../model-manager-D0VztE8M.mjs";
3
3
  import "../fs-BFTuXbbw.mjs";
4
4
  import "../meta-manager-CSpKtjnP.mjs";
5
- import "../config-BLwZtwlI.mjs";
5
+ import "../config-BnNYu128.mjs";
6
6
  import "../identity-CxAB9Qb8.mjs";
7
7
  import "../base-branch-Cw9DWvCC.mjs";
8
- import "../branch-lifecycle-CcsSyfSw.mjs";
8
+ import { d as LOCAL_MODE_NOTE } from "../branch-lifecycle-D4DxPgQg.mjs";
9
9
  import "../id-jrHWdtqn.mjs";
10
- import { a as isAncestor, c as selectiveSync, i as ensureContentBranch, n as buildBranchName, o as mergeBranch, r as createTransaction, s as pushContentBranches, t as NETWORK_TIMEOUT_MS } from "../transaction-CcjGiVYg.mjs";
11
- export { NETWORK_TIMEOUT_MS, buildBranchName, createTransaction, ensureContentBranch, isAncestor, mergeBranch, pushContentBranches, selectiveSync };
10
+ import { a as isAncestor, c as selectiveSync, i as ensureContentBranch, n as buildBranchName, o as mergeBranch, r as createTransaction, s as pushContentBranches, t as NETWORK_TIMEOUT_MS } from "../transaction-DnrUATJF.mjs";
11
+ export { LOCAL_MODE_NOTE, NETWORK_TIMEOUT_MS, buildBranchName, createTransaction, ensureContentBranch, isAncestor, mergeBranch, pushContentBranches, selectiveSync };
package/dist/index.mjs CHANGED
@@ -1,30 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import "./contracts-DfL0BfrD.mjs";
3
- import "./reconcile-C7AaUfCl.mjs";
4
- import "./context-Cp8b8Nxm.mjs";
5
- import "./model-manager-BYF2z7qC.mjs";
6
- import "./ops-CQ_IIedh.mjs";
3
+ import "./reconcile-J-iorvSa.mjs";
4
+ import "./context-D9HXnnGj.mjs";
5
+ import "./model-manager-D0VztE8M.mjs";
6
+ import "./ops-CVI308jA.mjs";
7
7
  import "./fs-BFTuXbbw.mjs";
8
8
  import "./meta-manager-CSpKtjnP.mjs";
9
- import "./config-BLwZtwlI.mjs";
10
- import "./validator-Bz3jnSnb.mjs";
9
+ import "./config-BnNYu128.mjs";
10
+ import "./validator-S19GHR4V.mjs";
11
11
  import "./identity-CxAB9Qb8.mjs";
12
12
  import "./base-branch-Cw9DWvCC.mjs";
13
- import "./branch-lifecycle-CcsSyfSw.mjs";
13
+ import "./branch-lifecycle-D4DxPgQg.mjs";
14
14
  import "./id-jrHWdtqn.mjs";
15
- import "./transaction-CcjGiVYg.mjs";
16
- import "./local-BQVdC9MM.mjs";
15
+ import "./transaction-DnrUATJF.mjs";
16
+ import "./local-CBtRYFH4.mjs";
17
17
  import "./detect-B1QHYA0u.mjs";
18
18
  import "./annotations-BYkjoj2d.mjs";
19
- import { n as createServer } from "./server-D8oYrulE.mjs";
19
+ import { n as createServer } from "./server-CJbmjX48.mjs";
20
20
  import "./errors-De8Noahk.mjs";
21
- import "./reconcile-CRAukgae.mjs";
21
+ import "./reconcile-DSflMfhj.mjs";
22
22
  import "./scan-config-S5fkzNhL.mjs";
23
23
  import "./graph-builder-BPhi-1KH.mjs";
24
24
  import "./scanner-0sc_pARB.mjs";
25
25
  import "./tsx-parser-DgFdpsea.mjs";
26
- import "./apply-manager-BenQNb_b.mjs";
27
- import "./doctor-Qabcb-Z1.mjs";
26
+ import "./apply-manager-B-O3eAbz.mjs";
27
+ import "./doctor-Br1NqEl2.mjs";
28
28
  import { resolve } from "node:path";
29
29
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
30
30
  //#region src/index.ts
@@ -1,12 +1,12 @@
1
1
  import { t as LOCAL_CAPABILITIES } from "./contracts-DfL0BfrD.mjs";
2
- import { n as applyChangesToWorktree } from "./reconcile-C7AaUfCl.mjs";
3
- import { o as canonicalStringify$1, s as sortKeys$1 } from "./context-Cp8b8Nxm.mjs";
4
- import { k as LocalReader } from "./model-manager-BYF2z7qC.mjs";
5
- import { t as readConfig } from "./config-BLwZtwlI.mjs";
2
+ import { n as applyChangesToWorktree } from "./reconcile-J-iorvSa.mjs";
3
+ import { c as sortKeys$1, s as canonicalStringify$1 } from "./context-D9HXnnGj.mjs";
4
+ import { k as LocalReader } from "./model-manager-D0VztE8M.mjs";
5
+ import { t as readConfig } from "./config-BnNYu128.mjs";
6
6
  import { i as gitBinary, r as createGit } from "./identity-CxAB9Qb8.mjs";
7
7
  import { t as resolveBaseBranch } from "./base-branch-Cw9DWvCC.mjs";
8
- import { n as checkBranchHealth, o as deleteRemoteBranch, r as classifyMergedBranches } from "./branch-lifecycle-CcsSyfSw.mjs";
9
- import { o as mergeBranch$1, r as createTransaction } from "./transaction-CcjGiVYg.mjs";
8
+ import { n as checkBranchHealth, o as deleteRemoteBranch, r as classifyMergedBranches } from "./branch-lifecycle-D4DxPgQg.mjs";
9
+ import { o as mergeBranch$1, r as createTransaction } from "./transaction-DnrUATJF.mjs";
10
10
  import { join, resolve } from "node:path";
11
11
  import { readFile, stat } from "node:fs/promises";
12
12
  import { tmpdir } from "node:os";
@@ -688,4 +688,4 @@ function staleTreeGuidance(baseBranch, baseCheckedOut, readFromRef) {
688
688
  //#endregion
689
689
  export { withReadScope as n, isMerged as r, LocalProvider as t };
690
690
 
691
- //# sourceMappingURL=local-BQVdC9MM.mjs.map
691
+ //# sourceMappingURL=local-CBtRYFH4.mjs.map