@bridge_gpt/mcp-server 0.2.34 → 0.2.36
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 +456 -370
- package/build/agent-capabilities/probe-context.js +8 -1
- package/build/agent-capabilities/probes.js +7 -1
- package/build/agents.generated.js +1 -1
- package/build/claude-review-workflow.js +264 -0
- package/build/cli-release.js +53 -0
- package/build/commands.generated.js +4 -4
- package/build/conductor/bridge-api-client.js +215 -0
- package/build/conductor/deny-enforcement-preflight.js +1 -0
- package/build/conductor/done-gate.js +44 -5
- package/build/conductor/epic-reconcile.js +6 -0
- package/build/conductor/install-doctor.js +462 -0
- package/build/conductor-bin.js +3 -3
- package/build/conductor-bundle-artifacts.js +30 -9
- package/build/doctor.js +234 -1
- package/build/executor/cli.js +32 -5
- package/build/executor/credentials.js +45 -11
- package/build/executor/deps.js +14 -0
- package/build/executor/env.js +23 -6
- package/build/executor/index.js +4 -0
- package/build/executor/job-runner.js +119 -9
- package/build/executor/permissions.js +12 -2
- package/build/executor/preflight.js +95 -8
- package/build/executor/prompt-spec.js +51 -0
- package/build/executor/runner.js +15 -2
- package/build/executor/service-unit.js +876 -0
- package/build/executor/test-clock.js +8 -0
- package/build/executor/types.js +0 -17
- package/build/executor/worker-command.js +62 -9
- package/build/index.js +575 -143
- package/build/init.js +153 -51
- package/build/install-bridge-conductor.js +491 -0
- package/build/install-bridge.js +628 -175
- package/build/install-reexec.js +233 -0
- package/build/mcp-host-config.js +11 -1
- package/build/mcp-install-state.js +32 -0
- package/build/mcp-provisioning.js +22 -6
- package/build/pipelines.generated.js +14 -8
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +257 -0
- package/build/setup-epic.js +117 -8
- package/build/upgrade-cli.js +1 -15
- package/build/version.generated.js +1 -1
- package/docs/CONDUCTOR.md +115 -4
- package/docs/install/mcp-tool-integrations.md +29 -21
- package/package.json +8 -5
- package/pipelines/implement-ticket.json +6 -1
- package/build/conductor/supervisor-judgment-python.js +0 -141
- package/build/conductor/supervisor-judgment.js +0 -215
package/build/init.js
CHANGED
|
@@ -40,9 +40,16 @@ export function buildBridgeApiEntry(cwd) {
|
|
|
40
40
|
// `--offline`, it falls back automatically). It cannot resurrect a stale build
|
|
41
41
|
// — the BAPI-429 hazard above was specific to UNPINNED specs; here the exact
|
|
42
42
|
// `@${VERSION}` is immutable, so a cache hit is always the right build.
|
|
43
|
+
// BAPI-714 (Group C): NEWLY generated registrations name the server subcommand
|
|
44
|
+
// explicitly. `serve` is a no-op relative to bare invocation — it consumes its
|
|
45
|
+
// own token and falls through to the same startup path — but it makes a
|
|
46
|
+
// generated config self-describing, so a reader can tell a server launcher from
|
|
47
|
+
// an installer invocation. Bare invocation remains a permanent server-start
|
|
48
|
+
// contract; see `refreshBridgeApiPackageSpec` for why EXISTING configs are never
|
|
49
|
+
// migrated to this shape.
|
|
43
50
|
return {
|
|
44
51
|
command: "npx",
|
|
45
|
-
args: ["-y", "--prefer-offline", `@bridge_gpt/mcp-server@${VERSION}
|
|
52
|
+
args: ["-y", "--prefer-offline", `@bridge_gpt/mcp-server@${VERSION}`, "serve"],
|
|
46
53
|
env: {
|
|
47
54
|
BAPI_BASE_URL: "https://bridgegpt-api.com",
|
|
48
55
|
BAPI_REPO_NAME: "YOUR_REPO_NAME",
|
|
@@ -51,6 +58,44 @@ export function buildBridgeApiEntry(cwd) {
|
|
|
51
58
|
},
|
|
52
59
|
};
|
|
53
60
|
}
|
|
61
|
+
/** The package-name prefix every Bridge launcher spec starts with. */
|
|
62
|
+
const BRIDGE_PACKAGE_PREFIX = "@bridge_gpt/mcp-server";
|
|
63
|
+
/**
|
|
64
|
+
* Refresh ONLY the package-spec token of an existing launcher args array
|
|
65
|
+
* (BAPI-714, Group C).
|
|
66
|
+
*
|
|
67
|
+
* Adding `serve` to {@link buildBridgeApiEntry} changes the bytes of every FRESH
|
|
68
|
+
* registration, and the previous update path assigned the whole template array
|
|
69
|
+
* over an existing entry — which would have silently migrated every legacy bare
|
|
70
|
+
* launcher on the next `--init`, `install`, or `upgrade`. That is explicitly out
|
|
71
|
+
* of scope: legacy bare launchers can never be declared fully migrated, so bare
|
|
72
|
+
* invocation must keep meaning "server" permanently and no config-rewrite pass may
|
|
73
|
+
* add `serve`.
|
|
74
|
+
*
|
|
75
|
+
* So this helper replaces the single element beginning with `@bridge_gpt/mcp-server`
|
|
76
|
+
* with `spec` and preserves EVERY other token and its position — including whether
|
|
77
|
+
* `serve` is present. An args array with no package-spec token is returned
|
|
78
|
+
* unchanged (a custom launcher shape this helper must not invent tokens for).
|
|
79
|
+
*
|
|
80
|
+
* Pure: it returns a new array and never mutates its input.
|
|
81
|
+
*/
|
|
82
|
+
export function refreshBridgeApiPackageSpec(args, spec) {
|
|
83
|
+
if (!Array.isArray(args))
|
|
84
|
+
return null;
|
|
85
|
+
const tokens = args.filter((a) => typeof a === "string");
|
|
86
|
+
if (tokens.length !== args.length)
|
|
87
|
+
return null;
|
|
88
|
+
const index = tokens.findIndex((token) => token.startsWith(BRIDGE_PACKAGE_PREFIX));
|
|
89
|
+
if (index === -1)
|
|
90
|
+
return null;
|
|
91
|
+
const next = [...tokens];
|
|
92
|
+
next[index] = spec;
|
|
93
|
+
return next;
|
|
94
|
+
}
|
|
95
|
+
/** The exact package spec a launcher generated by THIS build should carry. */
|
|
96
|
+
export function currentBridgePackageSpec() {
|
|
97
|
+
return `${BRIDGE_PACKAGE_PREFIX}@${VERSION}`;
|
|
98
|
+
}
|
|
54
99
|
/**
|
|
55
100
|
* Choose the `repo_name` written into a scaffolded `.bridge/config`. Uses the
|
|
56
101
|
* cwd basename when it passes manifest repo-name validation; otherwise falls
|
|
@@ -86,6 +131,89 @@ export function buildBridgeConfigManifest(repoName) {
|
|
|
86
131
|
"",
|
|
87
132
|
].join("\n");
|
|
88
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Detect the editors present in `cwd`. Extracted from `runInit`'s Phase 1 so the
|
|
136
|
+
* asset manifest and the scaffolder cannot disagree about which conditional
|
|
137
|
+
* targets (`.cursor/commands/`, `.github/agents/`) this run actually writes.
|
|
138
|
+
*/
|
|
139
|
+
export async function detectInitIdes(options) {
|
|
140
|
+
const { cwd } = options;
|
|
141
|
+
const env = options.env ?? process.env;
|
|
142
|
+
const probe = options.stat ?? stat;
|
|
143
|
+
const exists = async (p) => {
|
|
144
|
+
try {
|
|
145
|
+
await probe(p);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const detection = {
|
|
153
|
+
claude: true,
|
|
154
|
+
vscode: await exists(path.join(cwd, ".vscode")),
|
|
155
|
+
cursor: await exists(path.join(cwd, ".cursor")),
|
|
156
|
+
windsurf: await exists(path.join(cwd, ".windsurf")),
|
|
157
|
+
};
|
|
158
|
+
if (!detection.cursor && env.CURSOR_TRACE_DIR)
|
|
159
|
+
detection.cursor = true;
|
|
160
|
+
if (!detection.windsurf)
|
|
161
|
+
detection.windsurf = await exists(path.join(cwd, ".windsurfrules"));
|
|
162
|
+
return detection;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Resolve the pipelines directory exactly the way `runInit`'s Phase 6 does, so a
|
|
166
|
+
* `BAPI_PIPELINES_DIR` override moves the scaffold AND the manifest together.
|
|
167
|
+
*/
|
|
168
|
+
export function resolveInitPipelinesDir(cwd, env = process.env) {
|
|
169
|
+
return path.resolve(cwd, env.BAPI_PIPELINES_DIR ?? ".bridge/pipelines");
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Render an absolute scaffold target as the path we print. Targets inside the
|
|
173
|
+
* project stay project-relative (in POSIX form, so the notice reads the same on
|
|
174
|
+
* every platform); anything resolved outside it keeps its absolute path rather
|
|
175
|
+
* than growing a `../../` prefix nobody can act on.
|
|
176
|
+
*/
|
|
177
|
+
function displayScaffoldPath(cwd, target, isDirectory) {
|
|
178
|
+
const relative = path.relative(cwd, target);
|
|
179
|
+
const inProject = relative.length > 0 && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
180
|
+
const base = inProject ? relative.split(path.sep).join("/") : target;
|
|
181
|
+
return isDirectory ? `${base.replace(/\/+$/, "")}/` : base;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The single authority on which files a `runInit` invocation scaffolds that are
|
|
185
|
+
* SAFE to recommend for commit (BAPI-708, Part C).
|
|
186
|
+
*
|
|
187
|
+
* Derived from the run's own resolved targets — the detected IDEs and the
|
|
188
|
+
* resolved `BAPI_PIPELINES_DIR` — rather than a hand-copied literal, so the list
|
|
189
|
+
* cannot drift from what was actually written. See {@link InitScaffoldAssets} for
|
|
190
|
+
* the hard exclusion contract; nothing gitignored or credential-bearing is
|
|
191
|
+
* constructible from this function's inputs.
|
|
192
|
+
*/
|
|
193
|
+
export async function resolveInitScaffoldAssets(options) {
|
|
194
|
+
const { cwd } = options;
|
|
195
|
+
const env = options.env ?? process.env;
|
|
196
|
+
const ide = await detectInitIdes({ ...options, env });
|
|
197
|
+
const pipelinesDir = resolveInitPipelinesDir(cwd, env);
|
|
198
|
+
const instructionsDir = path.join(path.dirname(pipelinesDir), "instructions");
|
|
199
|
+
const commitSafePaths = [
|
|
200
|
+
displayScaffoldPath(cwd, path.join(cwd, ".bridge", "config"), false),
|
|
201
|
+
displayScaffoldPath(cwd, path.join(pipelinesDir, "README.md"), false),
|
|
202
|
+
displayScaffoldPath(cwd, path.join(pipelinesDir, "example-pipeline.json"), false),
|
|
203
|
+
displayScaffoldPath(cwd, instructionsDir, true),
|
|
204
|
+
displayScaffoldPath(cwd, path.join(cwd, ".claude", "commands"), true),
|
|
205
|
+
...(ide.cursor ? [displayScaffoldPath(cwd, path.join(cwd, ".cursor", "commands"), true)] : []),
|
|
206
|
+
displayScaffoldPath(cwd, path.join(cwd, ".claude", "agents"), true),
|
|
207
|
+
...(ide.vscode ? [displayScaffoldPath(cwd, path.join(cwd, ".github", "agents"), true)] : []),
|
|
208
|
+
// Sourced from the bundled DOCS map, so a new scaffolded doc is covered
|
|
209
|
+
// automatically rather than needing a second edit here.
|
|
210
|
+
...Object.keys(DOCS)
|
|
211
|
+
.slice()
|
|
212
|
+
.sort()
|
|
213
|
+
.map((target) => displayScaffoldPath(cwd, path.join(cwd, target), false)),
|
|
214
|
+
];
|
|
215
|
+
return { ide, pipelinesDir, instructionsDir, commitSafePaths };
|
|
216
|
+
}
|
|
89
217
|
/**
|
|
90
218
|
* Merge a group token (e.g. "sfcc") into the BRIDGE_MCP_PROFILE env value of the
|
|
91
219
|
* `bridge-api` entry in each present MCP config file (.mcp.json, .vscode/mcp.json,
|
|
@@ -182,36 +310,9 @@ async function scaffoldFile(target, content, mkdirDir) {
|
|
|
182
310
|
*/
|
|
183
311
|
export async function runInit(cwd) {
|
|
184
312
|
// ---- Phase 1: IDE Detection ----
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
cursor: false,
|
|
189
|
-
windsurf: false,
|
|
190
|
-
};
|
|
191
|
-
try {
|
|
192
|
-
await stat(path.join(cwd, ".vscode"));
|
|
193
|
-
ideDetection.vscode = true;
|
|
194
|
-
}
|
|
195
|
-
catch { }
|
|
196
|
-
try {
|
|
197
|
-
await stat(path.join(cwd, ".cursor"));
|
|
198
|
-
ideDetection.cursor = true;
|
|
199
|
-
}
|
|
200
|
-
catch { }
|
|
201
|
-
if (!ideDetection.cursor && process.env.CURSOR_TRACE_DIR)
|
|
202
|
-
ideDetection.cursor = true;
|
|
203
|
-
try {
|
|
204
|
-
await stat(path.join(cwd, ".windsurf"));
|
|
205
|
-
ideDetection.windsurf = true;
|
|
206
|
-
}
|
|
207
|
-
catch { }
|
|
208
|
-
if (!ideDetection.windsurf) {
|
|
209
|
-
try {
|
|
210
|
-
await stat(path.join(cwd, ".windsurfrules"));
|
|
211
|
-
ideDetection.windsurf = true;
|
|
212
|
-
}
|
|
213
|
-
catch { }
|
|
214
|
-
}
|
|
313
|
+
// BAPI-708: detection lives in `detectInitIdes` so the commit-safe asset
|
|
314
|
+
// manifest resolves the SAME conditional targets this scaffolder writes.
|
|
315
|
+
const ideDetection = await detectInitIdes({ cwd });
|
|
215
316
|
const detectedIDEs = Object.entries(ideDetection)
|
|
216
317
|
.filter(([, v]) => v)
|
|
217
318
|
.map(([k]) => k);
|
|
@@ -230,7 +331,6 @@ export async function runInit(cwd) {
|
|
|
230
331
|
{ path: ".cursor/mcp.json", topLevelKey: "mcpServers", shouldCreate: ideDetection.cursor },
|
|
231
332
|
];
|
|
232
333
|
const configActions = [];
|
|
233
|
-
let anyCreatedOrAdded = false;
|
|
234
334
|
for (const target of configTargets) {
|
|
235
335
|
const fullPath = path.join(cwd, target.path);
|
|
236
336
|
let fileExists = false;
|
|
@@ -257,7 +357,7 @@ export async function runInit(cwd) {
|
|
|
257
357
|
}
|
|
258
358
|
const topLevel = parsed[target.topLevelKey];
|
|
259
359
|
if (topLevel && topLevel["bridge-api"]) {
|
|
260
|
-
// Entry exists — update BAPI_PROJECT_ROOT and
|
|
360
|
+
// Entry exists — update BAPI_PROJECT_ROOT and refresh the version pin.
|
|
261
361
|
// Note: command is always rewritten to "npx". A custom launcher (e.g. "bun x", wrapper
|
|
262
362
|
// script) will be replaced. This is intentional for the standard npx-based setup.
|
|
263
363
|
const entryTemplate = buildBridgeApiEntry(cwd);
|
|
@@ -266,7 +366,14 @@ export async function runInit(cwd) {
|
|
|
266
366
|
console.warn(`Warning: replacing launcher "${prevCommand}" with "${entryTemplate.command}" in ${target.path}. If you use a custom launcher, re-add it after this upgrade.`);
|
|
267
367
|
}
|
|
268
368
|
topLevel["bridge-api"].command = entryTemplate.command;
|
|
269
|
-
|
|
369
|
+
// BAPI-714: refresh ONLY the package-spec token. Assigning the whole
|
|
370
|
+
// template array here would migrate every legacy bare launcher to
|
|
371
|
+
// `serve` on the next --init/install/upgrade, which requirement C-3
|
|
372
|
+
// forbids. An args array we cannot recognize falls back to the template
|
|
373
|
+
// (that is the pre-existing behavior for a shape we cannot preserve).
|
|
374
|
+
topLevel["bridge-api"].args =
|
|
375
|
+
refreshBridgeApiPackageSpec(topLevel["bridge-api"].args, currentBridgePackageSpec()) ??
|
|
376
|
+
entryTemplate.args;
|
|
270
377
|
if (!topLevel["bridge-api"].env)
|
|
271
378
|
topLevel["bridge-api"].env = {};
|
|
272
379
|
topLevel["bridge-api"].env.BAPI_PROJECT_ROOT = cwd;
|
|
@@ -280,7 +387,6 @@ export async function runInit(cwd) {
|
|
|
280
387
|
parsed[target.topLevelKey]["bridge-api"] = buildBridgeApiEntry(cwd);
|
|
281
388
|
await writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
282
389
|
configActions.push({ path: target.path, action: "added entry" });
|
|
283
|
-
anyCreatedOrAdded = true;
|
|
284
390
|
}
|
|
285
391
|
}
|
|
286
392
|
else {
|
|
@@ -290,7 +396,6 @@ export async function runInit(cwd) {
|
|
|
290
396
|
await writeFile(fullPath, JSON.stringify(content, null, 2) + "\n", "utf-8");
|
|
291
397
|
await ensureGitignored(cwd, target.path);
|
|
292
398
|
configActions.push({ path: target.path, action: "created" });
|
|
293
|
-
anyCreatedOrAdded = true;
|
|
294
399
|
}
|
|
295
400
|
}
|
|
296
401
|
console.log("\nMCP config files:");
|
|
@@ -415,7 +520,9 @@ export async function runInit(cwd) {
|
|
|
415
520
|
if (agentSkipped.size > 0)
|
|
416
521
|
console.log(` Skipped (unchanged): ${agentSkipped.size}`);
|
|
417
522
|
// ---- Phase 6: Scaffold custom pipeline directories ----
|
|
418
|
-
|
|
523
|
+
// BAPI-708: shared with `resolveInitScaffoldAssets`, so a `BAPI_PIPELINES_DIR`
|
|
524
|
+
// override moves the scaffold and the commit-safe manifest in lockstep.
|
|
525
|
+
const pipelinesDir = resolveInitPipelinesDir(cwd);
|
|
419
526
|
const instrDir = path.join(path.dirname(pipelinesDir), "instructions");
|
|
420
527
|
await mkdir(pipelinesDir, { recursive: true });
|
|
421
528
|
await mkdir(instrDir, { recursive: true });
|
|
@@ -575,21 +682,16 @@ body explicitly says to serialize structured output.
|
|
|
575
682
|
bridgeConfigExists = true;
|
|
576
683
|
}
|
|
577
684
|
catch { }
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
685
|
+
// BAPI-708 (Part B): the file is still written with UNCHANGED content — only
|
|
686
|
+
// its narration goes. The written/skipped line, the runtime-credential
|
|
687
|
+
// paragraph, and the old `Set BAPI_REPO_NAME…` summary were redundant on all
|
|
688
|
+
// three callers (`--init`, `upgrade`, and `install-bridge`): each scaffolds the
|
|
689
|
+
// same secret-free manifest, and install-bridge separately reports both where
|
|
690
|
+
// the credential landed and which generated assets are safe to commit. Removed
|
|
691
|
+
// outright rather than behind an output-mode flag — parameterizing would change
|
|
692
|
+
// the injected `deps.runInit` seam signature for no user-visible gain.
|
|
693
|
+
if (!bridgeConfigExists) {
|
|
582
694
|
await mkdir(path.dirname(bridgeConfigPath), { recursive: true });
|
|
583
695
|
await writeFile(bridgeConfigPath, buildBridgeConfigManifest(chooseScaffoldRepoName(cwd)), "utf-8");
|
|
584
|
-
console.log(`\n.bridge/config: written`);
|
|
585
|
-
}
|
|
586
|
-
console.log(" Credentials are resolved at runtime from BAPI_API_KEY or " +
|
|
587
|
-
"~/.config/bridge/credentials.json (no secrets are written to .bridge/config).");
|
|
588
|
-
// ---- Phase 7: Final summary ----
|
|
589
|
-
if (anyCreatedOrAdded) {
|
|
590
|
-
console.log("\nSet BAPI_REPO_NAME in your config files. Do NOT put BAPI_API_KEY in the " +
|
|
591
|
-
"generated MCP config — supply it via the BAPI_API_KEY environment variable, " +
|
|
592
|
-
"or store it under \"bapi:<repo_name>\" in ~/.config/bridge/credentials.json. " +
|
|
593
|
-
"Get your values from the Bridge API setup UI at https://bridgegpt-api.com");
|
|
594
696
|
}
|
|
595
697
|
}
|