@bridge_gpt/mcp-server 0.2.60 → 0.2.62

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.
@@ -78,7 +78,7 @@
78
78
  import { readFile as fsReadFile, stat as fsStat } from "node:fs/promises";
79
79
  import os from "node:os";
80
80
  import { createProductionEpicIntegrationGhRunner, ensureEpicIntegrationPullRequest, formatEpicIntegrationPullRequestOutcome, } from "./epic-integration-pr.js";
81
- import { approveEpicPlan, buildConductorJiraUrl, conductorGetHeaders, createEpicRunWithDisposition, fetchConductorReadiness, fetchEpicRunState, replaceEpicRunPolicy, resolveConductorBridgeApiAccess, storeEpicPlan, validateEpicPlan, ConductorBridgeApiError, } from "./conductor/bridge-api-client.js";
81
+ import { approveEpicPlan, buildConductorJiraUrl, conductorGetHeaders, createEpicRunWithDisposition, decodeCanonicalProvenance, fetchConductorReadiness, fetchEpicRunState, replaceEpicRunPolicy, resolveConductorBridgeApiAccess, resolveEpicLaunchBridgeApiAccess, storeEpicPlan, validateEpicPlan, ConductorBridgeApiError, } from "./conductor/bridge-api-client.js";
82
82
  // BAPI-1133 (A2) — the single legacy-to-typed merge-execution-mode mapping and
83
83
  // its total legacy resolver live in `merge-pull-request.ts` (no import cycle:
84
84
  // that module never imports from `setup-epic.ts`). Reused here rather than
@@ -97,7 +97,7 @@ import { validateBranchName } from "./base-ref.js";
97
97
  // conductor can drift on a field name or an endpoint shape. No MCP tool, no
98
98
  // input-schema field, and no description changes.
99
99
  import { getConfigFieldBaseBranch, getIndexScopeLifecycle, getParseStatus, } from "./epic-shared/bridge-client.js";
100
- import { boundScopeLifecycleLabel, createExecFileRunCommand, ensureCommitResolvableLocally, normalizeCommitSha, performExactIndexScopeCut, pollIndexScopeLifecycle, readRemoteBranchHead, runGit, SCOPE_BOOTSTRAP_POLL_INTERVAL_MS, SCOPE_LIFECYCLE_LABELS, } from "./epic-shared/cut-protocol.js";
100
+ import { boundScopeLifecycleLabel, createExecFileRunCommand, ensureCommitResolvableLocally, performExactIndexScopeCut, pollIndexScopeLifecycle, readRemoteBranchHead, runGit, SCOPE_BOOTSTRAP_POLL_INTERVAL_MS, SCOPE_LIFECYCLE_LABELS, } from "./epic-shared/cut-protocol.js";
101
101
  import { hashPlan } from "./conductor/plan.js";
102
102
  import { BRING_UP_REFUSAL_MESSAGES, firstBringUpRefusal, } from "./conductor/bring-up-facts.js";
103
103
  import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
@@ -148,6 +148,16 @@ export const SETUP_EPIC_AUTO_MERGE_POLICY_KEY = "v2_auto_merge_enabled";
148
148
  * neither changes merge execution until a later A2 child.
149
149
  */
150
150
  export const SETUP_EPIC_MERGE_EXECUTION_MODE_POLICY_KEY = "merge_execution_mode";
151
+ /**
152
+ * BAPI-1259 — the opt-in for automatic integration-PR preparation. Written into
153
+ * the UNATTENDED epic-branch default only (fill-if-absent), so a run the
154
+ * operator starts unattended is prepared and handed off without a manual base
155
+ * merge or a manual un-draft. Attended runs, `--into-base` runs, and runs whose
156
+ * `--policy-file` already declares the key keep exactly what they had; a run
157
+ * approved before this key existed is never changed retroactively.
158
+ */
159
+ export const SETUP_EPIC_INTEGRATION_PREPARATION_POLICY_KEY = "integration_preparation";
160
+ const SETUP_EPIC_INTEGRATION_PREPARATION_DEFAULT = { enabled: true };
151
161
  /** Accepted `policy_json.review_policy.source` values (the `ReviewPolicy` surface). */
152
162
  export const SETUP_EPIC_REVIEW_POLICY_SOURCES = [
153
163
  "verdict_protocol",
@@ -249,6 +259,10 @@ export function getSetupEpicUsage() {
249
259
  " sets no review policy of its own, so a value supplied",
250
260
  " here always wins.",
251
261
  " --json Emit a single JSON result object on stdout",
262
+ " --launch-profile <name> (BAPI-1260) Resolve the target, repository, and bound",
263
+ " credential from this host-local launch profile. Passed",
264
+ " by drive-epic; there is no production default with it.",
265
+ " --base-url <url> (BAPI-1260) Explicit target; must agree with the profile.",
252
266
  " -h, --help Show this help",
253
267
  "",
254
268
  "Policy controls:",
@@ -349,6 +363,8 @@ export function parseSetupEpicArgs(argv) {
349
363
  let localOnly = false;
350
364
  let dryRun = false;
351
365
  let json = false;
366
+ let launchProfile;
367
+ let baseUrl;
352
368
  for (let i = 0; i < argv.length; i++) {
353
369
  const arg = argv[i];
354
370
  // `--policy-file=<path>` inline form (BAPI-754).
@@ -450,6 +466,19 @@ export function parseSetupEpicArgs(argv) {
450
466
  i++;
451
467
  break;
452
468
  }
469
+ // BAPI-1260 — the launch selection forwarded by drive-epic.
470
+ case "--launch-profile":
471
+ case "--base-url": {
472
+ const v = takeValue(argv, i, arg);
473
+ if (v === null || v.trim() === "")
474
+ return { status: "error", message: `${arg} requires a value.` };
475
+ if (arg === "--launch-profile")
476
+ launchProfile = v.trim();
477
+ else
478
+ baseUrl = v.trim();
479
+ i++;
480
+ break;
481
+ }
453
482
  case "--into-base":
454
483
  intoBase = true;
455
484
  break;
@@ -531,6 +560,9 @@ export function parseSetupEpicArgs(argv) {
531
560
  localOnly,
532
561
  dryRun,
533
562
  json,
563
+ // BAPI-1260 — only when supplied, so a legacy options object is unchanged.
564
+ ...(launchProfile ? { launchProfile } : {}),
565
+ ...(baseUrl ? { baseUrl } : {}),
534
566
  },
535
567
  };
536
568
  }
@@ -1367,9 +1399,10 @@ export function requestedPolicyIsUnattended(policy, attended) {
1367
1399
  * requested `review_policy`, and nothing else. Byte-identical to the
1368
1400
  * pre-BAPI-1102 composition, which is precisely what the opt-out promises.
1369
1401
  * * **Feature branch, default** → adds `posture: "unattended"`,
1370
- * `v2_auto_merge_enabled: true`, and (BAPI-1133, A2) `merge_execution_mode`
1402
+ * `v2_auto_merge_enabled: true`, (BAPI-1133, A2) `merge_execution_mode`
1371
1403
  * when the caller resolved one — the server refuses an unattended auto-merge
1372
- * policy that omits it.
1404
+ * policy that omits it — and (BAPI-1259) `integration_preparation:
1405
+ * { enabled: true }`, so the run prepares its own integration PR.
1373
1406
  *
1374
1407
  * Deliberately absent from every shape:
1375
1408
  *
@@ -1394,6 +1427,9 @@ export function composeDefaultSetupEpicPolicy(args) {
1394
1427
  if (args.mergeExecutionMode !== undefined) {
1395
1428
  policy[SETUP_EPIC_MERGE_EXECUTION_MODE_POLICY_KEY] = args.mergeExecutionMode;
1396
1429
  }
1430
+ policy[SETUP_EPIC_INTEGRATION_PREPARATION_POLICY_KEY] = {
1431
+ ...SETUP_EPIC_INTEGRATION_PREPARATION_DEFAULT,
1432
+ };
1397
1433
  }
1398
1434
  }
1399
1435
  if (args.reviewPolicy !== undefined) {
@@ -1465,6 +1501,11 @@ export function applySetupEpicUnattendedDefault(policy, args) {
1465
1501
  args.mergeExecutionMode !== undefined) {
1466
1502
  composed[SETUP_EPIC_MERGE_EXECUTION_MODE_POLICY_KEY] = args.mergeExecutionMode;
1467
1503
  }
1504
+ if (!(SETUP_EPIC_INTEGRATION_PREPARATION_POLICY_KEY in composed)) {
1505
+ composed[SETUP_EPIC_INTEGRATION_PREPARATION_POLICY_KEY] = {
1506
+ ...SETUP_EPIC_INTEGRATION_PREPARATION_DEFAULT,
1507
+ };
1508
+ }
1468
1509
  return composed;
1469
1510
  }
1470
1511
  /**
@@ -1654,14 +1695,51 @@ export function resolveEffectiveFeatureBranch(featureBranch, requestedPolicy) {
1654
1695
  return trimmed;
1655
1696
  }
1656
1697
  /**
1657
- * Read canonical parse truth and require a `succeeded` parse with a full
1658
- * indexed commit — the commit `origin/<feature>` will be cut at.
1698
+ * BAPI-1252: the operator sentence for each canonical refusal. Every one names
1699
+ * the situation and what to do; none names a SHA, generation, or server error.
1700
+ */
1701
+ export function canonicalRefusalMessage(repoName, reason) {
1702
+ switch (reason) {
1703
+ case "canonical_capability_unsupported":
1704
+ return (`The Bridge server for ${repoName} did not report verified canonical index provenance ` +
1705
+ `(it predates this contract, or sent a value this CLI cannot verify). Upgrade the ` +
1706
+ `server; a cut is never authorized from the legacy indexed commit.`);
1707
+ case "canonical_index_unverified":
1708
+ return (`${SETUP_EPIC_PARSE_FIRST_MESSAGE}: the canonical index for ${repoName} has no verified ` +
1709
+ `generation (legacy, interrupted, or still-writing content is never trusted), so there ` +
1710
+ `is no commit to cut the feature branch at.`);
1711
+ case "canonical_source_missing":
1712
+ return (`${SETUP_EPIC_PARSE_FIRST_MESSAGE}: the last canonical parse of ${repoName} observed no ` +
1713
+ `source commit (for example it parsed a supplied directory), so its content cannot be ` +
1714
+ `verified. Re-parse it from its remote.`);
1715
+ case "canonical_manifest_mismatch":
1716
+ return (`The canonical index content for ${repoName} no longer matches its verified manifest ` +
1717
+ `and has been marked unverified. Re-parse the repository before cutting.`);
1718
+ case "canonical_generation_mismatch":
1719
+ case "canonical_index_advanced":
1720
+ return (`The canonical index for ${repoName} advanced while it was being checked. Retry once ` +
1721
+ `the current parse finishes.`);
1722
+ case "canonical_validity_unavailable":
1723
+ default:
1724
+ return (`The canonical validity for ${repoName} could not be read. Retry — this is not ` +
1725
+ `evidence the repository is unindexed.`);
1726
+ }
1727
+ }
1728
+ /**
1729
+ * Read canonical parse truth and require AFFIRMATIVE verified provenance — the
1730
+ * commit `origin/<feature>` will be cut at (BAPI-1252).
1731
+ *
1732
+ * The cut commit is the server's VERIFIED canonical source SHA, decoded through
1733
+ * `decodeCanonicalProvenance`. Lifecycle fields (`status`, `indexed_commit_sha`)
1734
+ * are display history and are never consulted: a `succeeded` row can describe a
1735
+ * newer-created parse whose content the index does not hold. An absent field
1736
+ * (an older server), an unknown state, a read failure, or an unverified
1737
+ * generation all refuse before any branch or run mutation.
1659
1738
  *
1660
1739
  * Fails CLOSED (ratified R-2). The first line of every refusal is the exact
1661
- * operator instruction; the closing lines state what did NOT happen. An
1662
- * UNREADABLE status is kept distinct from an unsuccessful one — "could not read"
1663
- * is not evidence the repository is unindexed, and telling that operator to
1664
- * re-index would send them to re-parse a repository that may be fine.
1740
+ * operator instruction; the closing line states what did NOT happen. An
1741
+ * UNREADABLE status is kept distinct from an unverified one — "could not read"
1742
+ * is not evidence the repository is unindexed.
1665
1743
  */
1666
1744
  export async function readCanonicalParseTruth(access, fetchImpl) {
1667
1745
  const notCreated = [
@@ -1678,30 +1756,14 @@ export async function readCanonicalParseTruth(access, fetchImpl) {
1678
1756
  ],
1679
1757
  };
1680
1758
  }
1681
- if (parse.value.status !== "succeeded") {
1759
+ const evidence = decodeCanonicalProvenance(parse.value);
1760
+ if (!evidence.verified) {
1682
1761
  return {
1683
1762
  ok: false,
1684
- lines: [
1685
- `${SETUP_EPIC_PARSE_FIRST_MESSAGE}: the canonical index for ${access.repoName} has no ` +
1686
- `successful parse (status: ${String(parse.value.status)}), so there is no commit to ` +
1687
- `cut the feature branch at.`,
1688
- ...notCreated,
1689
- ],
1763
+ lines: [canonicalRefusalMessage(access.repoName, evidence.reason), ...notCreated],
1690
1764
  };
1691
1765
  }
1692
- const indexed = normalizeCommitSha(parse.value.indexed_commit_sha);
1693
- if (indexed === null) {
1694
- return {
1695
- ok: false,
1696
- lines: [
1697
- `${SETUP_EPIC_PARSE_FIRST_MESSAGE}: the canonical index for ${access.repoName} published ` +
1698
- `no commit for its last successful parse, so there is no commit to cut the feature ` +
1699
- `branch at.`,
1700
- ...notCreated,
1701
- ],
1702
- };
1703
- }
1704
- return { ok: true, cutCommitSha: indexed };
1766
+ return { ok: true, cutCommitSha: evidence.sourceCommitSha };
1705
1767
  }
1706
1768
  /** The strict subset of `SetupEpicDeps` the shared cut protocol consumes. */
1707
1769
  function cutProtocolDeps(deps, runCommand) {
@@ -1936,7 +1998,10 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
1936
1998
  return 0;
1937
1999
  }
1938
2000
  // --- Resolve access ------------------------------------------------------
1939
- const accessResult = await resolveConductorBridgeApiAccess({
2001
+ // BAPI-1260: a caller that selected the epic-launch contract (drive-epic
2002
+ // forwards --launch-profile / --base-url) gets the target-first resolver and
2003
+ // the bound credential; the legacy resolver is kept byte-for-byte otherwise.
2004
+ const accessDeps = {
1940
2005
  env: deps.env,
1941
2006
  cwd: deps.cwd,
1942
2007
  homedir: deps.homedir,
@@ -1944,7 +2009,13 @@ async function executeSetupEpic(argv, overrides, runIdSink) {
1944
2009
  readFile: deps.readFile,
1945
2010
  stat: deps.stat,
1946
2011
  repoName: opts.repo,
1947
- });
2012
+ };
2013
+ const accessResult = opts.launchProfile !== undefined || opts.baseUrl !== undefined
2014
+ ? await resolveEpicLaunchBridgeApiAccess({
2015
+ ...(opts.launchProfile ? { profileName: opts.launchProfile } : {}),
2016
+ ...(opts.baseUrl ? { baseUrl: opts.baseUrl } : {}),
2017
+ }, accessDeps)
2018
+ : await resolveConductorBridgeApiAccess(accessDeps);
1948
2019
  if (!accessResult.ok) {
1949
2020
  deps.errorLog(`Cannot reach the Bridge API: ${accessResult.error}`);
1950
2021
  return 1;
@@ -1,4 +1,4 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.60";
3
- export const BUILD_COMMIT = "1920a31997d2";
4
- export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.60", "serve"];
2
+ export const VERSION = "0.2.62";
3
+ export const BUILD_COMMIT = "9351fac413bf";
4
+ export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.62", "serve"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.60",
3
+ "version": "0.2.62",
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",