@contentrain/mcp 1.2.1 → 1.3.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 (153) hide show
  1. package/README.md +93 -25
  2. package/dist/annotations-Clhbw2lh.mjs +126 -0
  3. package/dist/annotations-Clhbw2lh.mjs.map +1 -0
  4. package/dist/{apply-manager-BkPMfpsN.mjs → apply-manager-D5ym5Jba.mjs} +7 -7
  5. package/dist/{apply-manager-BkPMfpsN.mjs.map → apply-manager-D5ym5Jba.mjs.map} +1 -1
  6. package/dist/{astro-parser-BJ-54JGe.mjs → astro-parser-B-nLl0zE.mjs} +3 -3
  7. package/dist/{astro-parser-BJ-54JGe.mjs.map → astro-parser-B-nLl0zE.mjs.map} +1 -1
  8. package/dist/{branch-lifecycle-wGJg25LT.mjs → branch-lifecycle-CFVpTpNT.mjs} +33 -3
  9. package/dist/{branch-lifecycle-wGJg25LT.mjs.map → branch-lifecycle-CFVpTpNT.mjs.map} +1 -1
  10. package/dist/{compiler-rIC4ETHv.mjs → compiler-k4yttlzM.mjs} +2 -2
  11. package/dist/{compiler-rIC4ETHv.mjs.map → compiler-k4yttlzM.mjs.map} +1 -1
  12. package/dist/{compiler-sfc.cjs-BbD04rn_.mjs → compiler-sfc.cjs-D0d_OicF.mjs} +2 -2
  13. package/dist/{compiler-sfc.cjs-BbD04rn_.mjs.map → compiler-sfc.cjs-D0d_OicF.mjs.map} +1 -1
  14. package/dist/config-BIl5Ybj0.mjs +40 -0
  15. package/dist/config-BIl5Ybj0.mjs.map +1 -0
  16. package/dist/content-manager-DqDLSNQ-.d.mts +63 -0
  17. package/dist/content-manager-DqDLSNQ-.d.mts.map +1 -0
  18. package/dist/context-J0OU9Nfp.mjs +102 -0
  19. package/dist/context-J0OU9Nfp.mjs.map +1 -0
  20. package/dist/contracts-DfL0BfrD.mjs +2 -0
  21. package/dist/core/apply-manager.mjs +10 -10
  22. package/dist/core/config.d.mts +13 -0
  23. package/dist/core/config.d.mts.map +1 -1
  24. package/dist/core/config.mjs +2 -3
  25. package/dist/core/content-manager.d.mts +2 -45
  26. package/dist/core/content-manager.mjs +3 -4
  27. package/dist/core/context.d.mts +32 -2
  28. package/dist/core/context.d.mts.map +1 -1
  29. package/dist/core/context.mjs +7 -7
  30. package/dist/core/contracts/index.d.mts +2 -0
  31. package/dist/core/contracts/index.mjs +2 -0
  32. package/dist/core/doctor.d.mts +76 -0
  33. package/dist/core/doctor.d.mts.map +1 -0
  34. package/dist/core/doctor.mjs +8 -0
  35. package/dist/core/graph-builder.mjs +3 -4
  36. package/dist/core/meta-manager.mjs +2 -3
  37. package/dist/core/model-manager.d.mts +34 -1
  38. package/dist/core/model-manager.d.mts.map +1 -1
  39. package/dist/core/model-manager.mjs +3 -4
  40. package/dist/core/ops/index.d.mts +137 -0
  41. package/dist/core/ops/index.d.mts.map +1 -0
  42. package/dist/core/ops/index.mjs +4 -0
  43. package/dist/core/overlay-reader.d.mts +42 -0
  44. package/dist/core/overlay-reader.d.mts.map +1 -0
  45. package/dist/core/overlay-reader.mjs +2 -0
  46. package/dist/core/scan-config.mjs +2 -3
  47. package/dist/core/scanner.mjs +4 -5
  48. package/dist/core/validator/index.d.mts +129 -0
  49. package/dist/core/validator/index.d.mts.map +1 -0
  50. package/dist/core/validator/index.mjs +6 -0
  51. package/dist/{detect-DinOQAmK.mjs → detect-DL9mFmaS.mjs} +2 -2
  52. package/dist/{detect-DinOQAmK.mjs.map → detect-DL9mFmaS.mjs.map} +1 -1
  53. package/dist/doctor-CgBoT6h1.mjs +338 -0
  54. package/dist/doctor-CgBoT6h1.mjs.map +1 -0
  55. package/dist/{fs-BnIQ4dKj.mjs → fs-DLbVB-Ek.mjs} +2 -2
  56. package/dist/fs-DLbVB-Ek.mjs.map +1 -0
  57. package/dist/git/branch-lifecycle.d.mts +30 -1
  58. package/dist/git/branch-lifecycle.d.mts.map +1 -1
  59. package/dist/git/branch-lifecycle.mjs +4 -5
  60. package/dist/git/transaction.d.mts +2 -2
  61. package/dist/git/transaction.d.mts.map +1 -1
  62. package/dist/git/transaction.mjs +8 -8
  63. package/dist/{graph-builder-tM7zj-SX.mjs → graph-builder-DXv2JEp-.mjs} +3 -3
  64. package/dist/{graph-builder-tM7zj-SX.mjs.map → graph-builder-DXv2JEp-.mjs.map} +1 -1
  65. package/dist/{id-DV_T9Ic8.mjs → id-CIO9s0x_.mjs} +1 -1
  66. package/dist/{id-DV_T9Ic8.mjs.map → id-CIO9s0x_.mjs.map} +1 -1
  67. package/dist/index-D0wQJSgs.d.mts +2 -0
  68. package/dist/index.mjs +22 -17
  69. package/dist/index.mjs.map +1 -1
  70. package/dist/local-DvP4Y4T-.mjs +182 -0
  71. package/dist/local-DvP4Y4T-.mjs.map +1 -0
  72. package/dist/{meta-manager--0IwAPCl.mjs → meta-manager-C900rEyG.mjs} +2 -2
  73. package/dist/{meta-manager--0IwAPCl.mjs.map → meta-manager-C900rEyG.mjs.map} +1 -1
  74. package/dist/{model-manager-DW_fOGRM.mjs → model-manager-BhLsUgaB.mjs} +241 -49
  75. package/dist/model-manager-BhLsUgaB.mjs.map +1 -0
  76. package/dist/{node-Cbq5eR5i.mjs → node-DN9hZeil.mjs} +1 -1
  77. package/dist/{node-Cbq5eR5i.mjs.map → node-DN9hZeil.mjs.map} +1 -1
  78. package/dist/ops-DjEWi5Wa.mjs +486 -0
  79. package/dist/ops-DjEWi5Wa.mjs.map +1 -0
  80. package/dist/overlay-reader-DOdyDRIv.mjs +74 -0
  81. package/dist/overlay-reader-DOdyDRIv.mjs.map +1 -0
  82. package/dist/paths-C3xNyc7z.mjs +52 -0
  83. package/dist/paths-C3xNyc7z.mjs.map +1 -0
  84. package/dist/paths-CYl5vCBD.mjs +54 -0
  85. package/dist/paths-CYl5vCBD.mjs.map +1 -0
  86. package/dist/providers/github/index.d.mts +144 -0
  87. package/dist/providers/github/index.d.mts.map +1 -0
  88. package/dist/providers/github/index.mjs +414 -0
  89. package/dist/providers/github/index.mjs.map +1 -0
  90. package/dist/providers/gitlab/index.d.mts +158 -0
  91. package/dist/providers/gitlab/index.d.mts.map +1 -0
  92. package/dist/providers/gitlab/index.mjs +361 -0
  93. package/dist/providers/gitlab/index.mjs.map +1 -0
  94. package/dist/providers/local/index.d.mts +106 -0
  95. package/dist/providers/local/index.d.mts.map +1 -0
  96. package/dist/providers/local/index.mjs +12 -0
  97. package/dist/{scan-config-QgnJ1-52.mjs → scan-config-jC6XmiqF.mjs} +2 -2
  98. package/dist/{scan-config-QgnJ1-52.mjs.map → scan-config-jC6XmiqF.mjs.map} +1 -1
  99. package/dist/{scanner-DUoXnmtI.mjs → scanner-DKMwc03k.mjs} +7 -7
  100. package/dist/{scanner-DUoXnmtI.mjs.map → scanner-DKMwc03k.mjs.map} +1 -1
  101. package/dist/serialization-B1CEzR4H.mjs +2 -0
  102. package/dist/server/http/index.d.mts +65 -0
  103. package/dist/server/http/index.d.mts.map +1 -0
  104. package/dist/server/http/index.mjs +137 -0
  105. package/dist/server/http/index.mjs.map +1 -0
  106. package/dist/{server-Dtq8vqbK.mjs → server-B1OJrUQ9.mjs} +486 -355
  107. package/dist/server-B1OJrUQ9.mjs.map +1 -0
  108. package/dist/server-WqGx6ait.d.mts +50 -0
  109. package/dist/server-WqGx6ait.d.mts.map +1 -0
  110. package/dist/server.d.mts +2 -7
  111. package/dist/server.mjs +22 -17
  112. package/dist/{source-map-generator-DpO-ejrt.mjs → source-map-generator-BWDva49I.mjs} +1 -1
  113. package/dist/{source-map-generator-DpO-ejrt.mjs.map → source-map-generator-BWDva49I.mjs.map} +1 -1
  114. package/dist/{svelte-parser-1bBcMx3T.mjs → svelte-parser-Dr9mmZuk.mjs} +3 -3
  115. package/dist/{svelte-parser-1bBcMx3T.mjs.map → svelte-parser-Dr9mmZuk.mjs.map} +1 -1
  116. package/dist/templates/index.mjs +1 -1
  117. package/dist/{templates-bRPzYq4M.mjs → templates-VrLVqfe5.mjs} +1 -1
  118. package/dist/{templates-bRPzYq4M.mjs.map → templates-VrLVqfe5.mjs.map} +1 -1
  119. package/dist/tools/annotations.d.mts +24 -0
  120. package/dist/tools/annotations.d.mts.map +1 -0
  121. package/dist/tools/annotations.mjs +2 -0
  122. package/dist/{transaction-C_Pvux78.mjs → transaction-DgkNgWSI.mjs} +61 -32
  123. package/dist/transaction-DgkNgWSI.mjs.map +1 -0
  124. package/dist/tsx-parser-BOMZE_yQ.mjs +2 -0
  125. package/dist/{tsx-parser-BWiMGAIR.mjs → tsx-parser-CQc6nr-o.mjs} +1 -1
  126. package/dist/{tsx-parser-BWiMGAIR.mjs.map → tsx-parser-CQc6nr-o.mjs.map} +1 -1
  127. package/dist/util/detect.mjs +2 -3
  128. package/dist/util/fs.mjs +1 -2
  129. package/dist/util/id.mjs +1 -1
  130. package/dist/validator-B0nNRFeU.mjs +906 -0
  131. package/dist/validator-B0nNRFeU.mjs.map +1 -0
  132. package/dist/{vue-parser-DTPTN3Yb.mjs → vue-parser-COgjArqv.mjs} +3 -3
  133. package/dist/{vue-parser-DTPTN3Yb.mjs.map → vue-parser-COgjArqv.mjs.map} +1 -1
  134. package/package.json +75 -21
  135. package/dist/config-B5kc2oeV.mjs +0 -27
  136. package/dist/config-B5kc2oeV.mjs.map +0 -1
  137. package/dist/context-DIDt_TxX.mjs +0 -46
  138. package/dist/context-DIDt_TxX.mjs.map +0 -1
  139. package/dist/core/content-manager.d.mts.map +0 -1
  140. package/dist/core/validator.d.mts +0 -23
  141. package/dist/core/validator.d.mts.map +0 -1
  142. package/dist/core/validator.mjs +0 -7
  143. package/dist/fs-BnIQ4dKj.mjs.map +0 -1
  144. package/dist/model-manager-DW_fOGRM.mjs.map +0 -1
  145. package/dist/serializer-CnHTtmc1.mjs +0 -2
  146. package/dist/server-Dtq8vqbK.mjs.map +0 -1
  147. package/dist/server.d.mts.map +0 -1
  148. package/dist/transaction-C_Pvux78.mjs.map +0 -1
  149. package/dist/tsx-parser-BFLv292Y.mjs +0 -2
  150. package/dist/util/serializer.d.mts +0 -2
  151. package/dist/util/serializer.mjs +0 -2
  152. package/dist/validator-SQYUV2JP.mjs +0 -581
  153. package/dist/validator-SQYUV2JP.mjs.map +0 -1
@@ -0,0 +1,52 @@
1
+ //#region src/providers/shared/errors.ts
2
+ /**
3
+ * Unified "is this a 404?" helper for API-backed providers.
4
+ *
5
+ * Two common error shapes in the provider SDKs we use:
6
+ *
7
+ * - **Octokit** (`@octokit/rest`) — rejects with an `Error` that has a
8
+ * top-level `.status` number set to the HTTP status code.
9
+ * - **Gitbeaker** (`@gitbeaker/rest`) — rejects with a plain `Error` whose
10
+ * `.cause` includes `{ response: { status } }`.
11
+ *
12
+ * Both forms converge on `404` meaning "resource missing", so we check
13
+ * both shapes strictly. We deliberately do NOT fall back to substring
14
+ * matching on the error message — that leniency can silently mask other
15
+ * 404-like errors (forbidden repo, deleted project, rate limits) and
16
+ * produce the wrong answer. If either SDK ever stops populating the
17
+ * status field on a legitimate 404, the regression will surface in tests
18
+ * rather than being papered over at the reader layer.
19
+ */
20
+ function isNotFoundError(error) {
21
+ if (typeof error !== "object" || error === null) return false;
22
+ if (error.status === 404) return true;
23
+ return error.cause?.response?.status === 404;
24
+ }
25
+ //#endregion
26
+ //#region src/providers/shared/paths.ts
27
+ /**
28
+ * Normalise an optional contentRoot — strip leading/trailing slashes,
29
+ * treat `''`, `/` and `undefined` as "no prefix". Used by API-backed
30
+ * providers (GitHub, GitLab, future Bitbucket) to anchor content-relative
31
+ * paths against a repo subdirectory when Contentrain lives under a
32
+ * monorepo path like `apps/web/.contentrain/`.
33
+ */
34
+ function normaliseContentRoot(raw) {
35
+ if (!raw || raw === "/" || raw === "") return "";
36
+ return raw.replace(/^\/+|\/+$/g, "");
37
+ }
38
+ /**
39
+ * Resolve a content-root-relative path to a repo-relative path. The result
40
+ * always uses forward slashes and has no leading slash — the form every
41
+ * REST git API consumes for `file_path` / `path` query parameters and the
42
+ * Git Data API tree entries.
43
+ */
44
+ function resolveRepoPath(contentRoot, relativePath) {
45
+ const prefix = normaliseContentRoot(contentRoot);
46
+ const cleanPath = relativePath.replace(/^\/+/, "");
47
+ return prefix ? `${prefix}/${cleanPath}` : cleanPath;
48
+ }
49
+ //#endregion
50
+ export { isNotFoundError as n, resolveRepoPath as t };
51
+
52
+ //# sourceMappingURL=paths-C3xNyc7z.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths-C3xNyc7z.mjs","names":[],"sources":["../src/providers/shared/errors.ts","../src/providers/shared/paths.ts"],"sourcesContent":["/**\n * Unified \"is this a 404?\" helper for API-backed providers.\n *\n * Two common error shapes in the provider SDKs we use:\n *\n * - **Octokit** (`@octokit/rest`) — rejects with an `Error` that has a\n * top-level `.status` number set to the HTTP status code.\n * - **Gitbeaker** (`@gitbeaker/rest`) — rejects with a plain `Error` whose\n * `.cause` includes `{ response: { status } }`.\n *\n * Both forms converge on `404` meaning \"resource missing\", so we check\n * both shapes strictly. We deliberately do NOT fall back to substring\n * matching on the error message — that leniency can silently mask other\n * 404-like errors (forbidden repo, deleted project, rate limits) and\n * produce the wrong answer. If either SDK ever stops populating the\n * status field on a legitimate 404, the regression will surface in tests\n * rather than being papered over at the reader layer.\n */\nexport function isNotFoundError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) return false\n const direct = (error as { status?: number }).status\n if (direct === 404) return true\n const nested = (error as { cause?: { response?: { status?: number } } }).cause?.response?.status\n return nested === 404\n}\n","/**\n * Normalise an optional contentRoot — strip leading/trailing slashes,\n * treat `''`, `/` and `undefined` as \"no prefix\". Used by API-backed\n * providers (GitHub, GitLab, future Bitbucket) to anchor content-relative\n * paths against a repo subdirectory when Contentrain lives under a\n * monorepo path like `apps/web/.contentrain/`.\n */\nexport function normaliseContentRoot(raw?: string): string {\n if (!raw || raw === '/' || raw === '') return ''\n return raw.replace(/^\\/+|\\/+$/g, '')\n}\n\n/**\n * Resolve a content-root-relative path to a repo-relative path. The result\n * always uses forward slashes and has no leading slash — the form every\n * REST git API consumes for `file_path` / `path` query parameters and the\n * Git Data API tree entries.\n */\nexport function resolveRepoPath(contentRoot: string | undefined, relativePath: string): string {\n const prefix = normaliseContentRoot(contentRoot)\n const cleanPath = relativePath.replace(/^\\/+/, '')\n return prefix ? `${prefix}/${cleanPath}` : cleanPath\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,SAAgB,gBAAgB,OAAyB;AACvD,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AAExD,KADgB,MAA8B,WAC/B,IAAK,QAAO;AAE3B,QADgB,MAAyD,OAAO,UAAU,WACxE;;;;;;;;;;;AChBpB,SAAgB,qBAAqB,KAAsB;AACzD,KAAI,CAAC,OAAO,QAAQ,OAAO,QAAQ,GAAI,QAAO;AAC9C,QAAO,IAAI,QAAQ,cAAc,GAAG;;;;;;;;AAStC,SAAgB,gBAAgB,aAAiC,cAA8B;CAC7F,MAAM,SAAS,qBAAqB,YAAY;CAChD,MAAM,YAAY,aAAa,QAAQ,QAAQ,GAAG;AAClD,QAAO,SAAS,GAAG,OAAO,GAAG,cAAc"}
@@ -0,0 +1,54 @@
1
+ //#region src/core/ops/paths.ts
2
+ /**
3
+ * Content-root-relative path helpers for plan-API consumers.
4
+ *
5
+ * These mirror the logic inside `core/content-manager.ts:resolveJsonFilePath`
6
+ * but return forward-slash relative strings suitable for `FileChange.path`.
7
+ * Providers are responsible for resolving these against their backing store
8
+ * (worktree on disk for LocalProvider, git tree path for GitHubProvider, ...).
9
+ *
10
+ * Kept inline to `core/ops/` for now; when Faz 2 finishes migrating every op
11
+ * the legacy absolute-path helpers in `core/content-manager.ts` and
12
+ * `core/meta-manager.ts` will call into these (or be replaced entirely).
13
+ */
14
+ /**
15
+ * Content-root-relative content directory for a model (forward-slash paths).
16
+ * Mirrors `resolveContentDir` with an empty `projectRoot`, so reader-backed
17
+ * callers and plan-API consumers share the same path resolution.
18
+ */
19
+ function contentDirPath(model) {
20
+ if (model.content_path) return model.content_path;
21
+ return `.contentrain/content/${model.domain}/${model.id}`;
22
+ }
23
+ function contentDir(model) {
24
+ return contentDirPath(model);
25
+ }
26
+ function contentFilePath(model, locale) {
27
+ const dir = contentDir(model);
28
+ if (!model.i18n) return `${dir}/data.json`;
29
+ switch (model.locale_strategy ?? "file") {
30
+ case "suffix": return `${dir}/${model.id}.${locale}.json`;
31
+ case "directory": return `${dir}/${locale}/${model.id}.json`;
32
+ case "none": return `${dir}/${model.id}.json`;
33
+ default: return `${dir}/${locale}.json`;
34
+ }
35
+ }
36
+ function documentFilePath(model, locale, slug) {
37
+ const dir = contentDir(model);
38
+ if (!model.i18n) return `${dir}/${slug}.md`;
39
+ switch (model.locale_strategy ?? "file") {
40
+ case "suffix": return `${dir}/${slug}.${locale}.md`;
41
+ case "directory": return `${dir}/${locale}/${slug}.md`;
42
+ case "none": return `${dir}/${slug}.md`;
43
+ default: return `${dir}/${slug}/${locale}.md`;
44
+ }
45
+ }
46
+ function metaFilePath(model, locale, slug) {
47
+ const base = `.contentrain/meta/${model.id}`;
48
+ if (model.kind === "document" && slug) return `${base}/${slug}/${locale}.json`;
49
+ return `${base}/${locale}.json`;
50
+ }
51
+ //#endregion
52
+ export { metaFilePath as i, contentFilePath as n, documentFilePath as r, contentDirPath as t };
53
+
54
+ //# sourceMappingURL=paths-CYl5vCBD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths-CYl5vCBD.mjs","names":[],"sources":["../src/core/ops/paths.ts"],"sourcesContent":["import type { ModelDefinition } from '@contentrain/types'\n\n/**\n * Content-root-relative path helpers for plan-API consumers.\n *\n * These mirror the logic inside `core/content-manager.ts:resolveJsonFilePath`\n * but return forward-slash relative strings suitable for `FileChange.path`.\n * Providers are responsible for resolving these against their backing store\n * (worktree on disk for LocalProvider, git tree path for GitHubProvider, ...).\n *\n * Kept inline to `core/ops/` for now; when Faz 2 finishes migrating every op\n * the legacy absolute-path helpers in `core/content-manager.ts` and\n * `core/meta-manager.ts` will call into these (or be replaced entirely).\n */\n\n/**\n * Content-root-relative content directory for a model (forward-slash paths).\n * Mirrors `resolveContentDir` with an empty `projectRoot`, so reader-backed\n * callers and plan-API consumers share the same path resolution.\n */\nexport function contentDirPath(\n model: Pick<ModelDefinition, 'domain' | 'id' | 'content_path'>,\n): string {\n if (model.content_path) return model.content_path\n return `.contentrain/content/${model.domain}/${model.id}`\n}\n\nfunction contentDir(model: Pick<ModelDefinition, 'domain' | 'id' | 'content_path'>): string {\n return contentDirPath(model)\n}\n\nexport function contentFilePath(\n model: Pick<ModelDefinition, 'id' | 'kind' | 'domain' | 'i18n' | 'content_path' | 'locale_strategy'>,\n locale: string,\n): string {\n const dir = contentDir(model)\n if (!model.i18n) return `${dir}/data.json`\n const strategy = model.locale_strategy ?? 'file'\n switch (strategy) {\n case 'suffix': return `${dir}/${model.id}.${locale}.json`\n case 'directory': return `${dir}/${locale}/${model.id}.json`\n case 'none': return `${dir}/${model.id}.json`\n default: return `${dir}/${locale}.json`\n }\n}\n\nexport function documentFilePath(\n model: Pick<ModelDefinition, 'id' | 'domain' | 'i18n' | 'content_path' | 'locale_strategy'>,\n locale: string,\n slug: string,\n): string {\n const dir = contentDir(model)\n if (!model.i18n) return `${dir}/${slug}.md`\n const strategy = model.locale_strategy ?? 'file'\n switch (strategy) {\n case 'suffix': return `${dir}/${slug}.${locale}.md`\n case 'directory': return `${dir}/${locale}/${slug}.md`\n case 'none': return `${dir}/${slug}.md`\n default: return `${dir}/${slug}/${locale}.md`\n }\n}\n\nexport function metaFilePath(\n model: Pick<ModelDefinition, 'id' | 'kind'>,\n locale: string,\n slug?: string,\n): string {\n const base = `.contentrain/meta/${model.id}`\n if (model.kind === 'document' && slug) {\n return `${base}/${slug}/${locale}.json`\n }\n return `${base}/${locale}.json`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoBA,SAAgB,eACd,OACQ;AACR,KAAI,MAAM,aAAc,QAAO,MAAM;AACrC,QAAO,wBAAwB,MAAM,OAAO,GAAG,MAAM;;AAGvD,SAAS,WAAW,OAAwE;AAC1F,QAAO,eAAe,MAAM;;AAG9B,SAAgB,gBACd,OACA,QACQ;CACR,MAAM,MAAM,WAAW,MAAM;AAC7B,KAAI,CAAC,MAAM,KAAM,QAAO,GAAG,IAAI;AAE/B,SADiB,MAAM,mBAAmB,QAC1C;EACE,KAAK,SAAU,QAAO,GAAG,IAAI,GAAG,MAAM,GAAG,GAAG,OAAO;EACnD,KAAK,YAAa,QAAO,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG;EACtD,KAAK,OAAQ,QAAO,GAAG,IAAI,GAAG,MAAM,GAAG;EACvC,QAAS,QAAO,GAAG,IAAI,GAAG,OAAO;;;AAIrC,SAAgB,iBACd,OACA,QACA,MACQ;CACR,MAAM,MAAM,WAAW,MAAM;AAC7B,KAAI,CAAC,MAAM,KAAM,QAAO,GAAG,IAAI,GAAG,KAAK;AAEvC,SADiB,MAAM,mBAAmB,QAC1C;EACE,KAAK,SAAU,QAAO,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO;EAC/C,KAAK,YAAa,QAAO,GAAG,IAAI,GAAG,OAAO,GAAG,KAAK;EAClD,KAAK,OAAQ,QAAO,GAAG,IAAI,GAAG,KAAK;EACnC,QAAS,QAAO,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO;;;AAI7C,SAAgB,aACd,OACA,QACA,MACQ;CACR,MAAM,OAAO,qBAAqB,MAAM;AACxC,KAAI,MAAM,SAAS,cAAc,KAC/B,QAAO,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO;AAEnC,QAAO,GAAG,KAAK,GAAG,OAAO"}
@@ -0,0 +1,144 @@
1
+ import { c as MergeResult, d as RepoReader, i as CommitAuthor, l as ProviderCapabilities, n as Branch, o as FileDiff, r as Commit, t as ApplyPlanInput, u as RepoProvider } from "../../index-D0wQJSgs.mjs";
2
+ import { Octokit } from "@octokit/rest";
3
+
4
+ //#region src/providers/github/capabilities.d.ts
5
+ /**
6
+ * Capability set for GitHubProvider.
7
+ *
8
+ * GitHub over the Git Data API has no working tree, so local worktree
9
+ * features, source-file access and AST scans are unavailable. Push /
10
+ * PR operations are free because every commit goes straight to the
11
+ * remote. Branch protection detection uses the Repos API.
12
+ *
13
+ * Tools that require `astScan`, `sourceRead` or `sourceWrite` must
14
+ * gracefully reject (with `capability_required`) when running against
15
+ * a GitHubProvider — this is the mechanism behind phase 6's normalize
16
+ * capability-gate.
17
+ */
18
+ declare const GITHUB_CAPABILITIES: ProviderCapabilities;
19
+ //#endregion
20
+ //#region src/providers/github/client.d.ts
21
+ /**
22
+ * GitHubClient — a typed alias for an `@octokit/rest` instance.
23
+ *
24
+ * We rely on the peer-installed `@octokit/rest` for its types only; the
25
+ * provider code never imports Octokit eagerly. Consumers install it
26
+ * themselves via the optional peer dependency; the factory in
27
+ * `factory.ts` `await import()`s Octokit at runtime so the rest of MCP
28
+ * stays usable without it.
29
+ */
30
+ type GitHubClient = Octokit;
31
+ //#endregion
32
+ //#region src/providers/github/types.d.ts
33
+ /**
34
+ * A reference to a GitHub repository.
35
+ *
36
+ * `contentRoot` is the repo-relative directory prefix where Contentrain
37
+ * content lives. For a flat content repo it stays `''`; for a monorepo
38
+ * where Contentrain is embedded (e.g. `apps/web/.contentrain/`) it holds
39
+ * that prefix. All reader/writer paths are joined against it.
40
+ */
41
+ interface RepoRef {
42
+ owner: string;
43
+ name: string;
44
+ contentRoot?: string;
45
+ }
46
+ /**
47
+ * Authentication options for the GitHub provider.
48
+ *
49
+ * - `pat` — a personal access token or fine-grained PAT. Simplest for
50
+ * self-hosted MCP or CI runners. Phase 5.1 ships with this mode only.
51
+ * - `app` — GitHub App installation auth (JWT + installation token
52
+ * exchange). Planned for Phase 5.2; see `.internal/refactor/
53
+ * 02-studio-handoff.md` S6 for how Studio's hosted MCP plugs in.
54
+ */
55
+ type GitHubAuth = {
56
+ type: 'pat';
57
+ token: string;
58
+ } | {
59
+ type: 'app';
60
+ appId: number;
61
+ privateKey: string;
62
+ installationId: number;
63
+ };
64
+ /** Default author used when a call does not provide one. */
65
+ declare const DEFAULT_GITHUB_AUTHOR: CommitAuthor;
66
+ //#endregion
67
+ //#region src/providers/github/provider.d.ts
68
+ /**
69
+ * GitHubProvider — `RepoProvider` backed by the Octokit-driven GitHub
70
+ * REST + Git Data APIs.
71
+ *
72
+ * The provider is transport-agnostic; it only talks to an `Octokit`
73
+ * instance passed into the constructor. The `createGitHubProvider`
74
+ * helper in `factory.ts` wraps the dynamic import so consumers never
75
+ * have to touch Octokit directly.
76
+ */
77
+ declare class GitHubProvider implements RepoProvider {
78
+ private readonly client;
79
+ readonly repo: RepoRef;
80
+ readonly capabilities: ProviderCapabilities;
81
+ private readonly reader;
82
+ constructor(client: GitHubClient, repo: RepoRef);
83
+ readFile(path: string, ref?: string): Promise<string>;
84
+ listDirectory(path: string, ref?: string): Promise<string[]>;
85
+ fileExists(path: string, ref?: string): Promise<boolean>;
86
+ applyPlan(input: ApplyPlanInput): Promise<Commit>;
87
+ listBranches(prefix?: string): Promise<Branch[]>;
88
+ createBranch(name: string, fromRef?: string): Promise<void>;
89
+ deleteBranch(name: string): Promise<void>;
90
+ getBranchDiff(branch: string, base?: string): Promise<FileDiff[]>;
91
+ mergeBranch(branch: string, into: string): Promise<MergeResult>;
92
+ isMerged(branch: string, into?: string): Promise<boolean>;
93
+ getDefaultBranch(): Promise<string>;
94
+ }
95
+ //#endregion
96
+ //#region src/providers/github/factory.d.ts
97
+ /**
98
+ * Create an Octokit-backed `GitHubClient` from an auth configuration.
99
+ *
100
+ * The `@octokit/rest` module is imported dynamically so it stays a pure
101
+ * optional peer dependency — self-hosted MCP on stdio can run without
102
+ * it. If the module is not installed, the import throws with a helpful
103
+ * hint pointing the operator at the peer dependency.
104
+ *
105
+ * Phase 5.1 ships with PAT auth only. App-based auth (JWT + installation
106
+ * token) lands in phase 5.2 together with the HTTP transport.
107
+ */
108
+ declare function createGitHubClient(auth: GitHubAuth): Promise<GitHubClient>;
109
+ /**
110
+ * Factory for the full provider — instantiates an Octokit client and
111
+ * wraps it in a `GitHubProvider`. Consumers who already hold an Octokit
112
+ * instance (HTTP server injecting shared clients, tests, etc.) should
113
+ * instantiate `GitHubProvider` directly instead.
114
+ */
115
+ declare function createGitHubProvider(opts: {
116
+ auth: GitHubAuth;
117
+ repo: RepoRef;
118
+ }): Promise<GitHubProvider>;
119
+ //#endregion
120
+ //#region src/providers/github/reader.d.ts
121
+ /**
122
+ * GitHubReader — `RepoReader` backed by the GitHub Repos + Git Data APIs.
123
+ *
124
+ * Reads pass through `repos.getContent`; directories return a list of
125
+ * names, files return decoded UTF-8 text. Files larger than ~1 MB are
126
+ * fetched by blob SHA through `git.getBlob` because `getContent` omits
127
+ * the body in that case.
128
+ *
129
+ * `ref` is forwarded verbatim and may be a branch name, tag name or
130
+ * commit SHA. When omitted, GitHub resolves to the repository's default
131
+ * branch — which is usually wrong for Contentrain flows, so callers
132
+ * should always pass the explicit `contentrain` tracking branch.
133
+ */
134
+ declare class GitHubReader implements RepoReader {
135
+ private readonly client;
136
+ private readonly repo;
137
+ constructor(client: GitHubClient, repo: RepoRef);
138
+ readFile(path: string, ref?: string): Promise<string>;
139
+ listDirectory(path: string, ref?: string): Promise<string[]>;
140
+ fileExists(path: string, ref?: string): Promise<boolean>;
141
+ }
142
+ //#endregion
143
+ export { DEFAULT_GITHUB_AUTHOR, GITHUB_CAPABILITIES, type GitHubAuth, type GitHubClient, GitHubProvider, GitHubReader, type RepoRef, createGitHubClient, createGitHubProvider };
144
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/providers/github/capabilities.ts","../../../src/providers/github/client.ts","../../../src/providers/github/types.ts","../../../src/providers/github/provider.ts","../../../src/providers/github/factory.ts","../../../src/providers/github/reader.ts"],"mappings":";;;;;;;AAeA;;;;;;;;ACJA;;cDIa,mBAAA,EAAqB,oBAAA;;;;;;AAAlC;;;;;;KCJY,YAAA,GAAe,OAAA;;;;;;ADI3B;;;;;UELiB,OAAA;EACf,KAAA;EACA,IAAA;EACA,WAAA;AAAA;;;;;;;AAHF;;;KAeY,UAAA;EACN,IAAA;EAAa,KAAA;AAAA;EAEb,IAAA;EACA,KAAA;EACA,UAAA;EACA,cAAA;AAAA;;cAIO,qBAAA,EAAuB,YAAA;;;;AFpBpC;;;;;;;;cGkBa,cAAA,YAA0B,YAAA;EAAA,iBAKlB,MAAA;EAAA,SACD,IAAA,EAAM,OAAA;EAAA,SALf,YAAA,EAAc,oBAAA;EAAA,iBACN,MAAA;cAGE,MAAA,EAAQ,YAAA,EACT,IAAA,EAAM,OAAA;EAKxB,QAAA,CAAS,IAAA,UAAc,GAAA,YAAe,OAAA;EAGtC,aAAA,CAAc,IAAA,UAAc,GAAA,YAAe,OAAA;EAG3C,UAAA,CAAW,IAAA,UAAc,GAAA,YAAe,OAAA;EAIxC,SAAA,CAAU,KAAA,EAAO,cAAA,GAAiB,OAAA,CAAQ,MAAA;EAI1C,YAAA,CAAa,MAAA,YAAkB,OAAA,CAAQ,MAAA;EAGvC,YAAA,CAAa,IAAA,UAAc,OAAA,YAAmB,OAAA;EAI9C,YAAA,CAAa,IAAA,WAAe,OAAA;EAG5B,aAAA,CAAc,MAAA,UAAgB,IAAA,YAAgB,OAAA,CAAQ,QAAA;EAItD,WAAA,CAAY,MAAA,UAAgB,IAAA,WAAe,OAAA,CAAQ,WAAA;EAGnD,QAAA,CAAS,MAAA,UAAgB,IAAA,YAAgB,OAAA;EAIzC,gBAAA,CAAA,GAAoB,OAAA;AAAA;;;;AHhEtB;;;;;;;;ACJA;;iBGIsB,kBAAA,CAAmB,IAAA,EAAM,UAAA,GAAa,OAAA,CAAQ,YAAA;;;;;;AFLpE;iBEiCsB,oBAAA,CAAqB,IAAA;EAAQ,IAAA,EAAM,UAAA;EAAY,IAAA,EAAM,OAAA;AAAA,IAAY,OAAA,CAAQ,cAAA;;;;AJ5B/F;;;;;;;;ACJA;;;;cIOa,YAAA,YAAwB,UAAA;EAAA,iBAEhB,MAAA;EAAA,iBACA,IAAA;cADA,MAAA,EAAQ,YAAA,EACR,IAAA,EAAM,OAAA;EAGnB,QAAA,CAAS,IAAA,UAAc,GAAA,YAAe,OAAA;EAmCtC,aAAA,CAAc,IAAA,UAAc,GAAA,YAAe,OAAA;EAkB3C,UAAA,CAAW,IAAA,UAAc,GAAA,YAAe,OAAA;AAAA"}
@@ -0,0 +1,414 @@
1
+ import { n as isNotFoundError, t as resolveRepoPath } from "../../paths-C3xNyc7z.mjs";
2
+ import { CONTENTRAIN_BRANCH } from "@contentrain/types";
3
+ //#region src/providers/github/capabilities.ts
4
+ /**
5
+ * Capability set for GitHubProvider.
6
+ *
7
+ * GitHub over the Git Data API has no working tree, so local worktree
8
+ * features, source-file access and AST scans are unavailable. Push /
9
+ * PR operations are free because every commit goes straight to the
10
+ * remote. Branch protection detection uses the Repos API.
11
+ *
12
+ * Tools that require `astScan`, `sourceRead` or `sourceWrite` must
13
+ * gracefully reject (with `capability_required`) when running against
14
+ * a GitHubProvider — this is the mechanism behind phase 6's normalize
15
+ * capability-gate.
16
+ */
17
+ const GITHUB_CAPABILITIES = {
18
+ localWorktree: false,
19
+ sourceRead: false,
20
+ sourceWrite: false,
21
+ pushRemote: true,
22
+ branchProtection: true,
23
+ pullRequestFallback: true,
24
+ astScan: false
25
+ };
26
+ //#endregion
27
+ //#region src/providers/github/apply-plan.ts
28
+ /**
29
+ * Apply a plan to a GitHub repository as a single atomic commit via the
30
+ * Git Data API. High-level flow:
31
+ *
32
+ * 1. Resolve the base commit SHA — either the current HEAD of the target
33
+ * branch, or the HEAD of `input.base` (or the repo's default branch)
34
+ * when the target branch does not yet exist.
35
+ * 2. Read the base tree SHA from that commit.
36
+ * 3. Walk `input.changes` in parallel — create a blob per non-null
37
+ * content, emit a tree entry with `sha: null` for each deletion.
38
+ * 4. Create a new tree layered on top of the base tree with the collected
39
+ * entries.
40
+ * 5. Create the commit (tree, parents, author).
41
+ * 6. Update an existing branch ref or create a new one pointing at the
42
+ * commit.
43
+ *
44
+ * No working tree, no transaction — the commit is durable as soon as the
45
+ * final ref update returns.
46
+ */
47
+ async function applyPlanToGitHub(client, repo, input) {
48
+ const { baseSha, branchExists } = await resolveBaseSha(client, repo, input.branch, input.base);
49
+ const baseTreeSha = (await client.rest.git.getCommit({
50
+ owner: repo.owner,
51
+ repo: repo.name,
52
+ commit_sha: baseSha
53
+ })).data.tree.sha;
54
+ const treeEntries = await Promise.all(input.changes.map((change) => buildTreeEntry(client, repo, change)));
55
+ const tree = await client.rest.git.createTree({
56
+ owner: repo.owner,
57
+ repo: repo.name,
58
+ base_tree: baseTreeSha,
59
+ tree: treeEntries
60
+ });
61
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
62
+ const commit = await client.rest.git.createCommit({
63
+ owner: repo.owner,
64
+ repo: repo.name,
65
+ message: input.message,
66
+ tree: tree.data.sha,
67
+ parents: [baseSha],
68
+ author: {
69
+ name: input.author.name,
70
+ email: input.author.email,
71
+ date: timestamp
72
+ }
73
+ });
74
+ if (branchExists) await client.rest.git.updateRef({
75
+ owner: repo.owner,
76
+ repo: repo.name,
77
+ ref: `heads/${input.branch}`,
78
+ sha: commit.data.sha
79
+ });
80
+ else await client.rest.git.createRef({
81
+ owner: repo.owner,
82
+ repo: repo.name,
83
+ ref: `refs/heads/${input.branch}`,
84
+ sha: commit.data.sha
85
+ });
86
+ return {
87
+ sha: commit.data.sha,
88
+ message: commit.data.message,
89
+ author: {
90
+ name: commit.data.author?.name ?? input.author.name,
91
+ email: commit.data.author?.email ?? input.author.email
92
+ },
93
+ timestamp: commit.data.author?.date ?? timestamp
94
+ };
95
+ }
96
+ async function buildTreeEntry(client, repo, change) {
97
+ const path = resolveRepoPath(repo.contentRoot, change.path);
98
+ if (change.content === null) return {
99
+ path,
100
+ mode: "100644",
101
+ type: "blob",
102
+ sha: null
103
+ };
104
+ return {
105
+ path,
106
+ mode: "100644",
107
+ type: "blob",
108
+ sha: (await client.rest.git.createBlob({
109
+ owner: repo.owner,
110
+ repo: repo.name,
111
+ content: change.content,
112
+ encoding: "utf-8"
113
+ })).data.sha
114
+ };
115
+ }
116
+ async function resolveBaseSha(client, repo, branch, base) {
117
+ try {
118
+ return {
119
+ baseSha: (await client.rest.git.getRef({
120
+ owner: repo.owner,
121
+ repo: repo.name,
122
+ ref: `heads/${branch}`
123
+ })).data.object.sha,
124
+ branchExists: true
125
+ };
126
+ } catch (error) {
127
+ if (!isNotFoundError(error)) throw error;
128
+ }
129
+ const baseRefName = base ?? CONTENTRAIN_BRANCH;
130
+ return {
131
+ baseSha: (await client.rest.git.getRef({
132
+ owner: repo.owner,
133
+ repo: repo.name,
134
+ ref: `heads/${baseRefName}`
135
+ })).data.object.sha,
136
+ branchExists: false
137
+ };
138
+ }
139
+ //#endregion
140
+ //#region src/providers/github/branch-ops.ts
141
+ /**
142
+ * Branch/merge/diff helpers backed by the Repos and Git Data APIs.
143
+ *
144
+ * Pure functions so they can be composed into `GitHubProvider` or used
145
+ * standalone. All throw on non-404 errors; 404s collapse to empty-ish
146
+ * results where that matches the `RepoProvider` contract (e.g. a
147
+ * missing branch prefix yields `[]` rather than raising).
148
+ */
149
+ async function getDefaultBranch(client, repo) {
150
+ return (await client.rest.repos.get({
151
+ owner: repo.owner,
152
+ repo: repo.name
153
+ })).data.default_branch;
154
+ }
155
+ async function listBranches(client, repo, prefix) {
156
+ const branches = [];
157
+ const iterator = client.paginate.iterator(client.rest.repos.listBranches, {
158
+ owner: repo.owner,
159
+ repo: repo.name,
160
+ per_page: 100
161
+ });
162
+ for await (const page of iterator) for (const b of page.data) {
163
+ if (prefix && !b.name.startsWith(prefix)) continue;
164
+ branches.push({
165
+ name: b.name,
166
+ sha: b.commit.sha,
167
+ protected: b.protected
168
+ });
169
+ }
170
+ return branches;
171
+ }
172
+ async function createBranch(client, repo, name, fromRef) {
173
+ const base = await client.rest.git.getRef({
174
+ owner: repo.owner,
175
+ repo: repo.name,
176
+ ref: `heads/${fromRef}`
177
+ });
178
+ await client.rest.git.createRef({
179
+ owner: repo.owner,
180
+ repo: repo.name,
181
+ ref: `refs/heads/${name}`,
182
+ sha: base.data.object.sha
183
+ });
184
+ }
185
+ async function deleteBranch(client, repo, name) {
186
+ await client.rest.git.deleteRef({
187
+ owner: repo.owner,
188
+ repo: repo.name,
189
+ ref: `heads/${name}`
190
+ });
191
+ }
192
+ async function getBranchDiff(client, repo, branch, base) {
193
+ return ((await client.rest.repos.compareCommits({
194
+ owner: repo.owner,
195
+ repo: repo.name,
196
+ base,
197
+ head: branch
198
+ })).data.files ?? []).map((f) => ({
199
+ path: f.filename,
200
+ status: normaliseStatus(f.status),
201
+ before: null,
202
+ after: null
203
+ }));
204
+ }
205
+ async function mergeBranch(client, repo, branch, into) {
206
+ try {
207
+ return {
208
+ merged: true,
209
+ sha: (await client.rest.repos.merge({
210
+ owner: repo.owner,
211
+ repo: repo.name,
212
+ base: into,
213
+ head: branch
214
+ })).data.sha,
215
+ pullRequestUrl: null
216
+ };
217
+ } catch (error) {
218
+ if (isNotModified(error)) return {
219
+ merged: true,
220
+ sha: null,
221
+ pullRequestUrl: null
222
+ };
223
+ throw error;
224
+ }
225
+ }
226
+ async function isMerged(client, repo, branch, into) {
227
+ return (await client.rest.repos.compareCommits({
228
+ owner: repo.owner,
229
+ repo: repo.name,
230
+ base: into,
231
+ head: branch
232
+ })).data.ahead_by === 0;
233
+ }
234
+ function normaliseStatus(status) {
235
+ if (status === "added") return "added";
236
+ if (status === "removed") return "removed";
237
+ return "modified";
238
+ }
239
+ function isNotModified(error) {
240
+ return typeof error === "object" && error !== null && error.status === 204;
241
+ }
242
+ //#endregion
243
+ //#region src/providers/github/reader.ts
244
+ /**
245
+ * GitHubReader — `RepoReader` backed by the GitHub Repos + Git Data APIs.
246
+ *
247
+ * Reads pass through `repos.getContent`; directories return a list of
248
+ * names, files return decoded UTF-8 text. Files larger than ~1 MB are
249
+ * fetched by blob SHA through `git.getBlob` because `getContent` omits
250
+ * the body in that case.
251
+ *
252
+ * `ref` is forwarded verbatim and may be a branch name, tag name or
253
+ * commit SHA. When omitted, GitHub resolves to the repository's default
254
+ * branch — which is usually wrong for Contentrain flows, so callers
255
+ * should always pass the explicit `contentrain` tracking branch.
256
+ */
257
+ var GitHubReader = class {
258
+ constructor(client, repo) {
259
+ this.client = client;
260
+ this.repo = repo;
261
+ }
262
+ async readFile(path, ref) {
263
+ const repoPath = resolveRepoPath(this.repo.contentRoot, path);
264
+ const data = (await this.client.rest.repos.getContent({
265
+ owner: this.repo.owner,
266
+ repo: this.repo.name,
267
+ path: repoPath,
268
+ ref
269
+ })).data;
270
+ if (Array.isArray(data)) throw new Error(`GitHubReader: path "${path}" is a directory, not a file`);
271
+ if (data.type !== "file") throw new Error(`GitHubReader: path "${path}" is a ${data.type}, not a file`);
272
+ if (data.content === "" && data.size > 0) {
273
+ const blob = await this.client.rest.git.getBlob({
274
+ owner: this.repo.owner,
275
+ repo: this.repo.name,
276
+ file_sha: data.sha
277
+ });
278
+ if (blob.data.encoding !== "base64") throw new Error(`GitHubReader: unexpected blob encoding "${blob.data.encoding}" for ${path}`);
279
+ return Buffer.from(blob.data.content, "base64").toString("utf-8");
280
+ }
281
+ if (data.encoding !== "base64") throw new Error(`GitHubReader: unexpected encoding "${data.encoding}" for ${path}`);
282
+ return Buffer.from(data.content, "base64").toString("utf-8");
283
+ }
284
+ async listDirectory(path, ref) {
285
+ const repoPath = resolveRepoPath(this.repo.contentRoot, path);
286
+ try {
287
+ const data = (await this.client.rest.repos.getContent({
288
+ owner: this.repo.owner,
289
+ repo: this.repo.name,
290
+ path: repoPath,
291
+ ref
292
+ })).data;
293
+ if (!Array.isArray(data)) return [];
294
+ return data.map((entry) => entry.name);
295
+ } catch (error) {
296
+ if (isNotFoundError(error)) return [];
297
+ throw error;
298
+ }
299
+ }
300
+ async fileExists(path, ref) {
301
+ const repoPath = resolveRepoPath(this.repo.contentRoot, path);
302
+ try {
303
+ await this.client.rest.repos.getContent({
304
+ owner: this.repo.owner,
305
+ repo: this.repo.name,
306
+ path: repoPath,
307
+ ref
308
+ });
309
+ return true;
310
+ } catch (error) {
311
+ if (isNotFoundError(error)) return false;
312
+ throw error;
313
+ }
314
+ }
315
+ };
316
+ //#endregion
317
+ //#region src/providers/github/provider.ts
318
+ /**
319
+ * GitHubProvider — `RepoProvider` backed by the Octokit-driven GitHub
320
+ * REST + Git Data APIs.
321
+ *
322
+ * The provider is transport-agnostic; it only talks to an `Octokit`
323
+ * instance passed into the constructor. The `createGitHubProvider`
324
+ * helper in `factory.ts` wraps the dynamic import so consumers never
325
+ * have to touch Octokit directly.
326
+ */
327
+ var GitHubProvider = class {
328
+ capabilities = GITHUB_CAPABILITIES;
329
+ reader;
330
+ constructor(client, repo) {
331
+ this.client = client;
332
+ this.repo = repo;
333
+ this.reader = new GitHubReader(client, repo);
334
+ }
335
+ readFile(path, ref) {
336
+ return this.reader.readFile(path, ref);
337
+ }
338
+ listDirectory(path, ref) {
339
+ return this.reader.listDirectory(path, ref);
340
+ }
341
+ fileExists(path, ref) {
342
+ return this.reader.fileExists(path, ref);
343
+ }
344
+ applyPlan(input) {
345
+ return applyPlanToGitHub(this.client, this.repo, input);
346
+ }
347
+ listBranches(prefix) {
348
+ return listBranches(this.client, this.repo, prefix);
349
+ }
350
+ createBranch(name, fromRef) {
351
+ const resolved = fromRef ?? "main";
352
+ return createBranch(this.client, this.repo, name, resolved);
353
+ }
354
+ deleteBranch(name) {
355
+ return deleteBranch(this.client, this.repo, name);
356
+ }
357
+ getBranchDiff(branch, base) {
358
+ const resolved = base ?? "main";
359
+ return getBranchDiff(this.client, this.repo, branch, resolved);
360
+ }
361
+ mergeBranch(branch, into) {
362
+ return mergeBranch(this.client, this.repo, branch, into);
363
+ }
364
+ isMerged(branch, into) {
365
+ const resolved = into ?? "main";
366
+ return isMerged(this.client, this.repo, branch, resolved);
367
+ }
368
+ getDefaultBranch() {
369
+ return getDefaultBranch(this.client, this.repo);
370
+ }
371
+ };
372
+ //#endregion
373
+ //#region src/providers/github/factory.ts
374
+ /**
375
+ * Create an Octokit-backed `GitHubClient` from an auth configuration.
376
+ *
377
+ * The `@octokit/rest` module is imported dynamically so it stays a pure
378
+ * optional peer dependency — self-hosted MCP on stdio can run without
379
+ * it. If the module is not installed, the import throws with a helpful
380
+ * hint pointing the operator at the peer dependency.
381
+ *
382
+ * Phase 5.1 ships with PAT auth only. App-based auth (JWT + installation
383
+ * token) lands in phase 5.2 together with the HTTP transport.
384
+ */
385
+ async function createGitHubClient(auth) {
386
+ if (auth.type !== "pat") throw new Error(`GitHub auth type "${auth.type}" is not yet supported. Phase 5.1 ships with "pat" only; "app" installation auth arrives in phase 5.2.`);
387
+ let OctokitCtor;
388
+ try {
389
+ ({Octokit: OctokitCtor} = await import("@octokit/rest"));
390
+ } catch (error) {
391
+ throw new Error("@octokit/rest is required for the GitHubProvider but could not be loaded. Install it as a peer dependency: pnpm add @octokit/rest.", { cause: error });
392
+ }
393
+ return new OctokitCtor({ auth: auth.token });
394
+ }
395
+ /**
396
+ * Factory for the full provider — instantiates an Octokit client and
397
+ * wraps it in a `GitHubProvider`. Consumers who already hold an Octokit
398
+ * instance (HTTP server injecting shared clients, tests, etc.) should
399
+ * instantiate `GitHubProvider` directly instead.
400
+ */
401
+ async function createGitHubProvider(opts) {
402
+ return new GitHubProvider(await createGitHubClient(opts.auth), opts.repo);
403
+ }
404
+ //#endregion
405
+ //#region src/providers/github/types.ts
406
+ /** Default author used when a call does not provide one. */
407
+ const DEFAULT_GITHUB_AUTHOR = {
408
+ name: "Contentrain",
409
+ email: "mcp@contentrain.io"
410
+ };
411
+ //#endregion
412
+ export { DEFAULT_GITHUB_AUTHOR, GITHUB_CAPABILITIES, GitHubProvider, GitHubReader, createGitHubClient, createGitHubProvider };
413
+
414
+ //# sourceMappingURL=index.mjs.map