@contentrain/mcp 3.6.8 → 3.8.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.
- package/README.md +2 -0
- package/dist/{annotations-CXHPnS-M.mjs → annotations-BYkjoj2d.mjs} +1 -1
- package/dist/{annotations-CXHPnS-M.mjs.map → annotations-BYkjoj2d.mjs.map} +1 -1
- package/dist/{apply-manager-llmSyK5K.mjs → apply-manager-B-VMXKiW.mjs} +3 -3
- package/dist/{apply-manager-llmSyK5K.mjs.map → apply-manager-B-VMXKiW.mjs.map} +1 -1
- package/dist/{astro-parser-dhFgc2wA.mjs → astro-parser-DsGdiUnu.mjs} +3 -3
- package/dist/{astro-parser-dhFgc2wA.mjs.map → astro-parser-DsGdiUnu.mjs.map} +1 -1
- package/dist/{availability-DYHNoP0_.mjs → availability-CO8Xfx3w.mjs} +1 -1
- package/dist/{availability-DYHNoP0_.mjs.map → availability-CO8Xfx3w.mjs.map} +1 -1
- package/dist/base-branch-Cw9DWvCC.mjs +121 -0
- package/dist/base-branch-Cw9DWvCC.mjs.map +1 -0
- package/dist/{branch-lifecycle-f4gI_eGo.mjs → branch-lifecycle-CcsSyfSw.mjs} +3 -68
- package/dist/branch-lifecycle-CcsSyfSw.mjs.map +1 -0
- package/dist/{branch-lifecycle-DGlgjnC7.d.mts → branch-lifecycle-CirYfTXu.d.mts} +1 -1
- package/dist/{branch-lifecycle-DGlgjnC7.d.mts.map → branch-lifecycle-CirYfTXu.d.mts.map} +1 -1
- package/dist/{compiler-CR9F0wZZ.mjs → compiler-CKelkozt.mjs} +2 -2
- package/dist/{compiler-CR9F0wZZ.mjs.map → compiler-CKelkozt.mjs.map} +1 -1
- package/dist/{compiler-sfc.cjs-Cpexs92i.mjs → compiler-sfc.cjs-BH5AEf2z.mjs} +2 -2
- package/dist/{compiler-sfc.cjs-Cpexs92i.mjs.map → compiler-sfc.cjs-BH5AEf2z.mjs.map} +1 -1
- package/dist/{content-manager-BqaMkMVk.d.mts → content-manager-2g0pkLFh.d.mts} +2 -2
- package/dist/{content-manager-BqaMkMVk.d.mts.map → content-manager-2g0pkLFh.d.mts.map} +1 -1
- package/dist/core/apply-manager.mjs +5 -4
- package/dist/core/config.d.mts +1 -1
- package/dist/core/content-manager.d.mts +1 -1
- package/dist/core/context.d.mts +1 -1
- package/dist/core/contracts/index.d.mts +2 -2
- package/dist/core/doctor.mjs +4 -3
- package/dist/core/graph-builder.mjs +2 -2
- package/dist/core/model-manager.d.mts +1 -1
- package/dist/core/ops/index.d.mts +3 -3
- package/dist/core/overlay-reader.d.mts +1 -1
- package/dist/core/scan-config.mjs +1 -1
- package/dist/core/scanner.mjs +3 -3
- package/dist/core/validator/index.d.mts +1 -1
- package/dist/{detect-DUYFv3-O.mjs → detect-B1QHYA0u.mjs} +1 -1
- package/dist/{detect-DUYFv3-O.mjs.map → detect-B1QHYA0u.mjs.map} +1 -1
- package/dist/{doctor-fIG4TtsO.mjs → doctor-Du4MVG5w.mjs} +3 -3
- package/dist/{doctor-fIG4TtsO.mjs.map → doctor-Du4MVG5w.mjs.map} +1 -1
- package/dist/{errors-DLkAWnzQ.mjs → errors-B0NcpPIp.mjs} +1 -1
- package/dist/{errors-DLkAWnzQ.mjs.map → errors-B0NcpPIp.mjs.map} +1 -1
- package/dist/{errors-BmLE-JSC.mjs → errors-De8Noahk.mjs} +2 -2
- package/dist/{errors-BmLE-JSC.mjs.map → errors-De8Noahk.mjs.map} +1 -1
- package/dist/git/base-branch.d.mts +61 -0
- package/dist/git/base-branch.d.mts.map +1 -0
- package/dist/git/base-branch.mjs +2 -0
- package/dist/git/branch-lifecycle.d.mts +1 -1
- package/dist/git/branch-lifecycle.mjs +2 -1
- package/dist/git/errors.mjs +1 -1
- package/dist/git/reconcile.d.mts +1 -1
- package/dist/git/reconcile.mjs +5 -4
- package/dist/git/transaction.d.mts +1 -1
- package/dist/git/transaction.mjs +4 -3
- package/dist/{graph-builder-CNswWNtb.mjs → graph-builder-BPhi-1KH.mjs} +2 -2
- package/dist/{graph-builder-CNswWNtb.mjs.map → graph-builder-BPhi-1KH.mjs.map} +1 -1
- package/dist/{id-CjGMPP1k.mjs → id-jrHWdtqn.mjs} +1 -1
- package/dist/{id-CjGMPP1k.mjs.map → id-jrHWdtqn.mjs.map} +1 -1
- package/dist/{index-DKZNUqpP.d.mts → index-BHpAYYFF.d.mts} +2 -2
- package/dist/{index-DKZNUqpP.d.mts.map → index-BHpAYYFF.d.mts.map} +1 -1
- package/dist/index-v0N9d4GT.d.mts +2 -0
- package/dist/index.mjs +16 -15
- package/dist/index.mjs.map +1 -1
- package/dist/{local-DkDTrGt_.mjs → local-BXTVAAr-.mjs} +314 -16
- package/dist/local-BXTVAAr-.mjs.map +1 -0
- package/dist/{node-CpLm3qr6.mjs → node-hrF4BrH2.mjs} +1 -1
- package/dist/{node-CpLm3qr6.mjs.map → node-hrF4BrH2.mjs.map} +1 -1
- package/dist/{paths-C6_C1gLp.mjs → paths-Dz5v-z00.mjs} +1 -1
- package/dist/{paths-C6_C1gLp.mjs.map → paths-Dz5v-z00.mjs.map} +1 -1
- package/dist/providers/github/index.d.mts +1 -1
- package/dist/providers/github/index.mjs +2 -2
- package/dist/providers/gitlab/index.d.mts +1 -1
- package/dist/providers/gitlab/index.mjs +2 -2
- package/dist/providers/local/index.d.mts +4 -1
- package/dist/providers/local/index.d.mts.map +1 -1
- package/dist/providers/local/index.mjs +5 -4
- package/dist/{reconcile-Cq60cldB.mjs → reconcile-BWbckvbv.mjs} +4 -3
- package/dist/{reconcile-Cq60cldB.mjs.map → reconcile-BWbckvbv.mjs.map} +1 -1
- package/dist/{scan-config-DkRKu_ON.mjs → scan-config-S5fkzNhL.mjs} +1 -1
- package/dist/{scan-config-DkRKu_ON.mjs.map → scan-config-S5fkzNhL.mjs.map} +1 -1
- package/dist/{scanner-BLo3ssiF.mjs → scanner-0sc_pARB.mjs} +6 -6
- package/dist/{scanner-BLo3ssiF.mjs.map → scanner-0sc_pARB.mjs.map} +1 -1
- package/dist/server/http/index.d.mts +1 -1
- package/dist/server/http/index.mjs +16 -15
- package/dist/server/http/index.mjs.map +1 -1
- package/dist/{server-CUtmhlno.d.mts → server-24UHN9Ll.d.mts} +2 -2
- package/dist/server-24UHN9Ll.d.mts.map +1 -0
- package/dist/{server-BJkE8D5N.mjs → server-Cl3LMiU3.mjs} +71 -44
- package/dist/server-Cl3LMiU3.mjs.map +1 -0
- package/dist/server.d.mts +1 -1
- package/dist/server.mjs +16 -15
- package/dist/{source-map-generator-Clb2Eqse.mjs → source-map-generator-DVDSWUZ1.mjs} +1 -1
- package/dist/{source-map-generator-Clb2Eqse.mjs.map → source-map-generator-DVDSWUZ1.mjs.map} +1 -1
- package/dist/{svelte-parser-CBN7Jxb7.mjs → svelte-parser-CVUIcakb.mjs} +3 -3
- package/dist/{svelte-parser-CBN7Jxb7.mjs.map → svelte-parser-CVUIcakb.mjs.map} +1 -1
- package/dist/templates/index.mjs +1 -1
- package/dist/{templates-BZbohJc8.mjs → templates-Dn-iZyzp.mjs} +1 -1
- package/dist/{templates-BZbohJc8.mjs.map → templates-Dn-iZyzp.mjs.map} +1 -1
- package/dist/testing/memory-provider.d.mts +1 -1
- package/dist/tools/annotations.mjs +1 -1
- package/dist/tools/availability.mjs +1 -1
- package/dist/{transaction-BWJt1KQb.mjs → transaction-DfrvGIfk.mjs} +4 -3
- package/dist/{transaction-BWJt1KQb.mjs.map → transaction-DfrvGIfk.mjs.map} +1 -1
- package/dist/{tsx-parser-go5V9zoc.mjs → tsx-parser-DgFdpsea.mjs} +1 -1
- package/dist/{tsx-parser-go5V9zoc.mjs.map → tsx-parser-DgFdpsea.mjs.map} +1 -1
- package/dist/tsx-parser-v8C-7cZv.mjs +2 -0
- package/dist/util/detect.mjs +1 -1
- package/dist/util/id.mjs +1 -1
- package/dist/{vue-parser-5Mp8w-mb.mjs → vue-parser-DvliI6UE.mjs} +3 -3
- package/dist/{vue-parser-5Mp8w-mb.mjs.map → vue-parser-DvliI6UE.mjs.map} +1 -1
- package/package.json +8 -4
- package/dist/branch-lifecycle-f4gI_eGo.mjs.map +0 -1
- package/dist/index-A5iw3yDk.d.mts +0 -2
- package/dist/local-DkDTrGt_.mjs.map +0 -1
- package/dist/server-BJkE8D5N.mjs.map +0 -1
- package/dist/server-CUtmhlno.d.mts.map +0 -1
- package/dist/tsx-parser-DOQpJVgK.mjs +0 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { CONTENTRAIN_BRANCH } from "@contentrain/types";
|
|
2
|
+
//#region src/git/base-branch.ts
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the base branch — the branch a local auto-merge advances with
|
|
5
|
+
* `update-ref`, pushes, and merges into `contentrain` before every write.
|
|
6
|
+
*
|
|
7
|
+
* Precedence, the same at every call site:
|
|
8
|
+
* 1. `CONTENTRAIN_BRANCH` env — a per-process override;
|
|
9
|
+
* 2. `config.repository.default_branch`;
|
|
10
|
+
* 3. the remote's default (`refs/remotes/<remote>/HEAD`), when that branch
|
|
11
|
+
* exists locally;
|
|
12
|
+
* 4. a local `main`, then a local `master`;
|
|
13
|
+
* 5. the checked-out branch — only when none of the above exists;
|
|
14
|
+
* 6. `'main'`.
|
|
15
|
+
*
|
|
16
|
+
* The checked-out branch used to be step 3. That made a developer's feature
|
|
17
|
+
* branch the base of every content write run while it was checked out: its
|
|
18
|
+
* code commits were merged into `contentrain`, the feature branch itself was
|
|
19
|
+
* advanced and pushed, and the code reached the real default branch on the
|
|
20
|
+
* next write by anyone else (#227). The checked-out branch is where the
|
|
21
|
+
* developer happens to be, not where content belongs.
|
|
22
|
+
*
|
|
23
|
+
* `currentBranch` may be passed when the caller already has it, to save a
|
|
24
|
+
* spawn; it is only consulted as the last resort.
|
|
25
|
+
*/
|
|
26
|
+
async function resolveBaseBranch(git, config, options = {}) {
|
|
27
|
+
return (await resolveBaseBranchSource(git, config, options)).branch;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* {@link resolveBaseBranch}, also saying where the answer came from.
|
|
31
|
+
* `env: false` skips the `CONTENTRAIN_BRANCH` override — for callers that
|
|
32
|
+
* record the result, where a per-process override must not become config.
|
|
33
|
+
*/
|
|
34
|
+
async function resolveBaseBranchSource(git, config, options = {}) {
|
|
35
|
+
const fromEnv = options.env === false ? void 0 : process.env["CONTENTRAIN_BRANCH"]?.trim();
|
|
36
|
+
if (fromEnv) return {
|
|
37
|
+
branch: fromEnv,
|
|
38
|
+
source: "env"
|
|
39
|
+
};
|
|
40
|
+
const fromConfig = config?.repository?.default_branch?.trim();
|
|
41
|
+
if (fromConfig) return {
|
|
42
|
+
branch: fromConfig,
|
|
43
|
+
source: "config"
|
|
44
|
+
};
|
|
45
|
+
const remoteName = options.remoteName ?? process.env["CONTENTRAIN_REMOTE"] ?? "origin";
|
|
46
|
+
const remoteHead = `refs/remotes/${remoteName}/HEAD`;
|
|
47
|
+
const refs = /* @__PURE__ */ new Map();
|
|
48
|
+
try {
|
|
49
|
+
const raw = await git.raw([
|
|
50
|
+
"for-each-ref",
|
|
51
|
+
"--format=%(refname) %(symref)",
|
|
52
|
+
"refs/heads/main",
|
|
53
|
+
"refs/heads/master",
|
|
54
|
+
remoteHead
|
|
55
|
+
]);
|
|
56
|
+
for (const line of raw.split("\n")) {
|
|
57
|
+
const [name, symref = ""] = line.trim().split(" ");
|
|
58
|
+
if (name) refs.set(name, symref);
|
|
59
|
+
}
|
|
60
|
+
} catch {}
|
|
61
|
+
const remoteDefault = refs.get(remoteHead)?.replace(`refs/remotes/${remoteName}/`, "");
|
|
62
|
+
if (remoteDefault && remoteDefault !== CONTENTRAIN_BRANCH && await localBranchExists(git, remoteDefault, refs)) return {
|
|
63
|
+
branch: remoteDefault,
|
|
64
|
+
source: "remote_head"
|
|
65
|
+
};
|
|
66
|
+
if (refs.has("refs/heads/main")) return {
|
|
67
|
+
branch: "main",
|
|
68
|
+
source: "main"
|
|
69
|
+
};
|
|
70
|
+
if (refs.has("refs/heads/master")) return {
|
|
71
|
+
branch: "master",
|
|
72
|
+
source: "master"
|
|
73
|
+
};
|
|
74
|
+
const current = options.currentBranch ?? (await git.raw(["branch", "--show-current"]).catch(() => "")).trim();
|
|
75
|
+
if (current && current !== CONTENTRAIN_BRANCH) return {
|
|
76
|
+
branch: current,
|
|
77
|
+
source: "checked_out"
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
branch: "main",
|
|
81
|
+
source: "fallback"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The branch `init` records as `repository.default_branch`, or `null` when it
|
|
86
|
+
* cannot be told apart from where the developer happens to be (#230).
|
|
87
|
+
*
|
|
88
|
+
* The same precedence as {@link resolveBaseBranch}, minus the env override (a
|
|
89
|
+
* per-process override is not the project's default). The checked-out branch
|
|
90
|
+
* is recorded only when it is the repository's only branch — a fresh
|
|
91
|
+
* `git init` on a host whose `init.defaultBranch` is `develop`/`trunk`.
|
|
92
|
+
* Anywhere else it may be a feature branch, and recording that would make
|
|
93
|
+
* every later write advance it; leaving the field out keeps inference.
|
|
94
|
+
*/
|
|
95
|
+
async function resolveInitDefaultBranch(git) {
|
|
96
|
+
const { branch, source } = await resolveBaseBranchSource(git, null, { env: false });
|
|
97
|
+
if (source === "remote_head" || source === "main" || source === "master") return branch;
|
|
98
|
+
if (source !== "checked_out") return null;
|
|
99
|
+
const heads = (await git.raw([
|
|
100
|
+
"for-each-ref",
|
|
101
|
+
"--format=%(refname:short)",
|
|
102
|
+
"refs/heads"
|
|
103
|
+
]).catch(() => "")).split("\n").map((line) => line.trim()).filter((name) => name && name !== CONTENTRAIN_BRANCH);
|
|
104
|
+
return heads.length === 1 && heads[0] === branch ? branch : null;
|
|
105
|
+
}
|
|
106
|
+
async function localBranchExists(git, branch, known) {
|
|
107
|
+
if (known.has(`refs/heads/${branch}`)) return true;
|
|
108
|
+
try {
|
|
109
|
+
return (await git.raw([
|
|
110
|
+
"for-each-ref",
|
|
111
|
+
"--format=%(refname)",
|
|
112
|
+
`refs/heads/${branch}`
|
|
113
|
+
])).trim() !== "";
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { resolveBaseBranchSource as n, resolveInitDefaultBranch as r, resolveBaseBranch as t };
|
|
120
|
+
|
|
121
|
+
//# sourceMappingURL=base-branch-Cw9DWvCC.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-branch-Cw9DWvCC.mjs","names":[],"sources":["../src/git/base-branch.ts"],"sourcesContent":["import type { SimpleGit } from 'simple-git'\nimport type { ContentrainConfig } from '@contentrain/types'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\n\n/**\n * Resolve the base branch — the branch a local auto-merge advances with\n * `update-ref`, pushes, and merges into `contentrain` before every write.\n *\n * Precedence, the same at every call site:\n * 1. `CONTENTRAIN_BRANCH` env — a per-process override;\n * 2. `config.repository.default_branch`;\n * 3. the remote's default (`refs/remotes/<remote>/HEAD`), when that branch\n * exists locally;\n * 4. a local `main`, then a local `master`;\n * 5. the checked-out branch — only when none of the above exists;\n * 6. `'main'`.\n *\n * The checked-out branch used to be step 3. That made a developer's feature\n * branch the base of every content write run while it was checked out: its\n * code commits were merged into `contentrain`, the feature branch itself was\n * advanced and pushed, and the code reached the real default branch on the\n * next write by anyone else (#227). The checked-out branch is where the\n * developer happens to be, not where content belongs.\n *\n * `currentBranch` may be passed when the caller already has it, to save a\n * spawn; it is only consulted as the last resort.\n */\nexport async function resolveBaseBranch(\n git: SimpleGit,\n config: ContentrainConfig | null | undefined,\n options: { remoteName?: string, currentBranch?: string } = {},\n): Promise<string> {\n return (await resolveBaseBranchSource(git, config, options)).branch\n}\n\n/** Which rung of {@link resolveBaseBranch}'s precedence answered. */\nexport type BaseBranchSource = 'env' | 'config' | 'remote_head' | 'main' | 'master' | 'checked_out' | 'fallback'\n\n/**\n * {@link resolveBaseBranch}, also saying where the answer came from.\n * `env: false` skips the `CONTENTRAIN_BRANCH` override — for callers that\n * record the result, where a per-process override must not become config.\n */\nexport async function resolveBaseBranchSource(\n git: SimpleGit,\n config: ContentrainConfig | null | undefined,\n options: { remoteName?: string, currentBranch?: string, env?: boolean } = {},\n): Promise<{ branch: string, source: BaseBranchSource }> {\n const fromEnv = options.env === false ? undefined : process.env['CONTENTRAIN_BRANCH']?.trim()\n if (fromEnv) return { branch: fromEnv, source: 'env' }\n const fromConfig = config?.repository?.default_branch?.trim()\n if (fromConfig) return { branch: fromConfig, source: 'config' }\n\n const remoteName = options.remoteName ?? process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n const remoteHead = `refs/remotes/${remoteName}/HEAD`\n\n // One spawn answers \"which of main/master exist\" and \"where does the\n // remote HEAD point\". for-each-ref prints nothing for a missing pattern.\n const refs = new Map<string, string>()\n try {\n const raw = await git.raw([\n 'for-each-ref', '--format=%(refname) %(symref)',\n 'refs/heads/main', 'refs/heads/master', remoteHead,\n ])\n for (const line of raw.split('\\n')) {\n const [name, symref = ''] = line.trim().split(' ')\n if (name) refs.set(name, symref)\n }\n } catch {\n // Not a repository or no refs yet — fall through to the checked-out branch.\n }\n\n const remoteDefault = refs.get(remoteHead)?.replace(`refs/remotes/${remoteName}/`, '')\n if (remoteDefault && remoteDefault !== CONTENTRAIN_BRANCH && await localBranchExists(git, remoteDefault, refs)) {\n return { branch: remoteDefault, source: 'remote_head' }\n }\n if (refs.has('refs/heads/main')) return { branch: 'main', source: 'main' }\n if (refs.has('refs/heads/master')) return { branch: 'master', source: 'master' }\n\n const current = options.currentBranch\n ?? (await git.raw(['branch', '--show-current']).catch(() => '')).trim()\n if (current && current !== CONTENTRAIN_BRANCH) return { branch: current, source: 'checked_out' }\n return { branch: 'main', source: 'fallback' }\n}\n\n/**\n * The branch `init` records as `repository.default_branch`, or `null` when it\n * cannot be told apart from where the developer happens to be (#230).\n *\n * The same precedence as {@link resolveBaseBranch}, minus the env override (a\n * per-process override is not the project's default). The checked-out branch\n * is recorded only when it is the repository's only branch — a fresh\n * `git init` on a host whose `init.defaultBranch` is `develop`/`trunk`.\n * Anywhere else it may be a feature branch, and recording that would make\n * every later write advance it; leaving the field out keeps inference.\n */\nexport async function resolveInitDefaultBranch(git: SimpleGit): Promise<string | null> {\n const { branch, source } = await resolveBaseBranchSource(git, null, { env: false })\n if (source === 'remote_head' || source === 'main' || source === 'master') return branch\n if (source !== 'checked_out') return null\n const heads = (await git.raw(['for-each-ref', '--format=%(refname:short)', 'refs/heads']).catch(() => ''))\n .split('\\n').map(line => line.trim()).filter(name => name && name !== CONTENTRAIN_BRANCH)\n return heads.length === 1 && heads[0] === branch ? branch : null\n}\n\nasync function localBranchExists(git: SimpleGit, branch: string, known: Map<string, string>): Promise<boolean> {\n if (known.has(`refs/heads/${branch}`)) return true\n try {\n return (await git.raw(['for-each-ref', '--format=%(refname)', `refs/heads/${branch}`])).trim() !== ''\n } catch {\n return false\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,eAAsB,kBACpB,KACA,QACA,UAA2D,EAAE,EAC5C;AACjB,SAAQ,MAAM,wBAAwB,KAAK,QAAQ,QAAQ,EAAE;;;;;;;AAW/D,eAAsB,wBACpB,KACA,QACA,UAA0E,EAAE,EACrB;CACvD,MAAM,UAAU,QAAQ,QAAQ,QAAQ,KAAA,IAAY,QAAQ,IAAI,uBAAuB,MAAM;AAC7F,KAAI,QAAS,QAAO;EAAE,QAAQ;EAAS,QAAQ;EAAO;CACtD,MAAM,aAAa,QAAQ,YAAY,gBAAgB,MAAM;AAC7D,KAAI,WAAY,QAAO;EAAE,QAAQ;EAAY,QAAQ;EAAU;CAE/D,MAAM,aAAa,QAAQ,cAAc,QAAQ,IAAI,yBAAyB;CAC9E,MAAM,aAAa,gBAAgB,WAAW;CAI9C,MAAM,uBAAO,IAAI,KAAqB;AACtC,KAAI;EACF,MAAM,MAAM,MAAM,IAAI,IAAI;GACxB;GAAgB;GAChB;GAAmB;GAAqB;GACzC,CAAC;AACF,OAAK,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE;GAClC,MAAM,CAAC,MAAM,SAAS,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI;AAClD,OAAI,KAAM,MAAK,IAAI,MAAM,OAAO;;SAE5B;CAIR,MAAM,gBAAgB,KAAK,IAAI,WAAW,EAAE,QAAQ,gBAAgB,WAAW,IAAI,GAAG;AACtF,KAAI,iBAAiB,kBAAkB,sBAAsB,MAAM,kBAAkB,KAAK,eAAe,KAAK,CAC5G,QAAO;EAAE,QAAQ;EAAe,QAAQ;EAAe;AAEzD,KAAI,KAAK,IAAI,kBAAkB,CAAE,QAAO;EAAE,QAAQ;EAAQ,QAAQ;EAAQ;AAC1E,KAAI,KAAK,IAAI,oBAAoB,CAAE,QAAO;EAAE,QAAQ;EAAU,QAAQ;EAAU;CAEhF,MAAM,UAAU,QAAQ,kBAClB,MAAM,IAAI,IAAI,CAAC,UAAU,iBAAiB,CAAC,CAAC,YAAY,GAAG,EAAE,MAAM;AACzE,KAAI,WAAW,YAAY,mBAAoB,QAAO;EAAE,QAAQ;EAAS,QAAQ;EAAe;AAChG,QAAO;EAAE,QAAQ;EAAQ,QAAQ;EAAY;;;;;;;;;;;;;AAc/C,eAAsB,yBAAyB,KAAwC;CACrF,MAAM,EAAE,QAAQ,WAAW,MAAM,wBAAwB,KAAK,MAAM,EAAE,KAAK,OAAO,CAAC;AACnF,KAAI,WAAW,iBAAiB,WAAW,UAAU,WAAW,SAAU,QAAO;AACjF,KAAI,WAAW,cAAe,QAAO;CACrC,MAAM,SAAS,MAAM,IAAI,IAAI;EAAC;EAAgB;EAA6B;EAAa,CAAC,CAAC,YAAY,GAAG,EACtG,MAAM,KAAK,CAAC,KAAI,SAAQ,KAAK,MAAM,CAAC,CAAC,QAAO,SAAQ,QAAQ,SAAS,mBAAmB;AAC3F,QAAO,MAAM,WAAW,KAAK,MAAM,OAAO,SAAS,SAAS;;AAG9D,eAAe,kBAAkB,KAAgB,QAAgB,OAA8C;AAC7G,KAAI,MAAM,IAAI,cAAc,SAAS,CAAE,QAAO;AAC9C,KAAI;AACF,UAAQ,MAAM,IAAI,IAAI;GAAC;GAAgB;GAAuB,cAAc;GAAS,CAAC,EAAE,MAAM,KAAK;SAC7F;AACN,SAAO"}
|
|
@@ -1,73 +1,8 @@
|
|
|
1
1
|
import { t as readConfig } from "./config-BLwZtwlI.mjs";
|
|
2
2
|
import { r as createGit, t as NETWORK_UNSAFE } from "./identity-CxAB9Qb8.mjs";
|
|
3
|
+
import { t as resolveBaseBranch } from "./base-branch-Cw9DWvCC.mjs";
|
|
3
4
|
import { CONTENTRAIN_BRANCH } from "@contentrain/types";
|
|
4
5
|
import "simple-git";
|
|
5
|
-
//#region src/git/base-branch.ts
|
|
6
|
-
/**
|
|
7
|
-
* Resolve the base branch — the branch a local auto-merge advances with
|
|
8
|
-
* `update-ref`, pushes, and merges into `contentrain` before every write.
|
|
9
|
-
*
|
|
10
|
-
* Precedence, the same at every call site:
|
|
11
|
-
* 1. `CONTENTRAIN_BRANCH` env — a per-process override;
|
|
12
|
-
* 2. `config.repository.default_branch`;
|
|
13
|
-
* 3. the remote's default (`refs/remotes/<remote>/HEAD`), when that branch
|
|
14
|
-
* exists locally;
|
|
15
|
-
* 4. a local `main`, then a local `master`;
|
|
16
|
-
* 5. the checked-out branch — only when none of the above exists;
|
|
17
|
-
* 6. `'main'`.
|
|
18
|
-
*
|
|
19
|
-
* The checked-out branch used to be step 3. That made a developer's feature
|
|
20
|
-
* branch the base of every content write run while it was checked out: its
|
|
21
|
-
* code commits were merged into `contentrain`, the feature branch itself was
|
|
22
|
-
* advanced and pushed, and the code reached the real default branch on the
|
|
23
|
-
* next write by anyone else (#227). The checked-out branch is where the
|
|
24
|
-
* developer happens to be, not where content belongs.
|
|
25
|
-
*
|
|
26
|
-
* `currentBranch` may be passed when the caller already has it, to save a
|
|
27
|
-
* spawn; it is only consulted as the last resort.
|
|
28
|
-
*/
|
|
29
|
-
async function resolveBaseBranch(git, config, options = {}) {
|
|
30
|
-
const fromEnv = process.env["CONTENTRAIN_BRANCH"]?.trim();
|
|
31
|
-
if (fromEnv) return fromEnv;
|
|
32
|
-
const fromConfig = config?.repository?.default_branch?.trim();
|
|
33
|
-
if (fromConfig) return fromConfig;
|
|
34
|
-
const remoteName = options.remoteName ?? process.env["CONTENTRAIN_REMOTE"] ?? "origin";
|
|
35
|
-
const remoteHead = `refs/remotes/${remoteName}/HEAD`;
|
|
36
|
-
const refs = /* @__PURE__ */ new Map();
|
|
37
|
-
try {
|
|
38
|
-
const raw = await git.raw([
|
|
39
|
-
"for-each-ref",
|
|
40
|
-
"--format=%(refname) %(symref)",
|
|
41
|
-
"refs/heads/main",
|
|
42
|
-
"refs/heads/master",
|
|
43
|
-
remoteHead
|
|
44
|
-
]);
|
|
45
|
-
for (const line of raw.split("\n")) {
|
|
46
|
-
const [name, symref = ""] = line.trim().split(" ");
|
|
47
|
-
if (name) refs.set(name, symref);
|
|
48
|
-
}
|
|
49
|
-
} catch {}
|
|
50
|
-
const remoteDefault = refs.get(remoteHead)?.replace(`refs/remotes/${remoteName}/`, "");
|
|
51
|
-
if (remoteDefault && remoteDefault !== CONTENTRAIN_BRANCH && await localBranchExists(git, remoteDefault, refs)) return remoteDefault;
|
|
52
|
-
if (refs.has("refs/heads/main")) return "main";
|
|
53
|
-
if (refs.has("refs/heads/master")) return "master";
|
|
54
|
-
const current = options.currentBranch ?? (await git.raw(["branch", "--show-current"]).catch(() => "")).trim();
|
|
55
|
-
if (current && current !== CONTENTRAIN_BRANCH) return current;
|
|
56
|
-
return "main";
|
|
57
|
-
}
|
|
58
|
-
async function localBranchExists(git, branch, known) {
|
|
59
|
-
if (known.has(`refs/heads/${branch}`)) return true;
|
|
60
|
-
try {
|
|
61
|
-
return (await git.raw([
|
|
62
|
-
"for-each-ref",
|
|
63
|
-
"--format=%(refname)",
|
|
64
|
-
`refs/heads/${branch}`
|
|
65
|
-
])).trim() !== "";
|
|
66
|
-
} catch {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
//#endregion
|
|
71
6
|
//#region src/git/branch-lifecycle.ts
|
|
72
7
|
/**
|
|
73
8
|
* Lists all local contentrain/* branches, deletes those already merged
|
|
@@ -541,6 +476,6 @@ async function contentBranchRelation(git, baseBranch) {
|
|
|
541
476
|
};
|
|
542
477
|
}
|
|
543
478
|
//#endregion
|
|
544
|
-
export { contentBranchRelation as a, listRemoteCrBranches as c,
|
|
479
|
+
export { contentBranchRelation as a, listRemoteCrBranches as c, cleanupMergedBranches as i, networkGit as l, checkBranchHealth as n, deleteRemoteBranch as o, classifyMergedBranches as r, isRefMerged as s, branchDiff as t, pruneMergedRemoteBranches as u };
|
|
545
480
|
|
|
546
|
-
//# sourceMappingURL=branch-lifecycle-
|
|
481
|
+
//# sourceMappingURL=branch-lifecycle-CcsSyfSw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-lifecycle-CcsSyfSw.mjs","names":[],"sources":["../src/git/branch-lifecycle.ts"],"sourcesContent":["import { type SimpleGit } from 'simple-git'\nimport { createGit } from '../git/identity.js'\nimport { CONTENTRAIN_BRANCH, type ContentrainConfig } from '@contentrain/types'\nimport { readConfig } from '../core/config.js'\nimport { NETWORK_UNSAFE } from './identity.js'\nimport { resolveBaseBranch } from './base-branch.js'\n\nexport interface CleanupResult {\n deleted: number\n remaining: number\n deletedBranches: string[]\n}\n\nexport interface BranchHealthCheck {\n total: number\n merged: number\n unmerged: number\n warning: boolean\n blocked: boolean\n message?: string\n}\n\n/**\n * Lists all local contentrain/* branches, deletes those already merged\n * into the base branch, and returns the count of remaining unmerged ones.\n */\nexport async function cleanupMergedBranches(projectRoot: string): Promise<CleanupResult> {\n const git = createGit(projectRoot)\n const config = await readConfig(projectRoot)\n\n // Determine base branch\n const baseBranch = await resolveBaseBranch(git, config)\n\n // Get all local branches (exclude the dedicated contentrain branch itself)\n const branchSummary = await git.branchLocal()\n const contentrainBranches = branchSummary.all\n .filter(b => b.startsWith('cr/'))\n .filter(b => b !== CONTENTRAIN_BRANCH)\n\n if (contentrainBranches.length === 0) {\n return { deleted: 0, remaining: 0, deletedBranches: [] }\n }\n\n // Check merged into the dedicated contentrain branch, falling back to baseBranch\n let mergedSet: Set<string>\n try {\n mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, CONTENTRAIN_BRANCH)\n } catch {\n // Contentrain branch may not exist yet (pre-init); fall back to baseBranch\n try {\n mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, baseBranch)\n } catch {\n // Base branch may not exist either; nothing is merged\n return { deleted: 0, remaining: contentrainBranches.length, deletedBranches: [] }\n }\n }\n\n const mergedContentrain = contentrainBranches.filter(b => mergedSet.has(b))\n const deletedBranches: string[] = []\n\n // Determine retention period (days). Default: 30\n const retentionDays = config?.branchRetention ?? 30\n const retentionMs = retentionDays * 24 * 60 * 60 * 1000\n const now = Date.now()\n\n // Delete merged branches only if older than retention period\n for (const branch of mergedContentrain) {\n try {\n const timestampRaw = (await git.raw(['log', '-1', '--format=%ct', branch])).trim()\n const commitTimestamp = Number(timestampRaw) * 1000\n if (now - commitTimestamp < retentionMs) {\n continue // Branch is within retention period — keep it\n }\n // -D, not -d: classifyMergedBranches already proved merged-ness (including\n // patch-id equivalence after a base-history rewrite, which -d's\n // ancestry-only check would refuse).\n await git.raw(['branch', '-D', branch])\n deletedBranches.push(branch)\n } catch {\n // Branch may be checked out, locked, or log failed — skip\n }\n }\n\n const remaining = contentrainBranches.length - deletedBranches.length\n return { deleted: deletedBranches.length, remaining, deletedBranches }\n}\n\n/**\n * Check branch health: count contentrain/* branches and return warning/blocked status.\n * - 50+ branches: warning\n * - 80+ branches: blocked\n */\nexport async function checkBranchHealth(projectRoot: string): Promise<BranchHealthCheck> {\n const git = createGit(projectRoot)\n const config = await readConfig(projectRoot)\n\n const baseBranch = await resolveBaseBranch(git, config)\n\n const branchSummary = await git.branchLocal()\n const contentrainBranches = branchSummary.all\n .filter(b => b.startsWith('cr/'))\n .filter(b => b !== CONTENTRAIN_BRANCH)\n const total = contentrainBranches.length\n\n const warnLimit = config?.branchWarnLimit ?? 50\n const blockLimit = config?.branchBlockLimit ?? 80\n\n // Count merged into contentrain branch, falling back to baseBranch.\n // This gate runs before EVERY write: the patch-id fallback (which rescues\n // rewrite-orphaned merged branches from being counted as unmerged) only\n // matters once the ancestry-unmerged count could trip the warning, so\n // below `warnLimit` the check costs exactly one `branch --merged`.\n let mergedCount = 0\n const classifyOpts = { fallbackThreshold: warnLimit }\n try {\n const mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, CONTENTRAIN_BRANCH, classifyOpts)\n mergedCount = mergedSet.size\n } catch {\n // Contentrain branch may not exist yet (pre-init); fall back to baseBranch\n try {\n const mergedSet = await classifyMergedBranches(projectRoot, contentrainBranches, baseBranch, classifyOpts)\n mergedCount = mergedSet.size\n } catch {\n // ignore — neither branch exists\n }\n }\n const unmerged = total - mergedCount\n const warning = unmerged >= warnLimit\n const blocked = unmerged >= blockLimit\n\n let message: string | undefined\n if (blocked) {\n message = `BLOCKED: ${unmerged} active contentrain branches (limit: ${blockLimit}). Run cleanup or merge/delete old branches before creating new ones.`\n } else if (warning) {\n message = `WARNING: ${unmerged} active contentrain branches. Consider merging or deleting old branches (warning at ${warnLimit}, blocked at ${blockLimit}).`\n }\n\n return { total, merged: mergedCount, unmerged, warning, blocked, message }\n}\n\nexport interface BranchDiffResult {\n /** The feature branch the diff was computed from. */\n branch: string\n /** The base ref the diff was computed against. Defaults to the `contentrain` branch. */\n base: string\n /** `git diff --stat` output — human-readable summary. */\n stat: string\n /** Raw unified diff. */\n patch: string\n /** Number of files touched in the diff. */\n filesChanged: number\n}\n\n/**\n * Compute the diff between a feature branch and its base.\n *\n * Defaults `base` to `CONTENTRAIN_BRANCH` — the singleton content-\n * tracking branch every feature branch forks from. Passing the repo's\n * default branch (e.g. `main`) is almost always a bug: when\n * `contentrain` is ahead of `main`, the diff picks up unrelated\n * historical content changes that the feature branch did not produce.\n *\n * Used by `contentrain serve` (branch detail view), the `contentrain\n * diff` CLI command, and any Studio-side driver that needs to preview\n * a feature branch before approving it.\n */\nexport async function branchDiff(\n projectRoot: string,\n opts: { branch: string, base?: string },\n): Promise<BranchDiffResult> {\n const git = createGit(projectRoot)\n const base = opts.base ?? CONTENTRAIN_BRANCH\n const range = `${base}...${opts.branch}`\n\n const [stat, patch, summary] = await Promise.all([\n git.diff([range, '--stat']),\n git.diff([range]),\n git.diffSummary([range]),\n ])\n\n return {\n branch: opts.branch,\n base,\n stat,\n patch,\n filesChanged: summary.changed,\n }\n}\n\n// ─── Merged-state classification (ancestry + patch-id fallback) ───\n\n/**\n * Merged-verdict caches.\n *\n * - `pairVerdictCache` — keyed by `(tipSha, intoTipSha, cap)`: the\n * relationship between two FIXED commits never changes, so entries are\n * permanently valid.\n * - `mergedTipCache` — keyed by `(tipSha, into NAME)`: once a tip is merged\n * into a branch, it stays merged as that branch advances (merged-ness is\n * monotonic), so positives survive `contentrain` moving forward. This is\n * what keeps the per-write branch-health gate cheap in long-lived\n * processes (MCP server, `contentrain serve`).\n */\nconst pairVerdictCache = new Map<string, boolean>()\nconst mergedTipCache = new Map<string, true>()\nconst MERGED_CACHE_LIMIT = 10_000\nconst DEFAULT_MAX_CHERRY_COMMITS = 200\n/** Concurrent `git cherry` subprocesses during classification. */\nconst CHERRY_CONCURRENCY = 8\n\nasync function isTipMerged(\n git: SimpleGit,\n tip: string,\n intoTip: string,\n maxCherryCommits: number,\n intoName?: string,\n): Promise<boolean> {\n const tipKey = intoName ? `${tip}→${intoName}` : undefined\n if (tipKey && mergedTipCache.has(tipKey)) return true\n const pairKey = `${tip}:${intoTip}:${maxCherryCommits}`\n const cached = pairVerdictCache.get(pairKey)\n if (cached !== undefined) return cached\n\n let merged = false\n try {\n // ONE subprocess answers both questions: `git cherry intoTip tip` lists\n // tip's commits missing from intoTip by ancestry — empty output means\n // ancestor (merged); otherwise a line without `+` is patch-id-equivalent\n // to a commit already in intoTip (survives base-history rewrites).\n // Deliberately NOT `merge-base --is-ancestor`: that plumbing signals via\n // exit code with EMPTY stderr, and simple-git reports exit-code-only\n // failures as success, silently inverting the check. Bounded by\n // maxCherryCommits so rewrite-orphaned deep histories cannot stall the\n // hot pre-write gate.\n const lines = (await git.raw(['cherry', intoTip, tip]))\n .split('\\n').map(line => line.trim()).filter(Boolean)\n if (lines.length === 0) {\n merged = true\n } else if (lines.length <= maxCherryCommits) {\n merged = !lines.some(line => line.startsWith('+'))\n }\n } catch {\n merged = false\n }\n\n if (pairVerdictCache.size >= MERGED_CACHE_LIMIT) pairVerdictCache.clear()\n pairVerdictCache.set(pairKey, merged)\n if (merged && tipKey) {\n if (mergedTipCache.size >= MERGED_CACHE_LIMIT) mergedTipCache.clear()\n mergedTipCache.set(tipKey, true)\n }\n return merged\n}\n\n/**\n * Robust merged check for a single ref: ancestry fast-path, then a bounded\n * `git cherry` (patch-id) fallback that survives base-history rewrites.\n * Returns false when either ref cannot be resolved.\n */\nexport async function isRefMerged(\n git: SimpleGit,\n ref: string,\n into: string,\n opts?: { maxCherryCommits?: number },\n): Promise<boolean> {\n let tip: string | undefined\n let intoTip: string | undefined\n try {\n const lines = (await git.raw(['rev-parse', ref, into])).trim().split('\\n')\n tip = lines[0]?.trim()\n intoTip = lines[1]?.trim()\n } catch {\n return false\n }\n if (!tip || !intoTip) return false\n return isTipMerged(git, tip, intoTip, opts?.maxCherryCommits ?? DEFAULT_MAX_CHERRY_COMMITS, into)\n}\n\n/**\n * Classify which of the given local branches are merged into `into`\n * (default: the contentrain branch). One `git branch --merged` call covers\n * the ancestry-merged majority; only the remainder pays the patch-id\n * fallback (bounded concurrency, verdicts cached).\n *\n * `opts.fallbackThreshold` skips the patch-id fallback entirely when fewer\n * than that many branches are ancestry-unmerged — the fallback can only\n * LOWER the unmerged count, so callers that merely compare the count\n * against a limit (the hot pre-write gate) pay nothing in the normal case.\n *\n * Throws when `into` does not resolve — callers use this to fall back to\n * the base branch (mirrors the previous `branch --merged` semantics).\n */\nexport async function classifyMergedBranches(\n projectRoot: string,\n branches: string[],\n into: string = CONTENTRAIN_BRANCH,\n opts?: { fallbackThreshold?: number },\n): Promise<Set<string>> {\n const git = createGit(projectRoot)\n if (branches.length === 0) {\n // Preserve the \"throws when into is missing\" contract even for empty input.\n await git.raw(['branch', '--merged', into])\n return new Set()\n }\n const mergedRaw = await git.raw(['branch', '--merged', into])\n const ancestryMerged = new Set(\n mergedRaw.split('\\n').map(b => b.replace(/^\\*?\\s+/, '').trim()).filter(Boolean),\n )\n\n const merged = new Set<string>()\n const rest: string[] = []\n for (const branch of branches) {\n if (ancestryMerged.has(branch)) merged.add(branch)\n else rest.push(branch)\n }\n if (rest.length === 0 || rest.length < (opts?.fallbackThreshold ?? 0)) return merged\n\n // Resolve all remaining tips + into in ONE subprocess, then run the\n // (mostly cached) patch-id fallback in bounded-concurrency chunks. Each\n // task gets its OWN simple-git instance — a shared instance serializes\n // its command queue, which turns 80 branches into 80 sequential spawns.\n let tips: string[]\n try {\n tips = (await git.raw(['rev-parse', into, ...rest])).trim().split('\\n').map(s => s.trim())\n } catch {\n return merged\n }\n const intoTip = tips[0]\n if (!intoTip) return merged\n for (let i = 0; i < rest.length; i += CHERRY_CONCURRENCY) {\n const chunk = rest.slice(i, i + CHERRY_CONCURRENCY)\n const verdicts = await Promise.all(chunk.map((branch, j) => {\n const tip = tips[i + j + 1]\n return tip\n ? isTipMerged(createGit(projectRoot), tip, intoTip, DEFAULT_MAX_CHERRY_COMMITS, into)\n : Promise.resolve(false)\n }))\n for (const [j, verdict] of verdicts.entries()) {\n if (verdict) merged.add(chunk[j]!)\n }\n }\n return merged\n}\n\n// ─── Remote cr/* branch lifecycle ───\n\nconst REMOTE_PUSH_TIMEOUT_MS = 10_000\nconst REMOTE_LIST_TIMEOUT_MS = 5_000\n\nfunction contentrainRemoteName(): string {\n return process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n}\n\n/**\n * simple-git instance hardened for network operations: `timeout.block` kills\n * the child after N ms without output (hung SSH passphrase prompts), and\n * `GIT_TERMINAL_PROMPT=0` refuses interactive HTTPS credential prompts.\n *\n * This is the one place that legitimately inherits the real environment — push\n * needs the host's askpass/SSH/proxy setup to authenticate. Because `.env()` is\n * therefore unavoidable, `unsafe` opts out of the block-unsafe guard categories\n * that inherited variables (EDITOR, GIT_ASKPASS, …) would otherwise trip; see\n * NETWORK_UNSAFE. Arg-injection protections stay intact.\n */\nexport function networkGit(projectRoot: string, timeoutMs: number): SimpleGit {\n return createGit(undefined, { baseDir: projectRoot, timeout: { block: timeoutMs }, unsafe: NETWORK_UNSAFE })\n .env({ ...process.env, GIT_TERMINAL_PROMPT: '0' })\n}\n\nasync function resolveRemote(git: SimpleGit): Promise<string | null> {\n try {\n const remotes = await git.getRemotes()\n const name = contentrainRemoteName()\n return remotes.some(r => r.name === name) ? name : null\n } catch {\n return null\n }\n}\n\nexport interface RemoteDeleteResult {\n deleted: boolean\n /** Why nothing was deleted, when that is expected (not a failure). */\n skipped?: 'disabled' | 'no-remote' | 'not-found' | 'protected'\n /** A real failure (offline, auth, protected ref) — surfaced, never thrown. */\n warning?: string\n}\n\n/**\n * Best-effort delete of a cr/* branch on the configured remote. Never\n * throws: expected conditions land in `skipped`, real failures in\n * `warning`. Gated by `config.remoteBranchCleanup` (default: on).\n *\n * Pass `opts.config` when the caller already read it (avoids a re-read);\n * `null` means \"no config\" and applies the default gate.\n */\nexport async function deleteRemoteBranch(\n projectRoot: string,\n branch: string,\n opts?: { config?: ContentrainConfig | null, timeoutMs?: number },\n): Promise<RemoteDeleteResult> {\n if (!branch.startsWith('cr/') || branch === CONTENTRAIN_BRANCH) {\n return { deleted: false, skipped: 'protected' }\n }\n const config = opts?.config === undefined ? await readConfig(projectRoot) : opts.config\n if (!(config?.remoteBranchCleanup ?? true)) {\n return { deleted: false, skipped: 'disabled' }\n }\n const git = networkGit(projectRoot, opts?.timeoutMs ?? REMOTE_PUSH_TIMEOUT_MS)\n const remote = await resolveRemote(git)\n if (!remote) return { deleted: false, skipped: 'no-remote' }\n try {\n await git.push([remote, '--delete', branch])\n return { deleted: true }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/remote ref does not exist|couldn't find remote ref/i.test(message)) {\n return { deleted: false, skipped: 'not-found' }\n }\n return { deleted: false, warning: `Could not delete \"${branch}\" on ${remote}: ${message}` }\n }\n}\n\nexport interface RemoteBranchList {\n remote: string\n branches: { name: string, sha: string }[]\n /** ls-remote failed (offline/timeout) — branches is empty, not authoritative. */\n error?: string\n}\n\n/**\n * Authoritative list of cr/* branches on the configured remote via\n * `ls-remote --heads` (no fetch, no stale remote-tracking refs). Returns\n * null when no remote is configured. Never throws.\n */\nexport async function listRemoteCrBranches(\n projectRoot: string,\n opts?: { timeoutMs?: number },\n): Promise<RemoteBranchList | null> {\n const git = networkGit(projectRoot, opts?.timeoutMs ?? REMOTE_LIST_TIMEOUT_MS)\n const remote = await resolveRemote(git)\n if (!remote) return null\n try {\n const raw = await git.raw(['ls-remote', '--heads', remote, 'refs/heads/cr/*'])\n const branches = raw.split('\\n')\n .map(line => line.trim())\n .filter(Boolean)\n .map((line) => {\n const [sha, ref] = line.split('\\t')\n return { sha: sha?.trim() ?? '', name: ref?.trim().replace(/^refs\\/heads\\//, '') ?? '' }\n })\n .filter(b => b.name.startsWith('cr/') && b.name !== CONTENTRAIN_BRANCH)\n return { remote, branches }\n } catch (error) {\n return { remote, branches: [], error: error instanceof Error ? error.message : String(error) }\n }\n}\n\nexport interface RemotePruneResult {\n /** Branches removed from the remote (in dryRun mode: the candidates). */\n deleted: string[]\n kept: string[]\n errors: string[]\n skipped?: 'disabled' | 'no-remote' | 'offline'\n}\n\nconst PRUNE_PUSH_CHUNK = 50\n\n/**\n * Delete already-merged cr/* branches on the remote in batches. Merged-state\n * uses the same ancestry + patch-id classification as the local cleanup, so\n * branches leaked before a base-history rewrite are still recognised.\n * Ignores `branchRetention` — a merged remote copy only produces phantom\n * reviews. Never throws; gated by `config.remoteBranchCleanup`.\n */\nexport async function pruneMergedRemoteBranches(\n projectRoot: string,\n opts?: { config?: ContentrainConfig | null, max?: number, dryRun?: boolean, timeoutMs?: number },\n): Promise<RemotePruneResult> {\n const config = opts?.config === undefined ? await readConfig(projectRoot) : opts.config\n if (!(config?.remoteBranchCleanup ?? true)) {\n return { deleted: [], kept: [], errors: [], skipped: 'disabled' }\n }\n\n const listed = await listRemoteCrBranches(projectRoot, { timeoutMs: opts?.timeoutMs })\n if (!listed) return { deleted: [], kept: [], errors: [], skipped: 'no-remote' }\n if (listed.error) return { deleted: [], kept: [], errors: [listed.error], skipped: 'offline' }\n if (listed.branches.length === 0) return { deleted: [], kept: [], errors: [] }\n\n const git = networkGit(projectRoot, opts?.timeoutMs ?? REMOTE_PUSH_TIMEOUT_MS)\n\n // Remote tips may predate this clone (leaked long ago, and our fetches are\n // single-refspec) — one scoped fetch backfills any missing objects.\n // `cat-file -t` (not `-e`): -e signals via exit code with empty stderr,\n // which simple-git reports as success.\n const presence = await Promise.all(listed.branches.map(b =>\n git.raw(['cat-file', '-t', b.sha]).then(() => true).catch(() => false),\n ))\n if (presence.some(present => !present)) {\n try {\n await git.fetch(listed.remote, `+refs/heads/cr/*:refs/remotes/${listed.remote}/cr/*`)\n } catch {\n // Best-effort: branches whose objects stay unresolvable are kept below.\n }\n }\n\n const kept: string[] = []\n const candidates: string[] = []\n const verdicts = await Promise.all(listed.branches.map(b => isRefMerged(git, b.sha, CONTENTRAIN_BRANCH)))\n for (const [i, merged] of verdicts.entries()) {\n const name = listed.branches[i]!.name\n if (merged) candidates.push(name)\n else kept.push(name)\n }\n\n const limit = opts?.max ?? Number.POSITIVE_INFINITY\n const toDelete = candidates.slice(0, limit)\n kept.push(...candidates.slice(toDelete.length))\n\n if (opts?.dryRun) return { deleted: toDelete, kept, errors: [] }\n\n const deleted: string[] = []\n const errors: string[] = []\n for (let i = 0; i < toDelete.length; i += PRUNE_PUSH_CHUNK) {\n const chunk = toDelete.slice(i, i + PRUNE_PUSH_CHUNK)\n try {\n await git.push([listed.remote, '--delete', ...chunk])\n deleted.push(...chunk)\n } catch {\n // One missing ref fails the whole multi-refspec push — retry per branch.\n for (const branch of chunk) {\n try {\n await git.push([listed.remote, '--delete', branch])\n deleted.push(branch)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/remote ref does not exist|couldn't find remote ref/i.test(message)) {\n deleted.push(branch) // already gone — net effect is pruned\n } else {\n errors.push(`${branch}: ${message}`)\n }\n }\n }\n }\n }\n return { deleted, kept, errors }\n}\n\nexport interface ContentBranchRelation {\n /** Commits on the content branch that the base branch lacks. */\n ahead: number\n /** Commits on the base branch that the content branch lacks. */\n behind: number\n relation: 'in_sync' | 'content_ahead' | 'base_ahead' | 'diverged'\n}\n\n/**\n * How the content-tracking branch relates to the base branch, counted in\n * BOTH directions. One-directional \"ahead\" counting reported a diverged base\n * as \"in sync\" — the state that blocks every auto-merge advance is exactly\n * the one it could not see. Shared by CLI `status` and `contentrain_status`.\n *\n * `base_ahead` self-heals on the next write (the pre-fork sync fast-forwards\n * contentrain); `diverged` blocks the base advance until the branches are\n * reconciled.\n */\nexport async function contentBranchRelation(\n git: SimpleGit,\n baseBranch: string,\n): Promise<ContentBranchRelation> {\n const [aheadRaw, behindRaw] = await Promise.all([\n git.raw(['rev-list', '--count', `${baseBranch}..${CONTENTRAIN_BRANCH}`]),\n git.raw(['rev-list', '--count', `${CONTENTRAIN_BRANCH}..${baseBranch}`]),\n ])\n const ahead = Number.parseInt(aheadRaw.trim(), 10) || 0\n const behind = Number.parseInt(behindRaw.trim(), 10) || 0\n const relation = ahead > 0 && behind > 0\n ? 'diverged' as const\n : ahead > 0\n ? 'content_ahead' as const\n : behind > 0\n ? 'base_ahead' as const\n : 'in_sync' as const\n return { ahead, behind, relation }\n}\n"],"mappings":"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"}
|
|
@@ -168,4 +168,4 @@ interface ContentBranchRelation {
|
|
|
168
168
|
declare function contentBranchRelation(git: SimpleGit, baseBranch: string): Promise<ContentBranchRelation>;
|
|
169
169
|
//#endregion
|
|
170
170
|
export { pruneMergedRemoteBranches as _, RemoteBranchList as a, branchDiff as c, cleanupMergedBranches as d, contentBranchRelation as f, networkGit as g, listRemoteCrBranches as h, ContentBranchRelation as i, checkBranchHealth as l, isRefMerged as m, BranchHealthCheck as n, RemoteDeleteResult as o, deleteRemoteBranch as p, CleanupResult as r, RemotePruneResult as s, BranchDiffResult as t, classifyMergedBranches as u };
|
|
171
|
-
//# sourceMappingURL=branch-lifecycle-
|
|
171
|
+
//# sourceMappingURL=branch-lifecycle-CirYfTXu.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-lifecycle-
|
|
1
|
+
{"version":3,"file":"branch-lifecycle-CirYfTXu.d.mts","names":[],"sources":["../src/git/branch-lifecycle.ts"],"mappings":";;;;UAOiB,aAAA;EACf,OAAA;EACA,SAAA;EACA,eAAA;AAAA;AAAA,UAGe,iBAAA;EACf,KAAA;EACA,MAAA;EACA,QAAA;EACA,OAAA;EACA,OAAA;EACA,OAAA;AAAA;;;;;iBAOoB,qBAAA,CAAsB,WAAA,WAAsB,OAAA,CAAQ,aAAA;;;;;;iBAkEpD,iBAAA,CAAkB,WAAA,WAAsB,OAAA,CAAQ,iBAAA;AAAA,UAgDrD,gBAAA;;EAEf,MAAA;EApH0C;EAsH1C,IAAA;EAtHwE;EAwHxE,IAAA;EAxHqF;EA0HrF,KAAA;EAxDqC;EA0DrC,YAAA;AAAA;;;;;;AAVF;;;;;;;;iBA0BsB,UAAA,CACpB,WAAA,UACA,IAAA;EAAQ,MAAA;EAAgB,IAAA;AAAA,IACvB,OAAA,CAAQ,gBAAA;;;;;;iBA0FW,WAAA,CACpB,GAAA,EAAK,SAAA,EACL,GAAA,UACA,IAAA,UACA,IAAA;EAAS,gBAAA;AAAA,IACR,OAAA;;;;AALH;;;;;;;;;;;iBAiCsB,sBAAA,CACpB,WAAA,UACA,QAAA,YACA,IAAA,WACA,IAAA;EAAS,iBAAA;AAAA,IACR,OAAA,CAAQ,GAAA;;;;;;;;;;;;iBAmEK,UAAA,CAAW,WAAA,UAAqB,SAAA,WAAoB,SAAA;AAAA,UAenD,kBAAA;EACf,OAAA;;EAEA,OAAA;EAlByB;EAoBzB,OAAA;AAAA;;;AALF;;;;;;iBAgBsB,kBAAA,CACpB,WAAA,UACA,MAAA,UACA,IAAA;EAAS,MAAA,GAAS,iBAAA;EAA0B,SAAA;AAAA,IAC3C,OAAA,CAAQ,kBAAA;AAAA,UAuBM,gBAAA;EACf,MAAA;EACA,QAAA;IAAY,IAAA;IAAc,GAAA;EAAA;EAzBlB;EA2BR,KAAA;AAAA;;;;;;iBAQoB,oBAAA,CACpB,WAAA,UACA,IAAA;EAAS,SAAA;AAAA,IACR,OAAA,CAAQ,gBAAA;AAAA,UAoBM,iBAAA;EAnCgB;EAqC/B,OAAA;EACA,IAAA;EACA,MAAA;EACA,OAAA;AAAA;;;;;AA5BF;;;iBAwCsB,yBAAA,CACpB,WAAA,UACA,IAAA;EAAS,MAAA,GAAS,iBAAA;EAA0B,GAAA;EAAc,MAAA;EAAkB,SAAA;AAAA,IAC3E,OAAA,CAAQ,iBAAA;AAAA,UAsEM,qBAAA;EA9GU;EAgHzB,KAAA;EA5FgC;EA8FhC,MAAA;EACA,QAAA;AAAA;;;;;;AA9EF;;;;;iBA2FsB,qBAAA,CACpB,GAAA,EAAK,SAAA,EACL,UAAA,WACC,OAAA,CAAQ,qBAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as __exportAll, t as __commonJSMin } from "./chunk-BEJ448es.mjs";
|
|
2
|
-
import { t as require_source_map_generator } from "./source-map-generator-
|
|
2
|
+
import { t as require_source_map_generator } from "./source-map-generator-DVDSWUZ1.mjs";
|
|
3
3
|
import { createRequire } from "module";
|
|
4
4
|
//#region ../../node_modules/.pnpm/svelte@4.2.20/node_modules/svelte/src/compiler/Stats.js
|
|
5
5
|
const now = () => performance.now();
|
|
@@ -44506,4 +44506,4 @@ async function preprocess(source, preprocessor, options) {
|
|
|
44506
44506
|
//#endregion
|
|
44507
44507
|
export { VERSION, compile, parse, preprocess, walk };
|
|
44508
44508
|
|
|
44509
|
-
//# sourceMappingURL=compiler-
|
|
44509
|
+
//# sourceMappingURL=compiler-CKelkozt.mjs.map
|