@bridge_gpt/mcp-server 0.2.54 → 0.2.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +45 -59
  2. package/build/agent-capabilities/types.js +4 -0
  3. package/build/agent-guidance.generated.js +3 -0
  4. package/build/agent-launchers/claude-executor-adapter.js +3 -0
  5. package/build/agent-notes.js +178 -0
  6. package/build/agent-registry.js +25 -5
  7. package/build/agent-utils.js +58 -0
  8. package/build/agents.generated.js +1 -1
  9. package/build/codex-launch-scope.js +49 -0
  10. package/build/codex-skill-adapter.js +58 -0
  11. package/build/command-host.js +9 -0
  12. package/build/command-provisioning.js +35 -4
  13. package/build/commands.generated.js +9 -8
  14. package/build/conductor/bridge-api-client.js +199 -6
  15. package/build/conductor/bring-up-facts.js +187 -0
  16. package/build/conductor/claude-hook.js +7 -5
  17. package/build/conductor/cli.js +28 -0
  18. package/build/conductor/doctor.js +80 -9
  19. package/build/conductor/epic-implementer-cli.js +1298 -0
  20. package/build/conductor/epic-runtime.js +1 -1
  21. package/build/conductor/errors.js +2 -2
  22. package/build/conductor/git-ci-types.js +1 -1
  23. package/build/conductor/git-hooks.js +28 -14
  24. package/build/conductor/install-doctor.js +11 -5
  25. package/build/conductor/readiness-cli.js +10 -10
  26. package/build/conductor/readiness-sections.js +58 -9
  27. package/build/conductor/readiness.js +37 -6
  28. package/build/conductor/recovery-cli.js +289 -10
  29. package/build/conductor/recovery-operations.js +125 -2
  30. package/build/conductor/repair-contract.js +58 -0
  31. package/build/conductor/store.js +2 -2
  32. package/build/conductor/supervisor-runtime.js +1 -1
  33. package/build/conductor-bin.js +2 -139
  34. package/build/conductor-claude-hook-bin.js +2 -2
  35. package/build/conductor-claude-hook-removed-stub-bin.js +31 -0
  36. package/build/conductor-removed-stub-bin.js +30 -0
  37. package/build/docs.generated.js +1 -1
  38. package/build/doctor.js +81 -18
  39. package/build/drive-epic.js +541 -115
  40. package/build/epic-implementer-bin.js +145 -0
  41. package/build/epic-implementer-bundle-cli.js +264 -0
  42. package/build/epic-implementer-claude-hook-bin.js +3 -0
  43. package/build/epic-integration-pr.js +5 -3
  44. package/build/executor/env.js +6 -0
  45. package/build/executor/interrupted-worktree.js +60 -0
  46. package/build/executor/job-errors.js +45 -0
  47. package/build/executor/job-runner.js +274 -9
  48. package/build/executor/job-types.js +25 -9
  49. package/build/executor/merge-tree-classifier.js +171 -0
  50. package/build/executor/reconcile-overlap-governance.js +129 -0
  51. package/build/executor/reconcile-overlap-job.js +989 -0
  52. package/build/executor/reconcile-overlap-types.js +14 -0
  53. package/build/executor/spawn-job-driver.js +1 -0
  54. package/build/executor/types.js +2 -0
  55. package/build/executor/worker-finalization.js +25 -2
  56. package/build/executor/worker-guard-hook.js +15 -7
  57. package/build/implement-epic/bridge-client.js +773 -0
  58. package/build/implement-epic/checkpoint-store.js +542 -0
  59. package/build/implement-epic/cli.js +3158 -0
  60. package/build/implement-epic/cut-protocol.js +392 -0
  61. package/build/implement-epic/lock.js +302 -0
  62. package/build/implement-epic/pr-state.js +286 -0
  63. package/build/implement-epic/spawn.js +113 -0
  64. package/build/index.js +617 -168
  65. package/build/init.js +74 -9
  66. package/build/install-bridge-conductor.js +5 -5
  67. package/build/install-bridge.js +425 -79
  68. package/build/mcp-host-config.js +22 -60
  69. package/build/mcp-host-entry-adapter.js +18 -0
  70. package/build/mcp-host-targets.js +19 -32
  71. package/build/mcp-provisioning.js +1 -1
  72. package/build/mcp-registration-doctor.js +1 -1
  73. package/build/merge-pull-request.js +1 -1
  74. package/build/pipelines.generated.js +7 -7
  75. package/build/plan-epic-conductor-eligibility.js +1 -1
  76. package/build/plane/cli.js +36 -5
  77. package/build/plane/preflight.js +128 -12
  78. package/build/plane/shutdown.js +4 -4
  79. package/build/readiness-check.js +3 -3
  80. package/build/readme.generated.js +1 -1
  81. package/build/review-tickets.js +50 -35
  82. package/build/run-unit-tests-launcher.js +1 -1
  83. package/build/setup-epic.js +69 -31
  84. package/build/start-tickets-conductor.js +8 -7
  85. package/build/start-tickets.js +266 -48
  86. package/build/version.generated.js +3 -3
  87. package/build/worker-guard-hook-bin.js +1 -1
  88. package/docs/CONDUCTOR.md +8 -6
  89. package/package.json +5 -3
@@ -9,17 +9,17 @@
9
9
  * Write strategy per target comes from the data-only registry
10
10
  * {@link file:./mcp-host-targets.ts}:
11
11
  * - vendor-first — prefer the official vendor CLI, fall back to a safe direct
12
- * write (Claude Code, Copilot CLI, Codex).
12
+ * write (Claude Code, Codex).
13
13
  * - direct — merge-write the JSON file directly (Cursor, Copilot VS Code).
14
14
  * - manual-instructions — never auto-write; emit redacted instructions
15
15
  * (Windsurf).
16
16
  *
17
17
  * SECRET DISCIPLINE: the API key lives only inside the logical entry's `env`.
18
- * When a verified vendor contract delivers it via argv (all three do — none
19
- * expose a stdin channel), the argv is constructed and spawned INSIDE the vendor
20
- * adapter and is never stringified, logged, previewed, returned, or interpolated
21
- * into a warning. Structured outcomes and inspectors carry presence + launcher
22
- * args only, never env values.
18
+ * When a verified vendor contract delivers it via argv (neither expose a stdin
19
+ * channel), the argv is constructed and spawned INSIDE the vendor adapter and is
20
+ * never stringified, logged, previewed, returned, or interpolated into a
21
+ * warning. Structured outcomes and inspectors carry presence + launcher args
22
+ * only, never env values.
23
23
  *
24
24
  * TOML NOTE: there is no comment-preserving TOML library available, so Codex
25
25
  * direct writes render a canonical document for a missing file, APPEND canonical
@@ -30,6 +30,8 @@
30
30
  import { VERSION } from "./version.generated.js";
31
31
  import { refreshBridgeApiPackageSpec } from "./init.js";
32
32
  import { hostAdapterForTarget, } from "./mcp-host-targets.js";
33
+ import { adaptBridgeEntryForHostTarget, } from "./mcp-host-entry-adapter.js";
34
+ export { adaptBridgeEntryForHostTarget, } from "./mcp-host-entry-adapter.js";
33
35
  import { LEGACY_SERVER_NAMES, MCP_SERVER_NAME, MCP_PACKAGE_NAME, RECOGNIZED_SERVER_NAMES, resolveRegistrationKey, } from "./mcp-identity.js";
34
36
  /**
35
37
  * The secret-free, repair-oriented detail every duplicate-registration outcome
@@ -64,31 +66,6 @@ export function resolveTargetAbsPath(target, ctx) {
64
66
  return target.absPathResolver(ctx.homedir);
65
67
  }
66
68
  // ---------------------------------------------------------------------------
67
- // Entry adaptation
68
- // ---------------------------------------------------------------------------
69
- /**
70
- * Adapt the shared logical entry into a target's physical JSON entry: copy
71
- * command/args/env, conditionally add the registry `transportType` as `type`,
72
- * and merge `extraEntryKeys`. Claude Code omits `type`; Cursor/Copilot VS Code
73
- * use `stdio`; Copilot CLI uses `local` + `tools: ["*"]`.
74
- */
75
- export function adaptBridgeEntryForHostTarget(entry, target) {
76
- const adapter = hostAdapterForTarget(target);
77
- const physical = {};
78
- if (adapter.transportType !== undefined) {
79
- physical.type = adapter.transportType;
80
- }
81
- physical.command = entry.command;
82
- physical.args = [...entry.args];
83
- physical.env = { ...entry.env };
84
- if (adapter.extraEntryKeys) {
85
- for (const [k, v] of Object.entries(adapter.extraEntryKeys)) {
86
- physical[k] = v;
87
- }
88
- }
89
- return physical;
90
- }
91
- // ---------------------------------------------------------------------------
92
69
  // JSON read / merge / write
93
70
  // ---------------------------------------------------------------------------
94
71
  function isEnoent(err) {
@@ -153,10 +130,17 @@ export function mergeJsonHostConfig(existing, target, adaptedEntry) {
153
130
  // key, so a legacy registration's args are preserved exactly like a canonical
154
131
  // one's.
155
132
  const prior = root[writeKey];
133
+ const priorEntry = prior && typeof prior === "object" && !Array.isArray(prior)
134
+ ? prior
135
+ : {};
156
136
  const preservedArgs = prior && typeof prior === "object" && !Array.isArray(prior)
157
- ? refreshBridgeApiPackageSpec(prior.args, currentLauncherSpec())
137
+ ? refreshBridgeApiPackageSpec(priorEntry.args, currentLauncherSpec())
158
138
  : null;
159
- root[writeKey] = preservedArgs ? { ...adaptedEntry, args: preservedArgs } : adaptedEntry;
139
+ root[writeKey] = {
140
+ ...priorEntry,
141
+ ...adaptedEntry,
142
+ ...(preservedArgs ? { args: preservedArgs } : {}),
143
+ };
160
144
  merged[adapter.topLevelKey] = root;
161
145
  return { status: "merged", document: merged, registrationKey: writeKey };
162
146
  }
@@ -467,7 +451,7 @@ export async function inspectHostEntry(target, ctx, deps) {
467
451
  *
468
452
  * Deliberately NOT derived from {@link file:./mcp-host-targets.ts}: the merge
469
453
  * path's surface is these three files and nothing else, so an inspector built
470
- * from the wider registry would report on Codex/Copilot hosts the writer never
454
+ * from the wider registry would report on Codex/Windsurf hosts the writer never
471
455
  * touches — and a report implying those hosts gained the token would be wrong.
472
456
  */
473
457
  export const BRIDGE_PROFILE_TOKEN_CONFIGS = [
@@ -567,12 +551,12 @@ export async function inspectBridgeApiProfileToken(projectRoot, token, deps) {
567
551
  * Whether a vendor kind's secret-delivery contract is VERIFIED. Only verified
568
552
  * contracts are ever invoked; an uncertain contract falls back to a direct
569
553
  * write rather than guessing argv. Verified from empirical probes (BAPI-635):
570
- * claude `mcp add-json <name> <json>` and copilot `mcp add --env` both deliver
571
- * the secret through argv. Codex's contract is unverified (binary absent at
572
- * authoring time) → never invoked; always direct-written.
554
+ * claude `mcp add-json <name> <json>` delivers the secret through argv.
555
+ * Codex's contract is unverified (binary absent at authoring time) → never
556
+ * invoked; always direct-written.
573
557
  */
574
558
  function isVendorContractVerified(kind) {
575
- return kind === "claude-add-json" || kind === "copilot-add";
559
+ return kind === "claude-add-json";
576
560
  }
577
561
  /**
578
562
  * Build the vendor add invocation for a target. Returns `null` when the
@@ -597,28 +581,6 @@ function buildVendorInvocation(target, entry) {
597
581
  args: ["mcp", "add-json", MCP_SERVER_NAME, json, "--scope", "project"],
598
582
  };
599
583
  }
600
- if (vendor.kind === "copilot-add") {
601
- // copilot mcp add <name> --tools "*" --env K=V ... -- <command> <args...>
602
- // Env pairs (incl BAPI_API_KEY) are secret-bearing argv.
603
- const envArgs = [];
604
- for (const [k, v] of Object.entries(entry.env)) {
605
- envArgs.push("--env", `${k}=${v}`);
606
- }
607
- return {
608
- bin: vendor.bin,
609
- args: [
610
- "mcp",
611
- "add",
612
- MCP_SERVER_NAME,
613
- "--tools",
614
- "*",
615
- ...envArgs,
616
- "--",
617
- entry.command,
618
- ...entry.args,
619
- ],
620
- };
621
- }
622
584
  return null;
623
585
  }
624
586
  // ---------------------------------------------------------------------------
@@ -0,0 +1,18 @@
1
+ import { hostAdapterForTarget, } from "./mcp-host-targets.js";
2
+ /**
3
+ * Applies only registry-owned physical fields at the host emission boundary.
4
+ * Logical entries remain portable across host formats.
5
+ */
6
+ export function adaptBridgeEntryForHostTarget(entry, target) {
7
+ const adapter = hostAdapterForTarget(target);
8
+ const physical = {
9
+ command: entry.command,
10
+ args: [...entry.args],
11
+ env: { ...entry.env },
12
+ };
13
+ if (adapter.transportType !== undefined)
14
+ physical.type = adapter.transportType;
15
+ if (adapter.extraEntryKeys)
16
+ Object.assign(physical, adapter.extraEntryKeys);
17
+ return physical;
18
+ }
@@ -51,12 +51,6 @@ export const HOST_ADAPTERS = {
51
51
  topLevelKey: "mcp_servers",
52
52
  transportType: undefined, // TOML table shape has no `type`.
53
53
  },
54
- "copilot-cli": {
55
- format: "json",
56
- topLevelKey: "mcpServers",
57
- transportType: "local",
58
- extraEntryKeys: { tools: ["*"] },
59
- },
60
54
  };
61
55
  /** Pure typed lookup from a target's declared {@link HostKind} to its adapter. */
62
56
  export function hostAdapterForTarget(target) {
@@ -79,7 +73,7 @@ export const MCP_HOST_TARGETS = {
79
73
  hostKind: "claude-code-json",
80
74
  vendorCli: { bin: "claude", kind: "claude-add-json" },
81
75
  launchAgent: "claude",
82
- worktreeSupported: true,
76
+ capabilities: { mcpConfiguration: true, customCommands: true, worktreeProvisioning: true, sessionSpawning: true },
83
77
  writeStrategy: "vendor-first",
84
78
  // Claude Code is always a candidate default.
85
79
  detect: () => true,
@@ -92,7 +86,7 @@ export const MCP_HOST_TARGETS = {
92
86
  displayPath: ".cursor/mcp.json",
93
87
  hostKind: "cursor-json",
94
88
  launchAgent: "cursor-agent",
95
- worktreeSupported: true,
89
+ capabilities: { mcpConfiguration: true, customCommands: true, worktreeProvisioning: true, sessionSpawning: true },
96
90
  writeStrategy: "direct",
97
91
  detect: (ctx) => ctx.exists(joinPath(ctx.cwd, ".cursor")) ||
98
92
  typeof ctx.env.CURSOR_TRACE_DIR === "string" &&
@@ -100,28 +94,15 @@ export const MCP_HOST_TARGETS = {
100
94
  },
101
95
  "copilot-vscode": {
102
96
  id: "copilot-vscode",
103
- label: "GitHub Copilot (VS Code)",
97
+ label: "GitHub Copilot",
104
98
  scope: "project",
105
99
  relPath: ".vscode/mcp.json",
106
100
  displayPath: ".vscode/mcp.json",
107
101
  hostKind: "vscode-json",
108
- worktreeSupported: false,
102
+ capabilities: { mcpConfiguration: true, customCommands: true, worktreeProvisioning: false, sessionSpawning: false },
109
103
  writeStrategy: "direct",
110
104
  detect: (ctx) => ctx.exists(joinPath(ctx.cwd, ".vscode")),
111
105
  },
112
- "copilot-cli": {
113
- id: "copilot-cli",
114
- label: "GitHub Copilot CLI",
115
- scope: "global",
116
- absPathResolver: (homedir) => joinPath(homedir, ".copilot/mcp-config.json"),
117
- displayPath: "~/.copilot/mcp-config.json",
118
- hostKind: "copilot-cli",
119
- vendorCli: { bin: "copilot", kind: "copilot-add" },
120
- worktreeSupported: false,
121
- writeStrategy: "vendor-first",
122
- // No existing probe supports Copilot CLI; never a default.
123
- detect: () => false,
124
- },
125
106
  codex: {
126
107
  id: "codex",
127
108
  label: "OpenAI Codex",
@@ -130,7 +111,10 @@ export const MCP_HOST_TARGETS = {
130
111
  displayPath: "~/.codex/config.toml",
131
112
  hostKind: "codex-toml",
132
113
  vendorCli: { bin: "codex", kind: "codex-add" },
133
- worktreeSupported: false,
114
+ launchAgent: "codex",
115
+ // The launcher injects a per-process, worktree-bound MCP table. The global
116
+ // registration remains the ordinary interactive default.
117
+ capabilities: { mcpConfiguration: true, customCommands: true, worktreeProvisioning: true, sessionSpawning: true },
134
118
  writeStrategy: "vendor-first",
135
119
  detect: (ctx) => ctx.exists(joinPath(ctx.homedir, ".codex")),
136
120
  },
@@ -141,7 +125,7 @@ export const MCP_HOST_TARGETS = {
141
125
  absPathResolver: (homedir) => joinPath(homedir, ".codeium/windsurf/mcp_config.json"),
142
126
  displayPath: "~/.codeium/windsurf/mcp_config.json",
143
127
  hostKind: "claude-code-json", // Windsurf uses the Claude-compatible JSON shape.
144
- worktreeSupported: false,
128
+ capabilities: { mcpConfiguration: true, customCommands: false, worktreeProvisioning: false, sessionSpawning: false },
145
129
  // Preserve current behavior: never auto-modify the global Windsurf file.
146
130
  writeStrategy: "manual-instructions",
147
131
  detect: (ctx) => ctx.exists(joinPath(ctx.cwd, ".windsurf")) ||
@@ -153,7 +137,6 @@ export const HOST_PLATFORM_ORDER = [
153
137
  "claude-code",
154
138
  "cursor",
155
139
  "copilot-vscode",
156
- "copilot-cli",
157
140
  "codex",
158
141
  "windsurf",
159
142
  ];
@@ -173,18 +156,22 @@ export function getHostTarget(id) {
173
156
  return isHostPlatformId(id) ? MCP_HOST_TARGETS[id] : undefined;
174
157
  }
175
158
  /**
176
- * The centrally-registered launchable agent for a host platform, or `undefined`
177
- * when the platform has no supported agentic CLI (Copilot / Codex / Windsurf).
159
+ * The centrally-registered agent identity for a host platform, or `undefined`
160
+ * when the platform has no registered agent (Copilot / Windsurf).
178
161
  * Pure projection of the registry's {@link HostTargetDefinition.launchAgent}
179
- * metadata — the single source of truth for which selected tools can open a
180
- * post-install configuration session (BAPI-657).
162
+ * metadata. Callers must check workflow-specific capabilities before launching.
181
163
  */
182
164
  export function agentForPlatform(id) {
183
165
  return MCP_HOST_TARGETS[id].launchAgent;
184
166
  }
185
- /** The two worktree-supported targets, in stable order (Claude, Cursor). */
167
+ /** Hosts whose declared workflow capability permits worktree provisioning. */
186
168
  export function getWorktreeHostTargets() {
187
- return allHostTargets().filter((t) => t.worktreeSupported);
169
+ return allHostTargets().filter((t) => t.capabilities.worktreeProvisioning);
170
+ }
171
+ /** True only when a host has every capability needed for a spawned workflow. */
172
+ export function isRunnableHostTarget(target) {
173
+ const c = target.capabilities;
174
+ return c.mcpConfiguration && c.customCommands && c.worktreeProvisioning && c.sessionSpawning && target.launchAgent !== undefined;
188
175
  }
189
176
  /** Project-scoped JSON targets (used by init, launcher-token merge, doctor). */
190
177
  export function getProjectJsonTargets() {
@@ -138,7 +138,7 @@ export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath,
138
138
  /** Both registration files written for every provisioned worktree. */
139
139
  export function getWorktreeMcpRegistrationTargets(worktreePath, platform) {
140
140
  const api = pathApiForProvisioningPlatform(platform);
141
- return getWorktreeHostTargets().map((target) => ({
141
+ return getWorktreeHostTargets().filter((target) => target.scope === "project").map((target) => ({
142
142
  filePath: api.join(worktreePath, target.relPath),
143
143
  topLevelKey: hostAdapterForTarget(target).topLevelKey,
144
144
  }));
@@ -104,7 +104,7 @@ export function isBridgeApiShimEntry(entry, worktreeRoot) {
104
104
  * to validate.
105
105
  */
106
106
  export async function probeWorktreeMcpRegistration(worktreeRoot, deps) {
107
- const targets = getWorktreeHostTargets().map((target) => ({
107
+ const targets = getWorktreeHostTargets().filter((target) => target.scope === "project").map((target) => ({
108
108
  filePath: path.join(worktreeRoot, target.relPath),
109
109
  topLevelKey: hostAdapterForTarget(target).topLevelKey,
110
110
  }));
@@ -356,7 +356,7 @@ function withReviewWaiver(env, waiver) {
356
356
  *
357
357
  * So the waiver mirrors the Python seam field for field: `api/library/vcs/epic_observers.py`
358
358
  * routes `sticky is None` to `PENDING` (waivable) and EVERY `"unknown"` to
359
- * `UNKNOWN` (never waivable), and `conductor_merge_service` admits only `PENDING`.
359
+ * `UNKNOWN` (never waivable), and `epic_implementer_merge_service` admits only `PENDING`.
360
360
  * No new server field is needed to do this — `sticky_verdict` already carries the
361
361
  * distinction on the wire, and `normalizeReviewSnapshot` already preserves it.
362
362
  *