@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.
Files changed (74) hide show
  1. package/README.md +10 -10
  2. package/build/agent-capabilities/cli.js +2 -1
  3. package/build/agent-launchers/claude-executor-adapter.js +17 -4
  4. package/build/claude-user-config-doctor.js +42 -11
  5. package/build/cli-release.js +2 -1
  6. package/build/commands.generated.js +4 -4
  7. package/build/conduct-epic/bridge-client.js +354 -113
  8. package/build/conduct-epic/checkpoint-store.js +75 -2
  9. package/build/conduct-epic/cli.js +795 -109
  10. package/build/conduct-epic/cut-protocol.js +327 -0
  11. package/build/conduct-epic/pr-state.js +113 -24
  12. package/build/conduct-epic/spawn.js +14 -2
  13. package/build/conductor/bridge-api-client.js +27 -1
  14. package/build/conductor/cli.js +46 -1
  15. package/build/conductor/doctor.js +101 -16
  16. package/build/conductor/epic-reconcile.js +72 -19
  17. package/build/conductor/epic-runtime.js +15 -3
  18. package/build/conductor/errors.js +47 -0
  19. package/build/conductor/git-hooks.js +205 -11
  20. package/build/conductor/install-doctor.js +230 -1
  21. package/build/conductor/local-merge.js +130 -28
  22. package/build/conductor/tools.js +32 -3
  23. package/build/conductor/worker-ledger-cli.js +27 -1
  24. package/build/conductor-bin.js +15 -15
  25. package/build/credentials-cli.js +3 -2
  26. package/build/doctor.js +107 -41
  27. package/build/executor/cli.js +48 -1
  28. package/build/executor/env.js +21 -0
  29. package/build/executor/index-scope.js +39 -0
  30. package/build/executor/job-log-registry.js +69 -0
  31. package/build/executor/job-runner.js +148 -26
  32. package/build/executor/live-worker-registry.js +83 -0
  33. package/build/executor/observation.js +167 -6
  34. package/build/executor/platform.js +147 -3
  35. package/build/executor/process.js +58 -14
  36. package/build/executor/runner.js +235 -48
  37. package/build/executor/test-clock.js +3 -2
  38. package/build/index-scope-contract.js +96 -0
  39. package/build/index.js +153 -204
  40. package/build/init.js +83 -22
  41. package/build/install-bridge-conductor.js +323 -14
  42. package/build/install-bridge.js +202 -38
  43. package/build/install-doctor.js +23 -9
  44. package/build/install-reexec.js +2 -1
  45. package/build/launcher-config-inspection.js +83 -22
  46. package/build/mcp-host-config.js +331 -67
  47. package/build/mcp-host-targets.js +45 -21
  48. package/build/mcp-identity.js +92 -0
  49. package/build/mcp-install-state.js +94 -1
  50. package/build/mcp-invoke.js +2 -1
  51. package/build/mcp-provisioning.js +45 -12
  52. package/build/mcp-registration-doctor.js +35 -13
  53. package/build/mcp-server-invocation.js +4 -2
  54. package/build/merge-pull-request.js +208 -9
  55. package/build/pipelines.generated.js +3 -3
  56. package/build/plane/defaults.js +4 -1
  57. package/build/plane/preflight.js +81 -10
  58. package/build/plane/test-fakes.js +9 -1
  59. package/build/readme.generated.js +1 -1
  60. package/build/regression-check.js +3 -2
  61. package/build/review-tickets.js +8 -7
  62. package/build/run-unit-tests-launcher.js +74 -1
  63. package/build/schedule-run.js +3 -2
  64. package/build/setup-epic.js +453 -78
  65. package/build/sfcc/tool-wrapper.js +15 -0
  66. package/build/start-tickets-prereqs.js +11 -6
  67. package/build/start-tickets.js +91 -85
  68. package/build/update-check.js +3 -2
  69. package/build/upgrade-advice.js +2 -1
  70. package/build/upgrade-cli.js +50 -18
  71. package/build/version.generated.js +1 -1
  72. package/docs/CONDUCTOR.md +22 -0
  73. package/docs/install/mcp-tool-integrations.md +19 -3
  74. package/package.json +2 -2
@@ -44,6 +44,11 @@ export async function runPlanePreflight(repoRoot, deps) {
44
44
  return { ok: false, diagnostics };
45
45
  add(await checkClaudeLogin(deps));
46
46
  const credentials = await checkBridgeCredentials(repoRoot, deps);
47
+ // Advisories first, then the failure. An insecure credentials file and an
48
+ // unresolvable credential are independent facts and can both be true, so the
49
+ // advisory is appended unconditionally rather than only on the success path.
50
+ for (const advisory of credentials.advisories)
51
+ add(advisory);
47
52
  if (!credentials.ok)
48
53
  add(credentials.diagnostic);
49
54
  add(await checkPlaneBuildFreshness(repoRoot, { fs: deps.fs }));
@@ -145,6 +150,24 @@ export async function checkClaudeLogin(deps) {
145
150
  * Both go through the shared resolvers. This is the shell-spawned credential
146
151
  * rule: a plane member is launched from a spawned shell, which never sees
147
152
  * `.mcp.json` env — that file is visible only to the MCP server process.
153
+ *
154
+ * ## Permission policy stays with the shared resolver (BAPI-773)
155
+ *
156
+ * The resolver owns the group/world-permission rule and the prose it warns
157
+ * with; plane neither re-implements that policy nor duplicates its threshold.
158
+ * What plane owns is *delivery*: the resolver writes its advisory to a `stderr`
159
+ * sink, and an unstructured write straight to the process stream would either
160
+ * be lost among the structured diagnostics `plane up` renders or interleave
161
+ * with them unpredictably. So the sink here is a line buffer, and each captured
162
+ * line becomes a non-blocking `"warning"` diagnostic that flows through the
163
+ * same rendering path as every other preflight finding.
164
+ *
165
+ * The resolver's warning is only as good as the mode it is given, which is why
166
+ * the stat adapter forwards the real one. When a successful stat carries no
167
+ * mode at all, `0o777` is substituted *deliberately*: an unknown mode is
168
+ * reported rather than silently treated as safe. A stat that rejects is left to
169
+ * reject — fabricating a successful stat out of a filesystem failure would
170
+ * invent a permission fact that was never observed.
148
171
  */
149
172
  export async function checkBridgeCredentials(repoRoot, deps) {
150
173
  const repo = await deps.resolveRepoName({
@@ -153,8 +176,11 @@ export async function checkBridgeCredentials(repoRoot, deps) {
153
176
  readFile: deps.fs.readFile,
154
177
  });
155
178
  if (!repo.ok) {
179
+ // Nothing has called the credential resolver yet, so there is no advisory
180
+ // source to have captured from.
156
181
  return {
157
182
  ok: false,
183
+ advisories: [],
158
184
  diagnostic: {
159
185
  check: "bridge-credentials",
160
186
  severity: "blocking",
@@ -163,6 +189,7 @@ export async function checkBridgeCredentials(repoRoot, deps) {
163
189
  },
164
190
  };
165
191
  }
192
+ const warnings = createResolverWarningBuffer();
166
193
  let result;
167
194
  try {
168
195
  result = await deps.resolveCredentials(repo.repoName, {
@@ -171,23 +198,22 @@ export async function checkBridgeCredentials(repoRoot, deps) {
171
198
  platform: deps.platform,
172
199
  readFile: deps.fs.readFile,
173
200
  stat: async (filePath) => {
174
- // The resolver only needs POSIX mode bits for its permission warning.
175
- // `PlaneFsDeps.stat` is deliberately minimal, so a missing mode is
176
- // reported as a fully-open value the resolver will simply warn about
177
- // rather than as a fabricated "safe" one.
178
- await deps.fs.stat(filePath);
179
- return { mode: 0o600 };
180
- },
181
- stderr: () => {
182
- /* the resolver's advisory warnings are not part of plane output */
201
+ // A rejection propagates untouched: only a *successful* stat that
202
+ // happens to carry no mode falls back, and it falls back to fully open
203
+ // so the resolver warns instead of staying silent about an unknown.
204
+ const statResult = await deps.fs.stat(filePath);
205
+ return { mode: statResult.mode !== undefined ? statResult.mode : 0o777 };
183
206
  },
207
+ stderr: warnings.write,
184
208
  });
185
209
  }
186
210
  catch {
187
211
  // Sanitized: a resolver throw becomes a category, never exception text
188
- // (which could echo file contents).
212
+ // (which could echo file contents). Advisories captured before the throw
213
+ // are still real observations and are kept.
189
214
  return {
190
215
  ok: false,
216
+ advisories: warnings.drain(),
191
217
  diagnostic: {
192
218
  check: "bridge-credentials",
193
219
  severity: "blocking",
@@ -199,6 +225,7 @@ export async function checkBridgeCredentials(repoRoot, deps) {
199
225
  if (!result.ok) {
200
226
  return {
201
227
  ok: false,
228
+ advisories: warnings.drain(),
202
229
  diagnostic: {
203
230
  check: "bridge-credentials",
204
231
  severity: "blocking",
@@ -213,6 +240,50 @@ export async function checkBridgeCredentials(repoRoot, deps) {
213
240
  repoName: repo.repoName,
214
241
  apiKey: result.credentials.apiKey,
215
242
  source: result.credentials.source,
243
+ advisories: warnings.drain(),
244
+ };
245
+ }
246
+ /**
247
+ * Line buffer for the shared credential resolver's `stderr` sink.
248
+ *
249
+ * The resolver is free to emit a single warning across several callback
250
+ * invocations, so a chunk is not a line: text is accumulated and only split on
251
+ * a newline boundary. Whatever remains unterminated when the resolver finishes
252
+ * is flushed as a final line by `drain`, which is why a resolver that writes
253
+ * `"third warning"` with no trailing newline still produces a diagnostic.
254
+ *
255
+ * Nothing here writes to a process stream. That is the entire point.
256
+ */
257
+ function createResolverWarningBuffer() {
258
+ const lines = [];
259
+ let pending = "";
260
+ const push = (line) => {
261
+ const trimmed = line.replace(/\r$/, "");
262
+ if (trimmed.length === 0)
263
+ return;
264
+ lines.push(trimmed);
265
+ };
266
+ return {
267
+ write: (message) => {
268
+ pending += message;
269
+ const segments = pending.split("\n");
270
+ // The final segment is whatever followed the last newline — possibly the
271
+ // start of a line the resolver has not finished writing yet.
272
+ pending = segments.pop() ?? "";
273
+ for (const segment of segments)
274
+ push(segment);
275
+ },
276
+ drain: () => {
277
+ if (pending.length > 0) {
278
+ push(pending);
279
+ pending = "";
280
+ }
281
+ return lines.map((message) => ({
282
+ check: "bridge-credentials",
283
+ severity: "warning",
284
+ message,
285
+ }));
286
+ },
216
287
  };
217
288
  }
218
289
  /**
@@ -16,6 +16,7 @@ export function dirent(name, kind) {
16
16
  export function createFakeFs(options = {}) {
17
17
  const files = new Map(Object.entries(options.files ?? {}));
18
18
  const mtimes = new Map(Object.entries(options.mtimes ?? {}));
19
+ const modes = new Map(Object.entries(options.modes ?? {}));
19
20
  const dirs = new Map(Object.entries(options.dirs ?? {}));
20
21
  const readErrors = new Map(Object.entries(options.readErrors ?? {}));
21
22
  const calls = {
@@ -29,6 +30,7 @@ export function createFakeFs(options = {}) {
29
30
  return {
30
31
  files,
31
32
  mtimes,
33
+ modes,
32
34
  calls,
33
35
  failNextCreateWith: (code) => {
34
36
  pendingCreateFailure = code;
@@ -80,7 +82,13 @@ export function createFakeFs(options = {}) {
80
82
  throw errno(failure);
81
83
  if (!files.has(filePath) && !dirs.has(filePath))
82
84
  throw errno("ENOENT");
83
- return { mtimeMs: mtimes.get(filePath) ?? 0 };
85
+ const mode = modes.get(filePath);
86
+ const result = { mtimeMs: mtimes.get(filePath) ?? 0 };
87
+ // Assigned only when the fixture supplied one: an absent `mode` must stay
88
+ // absent, not become `undefined`-shaped-but-present.
89
+ if (mode !== undefined)
90
+ result.mode = mode;
91
+ return result;
84
92
  },
85
93
  readdir: async (dirPath) => {
86
94
  const entries = dirs.get(dirPath);