@bridge_gpt/mcp-server 0.2.55 → 0.2.57

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.
@@ -18,11 +18,21 @@
18
18
  * This path is worktree-free and base-branch-free; the human CLI path leaves
19
19
  * `epic` unset and retains byte-for-byte identical behavior.
20
20
  */
21
- import { createDefaultStartTicketsDeps, detectTerminal, runWithConcurrency, buildAgentInvocation, shSquoteInner, powershellSquote, isCommandOnPath, resolveFirstCommandOnPath, tmuxSessionNameForTicket, TICKET_KEY_PATTERN, DEFAULT_MAX_PARALLEL, validateBranchName, fetchAndResolveBaseSha, fetchStartTicketsConfigField, resolveStartTicketsBridgeApiAccess, } from "./start-tickets.js";
21
+ import { createDefaultStartTicketsDeps, detectTerminal, runWithConcurrency, buildAgentInvocation, shSquoteInner, powershellSquote, isCommandOnPath, resolveFirstCommandOnPath, tmuxSessionNameForTicket, TICKET_KEY_PATTERN, DEFAULT_MAX_PARALLEL, validateBranchName, fetchAndResolveBaseSha, fetchStartTicketsConfigField, resolveStartTicketsBridgeApiAccess, buildCommandProvisioningDeps, buildMcpProvisioningDeps, } from "./start-tickets.js";
22
+ import { provisionCommandsForWorktree } from "./command-provisioning.js";
23
+ import { resolveCodexLaunchScope } from "./codex-launch-scope.js";
22
24
  import { WINDOWS_TERMINAL_COMMAND, WINDOWS_POWERSHELL_CANDIDATES, TMUX_COMMAND, isSupportedStartTicketsPlatform, unsupportedPlatformMessage, } from "./start-tickets-prereqs.js";
23
- import { DEFAULT_AGENT_NAME, resolveAgentSpec, isAgentName, formatValidAgentNames, isValidModelAlias, } from "./agent-registry.js";
25
+ import { DEFAULT_AGENT_NAME, resolveAgentSpec, isAgentName, isValidModelAlias, } from "./agent-registry.js";
24
26
  import { injectConductorEnvIntoShellCommand, buildEpicIdentityEnv, mintStartTicketsRunId, } from "./start-tickets-conductor.js";
25
27
  import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
28
+ /** Every registered agent with a review workflow the launcher can provision. */
29
+ const REVIEW_TICKETS_AGENT_NAMES = ["claude", "cursor-agent", "codex"];
30
+ function isReviewTicketsAgentName(value) {
31
+ return REVIEW_TICKETS_AGENT_NAMES.includes(value);
32
+ }
33
+ function formatReviewTicketsAgentNames() {
34
+ return REVIEW_TICKETS_AGENT_NAMES.join(", ");
35
+ }
26
36
  /** Default base context: no flags threaded through (matches pre-BAPI-474 behavior). */
27
37
  const NO_BASE_CONTEXT = { noRefreshBase: false };
28
38
  // ---------------------------------------------------------------------------
@@ -37,7 +47,7 @@ export function getReviewTicketsUsage() {
37
47
  " --auto Auto-approve all review gates (global, applies to all tickets)",
38
48
  " --rounds=1|2 Review depth: 1=single-pass, 2=full two-pass (default: adaptive — backend routes by difficulty)",
39
49
  " --review KEY=auto,rounds=1 Per-ticket review mode override (repeatable)",
40
- " --agent claude|cursor-agent Agent command to launch in each tab (default: claude)",
50
+ " --agent claude|cursor-agent|codex Agent command to launch in each tab (default: claude)",
41
51
  " --model VALUE Model alias to pass as --model to the agent (optional passthrough)",
42
52
  " --max-parallel N Max tabs to spawn concurrently (default: 3)",
43
53
  " --dry-run Print intended commands; creates no tabs and opens no sessions",
@@ -289,10 +299,10 @@ export function parseReviewTicketsArgs(argv) {
289
299
  return { status: "error", message: "--agent requires a value (an agent name)." };
290
300
  }
291
301
  }
292
- if (!isAgentName(value)) {
302
+ if (!isAgentName(value) || !isReviewTicketsAgentName(value)) {
293
303
  return {
294
304
  status: "error",
295
- message: `Invalid --agent value: '${value}' (allowed agents: ${formatValidAgentNames()}).`,
305
+ message: `Invalid --agent value: '${value}' (allowed agents: ${formatReviewTicketsAgentNames()}).`,
296
306
  };
297
307
  }
298
308
  agentName = value;
@@ -416,7 +426,7 @@ export function resolveEffectiveReviewMode(key, options) {
416
426
  // hard-wired to `/implement-ticket`. Review-aware builders are used instead so
417
427
  // the prompt uses `/review-ticket` and no directory change is prepended.
418
428
  // ---------------------------------------------------------------------------
419
- export function buildReviewTicketPrompt(key, mode, base = NO_BASE_CONTEXT) {
429
+ export function buildReviewTicketPrompt(key, mode, base = NO_BASE_CONTEXT, agentName = "claude") {
420
430
  const autoFlag = mode.auto ? " --auto" : "";
421
431
  // Omit --rounds entirely when adaptive (mode.rounds === undefined) so the
422
432
  // backend routes by difficulty; only forward it for an explicit 1/2 override.
@@ -424,22 +434,23 @@ export function buildReviewTicketPrompt(key, mode, base = NO_BASE_CONTEXT) {
424
434
  const baseBranchFlag = base.baseBranch ? ` --base-branch=${base.baseBranch}` : "";
425
435
  const baseShaFlag = base.baseSha ? ` --base-sha=${base.baseSha}` : "";
426
436
  const noRefreshFlag = base.noRefreshBase ? " --no-refresh-base" : "";
427
- return `/review-ticket ${key}${autoFlag}${roundsFlag}${baseBranchFlag}${baseShaFlag}${noRefreshFlag}`;
437
+ const command = agentName === "codex" ? "$bridge-review-ticket" : "/review-ticket";
438
+ return `${command} ${key}${autoFlag}${roundsFlag}${baseBranchFlag}${baseShaFlag}${noRefreshFlag}`;
428
439
  }
429
- export function buildPosixReviewAgentShellCommand(agent, key, mode, cwd, modelAlias, base = NO_BASE_CONTEXT) {
430
- const prompt = buildReviewTicketPrompt(key, mode, base);
431
- const invocation = buildAgentInvocation(agent, prompt, (p) => `'${shSquoteInner(p)}'`, modelAlias);
440
+ export function buildPosixReviewAgentShellCommand(agent, key, mode, cwd, modelAlias, base = NO_BASE_CONTEXT, launchArgs = []) {
441
+ const prompt = buildReviewTicketPrompt(key, mode, base, agent.name);
442
+ const invocation = buildAgentInvocation(agent, prompt, (p) => `'${shSquoteInner(p)}'`, modelAlias, launchArgs);
432
443
  return `cd '${shSquoteInner(cwd)}' && ${invocation}`;
433
444
  }
434
- export function buildPowerShellReviewAgentShellCommand(agent, key, mode, modelAlias, base = NO_BASE_CONTEXT) {
435
- const prompt = buildReviewTicketPrompt(key, mode, base);
436
- return buildAgentInvocation(agent, prompt, powershellSquote, modelAlias);
445
+ export function buildPowerShellReviewAgentShellCommand(agent, key, mode, modelAlias, base = NO_BASE_CONTEXT, launchArgs = []) {
446
+ const prompt = buildReviewTicketPrompt(key, mode, base, agent.name);
447
+ return buildAgentInvocation(agent, prompt, powershellSquote, modelAlias, launchArgs);
437
448
  }
438
- export function buildReviewAgentShellCommand(agent, key, mode, platform, cwd, modelAlias, base = NO_BASE_CONTEXT) {
449
+ export function buildReviewAgentShellCommand(agent, key, mode, platform, cwd, modelAlias, base = NO_BASE_CONTEXT, launchArgs = []) {
439
450
  if (platform === "win32") {
440
- return buildPowerShellReviewAgentShellCommand(agent, key, mode, modelAlias, base);
451
+ return buildPowerShellReviewAgentShellCommand(agent, key, mode, modelAlias, base, launchArgs);
441
452
  }
442
- return buildPosixReviewAgentShellCommand(agent, key, mode, cwd, modelAlias, base);
453
+ return buildPosixReviewAgentShellCommand(agent, key, mode, cwd, modelAlias, base, launchArgs);
443
454
  }
444
455
  // ---------------------------------------------------------------------------
445
456
  // Unsupported platform message for review-tickets
@@ -535,17 +546,18 @@ export async function orchestrateReviewTickets(deps, options, overrides = {}) {
535
546
  if (!agent) {
536
547
  return {
537
548
  ok: false,
538
- error: `Unknown agent: '${options.agentName}'. Valid agents: ${formatValidAgentNames()}.`,
549
+ error: `Unknown agent: '${options.agentName}'. Valid agents: ${formatReviewTicketsAgentNames()}.`,
550
+ };
551
+ }
552
+ // Reject an invalid multi-key claim before any Codex skill/config writes.
553
+ if (options.epic?.dispatch_key && options.keys.length > 1) {
554
+ return {
555
+ ok: false,
556
+ error: "epic.dispatch_key cannot be used with multiple keys: dispatch_key is a single-ticket claim " +
557
+ "and would be re-claimed for each key in the batch. Call orchestrateReviewTickets once per ticket instead.",
539
558
  };
540
559
  }
541
560
  if (options.dryRun) {
542
- if (options.epic?.dispatch_key && options.keys.length > 1) {
543
- return {
544
- ok: false,
545
- error: "epic.dispatch_key cannot be used with multiple keys: dispatch_key is a single-ticket claim " +
546
- "and would be re-claimed for each key in the batch. Call orchestrateReviewTickets once per ticket instead.",
547
- };
548
- }
549
561
  // --dry-run never performs the base-branch fetch (side-effect-free preview);
550
562
  // it reflects only what the user explicitly passed on the CLI.
551
563
  const rows = buildReviewPlanRows(deps, options, agent, "dry-run", overrides, previewBaseContextFromOptions(options));
@@ -554,15 +566,18 @@ export async function orchestrateReviewTickets(deps, options, overrides = {}) {
554
566
  const preflight = await runReviewTicketsPreflight(deps, options);
555
567
  if (!preflight.ok)
556
568
  return { ok: false, error: preflight.error };
557
- // A dispatch_key embeds a single ticket (dispatch:{epic}:{ticket}:{plan_version}).
558
- // Claiming the same key for every iteration of a multi-key batch would silently
559
- // drop all but the first ticket; reject early with a clear error instead.
560
- if (options.epic?.dispatch_key && options.keys.length > 1) {
561
- return {
562
- ok: false,
563
- error: "epic.dispatch_key cannot be used with multiple keys: dispatch_key is a single-ticket claim " +
564
- "and would be re-claimed for each key in the batch. Call orchestrateReviewTickets once per ticket instead.",
565
- };
569
+ let codexLaunchArgs = [];
570
+ if (agent.name === "codex") {
571
+ const prepare = overrides.prepareCodexSession ?? (async (d) => {
572
+ const provision = await provisionCommandsForWorktree(d.cwd, buildCommandProvisioningDeps(d), true);
573
+ if (!provision.ok)
574
+ return { ok: false, error: provision.error };
575
+ return resolveCodexLaunchScope(d.cwd, buildMcpProvisioningDeps(d));
576
+ });
577
+ const scope = await prepare(deps);
578
+ if (!scope.ok)
579
+ return { ok: false, error: scope.error };
580
+ codexLaunchArgs = scope.argv;
566
581
  }
567
582
  // BAPI-474 D-3: parent-fetch-once — resolve ONE base_branch/base_sha pin for
568
583
  // the whole batch before any tab is spawned. D-4: on failure (and no
@@ -578,7 +593,7 @@ export async function orchestrateReviewTickets(deps, options, overrides = {}) {
578
593
  const terminal = detectTerminal(undefined, deps.env);
579
594
  const rows = await runWithConcurrency(options.keys, options.maxParallel, async (key) => {
580
595
  const mode = resolveEffectiveReviewMode(key, options);
581
- const baseShellCommand = buildReviewAgentShellCommand(agent, key, mode, deps.platform, deps.cwd, options.modelAlias, baseContext);
596
+ const baseShellCommand = buildReviewAgentShellCommand(agent, key, mode, deps.platform, deps.cwd, options.modelAlias, baseContext, codexLaunchArgs);
582
597
  let shellCommand = baseShellCommand;
583
598
  let runId;
584
599
  if (options.epic) {
@@ -727,7 +742,7 @@ export async function runReviewTicketsCli(argv, overrides = {}) {
727
742
  if (options.dryRun) {
728
743
  const agent = resolveAgentSpec(options.agentName);
729
744
  if (!agent) {
730
- errorLog(`Error: Unknown agent: '${options.agentName}'. Valid agents: ${formatValidAgentNames()}.`);
745
+ errorLog(`Error: Unknown agent: '${options.agentName}'. Valid agents: ${formatReviewTicketsAgentNames()}.`);
731
746
  return 1;
732
747
  }
733
748
  const rows = buildReviewPlanRows(deps, options, agent, "dry-run", overrides.orchestrateOverrides, previewBaseContextFromOptions(options));