@bridge_gpt/mcp-server 0.2.39 → 0.2.42
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 +10 -10
- package/build/agent-capabilities/cli.js +2 -1
- package/build/agent-launchers/claude-executor-adapter.js +17 -4
- package/build/claude-user-config-doctor.js +42 -11
- package/build/cli-release.js +2 -1
- package/build/commands.generated.js +4 -4
- package/build/conduct-epic/bridge-client.js +354 -113
- package/build/conduct-epic/checkpoint-store.js +75 -2
- package/build/conduct-epic/cli.js +795 -109
- package/build/conduct-epic/cut-protocol.js +327 -0
- package/build/conduct-epic/pr-state.js +113 -24
- package/build/conduct-epic/spawn.js +14 -2
- package/build/conductor/bridge-api-client.js +27 -1
- package/build/conductor/cli.js +46 -1
- package/build/conductor/doctor.js +101 -16
- package/build/conductor/epic-reconcile.js +72 -19
- package/build/conductor/epic-runtime.js +15 -3
- package/build/conductor/errors.js +47 -0
- package/build/conductor/git-hooks.js +205 -11
- package/build/conductor/install-doctor.js +230 -1
- package/build/conductor/local-merge.js +130 -28
- package/build/conductor/tools.js +32 -3
- package/build/conductor/worker-ledger-cli.js +27 -1
- package/build/conductor-bin.js +15 -15
- package/build/credentials-cli.js +3 -2
- package/build/doctor.js +107 -41
- package/build/executor/cli.js +48 -1
- package/build/executor/env.js +21 -0
- package/build/executor/index-scope.js +39 -0
- package/build/executor/job-log-registry.js +69 -0
- package/build/executor/job-runner.js +148 -26
- package/build/executor/live-worker-registry.js +83 -0
- package/build/executor/observation.js +167 -6
- package/build/executor/platform.js +147 -3
- package/build/executor/process.js +58 -14
- package/build/executor/runner.js +235 -48
- package/build/executor/test-clock.js +3 -2
- package/build/index-scope-contract.js +96 -0
- package/build/index.js +153 -204
- package/build/init.js +83 -22
- package/build/install-bridge-conductor.js +323 -14
- package/build/install-bridge.js +202 -38
- package/build/install-doctor.js +23 -9
- package/build/install-reexec.js +2 -1
- package/build/launcher-config-inspection.js +83 -22
- package/build/mcp-host-config.js +331 -67
- package/build/mcp-host-targets.js +45 -21
- package/build/mcp-identity.js +92 -0
- package/build/mcp-install-state.js +94 -1
- package/build/mcp-invoke.js +2 -1
- package/build/mcp-provisioning.js +45 -12
- package/build/mcp-registration-doctor.js +35 -13
- package/build/mcp-server-invocation.js +4 -2
- package/build/merge-pull-request.js +208 -9
- package/build/pipelines.generated.js +3 -3
- package/build/plane/defaults.js +4 -1
- package/build/plane/preflight.js +81 -10
- package/build/plane/test-fakes.js +9 -1
- package/build/readme.generated.js +1 -1
- package/build/regression-check.js +3 -2
- package/build/review-tickets.js +8 -7
- package/build/run-unit-tests-launcher.js +74 -1
- package/build/schedule-run.js +3 -2
- package/build/setup-epic.js +453 -78
- package/build/sfcc/tool-wrapper.js +15 -0
- package/build/start-tickets-prereqs.js +11 -6
- package/build/start-tickets.js +91 -85
- package/build/update-check.js +3 -2
- package/build/upgrade-advice.js +2 -1
- package/build/upgrade-cli.js +50 -18
- package/build/version.generated.js +1 -1
- package/docs/CONDUCTOR.md +22 -0
- package/docs/install/mcp-tool-integrations.md +19 -3
- package/package.json +2 -2
package/build/init.js
CHANGED
|
@@ -20,7 +20,8 @@ import { reconstructAgentMarkdown, translateAgentToCopilot } from "./agent-utils
|
|
|
20
20
|
import { validateRepoName, resolveRepoNameForProjectRoot } from "./bridge-config.js";
|
|
21
21
|
import { ensureGitignored as ensureGitignoredShared } from "./git-ignore-utils.js";
|
|
22
22
|
import { resolveBapiCredentials } from "./credential-store.js";
|
|
23
|
-
import {
|
|
23
|
+
import { describeLauncherReason, DUPLICATE_REGISTRATION_GUIDANCE, inspectLauncherConfigs, isPrerequisiteFailure, launcherTargetFor, } from "./launcher-config-inspection.js";
|
|
24
|
+
import { MCP_SERVER_NAME, MCP_PACKAGE_NAME, resolveRegistrationKey, } from "./mcp-identity.js";
|
|
24
25
|
// ---------------------------------------------------------------------------
|
|
25
26
|
// CLI: --init scaffolds slash commands and MCP configs into the current project
|
|
26
27
|
// ---------------------------------------------------------------------------
|
|
@@ -59,7 +60,7 @@ export function buildBridgeApiEntry(cwd, metadata) {
|
|
|
59
60
|
// EVERY branch — the server self-resolves it at runtime.
|
|
60
61
|
return {
|
|
61
62
|
command: "npx",
|
|
62
|
-
args: ["-y", "--prefer-offline",
|
|
63
|
+
args: ["-y", "--prefer-offline", `${MCP_PACKAGE_NAME}@${VERSION}`, "serve"],
|
|
63
64
|
env: {
|
|
64
65
|
BAPI_BASE_URL: metadata?.baseUrl ?? DEFAULT_BRIDGE_BASE_URL,
|
|
65
66
|
BAPI_REPO_NAME: metadata?.repoName ?? PLACEHOLDER_REPO_NAME,
|
|
@@ -68,8 +69,6 @@ export function buildBridgeApiEntry(cwd, metadata) {
|
|
|
68
69
|
},
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
|
-
/** The package-name prefix every Bridge launcher spec starts with. */
|
|
72
|
-
const BRIDGE_PACKAGE_PREFIX = "@bridge_gpt/mcp-server";
|
|
73
72
|
/**
|
|
74
73
|
* Refresh ONLY the package-spec token of an existing launcher args array
|
|
75
74
|
* (BAPI-714, Group C).
|
|
@@ -95,7 +94,7 @@ export function refreshBridgeApiPackageSpec(args, spec) {
|
|
|
95
94
|
const tokens = args.filter((a) => typeof a === "string");
|
|
96
95
|
if (tokens.length !== args.length)
|
|
97
96
|
return null;
|
|
98
|
-
const index = tokens.findIndex((token) => token.startsWith(
|
|
97
|
+
const index = tokens.findIndex((token) => token.startsWith(MCP_PACKAGE_NAME));
|
|
99
98
|
if (index === -1)
|
|
100
99
|
return null;
|
|
101
100
|
const next = [...tokens];
|
|
@@ -104,7 +103,7 @@ export function refreshBridgeApiPackageSpec(args, spec) {
|
|
|
104
103
|
}
|
|
105
104
|
/** The exact package spec a launcher generated by THIS build should carry. */
|
|
106
105
|
export function currentBridgePackageSpec() {
|
|
107
|
-
return `${
|
|
106
|
+
return `${MCP_PACKAGE_NAME}@${VERSION}`;
|
|
108
107
|
}
|
|
109
108
|
/**
|
|
110
109
|
* Choose the `repo_name` written into a scaffolded `.bridge/config`. Uses the
|
|
@@ -224,16 +223,47 @@ export async function resolveInitScaffoldAssets(options) {
|
|
|
224
223
|
];
|
|
225
224
|
return { ide, pipelinesDir, instructionsDir, commitSafePaths };
|
|
226
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Thrown when a profile merge finds a config carrying BOTH recognized
|
|
228
|
+
* registration keys (BAPI-807). Raised before ANY file is written, so the
|
|
229
|
+
* operation is all-or-nothing.
|
|
230
|
+
*/
|
|
231
|
+
export class DuplicateRegistrationError extends Error {
|
|
232
|
+
relPaths;
|
|
233
|
+
constructor(relPaths) {
|
|
234
|
+
super(`Cannot update the Bridge MCP profile:\n${relPaths
|
|
235
|
+
.map((p) => ` ${p}: ${DUPLICATE_REGISTRATION_GUIDANCE}`)
|
|
236
|
+
.join("\n")}`);
|
|
237
|
+
this.name = "DuplicateRegistrationError";
|
|
238
|
+
this.relPaths = relPaths;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
227
241
|
/**
|
|
228
242
|
* Merge a group token (e.g. "sfcc") into the BRIDGE_MCP_PROFILE env value of the
|
|
229
|
-
*
|
|
243
|
+
* Bridge entry in each present MCP config file (.mcp.json, .vscode/mcp.json,
|
|
230
244
|
* .cursor/mcp.json). Existing tokens are preserved; the merge is idempotent.
|
|
231
245
|
*
|
|
232
|
-
* Only files where a
|
|
233
|
-
* are never created by this helper
|
|
246
|
+
* Only files where a recognized Bridge entry already exists are modified — new
|
|
247
|
+
* files are never created by this helper, and the entry is updated under the key
|
|
248
|
+
* it actually lives under (`bridge` or the permanently supported `bridge-api`),
|
|
249
|
+
* never renamed.
|
|
250
|
+
*
|
|
251
|
+
* BAPI-807: every present target is READ AND RESOLVED first, and only then is
|
|
252
|
+
* anything written. A both-key config in the third file must not be discovered
|
|
253
|
+
* after the first two were already modified — that would leave the project
|
|
254
|
+
* half-updated with no record of what moved, which is the same all-or-nothing
|
|
255
|
+
* guarantee `runInit`'s preflight provides.
|
|
234
256
|
*
|
|
235
257
|
* Returns the final resolved profile string (comma-joined) after merging, or an
|
|
236
258
|
* empty string if no config file was found or updated.
|
|
259
|
+
*
|
|
260
|
+
* Its READ-ONLY counterpart is `inspectBridgeApiProfileToken`
|
|
261
|
+
* ({@link file:./mcp-host-config.ts}), which reports token presence across the
|
|
262
|
+
* same three configs without writing. Callers that need to say WHICH config
|
|
263
|
+
* carries the token must use it: this function returns a merged profile string,
|
|
264
|
+
* not a per-config roster (BAPI-775).
|
|
265
|
+
*
|
|
266
|
+
* @throws {DuplicateRegistrationError} when any target carries both keys.
|
|
237
267
|
*/
|
|
238
268
|
export async function mergeBridgeApiProfileToken(cwd, token) {
|
|
239
269
|
const targets = [
|
|
@@ -241,7 +271,9 @@ export async function mergeBridgeApiProfileToken(cwd, token) {
|
|
|
241
271
|
{ path: ".vscode/mcp.json", topLevelKey: "servers" },
|
|
242
272
|
{ path: ".cursor/mcp.json", topLevelKey: "mcpServers" },
|
|
243
273
|
];
|
|
244
|
-
|
|
274
|
+
// ---- Pass 1: read + resolve every target. No writes. ----
|
|
275
|
+
const pending = [];
|
|
276
|
+
const conflicts = [];
|
|
245
277
|
for (const target of targets) {
|
|
246
278
|
const fullPath = path.join(cwd, target.path);
|
|
247
279
|
let raw;
|
|
@@ -259,10 +291,25 @@ export async function mergeBridgeApiProfileToken(cwd, token) {
|
|
|
259
291
|
continue; // invalid JSON — skip
|
|
260
292
|
}
|
|
261
293
|
const topLevel = parsed[target.topLevelKey];
|
|
262
|
-
|
|
263
|
-
|
|
294
|
+
const resolution = resolveRegistrationKey(topLevel);
|
|
295
|
+
if (resolution.state === "conflict") {
|
|
296
|
+
conflicts.push(target.path);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (resolution.state === "absent") {
|
|
300
|
+
continue; // no recognized Bridge entry — skip
|
|
264
301
|
}
|
|
265
|
-
const entry = topLevel[
|
|
302
|
+
const entry = topLevel[resolution.key];
|
|
303
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
304
|
+
continue; // present key but not an updatable entry — skip
|
|
305
|
+
}
|
|
306
|
+
pending.push({ fullPath, parsed, entry });
|
|
307
|
+
}
|
|
308
|
+
if (conflicts.length > 0)
|
|
309
|
+
throw new DuplicateRegistrationError(conflicts);
|
|
310
|
+
// ---- Pass 2: apply. Every target here was proven unambiguous above. ----
|
|
311
|
+
let lastProfile = "";
|
|
312
|
+
for (const { fullPath, parsed, entry } of pending) {
|
|
266
313
|
if (!entry.env)
|
|
267
314
|
entry.env = {};
|
|
268
315
|
const existing = entry.env.BRIDGE_MCP_PROFILE;
|
|
@@ -329,7 +376,7 @@ export class InitPreflightError extends Error {
|
|
|
329
376
|
}
|
|
330
377
|
}
|
|
331
378
|
/**
|
|
332
|
-
* Resolve the non-secret identity a NEW
|
|
379
|
+
* Resolve the non-secret identity a NEW Bridge entry should carry.
|
|
333
380
|
*
|
|
334
381
|
* Precedence (plan Step 3.9): an explicit caller context wins; then an explicit
|
|
335
382
|
* `BAPI_REPO_NAME`; then the committed `.bridge/config` / git-derived identity.
|
|
@@ -454,7 +501,7 @@ export async function runInit(cwd, options = {}) {
|
|
|
454
501
|
stat: options.io?.stat ?? stat,
|
|
455
502
|
};
|
|
456
503
|
const targetVersion = options.targetVersion ?? VERSION;
|
|
457
|
-
const targetSpec =
|
|
504
|
+
const targetSpec = `${MCP_PACKAGE_NAME}@${targetVersion}`;
|
|
458
505
|
const say = options.stdout ?? ((m) => console.log(m));
|
|
459
506
|
const warn = options.stderr ?? ((m) => console.error(m));
|
|
460
507
|
// ---- Phase 1: IDE Detection ----
|
|
@@ -467,7 +514,7 @@ export async function runInit(cwd, options = {}) {
|
|
|
467
514
|
console.log(`Bridge API --init: detected IDEs: ${detectedIDEs.join(", ")}`);
|
|
468
515
|
// ---- Phase 2: Windsurf manual instructions ----
|
|
469
516
|
if (ideDetection.windsurf) {
|
|
470
|
-
const windsurfSnippet = JSON.stringify({ mcpServers: {
|
|
517
|
+
const windsurfSnippet = JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: buildBridgeApiEntry(cwd) } }, null, 2);
|
|
471
518
|
console.log("\n⚠ Windsurf does not support project-local MCP configuration.\n" +
|
|
472
519
|
"Add the following to ~/.codeium/windsurf/mcp_config.json:\n\n" +
|
|
473
520
|
windsurfSnippet + "\n");
|
|
@@ -539,6 +586,7 @@ export async function runInit(cwd, options = {}) {
|
|
|
539
586
|
structuredActions.push({
|
|
540
587
|
relPath: target.path,
|
|
541
588
|
kind: "skipped-worktree-shim",
|
|
589
|
+
registrationKey: found.registrationKey ?? undefined,
|
|
542
590
|
});
|
|
543
591
|
continue;
|
|
544
592
|
}
|
|
@@ -555,14 +603,17 @@ export async function runInit(cwd, options = {}) {
|
|
|
555
603
|
kind: "skipped-ahead",
|
|
556
604
|
fromVersion: found.currentVersion ?? undefined,
|
|
557
605
|
fromSpec: found.spec ?? undefined,
|
|
606
|
+
registrationKey: found.registrationKey ?? undefined,
|
|
558
607
|
});
|
|
559
608
|
continue;
|
|
560
609
|
}
|
|
561
610
|
if (found.action === "create") {
|
|
611
|
+
// A CREATE is by definition an absent registration, so the key is the
|
|
612
|
+
// canonical one. `add` below is the same case in an existing file.
|
|
562
613
|
await io.mkdir(path.dirname(fullPath), { recursive: true });
|
|
563
614
|
const content = {
|
|
564
615
|
[target.topLevelKey]: {
|
|
565
|
-
[
|
|
616
|
+
[MCP_SERVER_NAME]: buildBridgeApiEntry(cwd, newEntryMetadata),
|
|
566
617
|
},
|
|
567
618
|
};
|
|
568
619
|
await io.writeFile(fullPath, JSON.stringify(content, null, 2) + "\n", "utf-8");
|
|
@@ -573,6 +624,7 @@ export async function runInit(cwd, options = {}) {
|
|
|
573
624
|
kind: "created",
|
|
574
625
|
toVersion: targetVersion,
|
|
575
626
|
unconfigured: !entryReady,
|
|
627
|
+
registrationKey: MCP_SERVER_NAME,
|
|
576
628
|
});
|
|
577
629
|
if (!entryReady) {
|
|
578
630
|
readinessWarnings.push({
|
|
@@ -589,9 +641,11 @@ export async function runInit(cwd, options = {}) {
|
|
|
589
641
|
const raw = await io.readFile(fullPath, "utf-8");
|
|
590
642
|
const parsed = JSON.parse(raw);
|
|
591
643
|
if (found.action === "add") {
|
|
644
|
+
// No recognized key was present (the preflight proved it), so this is a
|
|
645
|
+
// fresh registration and uses MCP_SERVER_NAME.
|
|
592
646
|
if (!parsed[target.topLevelKey])
|
|
593
647
|
parsed[target.topLevelKey] = {};
|
|
594
|
-
parsed[target.topLevelKey][
|
|
648
|
+
parsed[target.topLevelKey][MCP_SERVER_NAME] = buildBridgeApiEntry(cwd, newEntryMetadata);
|
|
595
649
|
await io.writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
596
650
|
configActions.push({ path: target.path, action: "added entry" });
|
|
597
651
|
structuredActions.push({
|
|
@@ -599,6 +653,7 @@ export async function runInit(cwd, options = {}) {
|
|
|
599
653
|
kind: "added",
|
|
600
654
|
toVersion: targetVersion,
|
|
601
655
|
unconfigured: !entryReady,
|
|
656
|
+
registrationKey: MCP_SERVER_NAME,
|
|
602
657
|
});
|
|
603
658
|
if (!entryReady) {
|
|
604
659
|
readinessWarnings.push({
|
|
@@ -614,7 +669,12 @@ export async function runInit(cwd, options = {}) {
|
|
|
614
669
|
// rewrote it to "npx" unconditionally, silently replacing a custom
|
|
615
670
|
// launcher), and `refreshBridgeApiPackageSpec` preserves the bare-versus-
|
|
616
671
|
// `serve` composition plus every surrounding argument (BAPI-714 C-3).
|
|
617
|
-
|
|
672
|
+
//
|
|
673
|
+
// BAPI-807: the entry is reached through the key the PREFLIGHT resolved,
|
|
674
|
+
// not through MCP_SERVER_NAME. Indexing by the canonical name here is what
|
|
675
|
+
// would turn a `bridge-api` project's repin into a second `bridge` entry.
|
|
676
|
+
const registrationKey = found.registrationKey ?? MCP_SERVER_NAME;
|
|
677
|
+
const entry = parsed[target.topLevelKey][registrationKey];
|
|
618
678
|
const refreshed = refreshBridgeApiPackageSpec(entry.args, targetSpec);
|
|
619
679
|
if (refreshed === null) {
|
|
620
680
|
// Unreachable: preflight classifies an unrecognizable args array as
|
|
@@ -641,6 +701,7 @@ export async function runInit(cwd, options = {}) {
|
|
|
641
701
|
fromVersion: found.currentVersion ?? undefined,
|
|
642
702
|
fromSpec: found.spec ?? undefined,
|
|
643
703
|
toVersion: targetVersion,
|
|
704
|
+
registrationKey,
|
|
644
705
|
});
|
|
645
706
|
// An existing entry that still says YOUR_REPO_NAME loads without error and
|
|
646
707
|
// authenticates as nobody. Its env is left exactly as-is — but the run is
|
|
@@ -666,7 +727,7 @@ export async function runInit(cwd, options = {}) {
|
|
|
666
727
|
const existing = readinessWarnings.filter((w) => w.kind === "unconfigured-entry-existing");
|
|
667
728
|
warn("\n⚠ Bridge API MCP configuration is NOT ready to authenticate.");
|
|
668
729
|
if (created.length > 0) {
|
|
669
|
-
warn(` Wrote an UNCONFIGURED
|
|
730
|
+
warn(` Wrote an UNCONFIGURED ${MCP_SERVER_NAME} entry (BAPI_REPO_NAME=${PLACEHOLDER_REPO_NAME}) to: ${created
|
|
670
731
|
.map((w) => w.relPath)
|
|
671
732
|
.join(", ")}`);
|
|
672
733
|
warn(identity.repoName
|
|
@@ -677,11 +738,11 @@ export async function runInit(cwd, options = {}) {
|
|
|
677
738
|
}
|
|
678
739
|
}
|
|
679
740
|
if (existing.length > 0) {
|
|
680
|
-
warn(` Existing
|
|
741
|
+
warn(` Existing Bridge entry still uses the ${PLACEHOLDER_REPO_NAME} placeholder in: ${existing
|
|
681
742
|
.map((w) => w.relPath)
|
|
682
743
|
.join(", ")}`);
|
|
683
744
|
}
|
|
684
|
-
warn(
|
|
745
|
+
warn(` Fix with: npx -y ${MCP_PACKAGE_NAME} install (or set BAPI_REPO_NAME and store a key with the install flow)`);
|
|
685
746
|
}
|
|
686
747
|
// ---- Phase 4: Scaffold slash command directories ----
|
|
687
748
|
const commandDirs = [path.join(cwd, ".claude", "commands")];
|