@bridge_gpt/mcp-server 0.2.38 → 0.2.41
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.
- package/README.md +189 -14
- package/build/agent-capabilities/probe-context.js +2 -1
- package/build/agent-launchers/claude-executor-adapter.js +392 -0
- package/build/agent-launchers/executor-adapter-inspection.js +163 -0
- package/build/agent-launchers/executor-adapter-registry.js +90 -0
- package/build/agent-launchers/executor-adapter.js +136 -0
- package/build/agent-registry.js +28 -0
- package/build/agents.generated.js +1 -1
- package/build/claude-login.js +85 -0
- package/build/claude-user-config-doctor.js +59 -33
- package/build/commands.generated.js +12 -11
- package/build/conduct-epic/bridge-client.js +345 -0
- package/build/conduct-epic/checkpoint-store.js +479 -0
- package/build/conduct-epic/cli.js +1765 -0
- package/build/conduct-epic/lock.js +302 -0
- package/build/conduct-epic/pr-state.js +286 -0
- package/build/conduct-epic/spawn.js +101 -0
- package/build/conductor/bridge-api-client.js +37 -2
- package/build/conductor/doctor.js +11 -1
- package/build/conductor/install-doctor.js +184 -10
- package/build/conductor-bin.js +7 -7
- package/build/credential-store.js +10 -4
- package/build/credentials-cli.js +34 -19
- package/build/docs.generated.js +1 -1
- package/build/doctor.js +579 -88
- package/build/executor/agent-identity.js +32 -0
- package/build/executor/cli.js +50 -39
- package/build/executor/deps.js +15 -1
- package/build/executor/env.js +56 -45
- package/build/executor/index.js +9 -1
- package/build/executor/install-preflight.js +138 -0
- package/build/executor/job-errors.js +200 -0
- package/build/executor/job-runner.js +619 -268
- package/build/executor/observation.js +165 -0
- package/build/executor/permissions.js +163 -36
- package/build/executor/platform.js +54 -0
- package/build/executor/preflight.js +175 -67
- package/build/executor/process.js +39 -7
- package/build/executor/runner.js +19 -0
- package/build/executor/service-lifecycle.js +269 -0
- package/build/executor/service-unit.js +121 -12
- package/build/executor/stale-artifacts.js +70 -0
- package/build/executor/test-clock.js +188 -24
- package/build/executor/worker-command.js +22 -58
- package/build/executor/worker-log.js +82 -0
- package/build/executor/worktree-lock.js +264 -0
- package/build/index.js +527 -357
- package/build/install-bridge-conductor.js +376 -38
- package/build/install-bridge.js +414 -114
- package/build/install-doctor.js +13 -0
- package/build/install-reexec.js +5 -3
- package/build/mcp-install-state.js +130 -0
- package/build/mcp-profile.js +11 -2
- package/build/mcp-provisioning.js +15 -0
- package/build/merge-pull-request.js +562 -0
- package/build/phase-result-artifacts.js +450 -0
- package/build/pipeline-orchestrator.js +4 -0
- package/build/pipeline-utils.js +16 -0
- package/build/pipelines.generated.js +7 -7
- package/build/plane/preflight.js +18 -14
- package/build/plane/supervisor.js +8 -1
- package/build/project-root.js +34 -0
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +36 -9
- package/build/setup-epic.js +57 -4
- package/build/sfcc/permissions.js +25 -6
- package/build/sfcc/reads-site-preference.js +6 -0
- package/build/sfcc/register.js +61 -23
- package/build/sfcc/registration-inventory.js +89 -0
- package/build/sfcc/setup-status.js +18 -34
- package/build/sfcc/tool-wrapper.js +294 -17
- package/build/sfcc/write-grants.js +33 -1
- package/build/sfcc/write-guard.js +41 -12
- package/build/sfcc/writes-custom-object-def.js +6 -2
- package/build/sfcc/writes-site-preference.js +6 -1
- package/build/sfcc/writes-system-object.js +11 -2
- package/build/sfcc/writes.js +13 -8
- package/build/start-tickets-prereqs.js +25 -15
- package/build/start-tickets.js +123 -21
- package/build/version.generated.js +1 -1
- package/build/worktree-core.js +9 -3
- package/docs/install/mcp-tool-integrations.md +54 -9
- package/docs/install/sfcc-integration.md +71 -24
- package/package.json +3 -3
- package/build/executor/worker-config-isolation.js +0 -287
|
@@ -18,7 +18,7 @@ import { resolveBapiCredentials, getPrimaryCredentialStorePath, } from "./creden
|
|
|
18
18
|
import { resolveStartTicketsRepoName } from "./start-tickets-repo.js";
|
|
19
19
|
import { probeWorktreeMcpRegistration } from "./mcp-registration-doctor.js";
|
|
20
20
|
import { probeWorktreeCommandAssets } from "./command-assets-doctor.js";
|
|
21
|
-
import {
|
|
21
|
+
import { CLAUDE_MCP_SHADOWING_REMEDIATION_COMMAND, formatClaudeMcpShadowFinding, formatClaudeUserConfigDiagnostic, inspectClaudeUserConfigForMcpShadowing, resolveClaudeUserConfigPath, } from "./claude-user-config-doctor.js";
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
23
23
|
// Constants (moved here from start-tickets.ts so both consumers share them)
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
@@ -531,12 +531,11 @@ export async function resolveMainWorktreePathForDoctor(deps) {
|
|
|
531
531
|
* which is why the advice below is phrased as "delete, or move aside". Keep every
|
|
532
532
|
* such identifier out of this file, even inside a comment like this one.
|
|
533
533
|
*/
|
|
534
|
-
const CLAUDE_MCP_SHADOWING_HINT =
|
|
535
|
-
"
|
|
536
|
-
"
|
|
537
|
-
"
|
|
538
|
-
|
|
539
|
-
"(doctor still reports it).";
|
|
534
|
+
const CLAUDE_MCP_SHADOWING_HINT = `Run \`${CLAUDE_MCP_SHADOWING_REMEDIATION_COMMAND}\` to clear the 'bridge-api' MCP ` +
|
|
535
|
+
"registration in ~/.claude.json (use the matching scope if it is registered at user scope, " +
|
|
536
|
+
"or under the projects entry for this repository/worktree). This is ADVISORY: workers load " +
|
|
537
|
+
"MCP servers with --strict-mcp-config from their own worktree registration, so this entry " +
|
|
538
|
+
"does not reach a worker and does not block a conductor run.";
|
|
540
539
|
const CLAUDE_MCP_SHADOWING_INSTALL_HINTS = {
|
|
541
540
|
darwin: CLAUDE_MCP_SHADOWING_HINT,
|
|
542
541
|
linux: CLAUDE_MCP_SHADOWING_HINT,
|
|
@@ -547,17 +546,25 @@ const CLAUDE_MCP_SHADOWING_INSTALL_HINTS = {
|
|
|
547
546
|
* configuration register a `bridge-api` MCP server that could shadow a worker's
|
|
548
547
|
* provisioned registration?
|
|
549
548
|
*
|
|
549
|
+
* ADVISORY SINCE BAPI-790. Every outcome reports `found: true`, so this never
|
|
550
|
+
* fails the prerequisite section or the exit code; a confirmed collision is
|
|
551
|
+
* surfaced through the DETAIL, which names the scope and carries the exact
|
|
552
|
+
* remediation command. The downgrade is not a softening of the diagnostic — it
|
|
553
|
+
* reflects that strict flag-scoped worker MCP loading means the collision can no
|
|
554
|
+
* longer reach a worker (inventory Finding 6), so reporting it as a broken
|
|
555
|
+
* prerequisite would tell an operator to fix something that is not breaking
|
|
556
|
+
* anything.
|
|
557
|
+
*
|
|
550
558
|
* Status mapping:
|
|
551
559
|
* - no registration, or only acceptable provisioned shims → `found: true`
|
|
552
|
-
* - a CONFIRMED foreign registration → `found:
|
|
560
|
+
* - a CONFIRMED foreign registration → `found: true` + advisory detail
|
|
553
561
|
* - unreadable / malformed config, or an unresolved main
|
|
554
562
|
* worktree path → `found: true` + detail
|
|
555
563
|
*
|
|
556
|
-
*
|
|
557
|
-
* `BAPI_CONDUCTOR_ALLOW_CLAUDE_MCP_SHADOWING
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
* condition an operator ran doctor to find.
|
|
564
|
+
* Reporting stays deliberately independent of the retired
|
|
565
|
+
* `BAPI_CONDUCTOR_ALLOW_CLAUDE_MCP_SHADOWING` override: whether that variable is
|
|
566
|
+
* exported has never had any bearing on whether the collision exists, and now it
|
|
567
|
+
* has no bearing on anything at all.
|
|
561
568
|
*/
|
|
562
569
|
export function claudeMcpShadowingDescriptor() {
|
|
563
570
|
return {
|
|
@@ -589,8 +596,11 @@ export function claudeMcpShadowingDescriptor() {
|
|
|
589
596
|
degraded.push("the main worktree path could not be resolved; only user and current-worktree scopes were checked");
|
|
590
597
|
}
|
|
591
598
|
if (inspection.findings.length > 0) {
|
|
592
|
-
|
|
593
|
-
|
|
599
|
+
// Advisory (BAPI-790): reported in full, with the remediation command,
|
|
600
|
+
// but `found: true` so it never fails the prerequisite or the exit code.
|
|
601
|
+
const findings = inspection.findings.map((finding) => `advisory: ${formatClaudeMcpShadowFinding(finding)} — not loaded by a worker under ` +
|
|
602
|
+
`strict MCP loading; clear it with \`${CLAUDE_MCP_SHADOWING_REMEDIATION_COMMAND}\``);
|
|
603
|
+
return { found: true, detail: [...findings, ...degraded].join("; ") };
|
|
594
604
|
}
|
|
595
605
|
return {
|
|
596
606
|
found: true,
|
package/build/start-tickets.js
CHANGED
|
@@ -59,6 +59,10 @@ import os from "node:os";
|
|
|
59
59
|
import path from "path";
|
|
60
60
|
import { VERSION } from "./version.generated.js";
|
|
61
61
|
import { resolveBapiCredentials, getPrimaryCredentialStorePath, } from "./credential-store.js";
|
|
62
|
+
// BAPI-814/E1b: the read-only index-branch override probe. `getIndexBranch` is
|
|
63
|
+
// reused from the conduct-epic client rather than re-wrapped, so both conductor
|
|
64
|
+
// paths read the one repository-wide override through the same client.
|
|
65
|
+
import { getIndexBranch } from "./conduct-epic/bridge-client.js";
|
|
62
66
|
import { resolveStartTicketsRepoName as resolveSharedStartTicketsRepoName, resolveRequiredStartTicketsRepoName, } from "./start-tickets-repo.js";
|
|
63
67
|
import { provisionMcpRegistrationsForCreatedWorktrees, } from "./mcp-provisioning.js";
|
|
64
68
|
import { provisionCommandsForCreatedWorktrees, } from "./command-provisioning.js";
|
|
@@ -173,6 +177,7 @@ export function parseStartTicketsArgs(argv) {
|
|
|
173
177
|
let maxParallelRaw;
|
|
174
178
|
let agentName = DEFAULT_AGENT_NAME;
|
|
175
179
|
let baseBranch = "main";
|
|
180
|
+
let baseBranchExplicit = false;
|
|
176
181
|
let conductorEnabled = false;
|
|
177
182
|
let workflow = "implement";
|
|
178
183
|
let reviewRoundsRaw;
|
|
@@ -342,6 +347,7 @@ export function parseStartTicketsArgs(argv) {
|
|
|
342
347
|
return { status: "error", message: `Invalid --base-branch value: ${error}` };
|
|
343
348
|
}
|
|
344
349
|
baseBranch = trimmed;
|
|
350
|
+
baseBranchExplicit = true;
|
|
345
351
|
continue;
|
|
346
352
|
}
|
|
347
353
|
if (arg === "--dry-run") {
|
|
@@ -449,6 +455,7 @@ export function parseStartTicketsArgs(argv) {
|
|
|
449
455
|
branchOverrides,
|
|
450
456
|
agentName,
|
|
451
457
|
baseBranch,
|
|
458
|
+
...(baseBranchExplicit ? { baseBranchExplicit: true } : {}),
|
|
452
459
|
conductorEnabled,
|
|
453
460
|
workflow,
|
|
454
461
|
reviewRounds,
|
|
@@ -506,16 +513,19 @@ export function resolveStartTicketsPlatformConfig(deps, agent, autoApprove = fal
|
|
|
506
513
|
// instruction to the resume-mode worker's prompt.
|
|
507
514
|
resumeMode = false, workflow = "implement", reviewRounds,
|
|
508
515
|
// BAPI-586: the effective run base branch (incl. epic.base_branch override).
|
|
509
|
-
// Injected as BAPI_BASE_BRANCH
|
|
510
|
-
// and
|
|
516
|
+
// Injected as BAPI_BASE_BRANCH so the spawned worker's PR targets it — for
|
|
517
|
+
// conductor runs always, and (BAPI-801) for plain runs whose base is a
|
|
518
|
+
// non-default branch — and threaded (BAPI-593) into the spawned workflow command.
|
|
511
519
|
baseBranch) {
|
|
512
520
|
if (!isSupportedStartTicketsPlatform(deps.platform)) {
|
|
513
521
|
return { ok: false, error: unsupportedPlatformMessage(deps.platform) };
|
|
514
522
|
}
|
|
515
523
|
const platform = deps.platform;
|
|
516
|
-
//
|
|
517
|
-
//
|
|
518
|
-
|
|
524
|
+
// BAPI-801: both conductor and plain dispatch carry the PR-base ENVIRONMENT
|
|
525
|
+
// contract (see resolvePrBaseBranchEnvValue). Only the conductor launch
|
|
526
|
+
// INSTRUCTION remains conductor-only — a plain worker's prompt stays bare and
|
|
527
|
+
// learns the base from the env plus the create-PR instruction files.
|
|
528
|
+
const prBaseBranch = resolvePrBaseBranchEnvValue(conductorEnabled, baseBranch);
|
|
519
529
|
return {
|
|
520
530
|
ok: true,
|
|
521
531
|
config: {
|
|
@@ -523,7 +533,8 @@ baseBranch) {
|
|
|
523
533
|
worktrunkBinary: resolveWorktrunkBinary(platform, deps.env),
|
|
524
534
|
// Inject the resolved repo identity so the spawned worktree session never
|
|
525
535
|
// falls back to the basename-derived repo name (the 403 root cause), and
|
|
526
|
-
// (BAPI-586) the run base so the
|
|
536
|
+
// (BAPI-586/BAPI-801) the run base so the spawned worker opens its PR
|
|
537
|
+
// against it. The base assignment stays outermost.
|
|
527
538
|
buildAgentShellCommand: (key, worktreePath, modelAlias) => prependBaseBranchEnvAssignment(prependRepoNameEnvAssignment(buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch), repoName, platform), prBaseBranch, platform),
|
|
528
539
|
spawnTerminalTab: deps.spawnTerminalTab,
|
|
529
540
|
},
|
|
@@ -555,14 +566,40 @@ export function prependRepoNameEnvAssignment(command, repoName, platform = "darw
|
|
|
555
566
|
return `export BAPI_REPO_NAME='${shSquoteInner(repoName)}' && ${command}`;
|
|
556
567
|
}
|
|
557
568
|
/**
|
|
558
|
-
* BAPI-
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
*
|
|
569
|
+
* BAPI-801: decide the `BAPI_BASE_BRANCH` value a spawned worker's shell should
|
|
570
|
+
* carry — the single rule shared by the real spawn builder
|
|
571
|
+
* ({@link resolveStartTicketsPlatformConfig}) and the dry-run preview builder
|
|
572
|
+
* ({@link getDryRunPlatformDetails}), so the preview can never drift from what
|
|
573
|
+
* is actually spawned.
|
|
574
|
+
*
|
|
575
|
+
* - Conductor runs export whatever base was resolved, `main` included — BAPI-586
|
|
576
|
+
* behavior, unchanged.
|
|
577
|
+
* - Plain runs export only a non-empty base other than the CLI default `main`.
|
|
578
|
+
* This mirrors the `--base-branch` threading rule in {@link buildAgentPrompt},
|
|
579
|
+
* keeps today's plain spawn strings byte-stable on the default base, and costs
|
|
580
|
+
* nothing: the create-PR fallback tier is `main` anyway.
|
|
581
|
+
*
|
|
582
|
+
* Returns `null` when nothing should be exported;
|
|
583
|
+
* {@link prependBaseBranchEnvAssignment} is fail-open on that value.
|
|
584
|
+
*/
|
|
585
|
+
function resolvePrBaseBranchEnvValue(conductorEnabled, baseBranch) {
|
|
586
|
+
if (conductorEnabled)
|
|
587
|
+
return baseBranch ?? null;
|
|
588
|
+
if (!baseBranch || baseBranch === "main")
|
|
589
|
+
return null;
|
|
590
|
+
return baseBranch;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* BAPI-586/BAPI-801: prepend a `BAPI_BASE_BRANCH` environment assignment to a
|
|
594
|
+
* spawned worker's shell command so the worker opens its PR against the run base
|
|
595
|
+
* instead of inferring the repo default branch. Conductor workers additionally
|
|
596
|
+
* receive the paired launch instruction; plain workers (BAPI-801) carry the same
|
|
597
|
+
* env and learn the contract from the create-PR instruction files, which resolve
|
|
598
|
+
* `$BAPI_BASE_BRANCH` first. Platform correct — `$env:VAR = '…'; …` on
|
|
599
|
+
* PowerShell, `export VAR='…' && …` on POSIX — with the value quoted by the same
|
|
600
|
+
* escaper used for the rest of the command. Fail-open: a null/empty `baseBranch`
|
|
601
|
+
* returns the command unchanged (the caller's rule already decided not to
|
|
602
|
+
* export — see {@link resolvePrBaseBranchEnvValue}).
|
|
566
603
|
*/
|
|
567
604
|
export function prependBaseBranchEnvAssignment(command, baseBranch, platform = "darwin") {
|
|
568
605
|
if (!baseBranch)
|
|
@@ -1061,6 +1098,9 @@ export function buildAgentPrompt(key, opts = {}) {
|
|
|
1061
1098
|
parts.push(buildConductorMessageRelayLaunchInstruction());
|
|
1062
1099
|
// BAPI-586: conductor implementation workers must open their PR against the
|
|
1063
1100
|
// run base (injected as BAPI_BASE_BRANCH), never the repo default branch.
|
|
1101
|
+
// The launch INSTRUCTION stays conductor-only. BAPI-801 gave plain runs the
|
|
1102
|
+
// same env, but their prompt stays bare — the create-PR instruction files
|
|
1103
|
+
// read $BAPI_BASE_BRANCH for them.
|
|
1064
1104
|
parts.push(buildPrBaseContractLaunchInstruction());
|
|
1065
1105
|
}
|
|
1066
1106
|
if (opts.resumeMode)
|
|
@@ -1723,11 +1763,13 @@ export function getDryRunPlatformDetails(agent, platform = process.platform, env
|
|
|
1723
1763
|
worktrunkBinary: resolveWorktrunkBinary(platform, env),
|
|
1724
1764
|
// The builder accepts an optional resolved modelAlias; the dry-run caller
|
|
1725
1765
|
// now passes the previewed tier's alias so `--model` shows in the preview.
|
|
1726
|
-
// The resolved repo name (when known) is injected as a BAPI_REPO_NAME prefix
|
|
1727
|
-
//
|
|
1728
|
-
//
|
|
1729
|
-
//
|
|
1730
|
-
|
|
1766
|
+
// The resolved repo name (when known) is injected as a BAPI_REPO_NAME prefix,
|
|
1767
|
+
// and (BAPI-801) the PR base as a BAPI_BASE_BRANCH prefix through the SAME
|
|
1768
|
+
// resolvePrBaseBranchEnvValue rule and the same outermost ordering the real
|
|
1769
|
+
// spawn builder uses — dry-run shares both environment renderings with real
|
|
1770
|
+
// spawning. Reuses the same buildAgentShellCommand/buildAgentPrompt path as a
|
|
1771
|
+
// real spawn — dry-run is never special-cased — so the preview is exact.
|
|
1772
|
+
buildAgentShellCommand: (key, worktreePath, modelAlias) => prependBaseBranchEnvAssignment(prependRepoNameEnvAssignment(buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled, false, workflow, reviewRounds, baseBranch), repoName, platform), resolvePrBaseBranchEnvValue(conductorEnabled, baseBranch), platform),
|
|
1731
1773
|
};
|
|
1732
1774
|
}
|
|
1733
1775
|
/**
|
|
@@ -1769,10 +1811,17 @@ export function buildDryRunDetailLines(agent, key, branch, platform = process.pl
|
|
|
1769
1811
|
const { worktrunkBinary, buildAgentShellCommand: build } = getDryRunPlatformDetails(agent, platform, env, autoApprove, conductorEnabled, repoName, workflow, reviewRounds, baseBranch);
|
|
1770
1812
|
const wtArgs = buildWtSwitchArgs(branch, false, baseBranch);
|
|
1771
1813
|
const agentInvocation = build(key, "<worktree-path>", modelAlias);
|
|
1814
|
+
// BAPI-801: one concise secondary detail naming the PR base, emitted only when
|
|
1815
|
+
// the shared rule actually exports BAPI_BASE_BRANCH. It sits AFTER the rendered
|
|
1816
|
+
// spawn command so the copyable command stays the primary scan target, and it
|
|
1817
|
+
// is ordinary routing detail — never warning-like. An ordinary plain run on
|
|
1818
|
+
// `main` exports nothing and so keeps its existing output density verbatim.
|
|
1819
|
+
const prBaseBranch = resolvePrBaseBranchEnvValue(conductorEnabled, baseBranch);
|
|
1772
1820
|
return [
|
|
1773
1821
|
`DRY-RUN: ${key} -> branch=${branch}`,
|
|
1774
1822
|
`DRY-RUN: ${worktrunkBinary} ${wtArgs.join(" ")}`,
|
|
1775
1823
|
`DRY-RUN: ${agentInvocation}`,
|
|
1824
|
+
...(prBaseBranch ? [`DRY-RUN: PR base: ${prBaseBranch}`] : []),
|
|
1776
1825
|
...buildDryRunMcpProvisioningLines("<worktree-path>", platform, mcpServerInvocation),
|
|
1777
1826
|
];
|
|
1778
1827
|
}
|
|
@@ -2792,8 +2841,10 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
|
|
|
2792
2841
|
const platformConfig = resolveStartTicketsPlatformConfig(deps, agent, options.autoApprove, options.conductorEnabled ?? false, resolvedRepoName,
|
|
2793
2842
|
// BAPI-494: resume-mode dispatches get the full-suite remediation finalize prompt.
|
|
2794
2843
|
options.resumeMode ?? false, options.workflow, options.reviewRounds,
|
|
2795
|
-
// BAPI-586: the effective run base (already carries any
|
|
2796
|
-
// override applied above) so
|
|
2844
|
+
// BAPI-586/BAPI-801: the effective run base (already carries any
|
|
2845
|
+
// epic.base_branch override applied above) so the spawned worker gets
|
|
2846
|
+
// BAPI_BASE_BRANCH — every conductor worker, and a plain worker whose base
|
|
2847
|
+
// is non-default.
|
|
2797
2848
|
options.baseBranch);
|
|
2798
2849
|
if (!platformConfig.ok)
|
|
2799
2850
|
return { ok: false, error: platformConfig.error };
|
|
@@ -2940,6 +2991,54 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
|
|
|
2940
2991
|
const rows = await spawnTabsFn(deps, routed, terminal, platformConfig.config.buildAgentShellCommand);
|
|
2941
2992
|
return { ok: true, rows };
|
|
2942
2993
|
}
|
|
2994
|
+
/**
|
|
2995
|
+
* Warn when an implicit base branch could be resolved through an active
|
|
2996
|
+
* repository-wide index-branch override (BAPI-814/E1b).
|
|
2997
|
+
*
|
|
2998
|
+
* Informational only, and deliberately narrow:
|
|
2999
|
+
*
|
|
3000
|
+
* - **Only for an IMPLICIT base.** An explicit `--base-branch` is the caller's
|
|
3001
|
+
* own decision and is unaffected by the override, so the probe is skipped
|
|
3002
|
+
* entirely — no request is made and no warning is printed.
|
|
3003
|
+
* - **Only with resolvable credentials.** `start-tickets` creates worktrees and
|
|
3004
|
+
* opens tabs; it does not require Bridge access to do its job. "No credential"
|
|
3005
|
+
* is therefore an ordinary state for this command, not a diagnostic, and
|
|
3006
|
+
* staying silent keeps an unrelated command from growing a credential
|
|
3007
|
+
* complaint. A lookup that FAILS after credentials resolved is different, and
|
|
3008
|
+
* does produce one bounded, sanitized line.
|
|
3009
|
+
* - **Never a refusal.** Nothing here changes the exit code, the base branch, or
|
|
3010
|
+
* any spawn behavior.
|
|
3011
|
+
*
|
|
3012
|
+
* Warnings go to `errorLog` (stderr), so machine-readable stdout stays clean.
|
|
3013
|
+
*/
|
|
3014
|
+
async function warnOnIndexOverrideForImplicitBase(deps, options, errorLog, fetchImpl) {
|
|
3015
|
+
if (options.baseBranchExplicit)
|
|
3016
|
+
return;
|
|
3017
|
+
const access = await resolveConductorBridgeApiAccess({
|
|
3018
|
+
env: deps.env,
|
|
3019
|
+
cwd: deps.cwd,
|
|
3020
|
+
homedir: os.homedir,
|
|
3021
|
+
platform: deps.platform,
|
|
3022
|
+
readFile: deps.readFile ?? ((filePath) => readFile(filePath, "utf-8")),
|
|
3023
|
+
stat: (filePath) => stat(filePath).then((info) => ({ mode: info.mode })),
|
|
3024
|
+
});
|
|
3025
|
+
if (!access.ok)
|
|
3026
|
+
return;
|
|
3027
|
+
const result = await getIndexBranch(access.access, fetchImpl);
|
|
3028
|
+
if (!result.ok) {
|
|
3029
|
+
errorLog(`Warning: the repository's indexed-branch override could not be read: ${result.error}. ` +
|
|
3030
|
+
"Continuing — ticket startup is unaffected.");
|
|
3031
|
+
return;
|
|
3032
|
+
}
|
|
3033
|
+
const override = result.value.override;
|
|
3034
|
+
if (override === null)
|
|
3035
|
+
return;
|
|
3036
|
+
errorLog(`Warning: ${access.access.repoName} has an ACTIVE index-branch override: ` +
|
|
3037
|
+
`'${override.override_branch}'. No --base-branch was given, so the implicit base — ` +
|
|
3038
|
+
"the base-branch config field and feature-branch provisioning — may resolve through " +
|
|
3039
|
+
"that override rather than the repository's usual base. Ticket startup is ALLOWED and " +
|
|
3040
|
+
"continues; pass --base-branch to choose explicitly.");
|
|
3041
|
+
}
|
|
2943
3042
|
/** Platform-specific guidance printed when one or more tabs fail to spawn. */
|
|
2944
3043
|
function spawnFailureHintForPlatform(platform) {
|
|
2945
3044
|
if (platform === "darwin") {
|
|
@@ -2995,6 +3094,9 @@ export async function runStartTicketsCli(argv, overrides = {}) {
|
|
|
2995
3094
|
errorLog("Warning: --tier value was not one of cheap|basic|premium; ignoring the override and " +
|
|
2996
3095
|
"using premium model routing for every ticket.");
|
|
2997
3096
|
}
|
|
3097
|
+
// BAPI-814/E1b: informational only, ahead of the dry-run branch so both a
|
|
3098
|
+
// preview and a real run report the same repository-wide condition.
|
|
3099
|
+
await warnOnIndexOverrideForImplicitBase(deps, options, errorLog, overrides.fetchImpl ?? globalThis.fetch);
|
|
2998
3100
|
if (options.dryRun) {
|
|
2999
3101
|
// Resolve the repo identity for the preview so the dry-run command matches
|
|
3000
3102
|
// what the real spawn injects (see prependRepoNameEnvAssignment). The real
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
|
|
2
|
-
export const VERSION = "0.2.
|
|
2
|
+
export const VERSION = "0.2.41";
|
package/build/worktree-core.js
CHANGED
|
@@ -186,9 +186,15 @@ const CLEANUP_FAILURE_DIAGNOSTIC = "untracked-file cleanup did not complete; con
|
|
|
186
186
|
* settings, MCP registrations/trust, and worker logging are provisioned again — so
|
|
187
187
|
* anything cleanup removes is re-created by the provisioning that follows it.
|
|
188
188
|
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
189
|
+
* `.conductor/` content is NOT removed here, and must not be assumed to be
|
|
190
|
+
* (BAPI-814/D1). `.conductor/` is an ignored path in this repository, and `-fd`
|
|
191
|
+
* without `-x` leaves ignored paths alone — so a prior attempt's
|
|
192
|
+
* `.conductor/result.json` and `.conductor/critique.md` survive this cleanup
|
|
193
|
+
* entirely. Removing them is the ATTEMPT OWNER's job, performed explicitly in
|
|
194
|
+
* `executor/job-runner.ts` after the worktree lock is acquired and before the
|
|
195
|
+
* worker spawns (`removeStaleVerdictArtifacts`). That placement is deliberate:
|
|
196
|
+
* cleanup must be serialized against the lock, whereas this function runs during
|
|
197
|
+
* unlocked worktree preparation.
|
|
192
198
|
*
|
|
193
199
|
* FAIL-OPEN. Pre-change behavior performed no cleanup at all, so being unable to
|
|
194
200
|
* clean must never change the worktree row or the job outcome. Every failure path —
|
|
@@ -58,7 +58,8 @@ activated. `core` is always implicitly included.
|
|
|
58
58
|
| `core` | always |
|
|
59
59
|
| `pipeline-authoring` | `BRIDGE_MCP_PROFILE` includes `pipeline-authoring` or `full` |
|
|
60
60
|
| `conductor` | includes `conductor` or `full` |
|
|
61
|
-
| `sfcc` | includes `sfcc` or `full` (2 diagnostics ship in `core
|
|
61
|
+
| `sfcc` | includes `sfcc` or `full` — the OCAPI **read** tools and `sfcc_log_query` (2 diagnostics ship in `core`) |
|
|
62
|
+
| `sfcc-write` | includes `sfcc-write` or `full` — the destructive SFCC **write** tools. Independent of `sfcc`: neither group implies the other (BAPI-817) |
|
|
62
63
|
|
|
63
64
|
The `pipeline-authoring` tools live in `index.ts` alongside core, gated by
|
|
64
65
|
`ACTIVE_GROUPS.has("pipeline-authoring")`.
|
|
@@ -178,10 +179,43 @@ by tier.
|
|
|
178
179
|
| Tool | Dependencies (class) |
|
|
179
180
|
|---|---|
|
|
180
181
|
| `create_pull_request` | VCS **[BLOCK]** (Tier-1) |
|
|
182
|
+
| `merge_pull_request` | VCS **[BLOCK]** (Tier-1; protected server-decided merge — see below) |
|
|
181
183
|
| `resolve_ci_checks` | VCS **[BLOCK]** (Tier-1) |
|
|
182
184
|
| `poll_ci_checks` | VCS **[BLOCK]** (Tier-1); also **hidden until `ci_check_config` is resolved** (needs a prior `resolve_ci_checks`) |
|
|
183
185
|
| `materialize_fresh_base` | VCS / git base ref **[BLOCK]** (Tier-1; materializes a pinned base tree) |
|
|
184
186
|
|
|
187
|
+
#### `merge_pull_request` — what the caller can observe
|
|
188
|
+
|
|
189
|
+
The tool holds no VCS credential and performs no merge of its own; it asks the
|
|
190
|
+
server and reports the answer. Four constraints are worth knowing before calling it:
|
|
191
|
+
|
|
192
|
+
- **Only `merged: true` is success.** `dry_run`, `pending_approval`, and
|
|
193
|
+
`lease_held` are non-progress outcomes — nothing was merged, and repeating the
|
|
194
|
+
call unchanged will produce the same answer until the named condition moves.
|
|
195
|
+
- **Workflow files are refused.** The server reads the pull request's complete
|
|
196
|
+
changed-file list and declines any PR touching `.github/workflows/`, including a
|
|
197
|
+
file *renamed out of* that directory. A file list it cannot prove complete is
|
|
198
|
+
refused the same way, as `workflow_files_unreadable` — an unreadable diff is not
|
|
199
|
+
a clean one.
|
|
200
|
+
- **`auto_merge_enabled` must be on.** Merge configuration that is missing,
|
|
201
|
+
disabled, or malformed produces `dry_run` rather than a merge. Enable it on the
|
|
202
|
+
project default via `PUT /jira/epic-runs/supervisor-config/defaults/`; the
|
|
203
|
+
`dry_run` envelope carries that exact remediation in its `hint`.
|
|
204
|
+
- **No required checks, no merge.** When no authoritative required-check set can be
|
|
205
|
+
resolved, the tool returns `gate_unresolved` and never sends the merge request —
|
|
206
|
+
"zero required checks are all green" is not a CI pass.
|
|
207
|
+
- **Review is refused too, when the repository opts in.** If the done gate carries a
|
|
208
|
+
`review_state` condition, the tool also refuses when the verdict is not approving
|
|
209
|
+
for the exact `expected_head_sha`. `review_not_approved` is a wait — a verdict for
|
|
210
|
+
this head may still be posted, so it carries `retry_hint: "retry_later"`. The other
|
|
211
|
+
four need intervention (`needs_human`): `review_unavailable` (review state could not
|
|
212
|
+
be read), `review_source_unsupported` (`min_approvals` / `combination`, refused
|
|
213
|
+
rather than downgraded), `review_config_invalid` (review configuration exists but is
|
|
214
|
+
malformed), and `head_sha_drift` (the verdict belongs to a different commit). The
|
|
215
|
+
tool's own check is advisory; the server's head-bound review observation is
|
|
216
|
+
authoritative, so a tool-side pass followed by a server refusal is expected for a
|
|
217
|
+
legacy approved comment that carries no head proof.
|
|
218
|
+
|
|
185
219
|
### Full-automation (composite)
|
|
186
220
|
|
|
187
221
|
These orchestrate an idea → ticket → review → start chain, so they inherit the
|
|
@@ -239,11 +273,16 @@ integration dependency.
|
|
|
239
273
|
| `sfcc_setup_status` | — none (its purpose is to report which SFCC prerequisites are missing) |
|
|
240
274
|
| `check_permissions` | SFCC OCAPI **[BLOCK]** (probes OCAPI; reports not-OK without access / version config) |
|
|
241
275
|
|
|
242
|
-
### Gated behind the `sfcc` group
|
|
276
|
+
### Gated behind the `sfcc` group (reads + logs)
|
|
243
277
|
|
|
244
278
|
All are profile-gated **[BLOCK]** to register, and all require SFCC OCAPI creds +
|
|
245
|
-
`version` config + a reachable sandbox → SFCC OCAPI **[BLOCK]**.
|
|
246
|
-
|
|
279
|
+
`version` config + a reachable sandbox → SFCC OCAPI **[BLOCK]**.
|
|
280
|
+
|
|
281
|
+
Every SFCC tool — including the two always-on diagnostics above — additionally
|
|
282
|
+
crosses the resolved-hostname sandbox boundary at invocation time: if the host
|
|
283
|
+
the credentials resolve to is not a recognized developer sandbox, the call is
|
|
284
|
+
refused with `403 TARGET_NOT_SANDBOX` before any request leaves the machine
|
|
285
|
+
(BAPI-817).
|
|
247
286
|
|
|
248
287
|
**Reads:**
|
|
249
288
|
|
|
@@ -258,7 +297,13 @@ sandbox-only and destructive.
|
|
|
258
297
|
| `site_preference_get` | Profile-gated **[BLOCK]** · SFCC OCAPI **[BLOCK]** |
|
|
259
298
|
| `site_preference_search` | Profile-gated **[BLOCK]** · SFCC OCAPI **[BLOCK]** |
|
|
260
299
|
|
|
261
|
-
|
|
300
|
+
### Gated behind the `sfcc-write` group
|
|
301
|
+
|
|
302
|
+
Writes are destructive and sandbox-only. They are **not** registered by the
|
|
303
|
+
`sfcc` group — enabling introspection must never enable mutation — so a session
|
|
304
|
+
wanting both selects `sfcc,sfcc-write` (or `full`).
|
|
305
|
+
|
|
306
|
+
**Writes (sandbox-only):**
|
|
262
307
|
|
|
263
308
|
| Tool | Dependencies (class) |
|
|
264
309
|
|---|---|
|
|
@@ -272,7 +317,7 @@ sandbox-only and destructive.
|
|
|
272
317
|
| `custom_object_definition_attribute_update` | Profile-gated **[BLOCK]** · SFCC OCAPI write **[BLOCK]** |
|
|
273
318
|
| `site_preference_values_set` | Profile-gated **[BLOCK]** · SFCC OCAPI write **[BLOCK]** |
|
|
274
319
|
|
|
275
|
-
**Logs
|
|
320
|
+
**Logs** (gated by `sfcc`, not `sfcc-write`)**:**
|
|
276
321
|
|
|
277
322
|
| Tool | Dependencies (class) |
|
|
278
323
|
|---|---|
|
|
@@ -286,12 +331,12 @@ sandbox-only and destructive.
|
|
|
286
331
|
|---|---|---|
|
|
287
332
|
| **Ticket backend** (jira mode) | `create_ticket`, `get_ticket(s)`, `update_ticket_description`, `update_jira_status`, `get_jira_transitions`, and every AI generator (they read the ticket) | — |
|
|
288
333
|
| **Jira (only)** | `get_comments`, `add_comment`, `attachment`, `estimate_epic` | — |
|
|
289
|
-
| **Version control (VCS)** | `create_pull_request`, `resolve_ci_checks`, `poll_ci_checks`, `materialize_fresh_base`, `parse_repository`, `regenerate_directory_map`, `wait_for_done_gate`; **Tier-3** `request_plan_generation`/`get_plan`, `request_architecture`/`get_architecture`, `request_reimplement_context`/`get_reimplement_context`, `create_doc`(tdd/architecture) | **Tier-4** `request_clarifying_questions`, `request_ticket_critique`, `request_ticket_review`, `request_prd`, `create_doc`(prd/fsd) |
|
|
334
|
+
| **Version control (VCS)** | `create_pull_request`, `merge_pull_request`, `resolve_ci_checks`, `poll_ci_checks`, `materialize_fresh_base`, `parse_repository`, `regenerate_directory_map`, `wait_for_done_gate`; **Tier-3** `request_plan_generation`/`get_plan`, `request_architecture`/`get_architecture`, `request_reimplement_context`/`get_reimplement_context`, `create_doc`(tdd/architecture) | **Tier-4** `request_clarifying_questions`, `request_ticket_critique`, `request_ticket_review`, `request_prd`, `create_doc`(prd/fsd) |
|
|
290
335
|
| **Code index** (succeeded parse) | Tier-3 plan/architecture/reimplement/`create_doc`(tdd/architecture); `request_council` in `technical`/`discovery` modes | Tier-4 clarifying-questions/critique/review/prd/fsd |
|
|
291
|
-
| **SFCC OCAPI** | `check_permissions` +
|
|
336
|
+
| **SFCC OCAPI** | `check_permissions` + every gated SFCC read and write tool | — |
|
|
292
337
|
| **SFCC WebDAV logs** | `sfcc_log_query` | — |
|
|
293
338
|
| **Deep-research flag** | `request_deep_research`, `get_deep_research` | — |
|
|
294
|
-
| **Profile gating** | all `pipeline-authoring`, `conductor`, and
|
|
339
|
+
| **Profile gating** | all `pipeline-authoring`, `conductor`, `sfcc`, and `sfcc-write` tools | — |
|
|
295
340
|
|
|
296
341
|
## Notes & caveats
|
|
297
342
|
|
|
@@ -6,11 +6,25 @@ small set of sandbox-only writes — through the **OCAPI Data API**. This guide
|
|
|
6
6
|
setting up the OCAPI client that those tools authenticate against.
|
|
7
7
|
|
|
8
8
|
> **Sandbox / local development only.** This integration is intended for a **developer
|
|
9
|
-
> sandbox
|
|
10
|
-
>
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
>
|
|
9
|
+
> sandbox**, and that restriction is **enforced in code**: before any SFCC tool runs,
|
|
10
|
+
> Bridge validates the hostname your credentials actually resolve to — from `dw.json`
|
|
11
|
+
> or `SFCC_*` — against the sandbox forms listed below. An unrecognized host is refused
|
|
12
|
+
> with a `403` (`error.code: "TARGET_NOT_SANDBOX"`) before any request leaves your
|
|
13
|
+
> machine. The check reads the resolved hostname, never the `instance` tool argument,
|
|
14
|
+
> so omitting `instance` or passing `"sandbox"` cannot bypass it.
|
|
15
|
+
>
|
|
16
|
+
> Accepted sandbox hostname forms:
|
|
17
|
+
>
|
|
18
|
+
> - `<realm>-<nnn>.sandbox.<region>.dx.commercecloud.salesforce.com`
|
|
19
|
+
> - `<realm>-<nnn>.sandbox.dx.commercecloud.salesforce.com`
|
|
20
|
+
> - `<realm>-<nnn>.dx.commercecloud.salesforce.com`
|
|
21
|
+
>
|
|
22
|
+
> Anything else — a `production-`/`staging-`/`development-` prefixed host, or any
|
|
23
|
+
> `*.demandware.net` host — is rejected.
|
|
24
|
+
>
|
|
25
|
+
> Still do not configure the grants below on an instance that holds real data.
|
|
26
|
+
> Credentials stay local (in `dw.json` or `SFCC_*` env vars) and are never sent to
|
|
27
|
+
> Bridge.
|
|
14
28
|
|
|
15
29
|
For the full per-tool list and what each SFCC tool depends on, see
|
|
16
30
|
[MCP Tool Integration Dependencies](./mcp-tool-integrations.md). For the tool reference
|
|
@@ -34,20 +48,39 @@ In Business Manager for the sandbox:
|
|
|
34
48
|
|
|
35
49
|
**Administration → Site Development → Open Commerce API Settings → Data API** tab.
|
|
36
50
|
|
|
37
|
-
Add the
|
|
38
|
-
**Save**.
|
|
39
|
-
|
|
51
|
+
Add the client entry below to the `clients` array of the Data API settings, then
|
|
52
|
+
**Save**. It grants only the resource families and HTTP methods Bridge's SFCC tools
|
|
53
|
+
actually call — not a global `/**` grant. `check_permissions` prints the same JSON on
|
|
54
|
+
a 401/403, split into the two blocks.
|
|
55
|
+
|
|
56
|
+
**READ/SEARCH TOOL GRANTS** — required by the `sfcc` read tools. (`post` is OCAPI's
|
|
57
|
+
convention for its `*_search` endpoints, not a mutation.)
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"client_id": "<your-client-id-here>",
|
|
62
|
+
"resources": [
|
|
63
|
+
{ "resource_id": "/system_object_definitions", "methods": ["get"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
64
|
+
{ "resource_id": "/system_object_definitions/**", "methods": ["get", "post"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
65
|
+
{ "resource_id": "/site_preferences/**", "methods": ["get", "post"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
66
|
+
{ "resource_id": "/custom_object_definitions/**", "methods": ["get", "post"], "read_attributes": "(**)", "write_attributes": "(**)" }
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**MUTATION GRANTS** — required **only if you enable `BRIDGE_MCP_PROFILE=sfcc-write`**,
|
|
72
|
+
which registers the nine destructive write tools. These are shipped capabilities, not
|
|
73
|
+
future work. No `delete` is granted, because no shipped write tool performs one; the
|
|
74
|
+
`get` entries are needed for the If-Match ETag round trip that precedes each `PATCH`.
|
|
40
75
|
|
|
41
76
|
```json
|
|
42
77
|
{
|
|
43
78
|
"client_id": "<your-client-id-here>",
|
|
44
79
|
"resources": [
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"resource_id": "/**"
|
|
50
|
-
}
|
|
80
|
+
{ "resource_id": "/system_object_definitions", "methods": ["get"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
81
|
+
{ "resource_id": "/system_object_definitions/**", "methods": ["get", "put", "patch"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
82
|
+
{ "resource_id": "/custom_object_definitions/**", "methods": ["get", "put", "patch"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
83
|
+
{ "resource_id": "/site_preferences/**", "methods": ["get", "patch"], "read_attributes": "(**)", "write_attributes": "(**)" }
|
|
51
84
|
]
|
|
52
85
|
}
|
|
53
86
|
```
|
|
@@ -57,7 +90,9 @@ Notes:
|
|
|
57
90
|
- The `client_id` **must match** the Account Manager API client whose credentials you
|
|
58
91
|
put in `dw.json` / `SFCC_*` below. Replace the value above with your own client id if
|
|
59
92
|
it differs.
|
|
60
|
-
- If the Data API settings are empty, wrap the
|
|
93
|
+
- If the Data API settings are empty, wrap the entries in the standard settings
|
|
94
|
+
envelope. Merge the resource lists from the block(s) above into one `resources`
|
|
95
|
+
array — do not substitute a global `"resource_id": "/**"` grant:
|
|
61
96
|
|
|
62
97
|
```json
|
|
63
98
|
{
|
|
@@ -66,12 +101,10 @@ Notes:
|
|
|
66
101
|
{
|
|
67
102
|
"client_id": "<your-client-id-here>",
|
|
68
103
|
"resources": [
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"resource_id": "/**"
|
|
74
|
-
}
|
|
104
|
+
{ "resource_id": "/system_object_definitions", "methods": ["get"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
105
|
+
{ "resource_id": "/system_object_definitions/**", "methods": ["get", "post"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
106
|
+
{ "resource_id": "/site_preferences/**", "methods": ["get", "post"], "read_attributes": "(**)", "write_attributes": "(**)" },
|
|
107
|
+
{ "resource_id": "/custom_object_definitions/**", "methods": ["get", "post"], "read_attributes": "(**)", "write_attributes": "(**)" }
|
|
75
108
|
]
|
|
76
109
|
}
|
|
77
110
|
]
|
|
@@ -109,14 +142,28 @@ or the `/teach-bridge` skill.
|
|
|
109
142
|
## 4. Enable the SFCC tools
|
|
110
143
|
|
|
111
144
|
The two diagnostic tools (`sfcc_setup_status`, `check_permissions`) are always
|
|
112
|
-
registered.
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
registered. Everything else is gated, behind **two independent profile groups**:
|
|
146
|
+
|
|
147
|
+
| Group | Registers |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `sfcc` | the 8 OCAPI read tools + `sfcc_log_query` — read-only |
|
|
150
|
+
| `sfcc-write` | the 9 destructive write tools |
|
|
151
|
+
|
|
152
|
+
Neither implies the other. Add what you need to `BRIDGE_MCP_PROFILE` in the MCP server
|
|
153
|
+
`env` block (it is comma-separated), then **restart the MCP client**:
|
|
115
154
|
|
|
116
155
|
```json
|
|
117
156
|
"env": { "BRIDGE_MCP_PROFILE": "sfcc" }
|
|
118
157
|
```
|
|
119
158
|
|
|
159
|
+
For reads plus writes, use `"sfcc,sfcc-write"`. `full` expands to every group and is
|
|
160
|
+
therefore write-capable.
|
|
161
|
+
|
|
162
|
+
> **Migration.** `sfcc` used to register the nine write tools too. It no longer does.
|
|
163
|
+
> If you were relying on SFCC writes through `BRIDGE_MCP_PROFILE=sfcc`, change it to
|
|
164
|
+
> `BRIDGE_MCP_PROFILE=sfcc,sfcc-write`. `full` users keep write access and need no
|
|
165
|
+
> change.
|
|
166
|
+
|
|
120
167
|
## 5. Verify
|
|
121
168
|
|
|
122
169
|
Ask your agent to run:
|