@bridge_gpt/mcp-server 0.2.26 → 0.2.27

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 (57) hide show
  1. package/build/commands.generated.js +1 -1
  2. package/build/conductor-bin.js +68 -9267
  3. package/build/conductor-claude-hook-bin.js +2 -172
  4. package/build/decision-page-template.js +2 -31
  5. package/build/index.js +4652 -37042
  6. package/build/init.js +80 -56
  7. package/build/install-bridge.js +36 -20
  8. package/build/version.generated.js +1 -1
  9. package/package.json +1 -3
  10. package/design-assets/favicon/android-chrome-192x192.png +0 -0
  11. package/design-assets/favicon/android-chrome-512x512.png +0 -0
  12. package/design-assets/favicon/apple-touch-icon.png +0 -0
  13. package/design-assets/favicon/favicon-16x16.png +0 -0
  14. package/design-assets/favicon/favicon.ico +0 -0
  15. package/design-assets/favicon/site.webmanifest +0 -1
  16. package/public/css/main.min.css +0 -4971
  17. package/public/css/main.min.css.map +0 -1
  18. package/public/fonts/OFL.txt +0 -93
  19. package/public/fonts/SourceSansPro-Black.ttf +0 -0
  20. package/public/fonts/SourceSansPro-BlackItalic.ttf +0 -0
  21. package/public/fonts/SourceSansPro-Bold.ttf +0 -0
  22. package/public/fonts/SourceSansPro-BoldItalic.ttf +0 -0
  23. package/public/fonts/SourceSansPro-ExtraLight.ttf +0 -0
  24. package/public/fonts/SourceSansPro-ExtraLightItalic.ttf +0 -0
  25. package/public/fonts/SourceSansPro-Italic.ttf +0 -0
  26. package/public/fonts/SourceSansPro-Light.ttf +0 -0
  27. package/public/fonts/SourceSansPro-LightItalic.ttf +0 -0
  28. package/public/fonts/SourceSansPro-Regular.ttf +0 -0
  29. package/public/fonts/SourceSansPro-SemiBold.ttf +0 -0
  30. package/public/fonts/SourceSansPro-SemiBoldItalic.ttf +0 -0
  31. package/public/img/bridge-logo-160x51.webp +0 -0
  32. package/public/img/bridge-logo-300x92.webp +0 -0
  33. package/public/img/favicon/android-chrome-192x192.png +0 -0
  34. package/public/img/favicon/android-chrome-512x512.png +0 -0
  35. package/public/img/favicon/apple-touch-icon.png +0 -0
  36. package/public/img/favicon/favicon-16x16.png +0 -0
  37. package/public/img/favicon/favicon-32x32.png +0 -0
  38. package/public/img/favicon/favicon.ico +0 -0
  39. package/public/img/favicon/site.webmanifest +0 -1
  40. package/public/img/installation/bitbucket/app-password-1.png +0 -0
  41. package/public/img/installation/bitbucket/app-password-2.png +0 -0
  42. package/public/img/installation/bitbucket/create-token-1.png +0 -0
  43. package/public/img/installation/bitbucket/create-token-2.png +0 -0
  44. package/public/img/installation/bitbucket/webhook-1.png +0 -0
  45. package/public/img/installation/github/github-review-webhook.png +0 -0
  46. package/public/img/installation/jira/credentials/api-key.png +0 -0
  47. package/public/img/installation/jira/webhook/create-rule.png +0 -0
  48. package/public/img/installation/jira/webhook/project-settings.png +0 -0
  49. package/public/img/installation/jira/webhook/rule-create-1.png +0 -0
  50. package/public/img/installation/jira/webhook/rule-create-2.png +0 -0
  51. package/public/img/installation/jira/webhook/rule-create-3.png +0 -0
  52. package/public/img/installation/pinecone/pinecone-api-key.png +0 -0
  53. package/public/img/installation/pinecone/pinecone-index.png +0 -0
  54. package/public/js/main.min.js +0 -10409
  55. package/public/js/main.min.js.map +0 -1
  56. package/smoke-test/SMOKE-TEST.md +0 -560
  57. package/smoke-test/smoke-test-mcp.md +0 -23
package/build/init.js CHANGED
@@ -33,9 +33,16 @@ export function buildBridgeApiEntry(cwd) {
33
33
  // where 0.2.6 persisted despite an upgrade). Pinning keeps the running
34
34
  // configuration predictable and visible in version control. The `install-bridge`
35
35
  // config write reuses this same entry so both install paths agree.
36
+ //
37
+ // `--prefer-offline` makes npx resolve the (immutable, exact-pinned) version
38
+ // from the local npm cache and skip the redundant registry-metadata round-trip
39
+ // on every boot, only hitting the network on a genuine cache miss (unlike
40
+ // `--offline`, it falls back automatically). It cannot resurrect a stale build
41
+ // — the BAPI-429 hazard above was specific to UNPINNED specs; here the exact
42
+ // `@${VERSION}` is immutable, so a cache hit is always the right build.
36
43
  return {
37
44
  command: "npx",
38
- args: ["-y", `@bridge_gpt/mcp-server@${VERSION}`],
45
+ args: ["-y", "--prefer-offline", `@bridge_gpt/mcp-server@${VERSION}`],
39
46
  env: {
40
47
  BAPI_BASE_URL: "https://bridgegpt-api.com",
41
48
  BAPI_REPO_NAME: "YOUR_REPO_NAME",
@@ -143,6 +150,32 @@ async function ensureGitignored(cwd, filePath) {
143
150
  mkdir: (p, options) => mkdir(p, options),
144
151
  });
145
152
  }
153
+ /**
154
+ * Read-compare-write a single scaffold file. Returns "skipped" when the target
155
+ * already holds identical bytes (no write), "overwritten" when it existed with
156
+ * different bytes, and "written" when it was absent. Optionally ensures `mkdirDir`
157
+ * exists first (used by the DOCS phase, whose targets span different subdirs).
158
+ *
159
+ * Extracted so the COMMANDS/DOCS/AGENTS phases can run their per-file work
160
+ * concurrently via Promise.all instead of one-await-at-a-time: on a slow disk the
161
+ * sequential syscalls dominated `runInit`. The caller applies each outcome to its
162
+ * summary Sets AFTER the batch, iterating the ordered results, so behavior and
163
+ * console output are byte-for-byte identical to the sequential version.
164
+ */
165
+ async function scaffoldFile(target, content, mkdirDir) {
166
+ if (mkdirDir !== undefined)
167
+ await mkdir(mkdirDir, { recursive: true });
168
+ let existed = false;
169
+ try {
170
+ const existing = await readFile(target, "utf-8");
171
+ if (existing === content)
172
+ return "skipped";
173
+ existed = true;
174
+ }
175
+ catch { /* file doesn't exist */ }
176
+ await writeFile(target, content, "utf-8");
177
+ return existed ? "overwritten" : "written";
178
+ }
146
179
  /**
147
180
  * Core initialization logic shared by --init and --upgrade.
148
181
  * Runs all scaffolding phases without calling process.exit().
@@ -272,22 +305,22 @@ export async function runInit(cwd) {
272
305
  const writtenFiles = new Set();
273
306
  const skippedFiles = new Set();
274
307
  const overwrittenFiles = new Set();
308
+ const commandEntries = Object.entries(COMMANDS);
275
309
  for (const dir of commandDirs) {
276
310
  await mkdir(dir, { recursive: true });
277
- for (const [filename, content] of Object.entries(COMMANDS)) {
278
- const target = path.join(dir, filename);
279
- try {
280
- const existing = await readFile(target, "utf-8");
281
- if (existing === content) {
282
- skippedFiles.add(filename);
283
- continue;
284
- }
285
- overwrittenFiles.add(filename);
311
+ // Per-file read/compare/write runs concurrently; outcomes are applied to the
312
+ // Sets afterward in entry order so the summary counts are unchanged.
313
+ const outcomes = await Promise.all(commandEntries.map(([filename, content]) => scaffoldFile(path.join(dir, filename), content)));
314
+ commandEntries.forEach(([filename], i) => {
315
+ const outcome = outcomes[i];
316
+ if (outcome === "skipped") {
317
+ skippedFiles.add(filename);
318
+ return;
286
319
  }
287
- catch { /* file doesn't exist */ }
288
- await writeFile(target, content, "utf-8");
320
+ if (outcome === "overwritten")
321
+ overwrittenFiles.add(filename);
289
322
  writtenFiles.add(filename);
290
- }
323
+ });
291
324
  }
292
325
  // A file written in any directory takes priority over skipped
293
326
  for (const f of writtenFiles)
@@ -312,20 +345,21 @@ export async function runInit(cwd) {
312
345
  // project root; content is never secret.
313
346
  const docsWritten = new Set();
314
347
  const docsSkipped = new Set();
315
- for (const [relTarget, content] of Object.entries(DOCS)) {
348
+ const docEntries = Object.entries(DOCS);
349
+ // Each doc target may live in a different subdir, so mkdir(dirname) happens
350
+ // per-file inside scaffoldFile. Outcomes applied in entry order below preserve
351
+ // the insertion order of the joined summary list.
352
+ const docOutcomes = await Promise.all(docEntries.map(([relTarget, content]) => {
316
353
  const target = path.join(cwd, relTarget);
317
- await mkdir(path.dirname(target), { recursive: true });
318
- try {
319
- const existing = await readFile(target, "utf-8");
320
- if (existing === content) {
321
- docsSkipped.add(relTarget);
322
- continue;
323
- }
324
- }
325
- catch { /* file doesn't exist */ }
326
- await writeFile(target, content, "utf-8");
327
- docsWritten.add(relTarget);
328
- }
354
+ return scaffoldFile(target, content, path.dirname(target));
355
+ }));
356
+ docEntries.forEach(([relTarget], i) => {
357
+ // DOCS tracks only written vs skipped; an overwrite counts as written.
358
+ if (docOutcomes[i] === "skipped")
359
+ docsSkipped.add(relTarget);
360
+ else
361
+ docsWritten.add(relTarget);
362
+ });
329
363
  const docsTotal = Object.keys(DOCS).length;
330
364
  if (docsTotal > 0) {
331
365
  console.log(`\nDocumentation: scaffolded ${docsTotal} doc asset${docsTotal === 1 ? "" : "s"}`);
@@ -341,42 +375,32 @@ export async function runInit(cwd) {
341
375
  // Always scaffold to .claude/agents/
342
376
  const claudeAgentsDir = path.join(cwd, ".claude", "agents");
343
377
  await mkdir(claudeAgentsDir, { recursive: true });
344
- for (const [key, agent] of Object.entries(AGENTS)) {
345
- const filename = `${key}.md`;
346
- const content = reconstructAgentMarkdown(agent.frontmatter, agent.body);
347
- const target = path.join(claudeAgentsDir, filename);
348
- try {
349
- const existing = await readFile(target, "utf-8");
350
- if (existing === content) {
378
+ const agentEntries = Object.entries(AGENTS);
379
+ // Apply a batch of ordered outcomes to the agent Sets (same branches as the
380
+ // original per-file loop: skipped → skipped; overwritten → overwritten+written;
381
+ // written written).
382
+ const applyAgentOutcomes = (filenames, outcomes) => {
383
+ filenames.forEach((filename, i) => {
384
+ const outcome = outcomes[i];
385
+ if (outcome === "skipped") {
351
386
  agentSkipped.add(filename);
352
- continue;
387
+ return;
353
388
  }
354
- agentOverwritten.add(filename);
355
- }
356
- catch { /* file doesn't exist */ }
357
- await writeFile(target, content, "utf-8");
358
- agentWritten.add(filename);
359
- }
389
+ if (outcome === "overwritten")
390
+ agentOverwritten.add(filename);
391
+ agentWritten.add(filename);
392
+ });
393
+ };
394
+ const claudeAgentFilenames = agentEntries.map(([key]) => `${key}.md`);
395
+ const claudeAgentOutcomes = await Promise.all(agentEntries.map(([key, agent]) => scaffoldFile(path.join(claudeAgentsDir, `${key}.md`), reconstructAgentMarkdown(agent.frontmatter, agent.body))));
396
+ applyAgentOutcomes(claudeAgentFilenames, claudeAgentOutcomes);
360
397
  // Scaffold to .github/agents/ for VS Code / Copilot
361
398
  if (ideDetection.vscode) {
362
399
  const copilotAgentsDir = path.join(cwd, ".github", "agents");
363
400
  await mkdir(copilotAgentsDir, { recursive: true });
364
- for (const [key, agent] of Object.entries(AGENTS)) {
365
- const filename = `${key}.agent.md`;
366
- const content = translateAgentToCopilot(agent.frontmatter, agent.body);
367
- const target = path.join(copilotAgentsDir, filename);
368
- try {
369
- const existing = await readFile(target, "utf-8");
370
- if (existing === content) {
371
- agentSkipped.add(filename);
372
- continue;
373
- }
374
- agentOverwritten.add(filename);
375
- }
376
- catch { /* file doesn't exist */ }
377
- await writeFile(target, content, "utf-8");
378
- agentWritten.add(filename);
379
- }
401
+ const copilotAgentFilenames = agentEntries.map(([key]) => `${key}.agent.md`);
402
+ const copilotAgentOutcomes = await Promise.all(agentEntries.map(([key, agent]) => scaffoldFile(path.join(copilotAgentsDir, `${key}.agent.md`), translateAgentToCopilot(agent.frontmatter, agent.body))));
403
+ applyAgentOutcomes(copilotAgentFilenames, copilotAgentOutcomes);
380
404
  }
381
405
  // TODO: Add Cursor agent scaffolding when Cursor publishes a formal agent spec
382
406
  // A file written in any directory takes priority over skipped
@@ -113,7 +113,11 @@ export const MCP_TIMEOUT_GUIDANCE = "Note: if your MCP client has a very short c
113
113
  "(the initial npx package resolution/download can exceed a short connect timeout).";
114
114
  /** The exact, version-pinned launcher spec the pre-warm spawns (fast-exiting --version). */
115
115
  export function buildPrewarmArgs() {
116
- return ["-y", `@bridge_gpt/mcp-server@${VERSION}`, "--version"];
116
+ // `--prefer-offline`: the initial `@latest` bootstrap already populated the npm
117
+ // cache with the exact tarball this `@${VERSION}`-pinned warm needs, so resolve
118
+ // from cache and skip the redundant registry-metadata round-trip (falls back to
119
+ // the network on a genuine miss).
120
+ return ["-y", "--prefer-offline", `@bridge_gpt/mcp-server@${VERSION}`, "--version"];
117
121
  }
118
122
  /** Secret-free preview of the pre-warm command (no env, no key). */
119
123
  export function buildPrewarmCommandPreview() {
@@ -1942,6 +1946,21 @@ export async function runInstallBridgeCli(argv, overrides = {}) {
1942
1946
  "Check that the system temporary directory is writable so the launch script can be used.");
1943
1947
  return 1;
1944
1948
  }
1949
+ // ---- Step 3b (kickoff) — pre-warm the @${VERSION}-pinned _npx bucket ----
1950
+ // The @${VERSION}-pinned launcher written below lives in a DIFFERENT _npx bucket
1951
+ // than the @latest bucket this `npx … install-bridge` invocation warmed, so the
1952
+ // first real MCP launch would otherwise pay a cold install inline (which can
1953
+ // exceed the client's connect deadline). Nothing before the Step 5 terminal spawn
1954
+ // depends on the warmed bucket — only the spawned session's first MCP launch does
1955
+ // — so START the warm HERE (fire-and-forget) and AWAIT it just before that spawn.
1956
+ // Its multi-second `npx` resolve then overlaps the network round-trips (ping /
1957
+ // exchange), the scaffold, and the config/credential writes instead of adding to
1958
+ // them serially. Strictly fail-open: the default impl always RESOLVES (never
1959
+ // rejects), so a later abort-return that skips the await leaves no unhandled
1960
+ // rejection. Positioned AFTER the launch-command guards above so an aborted run
1961
+ // that never reaches an install side effect also never spawns the pre-warm.
1962
+ log(" pre-warming the version-pinned launcher bucket (in the background)…");
1963
+ const prewarmPromise = deps.spawnPrewarm("npx", buildPrewarmArgs(), deps.env);
1945
1964
  const credentialWriteDeps = {
1946
1965
  env: deps.env,
1947
1966
  homedir: deps.homedir,
@@ -2238,25 +2257,6 @@ export async function runInstallBridgeCli(argv, overrides = {}) {
2238
2257
  // ensureGitignored for the state file can still throw — also advisory.
2239
2258
  errorLog("Warning: could not persist the install-state file (non-fatal).");
2240
2259
  }
2241
- // ---- Step 3b — pre-warm the @${VERSION}-pinned _npx bucket (BAPI-451 W3) ----
2242
- // The launcher just written is pinned to @${VERSION}, a DIFFERENT _npx bucket
2243
- // than the @latest bucket this `npx … install-bridge` invocation warmed. Spawn
2244
- // the exact pinned launcher once with the fast-exiting `--version` flag so the
2245
- // first real MCP launch resolves from a warm bucket instead of paying the cold
2246
- // install inline (which can exceed the client's connect deadline). Strictly
2247
- // fail-open: a failure warns (secret-free) but never fails the install.
2248
- log(" pre-warming the version-pinned launcher bucket…");
2249
- const prewarm = await deps.spawnPrewarm("npx", buildPrewarmArgs(), deps.env);
2250
- if (prewarm.ok) {
2251
- log(" launcher bucket warmed (the first MCP launch will not pay a cold install).");
2252
- log(` ${MCP_TIMEOUT_GUIDANCE}`);
2253
- }
2254
- else {
2255
- // Escalate to a stronger, still secret-free warning when pre-warm didn't
2256
- // complete cleanly — but the install itself still succeeds (exit unaffected).
2257
- errorLog(`Warning: could not pre-warm the version-pinned launcher bucket${prewarm.warning ? ` (${prewarm.warning})` : ""}. ` +
2258
- `The first MCP launch may pay a one-time cold install and could be slow. ${MCP_TIMEOUT_GUIDANCE}`);
2259
- }
2260
2260
  // ---- Step 4 — persist the credential ----
2261
2261
  if (bootstrapInviteMode) {
2262
2262
  // FAIL-CLOSED (unlike normal mode below). The pending secret IS the minted admin
@@ -2306,6 +2306,22 @@ export async function runInstallBridgeCli(argv, overrides = {}) {
2306
2306
  // observe GitHub as configured if the user connects it here, and running it after the
2307
2307
  // spawn would put two prompts on the same terminal at once.
2308
2308
  await offerGithubConnection(repoName, deps, log);
2309
+ // ---- Step 3b (await) — settle the background pre-warm before the session opens ----
2310
+ // The warm was kicked off right after the launch-command guards (above); await it
2311
+ // HERE so the bucket is settled before the spawned session's first MCP launch, but
2312
+ // AFTER all the work it overlapped with. Strictly fail-open — the exit code is
2313
+ // never affected, mirroring the original inline Step 3b behavior.
2314
+ const prewarm = await prewarmPromise;
2315
+ if (prewarm.ok) {
2316
+ log(" launcher bucket warmed (the first MCP launch will not pay a cold install).");
2317
+ log(` ${MCP_TIMEOUT_GUIDANCE}`);
2318
+ }
2319
+ else {
2320
+ // Escalate to a stronger, still secret-free warning when pre-warm didn't
2321
+ // complete cleanly — but the install itself still succeeds (exit unaffected).
2322
+ errorLog(`Warning: could not pre-warm the version-pinned launcher bucket${prewarm.warning ? ` (${prewarm.warning})` : ""}. ` +
2323
+ `The first MCP launch may pay a one-time cold install and could be slow. ${MCP_TIMEOUT_GUIDANCE}`);
2324
+ }
2309
2325
  // ---- Step 5 — spawn a fresh agent session for the agentic remainder ----
2310
2326
  log(`Step 5/5 — opening a ${agent.name} session for /install-bridge configuration + capability report…`);
2311
2327
  const terminal = detectTerminal(undefined, deps.env);
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.26";
2
+ export const VERSION = "0.2.27";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,9 +15,7 @@
15
15
  "!build/**/*.test.js",
16
16
  "!build/integration/**",
17
17
  "pipelines/",
18
- "smoke-test/",
19
18
  "design-assets/",
20
- "public/",
21
19
  "README.md",
22
20
  "docs/",
23
21
  "LICENSE"
Binary file
@@ -1 +0,0 @@
1
- {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}