@bridge_gpt/mcp-server 0.2.54 → 0.2.55
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 +38 -54
- package/build/agent-guidance.generated.js +3 -0
- package/build/agent-launchers/claude-executor-adapter.js +3 -0
- package/build/agent-notes.js +178 -0
- package/build/agent-registry.js +5 -2
- package/build/agent-utils.js +58 -0
- package/build/agents.generated.js +1 -1
- package/build/codex-skill-adapter.js +55 -0
- package/build/commands.generated.js +5 -4
- package/build/conductor/bridge-api-client.js +199 -6
- package/build/conductor/bring-up-facts.js +187 -0
- package/build/conductor/claude-hook.js +7 -5
- package/build/conductor/cli.js +28 -0
- package/build/conductor/doctor.js +80 -9
- package/build/conductor/epic-implementer-cli.js +1298 -0
- package/build/conductor/epic-runtime.js +1 -1
- package/build/conductor/errors.js +2 -2
- package/build/conductor/git-ci-types.js +1 -1
- package/build/conductor/git-hooks.js +28 -14
- package/build/conductor/install-doctor.js +11 -5
- package/build/conductor/readiness-cli.js +10 -10
- package/build/conductor/readiness-sections.js +58 -9
- package/build/conductor/readiness.js +37 -6
- package/build/conductor/recovery-cli.js +289 -10
- package/build/conductor/recovery-operations.js +125 -2
- package/build/conductor/repair-contract.js +58 -0
- package/build/conductor/store.js +2 -2
- package/build/conductor/supervisor-runtime.js +1 -1
- package/build/conductor-bin.js +2 -139
- package/build/conductor-claude-hook-bin.js +2 -2
- package/build/conductor-claude-hook-removed-stub-bin.js +31 -0
- package/build/conductor-removed-stub-bin.js +30 -0
- package/build/docs.generated.js +1 -1
- package/build/doctor.js +77 -17
- package/build/drive-epic.js +541 -115
- package/build/epic-implementer-bin.js +145 -0
- package/build/epic-implementer-bundle-cli.js +264 -0
- package/build/epic-implementer-claude-hook-bin.js +3 -0
- package/build/epic-integration-pr.js +5 -3
- package/build/executor/env.js +6 -0
- package/build/executor/interrupted-worktree.js +60 -0
- package/build/executor/job-errors.js +45 -0
- package/build/executor/job-runner.js +274 -9
- package/build/executor/job-types.js +25 -9
- package/build/executor/merge-tree-classifier.js +171 -0
- package/build/executor/reconcile-overlap-governance.js +129 -0
- package/build/executor/reconcile-overlap-job.js +989 -0
- package/build/executor/reconcile-overlap-types.js +14 -0
- package/build/executor/spawn-job-driver.js +1 -0
- package/build/executor/types.js +2 -0
- package/build/executor/worker-finalization.js +25 -2
- package/build/executor/worker-guard-hook.js +15 -7
- package/build/implement-epic/bridge-client.js +773 -0
- package/build/implement-epic/checkpoint-store.js +542 -0
- package/build/implement-epic/cli.js +3158 -0
- package/build/implement-epic/cut-protocol.js +392 -0
- package/build/implement-epic/lock.js +302 -0
- package/build/implement-epic/pr-state.js +286 -0
- package/build/implement-epic/spawn.js +113 -0
- package/build/index.js +586 -138
- package/build/init.js +72 -8
- package/build/install-bridge-conductor.js +5 -5
- package/build/install-bridge.js +403 -70
- package/build/mcp-host-config.js +22 -60
- package/build/mcp-host-entry-adapter.js +18 -0
- package/build/mcp-host-targets.js +1 -21
- package/build/merge-pull-request.js +1 -1
- package/build/pipelines.generated.js +7 -7
- package/build/plan-epic-conductor-eligibility.js +1 -1
- package/build/plane/cli.js +36 -5
- package/build/plane/preflight.js +128 -12
- package/build/plane/shutdown.js +4 -4
- package/build/readiness-check.js +3 -3
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +1 -1
- package/build/setup-epic.js +69 -31
- package/build/start-tickets-conductor.js +8 -7
- package/build/version.generated.js +3 -3
- package/build/worker-guard-hook-bin.js +1 -1
- package/docs/CONDUCTOR.md +8 -6
- package/package.json +5 -3
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance-region comparison for `reconcile_overlap` (BAPI-1152).
|
|
3
|
+
*
|
|
4
|
+
* Before the wrapper pushes, the candidate tree is compared against the
|
|
5
|
+
* governance-guarded regions the SERVER found in the INPUT epic head
|
|
6
|
+
* (`required_epic_sha`). A candidate that changes any protected region — its
|
|
7
|
+
* content OR either marker line — is never pushed: the job reports the
|
|
8
|
+
* `governance_guard_change_required` fact and the reconciler parks naming the tag.
|
|
9
|
+
*
|
|
10
|
+
* This module deliberately does NOT parse the marker grammar. The server owns it
|
|
11
|
+
* (`api/library/epic_conductor/governance_markers.py`) and ships each region as
|
|
12
|
+
* its exact BEGIN and END marker lines plus the SHA-256 of the region's content,
|
|
13
|
+
* markers included. Here a region is located by those exact lines and compared by
|
|
14
|
+
* hash — never by line number, which an edit above the region would shift without
|
|
15
|
+
* touching anything it protects. A second grammar implementation would be a
|
|
16
|
+
* second place for the two to disagree about what is protected.
|
|
17
|
+
*
|
|
18
|
+
* Fail-closed everywhere the comparison cannot be proven:
|
|
19
|
+
* - the region's file is gone from the candidate (deleted or renamed), binary,
|
|
20
|
+
* or unreadable;
|
|
21
|
+
* - a marker line is missing, duplicated, or out of order;
|
|
22
|
+
* - the server reported the file's marker structure unprovable (unbalanced,
|
|
23
|
+
* nested, mismatched, or over the scan bound) and the candidate changes it;
|
|
24
|
+
* - the candidate changes a file the server did NOT scan and either side of that
|
|
25
|
+
* change contains a marker token — the server made no statement about it.
|
|
26
|
+
* The last two carry the synthetic tag {@link UNPROVABLE_GOVERNANCE_TAG}, which is
|
|
27
|
+
* itself valid marker grammar, so the park still names something greppable.
|
|
28
|
+
*/
|
|
29
|
+
import { createHash } from "node:crypto";
|
|
30
|
+
/** The synthetic tag for protection the comparison could not prove. */
|
|
31
|
+
export const UNPROVABLE_GOVERNANCE_TAG = "GOVERNANCE_MARKERS_UNPROVABLE";
|
|
32
|
+
/** The token every governance marker line contains. */
|
|
33
|
+
export const GOVERNANCE_MARKER_TOKEN = "BRIDGE-GOVERNANCE-GUARD-";
|
|
34
|
+
/** Bounds on the reported fact (mirrors the server's result-model bounds). */
|
|
35
|
+
export const GOVERNANCE_FACT_MAX_FINDINGS = 8;
|
|
36
|
+
export const GOVERNANCE_FACT_MAX_PATHS = 8;
|
|
37
|
+
/**
|
|
38
|
+
* Split text into lines exactly the way Python's `str.splitlines()` does, so the
|
|
39
|
+
* region hash computed here matches the one the server computed.
|
|
40
|
+
*/
|
|
41
|
+
export function splitLinesLikePython(text) {
|
|
42
|
+
const lines = text.split(/\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]/);
|
|
43
|
+
// `splitlines()` yields no trailing empty entry for a terminal line break.
|
|
44
|
+
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
45
|
+
lines.pop();
|
|
46
|
+
return lines;
|
|
47
|
+
}
|
|
48
|
+
function sha256(text) {
|
|
49
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
50
|
+
}
|
|
51
|
+
function indicesOf(lines, needle) {
|
|
52
|
+
const out = [];
|
|
53
|
+
lines.forEach((line, index) => {
|
|
54
|
+
if (line === needle)
|
|
55
|
+
out.push(index);
|
|
56
|
+
});
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/** True when a region is intact in `text`: both markers once, in order, same hash. */
|
|
60
|
+
function regionIntact(text, region) {
|
|
61
|
+
const lines = splitLinesLikePython(text);
|
|
62
|
+
const begins = indicesOf(lines, region.beginMarker);
|
|
63
|
+
const ends = indicesOf(lines, region.endMarker);
|
|
64
|
+
if (begins.length !== 1 || ends.length !== 1)
|
|
65
|
+
return false;
|
|
66
|
+
const [begin] = begins;
|
|
67
|
+
const [end] = ends;
|
|
68
|
+
if (end < begin)
|
|
69
|
+
return false;
|
|
70
|
+
return sha256(lines.slice(begin, end + 1).join("\n")) === region.contentSha256;
|
|
71
|
+
}
|
|
72
|
+
function containsMarkerToken(read) {
|
|
73
|
+
// An unreadable or binary side cannot be proven marker-free.
|
|
74
|
+
if (read.kind === "unreadable" || read.kind === "binary")
|
|
75
|
+
return true;
|
|
76
|
+
return read.kind === "text" && read.text.includes(GOVERNANCE_MARKER_TOKEN);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Compare the candidate against the input epic head's governance regions.
|
|
80
|
+
* Returns the structured fact, or `null` when nothing protected is touched.
|
|
81
|
+
*/
|
|
82
|
+
export async function scanGovernanceRegions(input) {
|
|
83
|
+
const hits = new Map();
|
|
84
|
+
const hit = (tag, path) => {
|
|
85
|
+
let paths = hits.get(tag);
|
|
86
|
+
if (!paths) {
|
|
87
|
+
paths = new Set();
|
|
88
|
+
hits.set(tag, paths);
|
|
89
|
+
}
|
|
90
|
+
paths.add(path);
|
|
91
|
+
};
|
|
92
|
+
const changed = new Set(input.changedPaths);
|
|
93
|
+
for (const region of input.snapshot.regions) {
|
|
94
|
+
const candidate = await input.readCandidate(region.path);
|
|
95
|
+
if (candidate.kind !== "text" || !regionIntact(candidate.text, region)) {
|
|
96
|
+
hit(region.tag, region.path);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const finding of input.snapshot.findings) {
|
|
100
|
+
if (changed.has(finding.path))
|
|
101
|
+
hit(UNPROVABLE_GOVERNANCE_TAG, finding.path);
|
|
102
|
+
}
|
|
103
|
+
const scanned = new Set(input.snapshot.scannedPaths);
|
|
104
|
+
for (const path of input.changedPaths) {
|
|
105
|
+
if (scanned.has(path))
|
|
106
|
+
continue;
|
|
107
|
+
const [candidate, original] = await Promise.all([
|
|
108
|
+
input.readCandidate(path),
|
|
109
|
+
input.readInput(path),
|
|
110
|
+
]);
|
|
111
|
+
if (containsMarkerToken(candidate) || containsMarkerToken(original)) {
|
|
112
|
+
hit(UNPROVABLE_GOVERNANCE_TAG, path);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (hits.size === 0)
|
|
116
|
+
return null;
|
|
117
|
+
const findings = Array.from(hits.entries())
|
|
118
|
+
.map(([tag, paths]) => ({ tag, paths: Array.from(paths).sort().slice(0, GOVERNANCE_FACT_MAX_PATHS) }))
|
|
119
|
+
.sort((a, b) => a.tag !== b.tag ? (a.tag < b.tag ? -1 : 1) : (a.paths[0] ?? "") < (b.paths[0] ?? "") ? -1 : 1);
|
|
120
|
+
const bounded = findings.slice(0, GOVERNANCE_FACT_MAX_FINDINGS);
|
|
121
|
+
const primary = bounded[0];
|
|
122
|
+
return {
|
|
123
|
+
tag: primary.tag,
|
|
124
|
+
paths: primary.paths,
|
|
125
|
+
findings: bounded,
|
|
126
|
+
truncated: findings.length > GOVERNANCE_FACT_MAX_FINDINGS ||
|
|
127
|
+
Array.from(hits.values()).some((paths) => paths.size > GOVERNANCE_FACT_MAX_PATHS),
|
|
128
|
+
};
|
|
129
|
+
}
|