@bridge_gpt/mcp-server 0.2.26 → 0.2.28

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 (70) hide show
  1. package/README.md +32 -24
  2. package/build/commands.generated.js +2 -2
  3. package/build/conductor/bridge-api-client.js +98 -1
  4. package/build/conductor/epic-reconcile.js +28 -0
  5. package/build/conductor/epic-runtime.js +28 -1
  6. package/build/conductor-bin.js +68 -9267
  7. package/build/conductor-claude-hook-bin.js +2 -172
  8. package/build/connect-github-api.js +9 -0
  9. package/build/connect-github.js +10 -0
  10. package/build/decision-page-template.js +2 -31
  11. package/build/doctor.js +2 -2
  12. package/build/env-flags.js +23 -0
  13. package/build/index.js +4699 -37043
  14. package/build/init.js +80 -56
  15. package/build/install-bridge.js +451 -153
  16. package/build/mcp-host-targets.js +12 -0
  17. package/build/readme.generated.js +1 -1
  18. package/build/setup-epic.js +198 -4
  19. package/build/start-tickets.js +25 -4
  20. package/build/tool-surface-gating.js +13 -2
  21. package/build/version.generated.js +1 -1
  22. package/package.json +3 -5
  23. package/design-assets/favicon/android-chrome-192x192.png +0 -0
  24. package/design-assets/favicon/android-chrome-512x512.png +0 -0
  25. package/design-assets/favicon/apple-touch-icon.png +0 -0
  26. package/design-assets/favicon/favicon-16x16.png +0 -0
  27. package/design-assets/favicon/favicon.ico +0 -0
  28. package/design-assets/favicon/site.webmanifest +0 -1
  29. package/public/css/main.min.css +0 -4971
  30. package/public/css/main.min.css.map +0 -1
  31. package/public/fonts/OFL.txt +0 -93
  32. package/public/fonts/SourceSansPro-Black.ttf +0 -0
  33. package/public/fonts/SourceSansPro-BlackItalic.ttf +0 -0
  34. package/public/fonts/SourceSansPro-Bold.ttf +0 -0
  35. package/public/fonts/SourceSansPro-BoldItalic.ttf +0 -0
  36. package/public/fonts/SourceSansPro-ExtraLight.ttf +0 -0
  37. package/public/fonts/SourceSansPro-ExtraLightItalic.ttf +0 -0
  38. package/public/fonts/SourceSansPro-Italic.ttf +0 -0
  39. package/public/fonts/SourceSansPro-Light.ttf +0 -0
  40. package/public/fonts/SourceSansPro-LightItalic.ttf +0 -0
  41. package/public/fonts/SourceSansPro-Regular.ttf +0 -0
  42. package/public/fonts/SourceSansPro-SemiBold.ttf +0 -0
  43. package/public/fonts/SourceSansPro-SemiBoldItalic.ttf +0 -0
  44. package/public/img/bridge-logo-160x51.webp +0 -0
  45. package/public/img/bridge-logo-300x92.webp +0 -0
  46. package/public/img/favicon/android-chrome-192x192.png +0 -0
  47. package/public/img/favicon/android-chrome-512x512.png +0 -0
  48. package/public/img/favicon/apple-touch-icon.png +0 -0
  49. package/public/img/favicon/favicon-16x16.png +0 -0
  50. package/public/img/favicon/favicon-32x32.png +0 -0
  51. package/public/img/favicon/favicon.ico +0 -0
  52. package/public/img/favicon/site.webmanifest +0 -1
  53. package/public/img/installation/bitbucket/app-password-1.png +0 -0
  54. package/public/img/installation/bitbucket/app-password-2.png +0 -0
  55. package/public/img/installation/bitbucket/create-token-1.png +0 -0
  56. package/public/img/installation/bitbucket/create-token-2.png +0 -0
  57. package/public/img/installation/bitbucket/webhook-1.png +0 -0
  58. package/public/img/installation/github/github-review-webhook.png +0 -0
  59. package/public/img/installation/jira/credentials/api-key.png +0 -0
  60. package/public/img/installation/jira/webhook/create-rule.png +0 -0
  61. package/public/img/installation/jira/webhook/project-settings.png +0 -0
  62. package/public/img/installation/jira/webhook/rule-create-1.png +0 -0
  63. package/public/img/installation/jira/webhook/rule-create-2.png +0 -0
  64. package/public/img/installation/jira/webhook/rule-create-3.png +0 -0
  65. package/public/img/installation/pinecone/pinecone-api-key.png +0 -0
  66. package/public/img/installation/pinecone/pinecone-index.png +0 -0
  67. package/public/js/main.min.js +0 -10409
  68. package/public/js/main.min.js.map +0 -1
  69. package/smoke-test/SMOKE-TEST.md +0 -560
  70. package/smoke-test/smoke-test-mcp.md +0 -23
@@ -24,8 +24,29 @@
24
24
  */
25
25
  import { readFile as fsReadFile, stat as fsStat } from "node:fs/promises";
26
26
  import os from "node:os";
27
+ import readline from "node:readline";
27
28
  import { approveEpicPlan, createEpicRun, fetchEpicRunState, resolveConductorBridgeApiAccess, storeEpicPlan, ConductorBridgeApiError, } from "./conductor/bridge-api-client.js";
29
+ import { validateBranchName } from "./base-ref.js";
28
30
  import { hashPlan } from "./conductor/plan.js";
31
+ /** Echoed single-line prompt on stderr (mirrors connect-github's helper). */
32
+ function defaultPromptLine(promptText) {
33
+ return new Promise((resolve) => {
34
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
35
+ // `rl.close()` emits `close` SYNCHRONOUSLY, so without this guard the close
36
+ // handler would settle the promise empty and discard a real answer. EOF must
37
+ // also resolve rather than deadlock a top-level await.
38
+ let answered = false;
39
+ rl.on("close", () => {
40
+ if (!answered)
41
+ resolve("");
42
+ });
43
+ rl.question(promptText, (answer) => {
44
+ answered = true;
45
+ rl.close();
46
+ resolve(answer.trim());
47
+ });
48
+ });
49
+ }
29
50
  export function createDefaultSetupEpicDeps() {
30
51
  return {
31
52
  env: process.env,
@@ -37,6 +58,8 @@ export function createDefaultSetupEpicDeps() {
37
58
  fetch: globalThis.fetch,
38
59
  log: (m) => console.log(m),
39
60
  errorLog: (m) => console.error(m),
61
+ isTTY: Boolean(process.stdin.isTTY),
62
+ promptLine: defaultPromptLine,
40
63
  };
41
64
  }
42
65
  /** User-facing usage text. */
@@ -54,6 +77,11 @@ export function getSetupEpicUsage() {
54
77
  "Options:",
55
78
  " --repo <name> Repo name (default: BAPI_REPO_NAME or .bridge/config)",
56
79
  " --plan-version <n> Assert the sidecar's plan_version equals <n>",
80
+ " --feature-branch <name> Run the epic on a dedicated feature branch.",
81
+ " The branch is created from the repository base branch",
82
+ " on origin, and every child-ticket PR targets it.",
83
+ " Omit (the default) to continue on the repository base",
84
+ " branch. Interactive runs are offered a proposal.",
57
85
  " --dry-run Validate and preview; make no mutating calls",
58
86
  " --json Emit a single JSON result object on stdout",
59
87
  " -h, --help Show this help",
@@ -69,6 +97,24 @@ function takeValue(argv, i, flag) {
69
97
  return null;
70
98
  return next;
71
99
  }
100
+ /**
101
+ * Normalize + validate a raw `--feature-branch` value.
102
+ *
103
+ * Trims the value; an explicitly blank value normalizes to `undefined` (no
104
+ * option), matching "omit to continue on the base branch". A nonblank value is
105
+ * validated with the shared {@link validateBranchName}; a malformed value yields
106
+ * a flag-specific error. Returns a discriminated result so the caller can report
107
+ * the error without consuming a following flag.
108
+ */
109
+ function parseFeatureBranchValue(raw) {
110
+ const trimmed = raw.trim();
111
+ if (trimmed === "")
112
+ return { ok: true, value: undefined };
113
+ const reason = validateBranchName(trimmed);
114
+ if (reason)
115
+ return { ok: false, error: `Invalid --feature-branch value: ${reason}` };
116
+ return { ok: true, value: trimmed };
117
+ }
72
118
  export function parseSetupEpicArgs(argv) {
73
119
  if (argv.includes("-h") || argv.includes("--help")) {
74
120
  return { status: "help", usage: getSetupEpicUsage() };
@@ -77,11 +123,32 @@ export function parseSetupEpicArgs(argv) {
77
123
  let planFile;
78
124
  let repo;
79
125
  let planVersion;
126
+ let featureBranch;
80
127
  let dryRun = false;
81
128
  let json = false;
82
129
  for (let i = 0; i < argv.length; i++) {
83
130
  const arg = argv[i];
131
+ // `--feature-branch=<name>` inline form (handled before the exact-match switch).
132
+ if (arg.startsWith("--feature-branch=")) {
133
+ const parsedFb = parseFeatureBranchValue(arg.slice("--feature-branch=".length));
134
+ if (!parsedFb.ok)
135
+ return { status: "error", message: parsedFb.error };
136
+ featureBranch = parsedFb.value;
137
+ continue;
138
+ }
84
139
  switch (arg) {
140
+ case "--feature-branch": {
141
+ // Do not consume a following flag as the value (Step 2.5).
142
+ const v = takeValue(argv, i, arg);
143
+ if (v === null)
144
+ return { status: "error", message: "--feature-branch requires a value." };
145
+ const parsedFb = parseFeatureBranchValue(v);
146
+ if (!parsedFb.ok)
147
+ return { status: "error", message: parsedFb.error };
148
+ featureBranch = parsedFb.value;
149
+ i++;
150
+ break;
151
+ }
85
152
  case "--epic-key": {
86
153
  const v = takeValue(argv, i, arg);
87
154
  if (v === null)
@@ -139,7 +206,7 @@ export function parseSetupEpicArgs(argv) {
139
206
  return { status: "error", message: "setup-epic requires --plan-file <path>." };
140
207
  return {
141
208
  status: "ok",
142
- options: { epicKey, planFile, repo, planVersion, dryRun, json },
209
+ options: { epicKey, planFile, repo, planVersion, featureBranch, dryRun, json },
143
210
  };
144
211
  }
145
212
  /**
@@ -258,6 +325,55 @@ function findCycle(keys, adjacency) {
258
325
  }
259
326
  return null;
260
327
  }
328
+ /** Propose a conductor epic feature branch name for an epic key (BAPI-655). */
329
+ function proposeFeatureBranchName(epicKey) {
330
+ return `epic/${epicKey}`;
331
+ }
332
+ /**
333
+ * Resolve the epic feature branch selection for this invocation.
334
+ *
335
+ * Returns a validated branch name, or `undefined` to continue on the repository
336
+ * base branch. Precedence:
337
+ *
338
+ * - An explicit `--feature-branch <name>` is already confirmed + validated at
339
+ * parse time and is returned unchanged (no prompt).
340
+ * - A non-interactive (piped/CI) run or a `--json` run never prompts and never
341
+ * blocks — it retains the absent-branch behavior.
342
+ * - An interactive run is shown the proposal (branch name, "create from the
343
+ * repository base branch" strategy, and the child-PR-targeting consequence)
344
+ * and may accept it, type a custom name, or decline to use the base branch.
345
+ * An edited nonblank value is re-validated; an invalid value is redisplayed
346
+ * for correction rather than silently rewritten.
347
+ */
348
+ async function resolveFeatureBranchSelection(opts, repoName, deps) {
349
+ if (opts.featureBranch !== undefined)
350
+ return opts.featureBranch;
351
+ if (!deps.isTTY || opts.json)
352
+ return undefined;
353
+ const proposed = proposeFeatureBranchName(opts.epicKey);
354
+ deps.errorLog("");
355
+ deps.errorLog(`Feature branch (optional) for epic ${opts.epicKey} on ${repoName}:`);
356
+ deps.errorLog(` Proposed: ${proposed}`);
357
+ deps.errorLog(` Strategy: create a new branch from the repository base branch`);
358
+ deps.errorLog(` Effect: every child-ticket PR will target this branch`);
359
+ for (;;) {
360
+ const answer = (await deps.promptLine(`Use feature branch? 'y' = ${proposed}, a name = custom, Enter = base branch: `)).trim();
361
+ if (answer === "")
362
+ return undefined; // decline → repository base branch
363
+ const lowered = answer.toLowerCase();
364
+ if (lowered === "n" || lowered === "no")
365
+ return undefined;
366
+ if (lowered === "y" || lowered === "yes")
367
+ return proposed;
368
+ // Otherwise the answer is an edited branch name.
369
+ const reason = validateBranchName(answer);
370
+ if (reason) {
371
+ deps.errorLog(` Invalid branch name: ${reason} Try again, or press Enter for the base branch.`);
372
+ continue; // redisplay for correction; never silently rewrite the value
373
+ }
374
+ return answer;
375
+ }
376
+ }
261
377
  function errorDetail(err) {
262
378
  if (err instanceof ConductorBridgeApiError) {
263
379
  const status = err.status !== undefined ? ` (HTTP ${err.status})` : "";
@@ -334,14 +450,35 @@ export async function runSetupEpicCli(argv, overrides = {}) {
334
450
  say(`Local hash: ${localHash}`);
335
451
  for (const w of warnings)
336
452
  say(` [warn] ${w}`);
453
+ // --- Feature branch selection (BAPI-655) --------------------------------
454
+ // Resolved AFTER local/access context is known but BEFORE any run-state read
455
+ // or mutating request, so malformed interactive input fails before network
456
+ // dispatch. Returns undefined to continue on the repository base branch.
457
+ const promptedInteractively = opts.featureBranch === undefined && deps.isTTY && !opts.json;
458
+ const featureBranch = await resolveFeatureBranchSelection(opts, access.repoName, deps);
459
+ if (featureBranch !== undefined) {
460
+ say(`Feature: ${featureBranch} (create from repository base branch on origin)`);
461
+ }
462
+ else if (promptedInteractively) {
463
+ // Ordinary, non-warning notice — only when an interactive operator declined.
464
+ say("Feature: none — continue using the repository base branch");
465
+ }
337
466
  // --- Step 0: pre-check ---------------------------------------------------
338
467
  // Never create on an ambiguous read. A wrong answer here mints a duplicate run.
339
468
  let existingRunId = null;
340
469
  let existingStatus = null;
470
+ let existingBaseBranch = null;
341
471
  try {
342
472
  const state = await fetchEpicRunState(access, opts.epicKey, deps.fetch);
343
473
  existingRunId = state.epic_run?.epic_run_id ?? null;
344
474
  existingStatus = state.epic_run?.status ?? null;
475
+ const existingPolicy = state.epic_run?.policy_json;
476
+ const existingBase = existingPolicy && typeof existingPolicy === "object"
477
+ ? existingPolicy.base_branch
478
+ : undefined;
479
+ existingBaseBranch = typeof existingBase === "string" && existingBase.trim() !== ""
480
+ ? existingBase
481
+ : null;
345
482
  }
346
483
  catch (err) {
347
484
  if (err instanceof ConductorBridgeApiError && err.status === 404) {
@@ -361,6 +498,19 @@ export async function runSetupEpicCli(argv, overrides = {}) {
361
498
  return 1;
362
499
  }
363
500
  }
501
+ // --- Feature-branch conflict guard against an existing live run ----------
502
+ // A selected branch that disagrees with the existing run's stored base branch
503
+ // must fail closed rather than patch or silently change that run. A matching
504
+ // branch is allowed (the rerun re-validates remote provisioning at approval);
505
+ // supplying no branch preserves the existing run's policy untouched.
506
+ if (existingRunId && featureBranch !== undefined && existingBaseBranch !== featureBranch) {
507
+ deps.errorLog(`Epic ${opts.epicKey} already has a live run (${existingRunId}) whose feature ` +
508
+ `branch is ${existingBaseBranch ? `'${existingBaseBranch}'` : "unset (base branch)"}, ` +
509
+ `which conflicts with the requested '${featureBranch}'. setup-epic will not ` +
510
+ `retarget or rebuild an existing run. Re-run without --feature-branch to reuse ` +
511
+ `it unchanged, or abandon the run to start over on a new branch.`);
512
+ return 1;
513
+ }
364
514
  if (opts.dryRun) {
365
515
  say("");
366
516
  say("[dry-run] No changes made. Would:");
@@ -370,6 +520,9 @@ export async function runSetupEpicCli(argv, overrides = {}) {
370
520
  else {
371
521
  say(` - POST /jira/epic-runs/runs (create run for ${opts.epicKey})`);
372
522
  }
523
+ if (featureBranch !== undefined) {
524
+ say(` - feature branch: ${featureBranch} (create from repository base branch; no request made in dry-run)`);
525
+ }
373
526
  say(` - POST /jira/epic-runs/runs/${opts.epicKey}/plan (v${plan.plan_version})`);
374
527
  say(` - POST /jira/epic-runs/runs/${opts.epicKey}/approve-plan (v${plan.plan_version})`);
375
528
  if (opts.json) {
@@ -380,6 +533,8 @@ export async function runSetupEpicCli(argv, overrides = {}) {
380
533
  plan_version: plan.plan_version,
381
534
  local_plan_hash: localHash,
382
535
  existing_run_id: existingRunId,
536
+ // Only present for a feature-branch run — no-feature JSON is unchanged.
537
+ ...(featureBranch !== undefined ? { feature_branch: featureBranch } : {}),
383
538
  warnings,
384
539
  }, null, 2));
385
540
  }
@@ -398,13 +553,20 @@ export async function runSetupEpicCli(argv, overrides = {}) {
398
553
  plan_approved: false,
399
554
  warnings,
400
555
  };
556
+ if (featureBranch !== undefined)
557
+ result.feature_branch = featureBranch;
401
558
  // --- Step 1: create (only when there is no live run) ---------------------
402
559
  if (existingRunId) {
403
560
  say(`Run: reusing ${existingRunId} (status: ${existingStatus})`);
404
561
  }
405
562
  else {
406
563
  try {
407
- const run = await createEpicRun(access, { epicKey: opts.epicKey }, deps.fetch);
564
+ // Persist the confirmed feature branch as policy_json.base_branch ONLY when
565
+ // one was selected; otherwise keep the exact legacy create request shape.
566
+ const createRequest = featureBranch !== undefined
567
+ ? { epicKey: opts.epicKey, policyJson: { base_branch: featureBranch } }
568
+ : { epicKey: opts.epicKey };
569
+ const run = await createEpicRun(access, createRequest, deps.fetch);
408
570
  result.epic_run_id = run.epic_run_id;
409
571
  result.status = run.status;
410
572
  result.run_created = true;
@@ -439,9 +601,29 @@ export async function runSetupEpicCli(argv, overrides = {}) {
439
601
  deps.errorLog(`Failed to store the plan: ${errorDetail(err)}`);
440
602
  return 1;
441
603
  }
442
- // --- Step 4: approve -----------------------------------------------------
604
+ // --- Step 4: approve (also provisions the feature branch server-side) -----
605
+ // For a feature-branch run the backend creates/validates the branch on origin
606
+ // as an approval prerequisite. Provisioning runs and completes BEFORE the
607
+ // approval (ticket-seeding + CAS activation) step server-side, so only a
608
+ // failure the backend tags with the structured FEATURE_BRANCH_PROVISIONING
609
+ // error_code is actually a branch/credentials problem — key the distinct
610
+ // message off that code, not merely off whether --feature-branch was passed,
611
+ // so an unrelated approval failure (e.g. ticket-seeding, a masked 500) is not
612
+ // misattributed to provisioning.
613
+ if (featureBranch !== undefined) {
614
+ say(`Branch: creating or validating ${featureBranch} on origin…`);
615
+ }
443
616
  const approval = await approveEpicPlan(access, { epicKey: opts.epicKey, planVersion: plan.plan_version }, deps.fetch).catch((err) => {
444
- deps.errorLog(`Failed to approve the plan: ${errorDetail(err)}`);
617
+ if (featureBranch !== undefined &&
618
+ err instanceof ConductorBridgeApiError &&
619
+ err.errorCode === "FEATURE_BRANCH_PROVISIONING") {
620
+ deps.errorLog(`Failed to provision the feature branch '${featureBranch}' — child-ticket ` +
621
+ `dispatch has NOT started. Correct repository access or the branch ` +
622
+ `configuration, then re-run setup-epic.\nDetail: ${errorDetail(err)}`);
623
+ }
624
+ else {
625
+ deps.errorLog(`Failed to approve the plan: ${errorDetail(err)}`);
626
+ }
445
627
  return null;
446
628
  });
447
629
  if (approval === null)
@@ -451,6 +633,18 @@ export async function runSetupEpicCli(argv, overrides = {}) {
451
633
  result.plan_hash = approval.plan_hash;
452
634
  result.status = "active";
453
635
  say(`Plan: approved v${plan.plan_version}`);
636
+ const prov = approval.featureBranchProvisioning;
637
+ if (prov) {
638
+ result.feature_branch_provisioning = prov;
639
+ if (prov.status === "created") {
640
+ say(`Branch: ready on origin — created '${prov.feature_branch}' from ` +
641
+ `'${prov.source_branch}' at ${prov.source_sha}`);
642
+ }
643
+ else {
644
+ say(`Branch: '${prov.feature_branch}' already exists — validated, unchanged ` +
645
+ `(the remote ref was not moved or reset); head ${prov.remote_head_sha}`);
646
+ }
647
+ }
454
648
  }
455
649
  else if (approval.reason === "multiple_active_runs") {
456
650
  deps.errorLog(`Epic ${opts.epicKey} has MULTIPLE active runs — the plan could not be approved ` +
@@ -1233,9 +1233,13 @@ export function buildITermAppleScript(shellCommand, title, badgeText) {
1233
1233
  * failure for expected spawn errors (never throws).
1234
1234
  */
1235
1235
  export async function spawnMacOSTerminalTab(deps, terminal, shellCommand, context) {
1236
- const title = terminalTitleForTicket(context?.key ?? "");
1236
+ // BAPI-657: an explicit `context.title` overrides the key-derived title; absent it,
1237
+ // real ticket tabs keep `<KEY> Implementation`. The iTerm badge follows the same
1238
+ // rule — the override when supplied, otherwise the existing key-based badge.
1239
+ const title = context?.title ?? terminalTitleForTicket(context?.key ?? "");
1240
+ const badgeText = context?.title ?? (context?.key || undefined);
1237
1241
  const script = terminal === "iterm"
1238
- ? buildITermAppleScript(shellCommand, title, context?.key || undefined)
1242
+ ? buildITermAppleScript(shellCommand, title, badgeText)
1239
1243
  : buildTerminalAppleScript(shellCommand, title);
1240
1244
  const result = await deps.runCommand("osascript", ["-e", script]);
1241
1245
  if (commandSucceeded(result))
@@ -1309,7 +1313,9 @@ export async function spawnWindowsTerminalTab(deps, _terminal, shellCommand, con
1309
1313
  error: "Windows spawner requires a worktreePath context to open a tab.",
1310
1314
  };
1311
1315
  }
1312
- const title = terminalTitleForTicket(context?.key ?? "");
1316
+ // BAPI-657: honor an explicit title override for both the Windows Terminal tab and
1317
+ // the PowerShell fallback window; absent it, keep the key-derived ticket title.
1318
+ const title = context?.title ?? terminalTitleForTicket(context?.key ?? "");
1313
1319
  if (await isCommandOnPath(deps, WINDOWS_TERMINAL_COMMAND)) {
1314
1320
  const args = buildWindowsTerminalArgs(worktreePath, shellCommand, title);
1315
1321
  const result = await deps.runCommand(WINDOWS_TERMINAL_COMMAND, args);
@@ -1365,6 +1371,17 @@ export function sanitizeTmuxName(value) {
1365
1371
  export function tmuxWindowNameForTicket(key) {
1366
1372
  return terminalTitleForTicket(sanitizeTmuxName(key));
1367
1373
  }
1374
+ /**
1375
+ * A tmux WINDOW label derived from an explicit `context.title` override (BAPI-657).
1376
+ * Unlike a session identifier, a tmux window name may contain spaces, so this keeps
1377
+ * them (`"Bridge Install"` stays `"Bridge Install"`) while stripping only the
1378
+ * tmux-target-hostile `.`/`:` and collapsing whitespace runs. Falls back to
1379
+ * `"session"` if the title reduces to empty.
1380
+ */
1381
+ export function tmuxWindowLabelFromTitle(title) {
1382
+ const cleaned = title.replace(/[.:]+/g, " ").replace(/\s+/g, " ").trim();
1383
+ return cleaned.length > 0 ? cleaned : "session";
1384
+ }
1368
1385
  /** Resolve the tmux session-name prefix (env override, else the default). */
1369
1386
  export function tmuxSessionPrefix(deps) {
1370
1387
  const override = deps.env[TMUX_SESSION_OVERRIDE_ENV];
@@ -1418,7 +1435,11 @@ export async function spawnLinuxTmuxTerminalTab(deps, _terminal, shellCommand, c
1418
1435
  };
1419
1436
  }
1420
1437
  const session = tmuxSessionNameForTicket(deps, key);
1421
- const window = tmuxWindowNameForTicket(key);
1438
+ // BAPI-657: an explicit title overrides only the WINDOW label; the SESSION
1439
+ // identifier stays key-derived so `tmux attach -t <session>` is unchanged.
1440
+ const window = context?.title
1441
+ ? tmuxWindowLabelFromTitle(context.title)
1442
+ : tmuxWindowNameForTicket(key);
1422
1443
  const paneCommand = buildTmuxPaneCommand(shellCommand);
1423
1444
  const hasSession = await deps.runCommand(TMUX_COMMAND, ["has-session", "-t", session]);
1424
1445
  const args = commandSucceeded(hasSession)
@@ -36,8 +36,19 @@ import { getMethodLiteral } from "@modelcontextprotocol/sdk/server/zod-json-sche
36
36
  export const RECOGNIZED_TOOL_SURFACE_SCHEMA_VERSIONS = new Set([1]);
37
37
  /** The single recognized schema version (convenience export for tests). */
38
38
  export const TOOL_SURFACE_SCHEMA_VERSION = 1;
39
- /** Absolute end-to-end deadline for one probe (header resolve + fetch + parse). */
40
- export const TOOL_SURFACE_PROBE_DEADLINE_MS = 500;
39
+ /**
40
+ * Absolute end-to-end deadline for one probe (header resolve + fetch + parse).
41
+ *
42
+ * The server handler does a catalog read plus two sequential Postgres round-trips
43
+ * and routinely lands at 600-900ms on Heroku, so a 500ms deadline aborted nearly
44
+ * every probe mid-response — the socket teardown surfaced as a continuous stream
45
+ * of Heroku `H27 "Client Request Interrupted"` router warnings, and the gating
46
+ * feature never took effect because the client always fell open on timeout. The
47
+ * deadline sits comfortably above observed server p99 with headroom, while
48
+ * staying well under the 12-18s poll interval so a slow probe never overlaps the
49
+ * next one.
50
+ */
51
+ export const TOOL_SURFACE_PROBE_DEADLINE_MS = 2_500;
41
52
  /** Inclusive lower bound of the recurring-poll jitter window. */
42
53
  export const TOOL_SURFACE_POLL_MIN_MS = 12_000;
43
54
  /** Inclusive upper bound of the recurring-poll jitter window. */
@@ -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.28";
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.28",
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"
@@ -27,8 +25,8 @@
27
25
  "check:version-generated": "node scripts/bundle-version.js && node scripts/check-version-generated.js",
28
26
  "postbuild": "node scripts/prepend-shebang.cjs",
29
27
  "start": "node build/index.js",
30
- "test": "node --test --test-force-exit build/pipeline-utils.test.js build/backend-warnings.test.js build/update-check.test.js build/cli-upgrade.test.js build/decision-page-schema.test.js build/decision-page-template.test.js build/bundle-pipelines.test.js build/instructions-contract.test.js build/pipeline-orchestrator-persistence.test.js build/pipeline-orchestrator-execution.test.js build/pipeline-orchestrator-integration.test.js build/index-static.test.js build/config-fields.static.test.js build/request-brainstorm.static.test.js build/execute-plan-instructions.static.test.js build/index-resolvers.test.js build/index-project-root.test.js build/index-pipelines.test.js build/index.test.js build/bridge-config.test.js build/credential-store.test.js build/agent-config-credential-migration.test.js build/mcp-invoke.test.js build/mcp-provisioning.test.js build/mcp-server-invocation.test.js build/third-party-mcp-targets.test.js build/git-ignore-utils.test.js build/credential-materialization.test.js build/mcp-registration-doctor.test.js build/mcp-host-targets.test.js build/mcp-install-state.test.js build/mcp-host-config.test.js build/secret-safety.test.js build/base-ref.test.js build/pr-base-contract.test.js build/start-tickets.test.js build/review-tickets.test.js build/start-tickets-base-branch.test.js build/agent-registry.test.js build/agent-registry.model-routing.test.js build/start-tickets.shell-model-routing.test.js build/start-tickets.bridge-api-model-routing.test.js build/start-tickets.tier-fetch-model-routing.test.js build/start-tickets.resolve-model-routing.test.js build/start-tickets.orchestrate-model-routing.test.js build/start-tickets.routing-diagnostics.test.js build/start-tickets-repo.test.js build/start-tickets-credential-invariants.static.test.js build/credentials-cli.test.js build/start-tickets-prereqs.test.js build/regression-check.test.js build/doctor.test.js build/install-doctor.test.js build/install-bridge.test.js build/install-bridge-invite.test.js build/install-bridge-prompt.test.js build/install-bridge-tools.test.js build/init.test.js build/init-docs.test.js build/resolveUploadAttachment.test.js build/package-static.test.js build/chain-utils.test.js build/chain-orchestrator.test.js build/scheduler-backends/types.test.js build/scheduler-backends/escaping.test.js build/scheduler-backends/launchd.test.js build/scheduler-backends/task-scheduler.test.js build/scheduler-backends/systemd-user.test.js build/scheduler-backends/at-fallback.test.js build/scheduler-backends/index.test.js build/command-catalog.test.js build/scheduled-prompt.test.js build/agent-launchers/claude.test.js build/agent-launchers/cursor.test.js build/agent-launchers/index.test.js build/schedule-store.test.js build/schedule-run.test.js build/setup-epic.test.js build/conductor-bundle-artifacts.test.js build/conductor-bundle-cli.test.js build/agent-capabilities/cli.test.js build/agent-capabilities/runner.test.js build/agent-capabilities/probes.test.js build/agent-capabilities/probe-context.test.js build/agent-capabilities/reporter.test.js build/conductor/taxonomy-and-errors.test.js build/conductor/github-mergeability.test.js build/conductor/merge-conflict-routing.test.js build/conductor/redaction-normalization.test.js build/conductor/claude-hook.test.js build/conductor/git-ci-types.test.js build/conductor/done-gate.test.js build/conductor/git-ci-taxonomy-payload.test.js build/conductor/bridge-api-client.test.js build/conductor/plan.test.js build/conductor/producer-ledger.test.js build/conductor/spec-review-producer.test.js build/conductor/git-producer.test.js build/conductor/git-hooks.test.js build/conductor/store-migration.test.js build/conductor/pr-discovery.test.js build/conductor/pr-ci-producer.test.js build/conductor/pr-review-producer.test.js build/conductor/doctor.test.js build/conductor/deny-enforcement-preflight.test.js build/conductor/errors.test.js build/conductor/store.test.js build/conductor/index-poll-ci-producer.test.js build/start-tickets-conductor.test.js build/start-tickets-conductor.spawn.test.js build/start-tickets-conductor-node-propagation.test.js build/start-tickets.non-mutating-base.test.js build/start-tickets-live-source-guard.test.js build/conductor/worker-ledger-cli.test.js build/conductor/supervisor-config.test.js build/conductor/supervisor-ledger.test.js build/conductor/supervisor-state-reducer.test.js build/conductor/supervisor-housekeeping-projection.test.js build/conductor/supervisor-escalation.test.js build/conductor/supervisor-retired-judgment.static.test.js build/conductor/supervisor-runtime.test.js build/conductor/supervisor-store-projection.test.js build/conductor/supervisor-cli.test.js build/conductor/supervisor-start-tickets.test.js build/conductor/supervisor-message-relay.test.js build/conductor/supervisor-state-message-events.test.js build/conductor/store-message-relay.test.js build/start-tickets-message-relay.test.js build/conductor/event-accessors.test.js build/conductor/merge-ledger.test.js build/conductor/deterministic-completion.static.test.js build/conductor/deterministic-completion.integration.test.js build/conductor/local-merge.test.js build/conductor/local-merge.static.test.js build/conductor/supervisor-merge.test.js build/conductor/bridge-api-merge-client.test.js build/conductor/bridge-api-epic-client.test.js build/conductor/supervisor-merge-runtime-state.test.js build/conductor/epic-state.test.js build/executor/claude-settings.test.js build/executor/cli.test.js build/executor/credentials.test.js build/executor/echo-acceptance.test.js build/executor/env.test.js build/executor/base-branch.test.js build/executor/heartbeat.test.js build/executor/http-client.test.js build/executor/job-runner.test.js build/executor/job-runner.payload-timeout.test.js build/executor/worker-finalization.test.js build/executor/job-runner.static.test.js build/executor/prompt-spec.test.js build/executor/job-types.test.js build/executor/recovery-job.static.test.js build/executor/observation.test.js build/executor/permissions.test.js build/executor/preflight.test.js build/executor/process.test.js build/executor/results.test.js build/executor/runner.test.js build/executor/terminal-mutation.test.js build/executor/worker-command.test.js build/executor/worktree-core.test.js build/executor/worktree.test.js build/executor/job-errors.test.js build/executor/worktree-inspection.test.js build/executor/resume-pre-spawn.test.js build/executor/verdict-artifact.test.js build/executor/worker-log.test.js build/executor/job-log-registry.test.js build/executor/viewer-tabs.test.js build/executor/watch-cli.test.js build/executor/merge-job.test.js build/executor/job-runner.merge.test.js build/executor/worktree-gc.test.js build/executor/runner.job-behaviors.test.js build/conductor/cli-freeze.test.js build/conductor/epic-reconcile.test.js build/conductor/epic-runtime.test.js build/conductor/epic-tick-sequence.test.js build/conductor/epic-runtime-post-action.test.js build/conductor/file-scope-guard.test.js build/conductor/file-scope-guard.integration.test.js build/mcp-profile.test.js build/mcp-profile-registration.test.js build/env-flags.test.js build/bridge-api-urls.test.js build/tool-surface-gating.test.js build/tools-budget.test.js build/visual-diff-worker.test.js build/visual-diff.test.js build/estimate-epic.test.js build/integration/measure-tools.test.js build/sfcc/config.test.js build/sfcc/ocapi-shape.test.js build/sfcc/output.test.js build/sfcc/credentials.test.js build/sfcc/ocapi-write-faults.test.js build/sfcc/write-guard.test.js build/sfcc/write-grants.test.js build/sfcc/write-result.test.js build/sfcc/writes.test.js build/sfcc/writes-system-object-payloads.test.js build/sfcc/writes-payloads.test.js build/sfcc/log-gate.test.js build/sfcc/log-query.test.js && node --experimental-test-module-mocks --test --test-force-exit build/index-heavy-read-truncation.test.js build/index-artifacts.test.js build/automation-progress.test.js build/index.review-rounds.test.js build/index-brainstorm-filenames.test.js build/index-output-path.test.js build/index-generate-decision-page.test.js build/index-generate-decision-page.integration.test.js build/visual-diff.registration.test.js build/visual-diff.attachment-adapter.test.js build/attachment-download.test.js build/attachment-upload.test.js build/conductor/paths.test.js build/conductor/store-lifecycle.test.js build/conductor/store-queries.test.js build/conductor/tools.test.js build/conductor/cli.test.js build/conductor/security-regressions.test.js build/conductor/git-inspection.test.js build/conductor/tools-done-gate.test.js build/conductor/pr-ci-producer-emit-seam.test.js build/conductor/cli-git-hooks.test.js build/sfcc/client.test.js build/sfcc/tool-wrapper.test.js build/sfcc/setup-status.test.js build/sfcc/permissions.test.js build/sfcc/register.test.js build/sfcc/reads-system-object.test.js build/sfcc/reads-custom-object-def.test.js build/sfcc/reads-site-preference.test.js build/sfcc/writes-system-object.test.js build/sfcc/writes-custom-object-def.test.js build/sfcc/writes-site-preference.test.js build/connect-github.test.js",
31
- "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/start-tickets.integration.test.js build/integration/start-tickets-tier-handoff.integration.test.js build/integration/doctor.integration.test.js build/integration/agent-capabilities.integration.test.js build/integration/conductor-producer.integration.test.js build/integration/conductor-message-relay.integration.test.js build/integration/executor-http-runner.integration.test.js build/integration/executor-job-behaviors.integration.test.js build/integration/executor-recovery-jobs.integration.test.js build/integration/executor-spec-review-prompt.integration.test.js build/integration/resume-pre-spawn.git.integration.test.js build/integration/worker-finalization-origin.integration.test.js build/integration/post-remediation-merge-ci-wait.integration.test.js build/integration/executor-merge-supervision.integration.test.js build/integration/attachment-binary-roundtrip.integration.test.js build/integration/dependent-ticket-fresh-base.integration.test.js build/integration/execute-plan-instructions.integration.test.js build/integration/conductor-bundle-artifacts.integration.test.js build/integration/install-bridge-repo-resolution.integration.test.js build/integration/capability-report-contract.integration.test.js build/integration/request-brainstorm-general.integration.test.js",
28
+ "test": "node --test --test-force-exit build/pipeline-utils.test.js build/backend-warnings.test.js build/update-check.test.js build/cli-upgrade.test.js build/decision-page-schema.test.js build/decision-page-template.test.js build/bundle-pipelines.test.js build/instructions-contract.test.js build/pipeline-orchestrator-persistence.test.js build/pipeline-orchestrator-execution.test.js build/pipeline-orchestrator-integration.test.js build/index-static.test.js build/config-fields.static.test.js build/request-brainstorm.static.test.js build/execute-plan-instructions.static.test.js build/index-resolvers.test.js build/index-project-root.test.js build/index-pipelines.test.js build/index.test.js build/bridge-config.test.js build/credential-store.test.js build/agent-config-credential-migration.test.js build/mcp-invoke.test.js build/mcp-provisioning.test.js build/mcp-server-invocation.test.js build/third-party-mcp-targets.test.js build/git-ignore-utils.test.js build/credential-materialization.test.js build/mcp-registration-doctor.test.js build/mcp-host-targets.test.js build/mcp-install-state.test.js build/mcp-host-config.test.js build/secret-safety.test.js build/base-ref.test.js build/pr-base-contract.test.js build/start-tickets.test.js build/review-tickets.test.js build/start-tickets-base-branch.test.js build/agent-registry.test.js build/agent-registry.model-routing.test.js build/start-tickets.shell-model-routing.test.js build/start-tickets.bridge-api-model-routing.test.js build/start-tickets.tier-fetch-model-routing.test.js build/start-tickets.resolve-model-routing.test.js build/start-tickets.orchestrate-model-routing.test.js build/start-tickets.routing-diagnostics.test.js build/start-tickets-repo.test.js build/start-tickets-credential-invariants.static.test.js build/credentials-cli.test.js build/start-tickets-prereqs.test.js build/regression-check.test.js build/doctor.test.js build/install-doctor.test.js build/install-bridge.test.js build/install-bridge-invite.test.js build/install-bridge-prompt.test.js build/install-bridge-tools.test.js build/init.test.js build/init-docs.test.js build/resolveUploadAttachment.test.js build/package-static.test.js build/chain-utils.test.js build/chain-orchestrator.test.js build/scheduler-backends/types.test.js build/scheduler-backends/escaping.test.js build/scheduler-backends/launchd.test.js build/scheduler-backends/task-scheduler.test.js build/scheduler-backends/systemd-user.test.js build/scheduler-backends/at-fallback.test.js build/scheduler-backends/index.test.js build/command-catalog.test.js build/scheduled-prompt.test.js build/agent-launchers/claude.test.js build/agent-launchers/cursor.test.js build/agent-launchers/index.test.js build/schedule-store.test.js build/schedule-run.test.js build/setup-epic.test.js build/conductor-bundle-artifacts.test.js build/conductor-bundle-cli.test.js build/agent-capabilities/cli.test.js build/agent-capabilities/runner.test.js build/agent-capabilities/probes.test.js build/agent-capabilities/probe-context.test.js build/agent-capabilities/reporter.test.js build/conductor/taxonomy-and-errors.test.js build/conductor/github-mergeability.test.js build/conductor/merge-conflict-routing.test.js build/conductor/redaction-normalization.test.js build/conductor/claude-hook.test.js build/conductor/git-ci-types.test.js build/conductor/done-gate.test.js build/conductor/git-ci-taxonomy-payload.test.js build/conductor/bridge-api-client.test.js build/conductor/plan.test.js build/conductor/producer-ledger.test.js build/conductor/spec-review-producer.test.js build/conductor/git-producer.test.js build/conductor/git-hooks.test.js build/conductor/store-migration.test.js build/conductor/pr-discovery.test.js build/conductor/pr-ci-producer.test.js build/conductor/pr-review-producer.test.js build/conductor/doctor.test.js build/conductor/deny-enforcement-preflight.test.js build/conductor/errors.test.js build/conductor/store.test.js build/conductor/index-poll-ci-producer.test.js build/start-tickets-conductor.test.js build/start-tickets-conductor.spawn.test.js build/start-tickets-conductor-node-propagation.test.js build/start-tickets.non-mutating-base.test.js build/start-tickets-live-source-guard.test.js build/conductor/worker-ledger-cli.test.js build/conductor/supervisor-config.test.js build/conductor/supervisor-ledger.test.js build/conductor/supervisor-state-reducer.test.js build/conductor/supervisor-housekeeping-projection.test.js build/conductor/supervisor-escalation.test.js build/conductor/supervisor-retired-judgment.static.test.js build/conductor/supervisor-runtime.test.js build/conductor/supervisor-store-projection.test.js build/conductor/supervisor-cli.test.js build/conductor/supervisor-start-tickets.test.js build/conductor/supervisor-message-relay.test.js build/conductor/supervisor-state-message-events.test.js build/conductor/store-message-relay.test.js build/start-tickets-message-relay.test.js build/conductor/event-accessors.test.js build/conductor/merge-ledger.test.js build/conductor/deterministic-completion.static.test.js build/conductor/deterministic-completion.integration.test.js build/conductor/local-merge.test.js build/conductor/local-merge.static.test.js build/conductor/supervisor-merge.test.js build/conductor/bridge-api-merge-client.test.js build/conductor/bridge-api-epic-client.test.js build/conductor/supervisor-merge-runtime-state.test.js build/conductor/epic-state.test.js build/executor/claude-settings.test.js build/executor/cli.test.js build/executor/credentials.test.js build/executor/echo-acceptance.test.js build/executor/env.test.js build/executor/base-branch.test.js build/executor/heartbeat.test.js build/executor/http-client.test.js build/executor/job-runner.test.js build/executor/job-runner.payload-timeout.test.js build/executor/worker-finalization.test.js build/executor/job-runner.static.test.js build/executor/prompt-spec.test.js build/executor/job-types.test.js build/executor/recovery-job.static.test.js build/executor/observation.test.js build/executor/permissions.test.js build/executor/preflight.test.js build/executor/process.test.js build/executor/results.test.js build/executor/runner.test.js build/executor/terminal-mutation.test.js build/executor/worker-command.test.js build/executor/worktree-core.test.js build/executor/worktree.test.js build/executor/job-errors.test.js build/executor/worktree-inspection.test.js build/executor/resume-pre-spawn.test.js build/executor/verdict-artifact.test.js build/executor/worker-log.test.js build/executor/job-log-registry.test.js build/executor/viewer-tabs.test.js build/executor/watch-cli.test.js build/executor/merge-job.test.js build/executor/job-runner.merge.test.js build/executor/worktree-gc.test.js build/executor/runner.job-behaviors.test.js build/conductor/cli-freeze.test.js build/conductor/epic-reconcile.test.js build/conductor/epic-runtime.test.js build/conductor/epic-tick-sequence.test.js build/conductor/epic-runtime-post-action.test.js build/conductor/file-scope-guard.test.js build/conductor/file-scope-guard.integration.test.js build/mcp-profile.test.js build/mcp-profile-registration.test.js build/env-flags.test.js build/bridge-api-urls.test.js build/tool-surface-gating.test.js build/tools-budget.test.js build/visual-diff-worker.test.js build/visual-diff.test.js build/estimate-epic.test.js build/integration/measure-tools.test.js build/sfcc/config.test.js build/sfcc/ocapi-shape.test.js build/sfcc/output.test.js build/sfcc/credentials.test.js build/sfcc/ocapi-write-faults.test.js build/sfcc/write-guard.test.js build/sfcc/write-grants.test.js build/sfcc/write-result.test.js build/sfcc/writes.test.js build/sfcc/writes-system-object-payloads.test.js build/sfcc/writes-payloads.test.js build/sfcc/log-gate.test.js build/sfcc/log-query.test.js && node --experimental-test-module-mocks --test --test-force-exit build/index-heavy-read-truncation.test.js build/index-artifacts.test.js build/automation-progress.test.js build/recovery-formatting.test.js build/wait-for-result.test.js build/ticket-wait-recovery.test.js build/council-wait-recovery.test.js build/index.review-rounds.test.js build/index-brainstorm-filenames.test.js build/index-output-path.test.js build/index-generate-decision-page.test.js build/index-generate-decision-page.integration.test.js build/visual-diff.registration.test.js build/visual-diff.attachment-adapter.test.js build/attachment-download.test.js build/attachment-upload.test.js build/conductor/paths.test.js build/conductor/store-lifecycle.test.js build/conductor/store-queries.test.js build/conductor/tools.test.js build/conductor/cli.test.js build/conductor/security-regressions.test.js build/conductor/git-inspection.test.js build/conductor/tools-done-gate.test.js build/conductor/pr-ci-producer-emit-seam.test.js build/conductor/cli-git-hooks.test.js build/sfcc/client.test.js build/sfcc/tool-wrapper.test.js build/sfcc/setup-status.test.js build/sfcc/permissions.test.js build/sfcc/register.test.js build/sfcc/reads-system-object.test.js build/sfcc/reads-custom-object-def.test.js build/sfcc/reads-site-preference.test.js build/sfcc/writes-system-object.test.js build/sfcc/writes-custom-object-def.test.js build/sfcc/writes-site-preference.test.js build/connect-github.test.js build/connect-github-api.test.js",
29
+ "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/start-tickets.integration.test.js build/integration/start-tickets-tier-handoff.integration.test.js build/integration/doctor.integration.test.js build/integration/agent-capabilities.integration.test.js build/integration/conductor-producer.integration.test.js build/integration/conductor-message-relay.integration.test.js build/integration/executor-http-runner.integration.test.js build/integration/executor-job-behaviors.integration.test.js build/integration/executor-recovery-jobs.integration.test.js build/integration/executor-spec-review-prompt.integration.test.js build/integration/resume-pre-spawn.git.integration.test.js build/integration/worker-finalization-origin.integration.test.js build/integration/post-remediation-merge-ci-wait.integration.test.js build/integration/executor-merge-supervision.integration.test.js build/integration/attachment-binary-roundtrip.integration.test.js build/integration/dependent-ticket-fresh-base.integration.test.js build/integration/execute-plan-instructions.integration.test.js build/integration/conductor-bundle-artifacts.integration.test.js build/integration/install-bridge-repo-resolution.integration.test.js build/integration/capability-report-contract.integration.test.js build/integration/request-brainstorm-general.integration.test.js build/integration/request-council-trigger-drop.integration.test.js",
32
30
  "test:smoke": "node --test build/integration/packaged-cli-smoke.test.js",
33
31
  "prepublishOnly": "node scripts/bundle-assets.js && npm run build && node scripts/verify-shebang.cjs"
34
32
  },
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"}