@bridge_gpt/mcp-server 0.2.19 → 0.2.21

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 (35) hide show
  1. package/README.md +6 -3
  2. package/build/agents.generated.js +1 -1
  3. package/build/commands.generated.js +4 -3
  4. package/build/conductor/local-merge.js +458 -95
  5. package/build/estimate-epic.js +84 -0
  6. package/build/executor/job-runner.js +151 -17
  7. package/build/executor/merge-job.js +84 -10
  8. package/build/executor/worker-finalization.js +98 -18
  9. package/build/index.js +1843 -401
  10. package/build/pipelines.generated.js +16 -20
  11. package/build/readme.generated.js +1 -1
  12. package/build/review-tickets.js +15 -5
  13. package/build/sfcc/client.js +192 -50
  14. package/build/sfcc/ocapi-write-faults.js +94 -0
  15. package/build/sfcc/permissions.js +7 -22
  16. package/build/sfcc/register.js +9 -0
  17. package/build/sfcc/write-grants.js +80 -0
  18. package/build/sfcc/write-guard.js +39 -0
  19. package/build/sfcc/write-result.js +47 -0
  20. package/build/sfcc/write-tool-common.js +85 -0
  21. package/build/sfcc/writes-custom-object-def.js +141 -0
  22. package/build/sfcc/writes-object-attribute-payloads.js +97 -0
  23. package/build/sfcc/writes-site-preference-payloads.js +59 -0
  24. package/build/sfcc/writes-site-preference.js +96 -0
  25. package/build/sfcc/writes-system-object-payloads.js +213 -0
  26. package/build/sfcc/writes-system-object.js +348 -0
  27. package/build/sfcc/writes.js +66 -0
  28. package/build/version.generated.js +1 -1
  29. package/package.json +3 -3
  30. package/pipelines/idea-to-ticket.json +7 -0
  31. package/pipelines/review-ticket.json +5 -18
  32. package/public/css/main.min.css +1583 -117
  33. package/public/css/main.min.css.map +1 -1
  34. package/public/js/main.min.js +2792 -449
  35. package/public/js/main.min.js.map +1 -1
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
2
  // This file is produced by scripts/bundle-readme.js
3
- export const README = "# @bridge_gpt/mcp-server\n\nThe Bridge MCP is an end-to-end accelerator for shipping code within SFCC, powered by [Bridge API](https://bridgegpt-api.com). Works with Claude Code, Github Copilot, Cursor, Windsurf, and OpenAI Codex.\n\n## Contents\n\n- [Getting Started](#getting-started)\n- [Usage Documentation](#usage-documentation)\n - [Tier 1 — Regularly useful](#tier-1--regularly-useful)\n - [Tier 2 — Occasionally useful](#tier-2--occasionally-useful)\n - [Tier 3 — Now and then](#tier-3--now-and-then)\n - [Operational commands](#operational-commands)\n - [Extra Capabilities](#extra-capabilities)\n- [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools)\n- [CLI Subcommands](#cli-subcommands)\n- [Custom Pipelines](#custom-pipelines)\n- [Environment Variables](#environment-variables)\n- [Worktree credentials and the `mcp-invoke` shim](#worktree-credentials-and-the-mcp-invoke-shim)\n- [Reference](#reference)\n\nFor advanced epic/multi-agent orchestration, see [CONDUCTOR.md](./CONDUCTOR.md).\n\n## Getting Started\n\n### Quick start\n\nFrom your **project root**, install and connect in one command:\n\n```bash\nnpx -y @bridge_gpt/mcp-server@latest install-bridge\n```\n\n`install-bridge` scaffolds the project, writes your editor's MCP config with real\nvalues, verifies connectivity, persists your API key to the user-scoped credential\nstore, and opens a fresh agent session to finish setup (`/install-bridge` then\n`/learn-repository`). The only inputs are an **API key** (generate one on the Bridge\nAPI web UI **Security** page) and a **repo name** — everything else is derived. Add\n`--dry-run` to preview every step without writing, pinging, or spawning anything.\n\nTo upgrade later, run:\n\n```bash\nnpx -y @bridge_gpt/mcp-server upgrade\n```\n\n`upgrade` re-execs from `@latest`, refreshes all scaffolded artifacts (slash\ncommands, agents, pipelines), rewrites the version pin, and reconnects — also\navailable as the `/upgrade-bridge` slash command. (The legacy `--upgrade` flag still\nworks as a fallback.)\n\n<details>\n<summary><strong>Installation Instructions</strong></summary>\n\n#### What `install-bridge` does\n\n`install-bridge` collapses the whole setup into a single command. It:\n\n1. **Scaffolds** the project (the same artifacts `--init` writes: slash commands,\n agents, `.bridge/pipelines/`, and secret-free MCP config placeholders).\n2. **Writes the per-host MCP config** (`.mcp.json` / `.cursor/mcp.json` /\n `.vscode/mcp.json`) with your real `BAPI_REPO_NAME` / `BAPI_API_KEY` /\n `BAPI_BASE_URL` / `BAPI_DOCS_DIR`, preserving any unrelated servers. The\n launcher it writes is pinned to the exact installed version so `npx` never\n silently reuses a stale local copy. (Windsurf and Codex are global configs it\n can't safely write — it prints copy-paste instructions for those.)\n3. **Verifies connectivity** against the Bridge API before persisting anything.\n4. **Persists your key** to the user-scoped credential store\n (`~/.config/bridge/credentials.json`, target `bapi:<repo>`) so shell-spawned\n tooling (e.g. `start-tickets`) can resolve it.\n5. **Opens a fresh agent session** that runs `/install-bridge` (to derive the\n remaining config fields from your codebase) and then `/learn-repository`.\n\nThe only inputs are an **API key** and a **repo name** (everything else is\nderived). Resolution order:\n\n- **API key:** `--api-key <key>` → `BAPI_API_KEY` env → an interactive (no-echo)\n prompt. Generate one first on the Bridge API web UI **Security** page (see\n [Generate an API Key](#2-generate-an-api-key)); the command consumes a key, it\n never mints one. The key is **never printed or logged**.\n- **Repo name:** `--repo <name>` → `BAPI_REPO_NAME` env → an inferred default you\n confirm interactively. It MUST match the server-side repository registration.\n\nUseful flags:\n\n- `--dry-run` — preview every step (scaffold targets, config files and keys with\n the key value **redacted**, the ping target, the credential store target, and\n the exact agent spawn command) without writing, pinging, or spawning anything.\n- `--force` — overwrite an existing real `BAPI_API_KEY` in a host config without\n prompting (re-running is otherwise non-destructive).\n- `--agent claude|cursor-agent` — which agent to launch for the agentic remainder\n (default `claude`).\n\nThat's it — once `install-bridge` finishes you're connected. If you prefer to do\nit by hand (or just want to understand each step), the manual flow below does the\nsame thing.\n\n#### Manual Setup (Alternative)\n\n##### 1. Install the Package\n\nFrom your **project root**, install the MCP server and scaffold slash commands:\n\n```bash\nnpm i @bridge_gpt/mcp-server\nnpx -y @bridge_gpt/mcp-server --init\n```\n\n`--init` must be run from the directory containing your `package.json`. It:\n\n- Creates slash commands in `.claude/commands/` and `.cursor/commands/`\n- Detects existing MCP config files and sets `BAPI_PROJECT_ROOT` so local file output resolves correctly\n- Scaffolds `.bridge/pipelines/` for custom pipeline authoring\n\nRe-run `--init` after upgrading the package to get updated commands.\n\n##### 2. Generate an API Key\n\n1. Log in to [Bridge API](https://bridgegpt-api.com) and navigate to your project's **Security** page\n2. Click **Create New Key**\n3. Enter your email, an optional label (e.g., \"MCP Server\"), and select the **Admin** role\n4. Click **Create Key**\n5. **Copy the key immediately** — it will not be shown again\n\n##### 3. Configure the MCP Server\n\nAdd the following to your editor's MCP configuration file, pasting in the API key from step 2:\n\n<details>\n<summary><strong>Claude Code (.mcp.json)</strong></summary>\n\nThe `--init` command (step 1) detects Claude Code and creates a `.mcp.json` at your project root with placeholder values. Open it and replace `your-repo` and `your-api-key` with your actual values from step 2:\n\n```json\n{\n \"mcpServers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>VS Code / Copilot (.vscode/mcp.json)</strong></summary>\n\n```json\n{\n \"servers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>Cursor (.cursor/mcp.json)</strong></summary>\n\n```json\n{\n \"mcpServers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n\n> If project-local config is not supported in your Cursor version, use `~/.cursor/config/mcp.json` instead.\n</details>\n\n<details>\n<summary><strong>Windsurf (~/.codeium/windsurf/mcp_config.json)</strong></summary>\n\nWindsurf only supports global MCP configuration.\n\n```json\n{\n \"mcpServers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>OpenAI Codex (~/.codex/config.toml)</strong></summary>\n\n```toml\n[mcp_servers.bridge-api]\ncommand = \"npx\"\nargs = [\"-y\", \"@bridge_gpt/mcp-server\"]\n\n[mcp_servers.bridge-api.env]\nBAPI_BASE_URL = \"https://bridgegpt-api.com\"\nBAPI_REPO_NAME = \"your-repo\"\nBAPI_API_KEY = \"your-api-key\"\nBAPI_DOCS_DIR = \"docs/tmp\"\n```\n\n> Codex users: set `BAPI_PROJECT_ROOT` manually in your config (see Environment Variables below).\n</details>\n\nAfter saving the config, restart your editor or reload the MCP server connection. Verify connectivity by asking your AI assistant to call the `ping` tool.\n\n##### 4. First-Time Setup: Teach Bridge Your Codebase\n\nIf you're the first person to install Bridge API on your project, run the `/learn-repository` slash command after completing setup. This analyzes your codebase's architecture, testing patterns, code review standards, and documentation conventions, then uploads the findings to Bridge API. This gives Bridge the context it needs to generate implementation plans, ticket critiques, and code reviews that are consistent with your project's actual architecture and conventions.\n\nYou only need to do this once per project — the learned standards persist for all team members.\n\n##### Upgrading (details)\n\nThe one-command `npx -y @bridge_gpt/mcp-server upgrade` (shown above) is the\nrecommended path. The legacy flag `npx -y @bridge_gpt/mcp-server --upgrade` still\nworks: it runs `npm i @bridge_gpt/mcp-server@latest`, prints a before/after version\nsummary, then re-runs the full `--init` scaffolding flow to update your slash\ncommands, agents, and pipeline definitions.\n\nThe MCP server also checks for updates automatically on startup. If a newer version is available, you'll see a notice in your editor's MCP output logs with the upgrade command to run. This check is cached for 24 hours and never blocks server startup.\n\n</details>\n\n## Usage Documentation\n\nThis is the Bridge API tooling worth knowing about as a software engineer — the things you'd ask an agent to do — grouped by how often you would use them. Each entry covers **what it does**, **when it's useful** and **how to use it**. The behind-the-scenes plumbing is summarized at the end under [Extra Capabilities](#extra-capabilities), and a full enumeration lives in [Reference](#reference).\n\nWorking in a Salesforce B2C Commerce codebase? Bridge also ships read-only SFCC platform-introspection tools — see [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools).\n\nFor invocation, prefer the slash command — it's deterministic. A free-text example is shown only where natural-language phrasing reliably maps to the right automation; high-consequence or easily-misread automations show only the slash command on purpose.\n\n### Tier 1 — Regularly useful\n\nThese features are useful for most tickets.\n\n**1. Review Ticket**\n- **What it does:** Runs a full quality review of a ticket: clarifying questions + critique (initial round), an automatic alternate-model second opinion, then evaluates findings and produces a decision page to accept/reject them. The second-opinion pass is included by default and can be skipped with `--rounds=1`.\n- **When it's useful:** (Refinement) Right after a ticket is drafted, before anyone starts building — to surface gaps and tighten it.\n- **How to use it:** `/review-ticket BAPI-123` (command only — \"review\" as free text is easily mistaken for a freehand agent review).\n- **Flags:** `--auto` auto-accept findings / skip the approval gates · `--rounds=1` skip the automatic second-opinion review step while preserving downstream evaluation and decision-capture work (cheaper single-pass review). `--rounds=2` (default) runs the full two-round review.\n- **Multi-ticket fan-out:** `/review-tickets BAPI-123 BAPI-456` opens one terminal tab per ticket for parallel review with no worktrees (terminal launcher only — no `wt`/`git`). All `/review-ticket` flags apply; `--review KEY=auto,rounds=N` sets per-ticket overrides. Packaged CLI: `npx -y @bridge_gpt/mcp-server review-tickets KEY [KEY ...]`.\n\n**2. Start Tickets**\n- **What it does:** Creates one git worktree per ticket and spawns an agent session in each to implement them in parallel.\n- **When it's useful:** (Implementation | Automation) When you're ready to start building one or more refined tickets concurrently.\n- **How to use it:** `/start-tickets BAPI-248 BAPI-250` (see [CLI Subcommands](#cli-subcommands) for the full flag table and cross-platform behavior).\n- **Flags:** `--auto` skip the approval gates · `--base-branch <branch>` branch off something other than the default.\n\n**3. Brainstorm**\n- **What it does:** Fans your problem out to two different LLMs and returns their approaches directly. Runs in one of three modes, selected via `mode`: **`technical`** (default — implementation/architecture approaches), **`design`** (UI/UX and visual direction), or **`discovery`** (stakeholder discovery questions for early/vague tasks, grouped into `Technical Discovery Questions` and `Business / Stakeholder Discovery Questions` and tagged `[HUMAN]`/`[CODE]`/`[TICKET]`). Discovery needs no extra configuration. The legacy boolean `design=true` still works and maps to `mode: \"design\"`.\n- **When it's useful:** (Architecture | Refinement) Early, when you want a spread of approaches — `technical` for *how to build it*, `design` for *how it should look*, `discovery` for *what we still need to figure out* before a real ticket exists.\n- **How to use it:** ask your agent to brainstorm — *\"Brainstorm approaches for adding rate limiting to the LLM client; fan it out to multiple models.\"* For a design pass: *\"Run a design brainstorm for the evidence-freshness dashboard UI.\"* For early discovery: *\"Run a discovery brainstorm — `request_brainstorm` with `mode: \"discovery\"` — for this vague request so we can collect the questions stakeholders need to answer first.\"*\n\n**4. Deep Research**\n- **What it does:** Runs multi-source, fact-checked web research on a technical topic and returns a cited report.\n- **When it's useful:** (Architecture | Refinement) When a decision hinges on outside knowledge (libraries, best practices, standards) you don't already have.\n- **How to use it:** `/bridge-research <question>`\n\n**5. Jira Ticket Writer**\n- **What it does:** An agent that drafts a well-structured Jira ticket from a plain description, applying your project's standards.\n- **When it's useful:** (Refinement) When you have an idea in your head and want a properly-formatted ticket draft without writing it by hand.\n- **How to use it:** `/write-ticket <description>` (or ask your agent) — *\"Use the jira ticket writer to turn our conversation into a ticket.\"*\n- **Flags:** `--standards <path>` apply a specific standards file when drafting.\n\n**6. Upload Ticket**\n- **What it does:** Pushes a drafted ticket up to Jira as a real issue (the `create_ticket` capability); handles markdown and child tickets under an Epic.\n- **When it's useful:** (Refinement) The final step after drafting — to get the ticket into Jira so it can be tracked and worked.\n- **How to use it:** Ask your agent to create the ticket, and it should confirm before creating the live Jira issue.\n- **Options:** Describe the issue type (Bug / Story / Task / Epic) and, for a child under an Epic, the parent key.\n\n### Tier 2 — Occasionally useful\n\nThese features are good to know, but you probably won't use them every day.\n\n**1. Plan Ticket**\n- **What it does:** Generates a step-by-step implementation plan for a ticket, with references to real code files, and saves it locally.\n- **When it's useful:** (Refinement | Implementation) Once a ticket is solid and you want a concrete build plan before (or instead of) auto-implementing.\n- **How to use it:** `/plan-ticket BAPI-123`\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check the plan with a second provider.\n\n**2. Clarify Ticket**\n- **What it does:** Generates clarifying questions for a ticket (or debugging guidance for bugs) and saves them locally.\n- **When it's useful:** (Refinement) When a ticket feels under-specified and you want the open questions made explicit.\n- **How to use it:** `/clarify-ticket BAPI-123` — *\"Generate clarifying questions for BAPI-123\"*\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**3. Critique Ticket**\n- **What it does:** Critiques a ticket's quality against your project standards and lists deviations + improvements.\n- **When it's useful:** (Refinement) When you want a quality gate on a ticket before it's worked.\n- **How to use it:** `/critique-ticket BAPI-123` — *\"Critique BAPI-123 against our project standards and list what's missing or deviating.\"*\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**4. Create Doc**\n- **What it does:** Generates a design document for a ticket — a TDD (technical design, engineer audience), an FSD (functional spec, for product/design/QA), or a PRD (product requirements: problem, goals, success metrics) — and saves it locally.\n- **When it's useful:** (Architecture | Refinement) When a ticket needs a fuller design write-up before planning or implementation, in the shape that fits your audience.\n- **How to use it:** `/create-doc BAPI-123 --doc-type tdd` (or `fsd` / `prd`)\n- **Flags:** `--doc-type tdd|fsd|prd` which document to generate (required) · `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**5. Explore Ticket**\n- **What it does:** Explores the codebase for a task and recommends implementation options or surfaces clarifying questions, with optional research.\n- **When it's useful:** (Architecture | Refinement) Before writing a ticket or plan, when you're unsure how a change would fit the existing code.\n- **How to use it:** `/explore-ticket <task>` — *\"Explore the codebase for how we'd add a Mistral LLM provider and recommend 2–3 implementation options.\"*\n\n**6. Second Opinion**\n- **What it does:** Gets an immediate critique of any text from a different model family — no artifact saved, just the reply.\n- **When it's useful:** (Architecture | Refinement | Implementation) Any time you want a quick sanity check on a plan, draft, or decision from a fresh perspective.\n- **How to use it:** ask your agent — *\"Get a second opinion from Gemini on whether the BAPI-123 plan's migration step is safe to run against prod.\"*\n- **Options:** pick the provider (anthropic / openai / gemini) and the tier (cheap / basic / premium).\n\n**7. Generate Image**\n- **What it does:** Generates an image from a text prompt using a provider image model (OpenAI `gpt-image-2` by default, or Google Imagen) and returns the image directly. Spends provider credits on every call.\n- **When it's useful:** (Architecture | Refinement) When you want a quick visual — a UI mockup, diagram, or illustration — to anchor a design discussion or attach to a ticket.\n- **How to use it:** ask your agent — *\"Generate an image of a dashboard showing SOC2 evidence freshness as a traffic-light grid.\"*\n- **Options:** `provider` openai (`gpt-image-2`) / gemini (Imagen — adds an invisible SynthID watermark) · `quality` low (default, cheapest) / medium / high · `size` 1024x1024 / 1024x1536 / 1536x1024. The image is always saved to `BAPI_DOCS_DIR/images/` and also returned inline.\n\n**8. Implement Ticket**\n- **What it does:** Full build for one ticket: generate a plan, write the code, commit, open a PR, and monitor CI.\n- **When it's useful:** (Implementation) When a ticket is ready and you want it taken from plan to open PR in one go.\n- **How to use it:** `/implement-ticket BAPI-123` (command only — \"implement X\" as free text almost always triggers a freehand build instead of the Bridge plan→code→PR→CI pipeline).\n- **Flags:** `--auto` skip the approval gates (e.g. auto-commit/push).\n\n**9. Full Automation**\n- **What it does:** Drives the whole chain end-to-end: idea → ticket(s) → review each → spawn worktrees to implement.\n- **When it's useful:** (Automation) When you want to go from a raw idea to in-progress implementation with minimal hands-on steps.\n- **How to use it:** `/full-automation <idea>` (command only — creates tickets, spawns worktrees, and carries scheduling/`--max-children` flags that free text can't).\n- **Flags:** `--require-approval` toggle the approval gates, full automation runs end to end by default.\n\n**10. Idea to Ticket**\n- **What it does:** Turns a one-line idea into a Jira Task/Spike (or an Epic plus child tickets), with research, duplicate detection, and a critique pass built in.\n- **When it's useful:** (Refinement | Automation) When you have a rough idea and want a fully-formed, uploaded ticket without the manual draft-and-refine loop.\n- **How to use it:** `/idea-to-ticket <idea>`\n\n### Tier 3 — Now and then\n\nThese features are useful once in a while, but you probably won't need them everyday.\n\n**1. Reimplement Ticket**\n- **What it does:** Pulls in new context/attachments since the last pass and implements small follow-up changes on an already-built ticket.\n- **When it's useful:** (Implementation) After review feedback or new screenshots, when you need a targeted second pass rather than a fresh build.\n- **How to use it:** `/reimplement-ticket BAPI-123`\n\n**2. Run Tests**\n- **What it does:** Runs the unit and E2E suites and autonomously triages/fixes failures (via the test-correction agent).\n- **When it's useful:** (Implementation) After making changes, to confirm everything passes and auto-fix straightforward breakages.\n- **How to use it:** `/run-tests` (`--unit-only`, `--skip-e2e`)\n\n**3. Plan Epic**\n- **What it does:** Decomposes a large epic into sub-tasks with a structured exploration doc for each.\n- **When it's useful:** (Architecture | Refinement) When a feature is too big for one ticket and you need it broken down and scoped.\n- **How to use it:** `/plan-epic <epic>` — *\"Decompose the epic 'migrate PayPal token storage off Custom Objects' into sub-tasks with an exploration doc for each.\"*\n\n**4. Update Ticket**\n- **What it does:** Synthesizes a ticket's clarifying answers and critique into a rewritten description and pushes it to Jira.\n- **When it's useful:** (Refinement) After review, to fold the resolved questions and fixes back into the ticket itself.\n- **How to use it:** `/update-ticket BAPI-123` (command only — does a full overwrite of the live Jira description; \"update\" as free text is both vague and hard to reverse).\n\n**5. Get Ticket**\n- **What it does:** Retrieves the full details of a Jira ticket (summary, status, description, etc.).\n- **When it's useful:** (Refinement | Implementation) Any time you want the agent to read a ticket before acting on it.\n- **How to use it:** ask your agent — *\"Pull up BAPI-123 and show me its description, status, and acceptance criteria.\"*\n\n**6. Write Comment**\n- **What it does:** Posts a comment on a Jira ticket (markdown; long ones can attach as a file).\n- **When it's useful:** (Refinement | Implementation) To leave context, status, or a decision trail on the ticket.\n- **How to use it:** ask your agent — *\"Post a comment on BAPI-123: blocked on the expired Atlassian token — will retry after it's rotated.\"*\n\n**7. Download / Upload Attachment**\n- **What it does:** Pulls files off a Jira ticket to disk, or attaches a local file to a ticket.\n- **When it's useful:** (Refinement | Implementation) When a ticket has design files/logs you need locally, or you want to attach output back to it.\n- **How to use it:** ask your agent — *\"Download the design mockups attached to BAPI-123 into my docs folder.\"* / *\"Attach build-log.txt to BAPI-123.\"*\n\n**8. Learn Repository**\n- **What it does:** Researches and documents the repo's architecture, testing, review, and correctness standards, then saves them to Bridge for future agents.\n- **When it's useful:** (Setup/Learning) When onboarding a new repo, or after big changes, so Bridge's agents follow your conventions.\n- **How to use it:** `/learn-repository`\n\n**9. Teach Bridge**\n- **What it does:** Takes a plain-English instruction, figures out which standards field it belongs to, and merges it in (admin only).\n- **When it's useful:** (Setup/Learning) When you notice the agents missing a convention and want to correct it in one sentence.\n- **How to use it:** `/teach-bridge <teaching>` — *\"Teach Bridge: always use data-testid selectors in E2E tests.\"*\n\n### Operational commands\n\nWorkflow commands you'll reach for during implementation and CI, beyond the tiers above:\n\n| Command | What it does |\n|---|---|\n| `/code-ticket PROJ-123` | Download the implementation plan and questions, then execute the plan inline |\n| `/commit-ticket PROJ-123` | Stage, commit, and push changes; transition Jira status; post a smoke-test comment |\n| `/create-pr PROJ-123` | Commit staged changes and open a pull request |\n| `/check-ci [PROJ-123]` | Monitor CI checks for the current branch, triage failures, apply fixes, and report results |\n| `/parse-repository` | Queue a background job to index the repository for Bridge AI agents |\n| `/check-parse-status` | Check whether a background repository parse job is still running |\n| `/scan-tickets` | Sync recently-updated Jira tickets and backfill workflow timestamps |\n\n> Commands are designed for Claude Code. Other editors may support slash commands differently — check your editor's documentation for how to invoke prompt files.\n\n### Extra Capabilities\n\nBehind-the-scenes capabilities an agent gains from the MCP tools — mostly invoked automatically by the commands above, rarely requested by name:\n\n- **Ship a PR end-to-end:** commit & push, open a pull request, transition the Jira status, and discover/poll CI checks (powers `commit-ticket`, `create-pr`, `check-ci`, `implement-ticket`).\n- **Architecture plan** for a ticket (design-level guidance, separate from the implementation plan).\n- **Index the codebase** so Bridge's agents can reason about it: queue/parse the repo, check parse status, regenerate the directory map.\n- **Read & tune project config/standards:** list/read/update config fields, fetch project standards, and the per-topic `learn-*` commands that populate them.\n- **Ticket lifecycle bookkeeping:** track tickets and backfill workflow-state timestamps (`scan-tickets`), search across tickets, read comments, list attachments.\n- **Pipeline machinery:** list/inspect pipeline recipes and run/resume/list/delete pipeline runs (the engine under the orchestration commands).\n- **Decision page** generation for capturing human review decisions as structured data.\n- **Connectivity & identity checks:** ping Bridge, check your role, resolve the local docs directory.\n- **Retrieve any generated artifact** (`get_*` for plans, critiques, questions, brainstorms, research, architecture) without regenerating it.\n- **Tiered-section execution telemetry** recording (internal measurement).\n\n## Salesforce B2C Commerce (SFCC) Tools\n\nSalesforce's official MCP server, `@salesforce/b2c-dx-mcp`, covers developer-experience tasks — cartridge deploy, step debugging, docs search, logs/MRT, and SCAPI Custom API scaffolding. As of its current toolset it has no way to *read* the platform's object model, custom objects, or site configuration — exactly the context an AI coding agent needs to write correct Commerce code and to inspect a sandbox before touching it. Bridge's SFCC tools install side-by-side with `b2c-dx-mcp` (they don't duplicate its surface) and close that gap with **read-only OCAPI Data API introspection** of system objects, custom object definitions, and site preferences.\n\n**v1 is read-only and developer-sandbox-only** — no writes, and non-sandbox instances are rejected. Credentials stay local (in `dw.json` or `SFCC_*` env vars) and are never sent to Bridge.\n\n<details>\n<summary><strong>Setup</strong></summary>\n\nThe two diagnostic tools (`sfcc_setup_status`, `check_permissions`) are always available. The seven read tools must be enabled with a profile (step 3).\n\n**Prerequisites:** a running SFCC **sandbox** and its hostname, plus an Account Manager API client (`client-id` + `client-secret`).\n\n**1. Set the repo `version` config field** to your SFCC project type — one of `sfra | pwakit | sitegenesis | storefrontnext | hybrid`. The call-time gate reads this; a non-SFCC value blocks every SFCC tool except `sfcc_setup_status`. Set it via your normal config path, the `config_field` MCP tool (operation `update`, field `version`), or the `/teach-bridge` skill.\n\n**2. Provide SFCC credentials.** Create a `dw.json` in your project root:\n\n```json\n{\n \"hostname\": \"zzzz-001.sandbox.us01.dx.commercecloud.salesforce.com\",\n \"client-id\": \"<account-manager-client-id>\",\n \"client-secret\": \"<account-manager-client-secret>\"\n}\n```\n\nAccepted key spellings: `hostname`/`host`, `client-id`/`clientId`/`client_id`, `client-secret`/`clientSecret`/`client_secret`. Prefer a single config — a multi-entry `configs[]` array forces you to pass an explicit `instance` on every call. `dw.json` is auto-added to git exclude and must never be committed. Alternatively, export `SFCC_HOSTNAME` / `SFCC_CLIENT_ID` / `SFCC_CLIENT_SECRET` in the MCP server environment.\n\n**3. Enable the read tools.** Add `sfcc` to `BRIDGE_MCP_PROFILE` in the MCP server `env` block (it is comma-separated; `full` also works), then **restart the MCP client**:\n\n```json\n\"env\": { \"BRIDGE_MCP_PROFILE\": \"sfcc\" }\n```\n\nWithout this, only the diagnostic tools are registered.\n\n**4. Verify and grant OCAPI access.** Ask your agent to run `sfcc_setup_status` (expect all checks ✓), then `check_permissions`. If it reports HTTP 401/403, it prints the exact OCAPI Settings JSON to paste in Business Manager (**Administration → Site Development → Open Commerce API Settings → Data API** tab); save it there and re-run. Restart the MCP client after any credential or env change — a running session does not pick them up.\n\n</details>\n\n### Tools\n\nAll SFCC tools are read-only and target a developer sandbox. Oversized responses are automatically saved to `BAPI_DOCS_DIR/sfcc/` and previewed inline.\n\n**Diagnostics** (always available, no profile needed)\n- `sfcc_setup_status` — report on every prerequisite: Bridge API key, repo name, `version` config, `dw.json` presence/uniqueness, and AM token acquisition.\n- `check_permissions` — probe OCAPI access via `GET /system_object_definitions`; on 401/403, print the exact OCAPI Settings JSON to paste in Business Manager (split read-only vs. write/import grants).\n\n**System object model** (needs the `sfcc` profile)\n- `system_object_list` — list system object types (Product, Order, Customer, …).\n- `system_object_get` — fetch one type's definition, optionally with its full attribute definitions (`expand_attribute_definitions`).\n- `system_object_attribute_search` — search a type's attribute definitions; prefer this over a full dump when hunting a specific `c_` custom attribute.\n\n**Custom object definitions** (needs the `sfcc` profile)\n- `custom_object_definition_attributes_get` — fetch attribute definitions for a known custom object type. OCAPI cannot enumerate custom object types, so `object_type` must already be known.\n- `custom_object_definition_attribute_search` — search attribute definitions within a known custom object type. Read-only — creating a custom object *type* isn't possible via OCAPI; that's a future v2 metadata-import capability.\n\n**Site preferences** (needs the `sfcc` profile; sandbox only)\n- `site_preference_get` — read a preference group's effective preferences.\n- `site_preference_search` — search/filter preferences within a group.\n\n## CLI Subcommands\n\nBeyond `--init` / `--upgrade`, the package ships operational subcommands of the **single `bridge-api-mcp-server` bin** (not separate binaries) — so they travel with the package to every consumer. See [Usage Documentation → Start Tickets](#tier-1--regularly-useful) for *when* to use `start-tickets`; this section is the full CLI reference.\n\n### `start-tickets`\n\nSpawns one Worktrunk worktree + selected-agent session per Jira ticket and backs the `/start-tickets` slash command. The agent defaults to **Claude Code** (`claude`) and is configurable via `--agent`.\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets [flags] KEY [KEY ...]\n```\n\n| Flag | Default | Meaning |\n|---|---|---|\n| `--agent claude\\|cursor-agent` | `claude` | Agent command to launch in each worktree |\n| `--terminal terminal\\|iterm` | auto-detect via `$TERM_PROGRAM` | Override the macOS terminal app (honored on macOS only) |\n| `--dry-run` | off | Print intended actions; create no worktrees, open no tabs (any OS) |\n| `--branch KEY=BRANCH` | `feature/<KEY>` | Use a custom branch for that ticket (repeatable) |\n| `--base-branch <BRANCH>` | `main` | Cut new worktrees from `<BRANCH>` and refresh `origin/<BRANCH>` instead of `main` |\n| `--no-refresh-main` | off (the configured base branch is refreshed) | Skip refresh of the configured base branch (default `main`). Historical flag name preserved for backward compatibility — despite the name, it now skips refresh of whatever `--base-branch` resolves to. |\n| `--max-parallel N` | `3` | Max worktrees created concurrently |\n| `--conductor` | off | Opt into the Conductor system (per-worker `BAPI_CONDUCTOR_*` env + Claude hook injection, a supervisor peer tab, and the `check_messages` message-relay prompt). **Default off** — a plain run spawns `cd <worktree> && <agent> '/implement-ticket <KEY>'`. |\n| `-h`, `--help` | — | Show usage |\n\nEach `KEY` must match `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). The CLI creates/switches each worktree up front (throttled by `--max-parallel`), then opens one tab/session per successful worktree running the selected agent's `'/implement-ticket <KEY>'` — `claude '/implement-ticket <KEY>'` by default, or `cursor-agent '/implement-ticket <KEY>'` with `--agent cursor-agent`. The `/implement-ticket <KEY>` prompt is unchanged for both agents. To launch Cursor Agent instead of Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\n**Difficulty-based model routing.** Before launching each agent, the CLI selects an implementation **model tier** from the ticket's `difficulty` (1-2 → cheap, 3-5 → basic, 6+ → premium) and injects it as a `--model` flag at the spawn boundary. The Python backend returns only the coarse tier (`GET /jira/tickets/{KEY}/model-tier`, computing + caching difficulty on demand); this CLI alone maps a tier to the agent-specific alias (`claude`: `haiku`/`sonnet`/`opus`; `cursor-agent`: version-suffixed strings validated against `cursor-agent --list-models`). It is gated per repo by `difficulty_model_routing_enabled` (default **ON**) with an optional `difficulty_model_tier_overrides` JSON map (tier → alias). Routing is **fail-open**: missing credentials, an evaluation failure/timeout, a backend `fallback`, an invalid/unavailable alias, an unadvertised Cursor model, or an agent without `--model` support all omit `--model` (the agent uses its default) and surface a per-ticket warning rather than failing the spawn. `--dry-run` does **not** fetch tiers or inject `--model`.\n\n**Conductor observability (opt-in via `--conductor`, BAPI-394).** Conductor is **off by default**. With `--conductor`, a run mints a conductor `run_id`, emits events into the local ledger (`~/.config/bridge/events.db`), opens a supervisor peer tab, and (for Claude Code) injects a secret-free lifecycle hook into each worktree; it also sets `BRIDGE_MCP_PROFILE=conductor` so workers get the conductor MCP tools. Observability is best-effort — a conductor failure never blocks a spawn, and `--dry-run` performs no conductor side effects. (Epic-tick dispatch always enables conductor internally.) See **[CONDUCTOR.md](./CONDUCTOR.md)** for the full observability, git-hook, done-gate, and auto-merge reference.\n\n**Cross-platform spawning.** The CLI routes spawning per platform; `--dry-run` previews the platform-correct command form on any OS. An unsupported `process.platform` (not `darwin`/`win32`/`linux`) fails fast with a clear \"unsupported platform\" message.\n\n- **macOS** — opens a Terminal.app or iTerm tab via `osascript`.\n- **Windows** — creates worktrees with **`git-wt`** (Worktrunk's winget alias) and opens a tab via **Windows Terminal (`wt.exe new-tab`)**, falling back to **`Start-Process powershell.exe`** when Windows Terminal is absent. Requires **Git for Windows / Git Bash** (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash). The Worktrunk binary (`git-wt`) and the tab launcher (`wt.exe`) are resolved independently and never conflated.\n- **Linux** — creates one detached **tmux** session per ticket (pane kept open after the agent exits); attach with `tmux attach -t <session>`. A missing `tmux` produces a clear, actionable error.\n\nPer-OS prerequisites: macOS `wt`, `git`, `osascript`; Windows `git-wt`, Git for Windows / Git Bash, Windows Terminal or PowerShell; Linux `wt`, `git`, `tmux`. Set `BAPI_WORKTRUNK_BIN` to override the Worktrunk executable name/path for nonstandard installs (`doctor` honors it too). The read-only `doctor` subcommand (below) additionally surfaces a missing `uv` — Worktrunk's `pre-start` hook runs `uv`, but live preflight does not check it — and the selected agent's command; run `doctor --agent cursor-agent` to also check `cursor-agent` (it prints `cursor-agent login` as an informational auth reminder).\n\n### `doctor`\n\nThe package also ships a strictly **read-only** `doctor` subcommand that diagnoses the `start-tickets` prerequisites for the current OS without changing anything:\n\n```\nnpx -y @bridge_gpt/mcp-server doctor [--agent <name>]\n```\n\nIt is **read-only**: it never installs anything, modifies your system, adds an npm `postinstall`, spawns a terminal, or starts the MCP server, and there is no `--fix`. For each prerequisite it prints found/missing and, when missing, the exact per-OS install command **as a manual instruction you run yourself**. The checked set is the `start-tickets` preflight prerequisites **plus `uv`** **plus the selected agent's command** (`claude` by default, or `cursor-agent` with `--agent cursor-agent`). The Worktrunk binary is probed via the resolved name (honoring `BAPI_WORKTRUNK_BIN`), not a hard-coded one. **Exit code:** `0` when all required prerequisites are present, non-zero when any is missing or the platform is unsupported. A failing `start-tickets` preflight now hints you to run `doctor` for an actionable diagnostics report.\n\n### Conductor (epic & multi-agent orchestration)\n\nConductor is an **opt-in, off-by-default** layer for epic supervision, inter-agent messaging, done-gate evaluation, local git-hook event producers, and conditional auto-merge. Its full reference — `conductor install-git-hooks`, the `conductor_done_gate` and `conductor_auto_merge_enabled` config fields, and the observability stream — lives in **[CONDUCTOR.md](./CONDUCTOR.md)**.\n\n## Custom Pipelines\n\nYou can create your own pipelines by adding JSON files to `.bridge/pipelines/`. Running `--init` scaffolds this directory with a `README.md` and an example pipeline to get you started.\n\nThe easiest way to write a custom pipeline is to describe what you want to automate to your AI coding agent and have it draft the JSON for you. The schema is straightforward, and agents like Claude Code understand it well — just describe the steps you want, and the agent will produce a working pipeline file.\n\n**What you can build:**\n\n- Any sequence of Bridge MCP tool calls and free-form agent tasks\n- Parameterized workflows using variables (e.g., `{ticket_key}`)\n- Approval gates that pause for user confirmation before sensitive steps\n- Per-step error handling — halt immediately or warn and continue\n\n**Ideas for custom pipelines:**\n\n- A standup pipeline that fetches your open tickets and summarizes their status\n- A ticket triage pipeline that runs critiques on a batch of new tickets\n- A pre-merge checklist that runs tests, checks linting, and posts a summary comment\n\n**Step types:**\n\n| Type | What it does |\n|---|---|\n| `mcp_call` | Calls an MCP tool with the given params |\n| `agent_task` | Gives the AI a free-form instruction (inline or from a file in `.bridge/instructions/`) |\n\nVariables are declared in the `variables` array and referenced as `{variable_name}` in params and instructions. Each step supports `on_error: \"halt\"` (default) or `\"warn_and_continue\"`, and `requires_approval: true` to pause before execution.\n\n**System variables:**\n\nTwo variables are automatically available in every pipeline without declaring them:\n\n| Variable | What it does |\n|---|---|\n| `{provider}` | Routes AI generation to a specific LLM provider (`openai`, `anthropic`, or `gemini`). Pass it through to any `request_*` tool param to control which provider handles that step. Omit it (or leave it empty) to use the project default. |\n| `{second_opinion}` | Runs AI generation through a different provider than the default, acting as a cross-check. Set to `\"auto\"` to let Bridge pick the second provider automatically. When set, it takes precedence over `{provider}`. Use this when you want two independent AI perspectives on the same task — for example, running clarifying questions and a critique twice (once with each provider) produces better results than a single pass. |\n\nSee `.bridge/pipelines/README.md` for the full schema reference.\n\nIf a custom pipeline has the same key as a built-in pipeline, the custom version takes precedence (a warning is logged at startup).\n\n## Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `BAPI_BASE_URL` | Yes | `https://bridgegpt-api.com` | Bridge API base URL |\n| `BAPI_REPO_NAME` | Yes | _(none)_ | Jira project/repository identifier configured in Bridge API |\n| `BAPI_API_KEY` | Yes | _(none)_ | API key obtained from the Bridge API setup UI |\n| `BAPI_PROJECT_ROOT` | No | _(auto-set by --init)_ | Absolute path to project root. Anchors `BAPI_DOCS_DIR` and `BAPI_PIPELINES_DIR` resolution |\n| `BAPI_DOCS_DIR` | No | `docs/tmp` | Local directory for saving plans, critiques, and research reports |\n| `BAPI_PIPELINES_DIR` | No | `.bridge/pipelines` | Directory for user-defined custom pipeline JSON files |\n| `BAPI_WORKTRUNK_BIN` | No | `wt` (`git-wt` on Windows) | Override the Worktrunk executable name/path used by `start-tickets` for nonstandard installs |\n| `BAPI_TMUX_SESSION` | No | `bridge-start-tickets` | Override the tmux session-name prefix used by `start-tickets` on Linux |\n| `BAPI_MCP_UPGRADE_ADVICE_ENABLED` | No | _(enabled)_ | MCP-local opt-out for proactively surfacing upgrade advice in pipeline recipe preambles. Set to `false`/`0`/`no`/`off`/`disabled` to suppress. Disabling it does **not** change the `/jira/ping` response or server-side upgrade computation — it only gates the recipe-preamble convention |\n| `CURSOR_API_KEY` | No | _(none)_ | API key used to authenticate `cursor-agent` when launching `start-tickets`/scheduled runs with `--agent cursor-agent`. Not needed for the default Claude Code agent |\n| `BRIDGE_MCP_PROFILE` | No | `core` | Startup-time tool registration profile — a **comma-separated** list of groups controlling which tool groups are registered when the server starts. `core` is always implicitly included. Valid groups: `core` (default — normal coding tools only), `conductor` (+ 8 conductor/event/supervisor tools), `pipeline-authoring` (+ 5 pipeline run/admin tools — `get_pipeline_recipe` is NOT gated; it stays in `core` because the recipe-driven slash commands depend on it), `sfcc` (+ the 7 heavy SFCC read tools — see [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools); the `sfcc_setup_status`/`check_permissions` diagnostics are always registered regardless), and `full` (shortcut that expands to every group). Example: `sfcc,conductor`. Unknown, blank, or malformed tokens are dropped (falling back to `core`). Dynamic mid-session switching via `tools/list_changed` is unsupported — groups are resolved once at process startup. Conductor/epic sessions launched via `start-tickets --conductor` automatically inject `BRIDGE_MCP_PROFILE=conductor` at the spawn boundary; a normal `start-tickets` run stays on `core`. |\n\n## Worktree credentials and the `mcp-invoke` shim\n\nWhen `start-tickets` creates a git worktree, it provisions a Bridge API MCP\nregistration into that worktree so Claude Code (`.mcp.json`) and Cursor\n(`.cursor/mcp.json`) can reach the server immediately. These registrations are\n**secret-free**: they contain no `env` block and no API key. Instead they point\nat an internal subcommand of the published single CLI bin, `mcp-invoke`:\n\n```bash\nnpx -y @bridge_gpt/mcp-server@<VERSION> mcp-invoke --target bapi --project-root <ABS_WORKTREE_PATH>\n```\n\n`mcp-invoke` is not a separate binary — it is a positional subcommand of\n`bridge-api-mcp-server`. It resolves the repo identity from the absolute\n`--project-root` (the committed `.bridge/config` manifest, falling back to the\ngit common dir), resolves credentials from the home-directory credential store,\nand then spawns the real MCP server with that environment.\n\n### Credential store\n\nCredentials live outside the repository, keyed by `bapi:<repo_name>`:\n\n```json\n{\n \"bapi:<repo_name>\": {\n \"BAPI_API_KEY\": \"...\"\n }\n}\n```\n\nResolution order:\n\n1. `BAPI_API_KEY` in the parent environment (overrides the file entirely).\n2. `$XDG_CONFIG_HOME/bridge/credentials.json`, else `~/.config/bridge/credentials.json`.\n3. `~/.bridge/credentials.json` (only when the primary path is absent).\n\nOn POSIX systems, lock the file down so only you can read it:\n\n```bash\nchmod 600 ~/.config/bridge/credentials.json\n```\n\n`mcp-invoke` warns (but continues) if the file is group/world-readable, and it\nnever creates or initializes the credential file for you.\n\n### Populating the credential store\n\nThe same store also backs the shell-spawned `start-tickets` CLI (its\ndifficulty→model routing runs in a Bash process that cannot see an `env` block in\n`.mcp.json` / `.cursor/mcp.json`), so the store must hold the key for routing to\nwork. Two supported paths write it for you:\n\n- **Install-time upsert.** `/install-bridge`'s final stage persists the validated\n routing credential into `~/.config/bridge/credentials.json` (target\n `bapi:<repo>`) via the `persist_routing_credential` MCP tool — the tool resolves\n the key inside the MCP process and writes the store, so no secret crosses the\n wire.\n- **One-shot migration.** If a key currently lives only in `.mcp.json` /\n `.cursor/mcp.json`, migrate it into the user-scoped store with the consent-gated\n command (a compatibility aid, not a live fallback):\n\n ```bash\n npx -y @bridge_gpt/mcp-server credentials migrate-agent-config [--write-credentials]\n ```\n\n Run it without `--write-credentials` to preview; add the flag to write the store.\n\n## Reference\n\nThe full surface, for when you need the complete enumeration. Day-to-day, use [Usage Documentation](#usage-documentation) instead — you don't call MCP tools directly; you ask your AI assistant to perform a task, or compose tools into a pipeline.\n\n### MCP tools\n\nThe server exposes **59 documented tools** (enumerated below). What's actually registered in a session depends on `BRIDGE_MCP_PROFILE`: the default `core` profile loads a trimmed subset, and the conductor/pipeline-authoring/SFCC tools are added only under their respective profiles (see [Environment Variables](#environment-variables)). Async AI tools follow a request/get pattern: call the `request_*` tool to kick off generation, then the matching `get_*` tool to retrieve the result (or pass `wait_for_result: true` to poll automatically).\n\n- **Connectivity & identity** — `ping`, `get_my_role`, `get_docs_dir`\n- **Jira tickets** — `get_tickets`, `get_ticket`, `create_ticket`, `update_ticket_description`, `add_comment`, `get_comments`\n- **Attachments** — `attachment` (operations: `upload`, `download`, `list`)\n- **AI generation (request/get)** — `request_plan_generation`/`get_plan`, `request_architecture`/`get_architecture`, `create_doc`/`get_doc` (design docs by `doc_type`: tdd/fsd/prd), `request_prd`/`get_prd`, `request_clarifying_questions`/`get_clarifying_questions`, `request_ticket_critique`/`get_ticket_critique`, `request_ticket_review`, `request_reimplement_context`/`get_reimplement_context`, `request_brainstorm`/`get_brainstorm`, `request_deep_research`/`get_deep_research`\n- **Other AI** — `second_opinion`, `generate_image`, `generate_decision_page`, `visual_diff` (deterministic pixel diff of a rendered URL vs a design comp)\n- **Ticket lifecycle** — `track_ticket`, `update_ticket_state`, `get_ticket_state`\n- **Jira status** — `get_jira_transitions`, `update_jira_status`, `resolve_target_status`\n- **Repository & CI** — `parse_repository`, `get_parse_status`, `regenerate_directory_map`, `create_pull_request`, `resolve_ci_checks`, `poll_ci_checks`\n- **Pipelines & automation** — `list_pipelines`, `get_pipeline_recipe`, `run_pipeline`, `resume_pipeline`, `list_pipeline_runs`, `delete_pipeline_run`, `run_full_automation`, `resume_full_automation`\n- **Config** — `get_project_standards`, `config_field` (operations: `get`, `update`, `list`)\n\n### Bundled pipelines\n\nPipelines are declarative, multi-step workflows your AI agent executes step-by-step — each a JSON recipe chaining MCP tool calls and free-form agent tasks, with variable substitution, per-step error handling, and optional approval gates. You can also write your own (see [Custom Pipelines](#custom-pipelines)).\n\n| Pipeline | Description | Invoke with |\n|---|---|---|\n| `implement-ticket` | Generate a plan, execute the implementation, commit, open a PR, and monitor CI | `/implement-ticket PROJ-123` |\n| `review-ticket` | Full ticket quality review: initial clarifying questions + critique, automatic second-opinion pass (default), then evaluation and decision capture. Pass `--rounds=1` to skip the second-opinion step (`--rounds=2` is the default). | `/review-ticket PROJ-123` |\n| `idea-to-ticket` | Turn an idea into a Jira Task/Spike (or Epic + children) with research, dedup, and critique | `/idea-to-ticket \"<idea>\"` |\n| `plan-epic` | Decompose an epic into sub-tasks with a structured exploration doc for each | `/plan-epic \"<epic>\"` |\n| `full-automation` | Chain: idea → ticket(s) → review each → spawn worktrees to implement | `/full-automation \"<idea>\"` |\n| `pr-ticket` | Commit changes and open a pull request | `/create-pr PROJ-123` |\n| `check-ci-ticket` | Commit, open a PR, then monitor CI checks until they pass or fail | `/check-ci PROJ-123` |\n| `learn-repository` | Analyze codebase architecture, testing, review, and documentation standards, then upload to Bridge | `/learn-repository` |\n\n### Pipeline response envelope\n\n`run_pipeline`, `resume_pipeline`, and `list_pipeline_runs` share a unified envelope keyed on `status`:\n\n- `completed` — terminal success; `results` holds per-step output.\n- `needs_agent_task` — the orchestrator paused. Read `instruction`, perform the task, then call `resume_pipeline` with `pipeline_run_id` and a string `agent_result`.\n- `failed` — terminal failure. `error_code` is one of `VALIDATION`, `NOT_FOUND`, `EXPIRED`, `REPO_MISMATCH`, `TOOL_ERROR`.\n\nPaused runs auto-expire after an idle TTL (default 24 hours; override with `ttl_seconds`). The TTL is reset on every state transition. List output is metadata-only — it never includes resolved recipes, params, instructions, results, or agent outputs.\n";
3
+ export const README = "# @bridge_gpt/mcp-server\n\nThe Bridge MCP is an end-to-end accelerator for shipping code within SFCC, powered by [Bridge API](https://bridgegpt-api.com). Works with Claude Code, Github Copilot, Cursor, Windsurf, and OpenAI Codex.\n\n## Contents\n\n- [Getting Started](#getting-started)\n- [Usage Documentation](#usage-documentation)\n - [Tier 1 — Regularly useful](#tier-1--regularly-useful)\n - [Tier 2 — Occasionally useful](#tier-2--occasionally-useful)\n - [Tier 3 — Now and then](#tier-3--now-and-then)\n - [Operational commands](#operational-commands)\n - [Extra Capabilities](#extra-capabilities)\n- [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools)\n- [CLI Subcommands](#cli-subcommands)\n- [Custom Pipelines](#custom-pipelines)\n- [Environment Variables](#environment-variables)\n- [Worktree credentials and the `mcp-invoke` shim](#worktree-credentials-and-the-mcp-invoke-shim)\n- [Reference](#reference)\n\nFor advanced epic/multi-agent orchestration, see [CONDUCTOR.md](./CONDUCTOR.md).\n\n## Getting Started\n\n### Quick start\n\nFrom your **project root**, install and connect in one command:\n\n```bash\nnpx -y @bridge_gpt/mcp-server@latest install-bridge\n```\n\n`install-bridge` scaffolds the project, writes your editor's MCP config with real\nvalues, verifies connectivity, persists your API key to the user-scoped credential\nstore, and opens a fresh agent session to finish setup (`/install-bridge` then\n`/learn-repository`). The only inputs are an **API key** (generate one on the Bridge\nAPI web UI **Security** page) and a **repo name** — everything else is derived. Add\n`--dry-run` to preview every step without writing, pinging, or spawning anything.\n\nTo upgrade later, run:\n\n```bash\nnpx -y @bridge_gpt/mcp-server upgrade\n```\n\n`upgrade` re-execs from `@latest`, refreshes all scaffolded artifacts (slash\ncommands, agents, pipelines), rewrites the version pin, and reconnects — also\navailable as the `/upgrade-bridge` slash command. (The legacy `--upgrade` flag still\nworks as a fallback.)\n\n<details>\n<summary><strong>Installation Instructions</strong></summary>\n\n#### What `install-bridge` does\n\n`install-bridge` collapses the whole setup into a single command. It:\n\n1. **Scaffolds** the project (the same artifacts `--init` writes: slash commands,\n agents, `.bridge/pipelines/`, and secret-free MCP config placeholders).\n2. **Writes the per-host MCP config** (`.mcp.json` / `.cursor/mcp.json` /\n `.vscode/mcp.json`) with your real `BAPI_REPO_NAME` / `BAPI_API_KEY` /\n `BAPI_BASE_URL` / `BAPI_DOCS_DIR`, preserving any unrelated servers. The\n launcher it writes is pinned to the exact installed version so `npx` never\n silently reuses a stale local copy. (Windsurf and Codex are global configs it\n can't safely write — it prints copy-paste instructions for those.)\n3. **Verifies connectivity** against the Bridge API before persisting anything.\n4. **Persists your key** to the user-scoped credential store\n (`~/.config/bridge/credentials.json`, target `bapi:<repo>`) so shell-spawned\n tooling (e.g. `start-tickets`) can resolve it.\n5. **Opens a fresh agent session** that runs `/install-bridge` (to derive the\n remaining config fields from your codebase) and then `/learn-repository`.\n\nThe only inputs are an **API key** and a **repo name** (everything else is\nderived). Resolution order:\n\n- **API key:** `--api-key <key>` → `BAPI_API_KEY` env → an interactive (no-echo)\n prompt. Generate one first on the Bridge API web UI **Security** page (see\n [Generate an API Key](#2-generate-an-api-key)); the command consumes a key, it\n never mints one. The key is **never printed or logged**.\n- **Repo name:** `--repo <name>` → `BAPI_REPO_NAME` env → an inferred default you\n confirm interactively. It MUST match the server-side repository registration.\n\nUseful flags:\n\n- `--dry-run` — preview every step (scaffold targets, config files and keys with\n the key value **redacted**, the ping target, the credential store target, and\n the exact agent spawn command) without writing, pinging, or spawning anything.\n- `--force` — overwrite an existing real `BAPI_API_KEY` in a host config without\n prompting (re-running is otherwise non-destructive).\n- `--agent claude|cursor-agent` — which agent to launch for the agentic remainder\n (default `claude`).\n\nThat's it — once `install-bridge` finishes you're connected. If you prefer to do\nit by hand (or just want to understand each step), the manual flow below does the\nsame thing.\n\n#### Manual Setup (Alternative)\n\n##### 1. Install the Package\n\nFrom your **project root**, install the MCP server and scaffold slash commands:\n\n```bash\nnpm i @bridge_gpt/mcp-server\nnpx -y @bridge_gpt/mcp-server --init\n```\n\n`--init` must be run from the directory containing your `package.json`. It:\n\n- Creates slash commands in `.claude/commands/` and `.cursor/commands/`\n- Detects existing MCP config files and sets `BAPI_PROJECT_ROOT` so local file output resolves correctly\n- Scaffolds `.bridge/pipelines/` for custom pipeline authoring\n\nRe-run `--init` after upgrading the package to get updated commands.\n\n##### 2. Generate an API Key\n\n1. Log in to [Bridge API](https://bridgegpt-api.com) and navigate to your project's **Security** page\n2. Click **Create New Key**\n3. Enter your email, an optional label (e.g., \"MCP Server\"), and select the **Admin** role\n4. Click **Create Key**\n5. **Copy the key immediately** — it will not be shown again\n\n##### 3. Configure the MCP Server\n\nAdd the following to your editor's MCP configuration file, pasting in the API key from step 2:\n\n<details>\n<summary><strong>Claude Code (.mcp.json)</strong></summary>\n\nThe `--init` command (step 1) detects Claude Code and creates a `.mcp.json` at your project root with placeholder values. Open it and replace `your-repo` and `your-api-key` with your actual values from step 2:\n\n```json\n{\n \"mcpServers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>VS Code / Copilot (.vscode/mcp.json)</strong></summary>\n\n```json\n{\n \"servers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>Cursor (.cursor/mcp.json)</strong></summary>\n\n```json\n{\n \"mcpServers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n\n> If project-local config is not supported in your Cursor version, use `~/.cursor/config/mcp.json` instead.\n</details>\n\n<details>\n<summary><strong>Windsurf (~/.codeium/windsurf/mcp_config.json)</strong></summary>\n\nWindsurf only supports global MCP configuration.\n\n```json\n{\n \"mcpServers\": {\n \"bridge-api\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@bridge_gpt/mcp-server\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>OpenAI Codex (~/.codex/config.toml)</strong></summary>\n\n```toml\n[mcp_servers.bridge-api]\ncommand = \"npx\"\nargs = [\"-y\", \"@bridge_gpt/mcp-server\"]\n\n[mcp_servers.bridge-api.env]\nBAPI_BASE_URL = \"https://bridgegpt-api.com\"\nBAPI_REPO_NAME = \"your-repo\"\nBAPI_API_KEY = \"your-api-key\"\nBAPI_DOCS_DIR = \"docs/tmp\"\n```\n\n> Codex users: set `BAPI_PROJECT_ROOT` manually in your config (see Environment Variables below).\n</details>\n\nAfter saving the config, restart your editor or reload the MCP server connection. Verify connectivity by asking your AI assistant to call the `ping` tool.\n\n##### 4. First-Time Setup: Teach Bridge Your Codebase\n\nIf you're the first person to install Bridge API on your project, run the `/learn-repository` slash command after completing setup. This analyzes your codebase's architecture, testing patterns, code review standards, and documentation conventions, then uploads the findings to Bridge API. This gives Bridge the context it needs to generate implementation plans, ticket critiques, and code reviews that are consistent with your project's actual architecture and conventions.\n\nYou only need to do this once per project — the learned standards persist for all team members.\n\n##### Upgrading (details)\n\nThe one-command `npx -y @bridge_gpt/mcp-server upgrade` (shown above) is the\nrecommended path. The legacy flag `npx -y @bridge_gpt/mcp-server --upgrade` still\nworks: it runs `npm i @bridge_gpt/mcp-server@latest`, prints a before/after version\nsummary, then re-runs the full `--init` scaffolding flow to update your slash\ncommands, agents, and pipeline definitions.\n\nThe MCP server also checks for updates automatically on startup. If a newer version is available, you'll see a notice in your editor's MCP output logs with the upgrade command to run. This check is cached for 24 hours and never blocks server startup.\n\n</details>\n\n## Usage Documentation\n\nThis is the Bridge API tooling worth knowing about as a software engineer — the things you'd ask an agent to do — grouped by how often you would use them. Each entry covers **what it does**, **when it's useful** and **how to use it**. The behind-the-scenes plumbing is summarized at the end under [Extra Capabilities](#extra-capabilities), and a full enumeration lives in [Reference](#reference).\n\nWorking in a Salesforce B2C Commerce codebase? Bridge also ships read-only SFCC platform-introspection tools — see [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools).\n\nFor invocation, prefer the slash command — it's deterministic. A free-text example is shown only where natural-language phrasing reliably maps to the right automation; high-consequence or easily-misread automations show only the slash command on purpose.\n\n### Tier 1 — Regularly useful\n\nThese features are useful for most tickets.\n\n**1. Review Ticket**\n- **What it does:** Runs a full quality review of a ticket: clarifying questions + critique plus an automatic alternate-model second opinion, then evaluates findings and produces a decision page to accept/reject them. The backend executor now owns all review round orchestration (including the second-opinion round) server-side; pass `--rounds=1` for a cheaper single-pass review, `--rounds=2` to force the full second-opinion review, or omit `--rounds` to let the backend's difficulty-adaptive review policy decide.\n- **When it's useful:** (Refinement) Right after a ticket is drafted, before anyone starts building — to surface gaps and tighten it.\n- **How to use it:** `/review-ticket BAPI-123` (command only — \"review\" as free text is easily mistaken for a freehand agent review).\n- **Flags:** `--auto` auto-accept findings / skip the approval gates · `--rounds=1` request a cheaper single-pass review (no second-opinion round) while preserving downstream evaluation and decision-capture work · `--rounds=2` force the full second-opinion review · omit `--rounds` to let the backend's difficulty-adaptive review policy decide (it falls back to a full second-opinion review when adaptive routing is disabled or the ticket's difficulty cannot be resolved).\n- **Multi-ticket fan-out:** `/review-tickets BAPI-123 BAPI-456` opens one terminal tab per ticket for parallel review with no worktrees (terminal launcher only — no `wt`/`git`). All `/review-ticket` flags apply; `--review KEY=auto,rounds=N` sets per-ticket overrides. Packaged CLI: `npx -y @bridge_gpt/mcp-server review-tickets KEY [KEY ...]`.\n\n**2. Start Tickets**\n- **What it does:** Creates one git worktree per ticket and spawns an agent session in each to implement them in parallel.\n- **When it's useful:** (Implementation | Automation) When you're ready to start building one or more refined tickets concurrently.\n- **How to use it:** `/start-tickets BAPI-248 BAPI-250` (see [CLI Subcommands](#cli-subcommands) for the full flag table and cross-platform behavior).\n- **Flags:** `--auto` skip the approval gates · `--base-branch <branch>` branch off something other than the default.\n\n**3. Brainstorm**\n- **What it does:** Fans your problem out to two different LLMs and returns their approaches directly. Runs in one of three modes, selected via `mode`: **`technical`** (default — implementation/architecture approaches), **`design`** (UI/UX and visual direction), or **`discovery`** (stakeholder discovery questions for early/vague tasks, grouped into `Technical Discovery Questions` and `Business / Stakeholder Discovery Questions` and tagged `[HUMAN]`/`[CODE]`/`[TICKET]`). Discovery needs no extra configuration. The legacy boolean `design=true` still works and maps to `mode: \"design\"`.\n- **When it's useful:** (Architecture | Refinement) Early, when you want a spread of approaches — `technical` for *how to build it*, `design` for *how it should look*, `discovery` for *what we still need to figure out* before a real ticket exists.\n- **How to use it:** ask your agent to brainstorm — *\"Brainstorm approaches for adding rate limiting to the LLM client; fan it out to multiple models.\"* For a design pass: *\"Run a design brainstorm for the evidence-freshness dashboard UI.\"* For early discovery: *\"Run a discovery brainstorm — `request_brainstorm` with `mode: \"discovery\"` — for this vague request so we can collect the questions stakeholders need to answer first.\"*\n\n**4. Deep Research**\n- **What it does:** Runs multi-source, fact-checked web research on a technical topic and returns a cited report.\n- **When it's useful:** (Architecture | Refinement) When a decision hinges on outside knowledge (libraries, best practices, standards) you don't already have.\n- **How to use it:** `/bridge-research <question>`\n\n**5. Jira Ticket Writer**\n- **What it does:** An agent that drafts a well-structured Jira ticket from a plain description, applying your project's standards.\n- **When it's useful:** (Refinement) When you have an idea in your head and want a properly-formatted ticket draft without writing it by hand.\n- **How to use it:** `/write-ticket <description>` (or ask your agent) — *\"Use the jira ticket writer to turn our conversation into a ticket.\"*\n- **Flags:** `--standards <path>` apply a specific standards file when drafting.\n\n**6. Upload Ticket**\n- **What it does:** Pushes a drafted ticket up to Jira as a real issue (the `create_ticket` capability); handles markdown and child tickets under an Epic.\n- **When it's useful:** (Refinement) The final step after drafting — to get the ticket into Jira so it can be tracked and worked.\n- **How to use it:** Ask your agent to create the ticket, and it should confirm before creating the live Jira issue.\n- **Options:** Describe the issue type (Bug / Story / Task / Epic) and, for a child under an Epic, the parent key.\n\n### Tier 2 — Occasionally useful\n\nThese features are good to know, but you probably won't use them every day.\n\n**1. Plan Ticket**\n- **What it does:** Generates a step-by-step implementation plan for a ticket, with references to real code files, and saves it locally.\n- **When it's useful:** (Refinement | Implementation) Once a ticket is solid and you want a concrete build plan before (or instead of) auto-implementing.\n- **How to use it:** `/plan-ticket BAPI-123`\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check the plan with a second provider.\n\n**2. Clarify Ticket**\n- **What it does:** Generates clarifying questions for a ticket (or debugging guidance for bugs) and saves them locally.\n- **When it's useful:** (Refinement) When a ticket feels under-specified and you want the open questions made explicit.\n- **How to use it:** `/clarify-ticket BAPI-123` — *\"Generate clarifying questions for BAPI-123\"*\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**3. Critique Ticket**\n- **What it does:** Critiques a ticket's quality against your project standards and lists deviations + improvements.\n- **When it's useful:** (Refinement) When you want a quality gate on a ticket before it's worked.\n- **How to use it:** `/critique-ticket BAPI-123` — *\"Critique BAPI-123 against our project standards and list what's missing or deviating.\"*\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**4. Create Doc**\n- **What it does:** Generates a design document for a ticket — a TDD (technical design, engineer audience), an FSD (functional spec, for product/design/QA), or a PRD (product requirements: problem, goals, success metrics) — and saves it locally.\n- **When it's useful:** (Architecture | Refinement) When a ticket needs a fuller design write-up before planning or implementation, in the shape that fits your audience.\n- **How to use it:** `/create-doc BAPI-123 --doc-type tdd` (or `fsd` / `prd`)\n- **Flags:** `--doc-type tdd|fsd|prd` which document to generate (required) · `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**5. Explore Ticket**\n- **What it does:** Explores the codebase for a task and recommends implementation options or surfaces clarifying questions, with optional research.\n- **When it's useful:** (Architecture | Refinement) Before writing a ticket or plan, when you're unsure how a change would fit the existing code.\n- **How to use it:** `/explore-ticket <task>` — *\"Explore the codebase for how we'd add a Mistral LLM provider and recommend 2–3 implementation options.\"*\n\n**6. Second Opinion**\n- **What it does:** Gets an immediate critique of any text from a different model family — no artifact saved, just the reply.\n- **When it's useful:** (Architecture | Refinement | Implementation) Any time you want a quick sanity check on a plan, draft, or decision from a fresh perspective.\n- **How to use it:** ask your agent — *\"Get a second opinion from Gemini on whether the BAPI-123 plan's migration step is safe to run against prod.\"*\n- **Options:** pick the provider (anthropic / openai / gemini) and the tier (cheap / basic / premium).\n\n**7. Generate Image**\n- **What it does:** Generates an image from a text prompt using a provider image model (OpenAI `gpt-image-2` by default, or Google Imagen) and returns the image directly. Spends provider credits on every call.\n- **When it's useful:** (Architecture | Refinement) When you want a quick visual — a UI mockup, diagram, or illustration — to anchor a design discussion or attach to a ticket.\n- **How to use it:** ask your agent — *\"Generate an image of a dashboard showing SOC2 evidence freshness as a traffic-light grid.\"*\n- **Options:** `provider` openai (`gpt-image-2`) / gemini (Imagen — adds an invisible SynthID watermark) · `quality` low (default, cheapest) / medium / high · `size` 1024x1024 / 1024x1536 / 1536x1024. The image is always saved to `BAPI_DOCS_DIR/images/` and also returned inline.\n\n**8. Implement Ticket**\n- **What it does:** Full build for one ticket: generate a plan, write the code, commit, open a PR, and monitor CI.\n- **When it's useful:** (Implementation) When a ticket is ready and you want it taken from plan to open PR in one go.\n- **How to use it:** `/implement-ticket BAPI-123` (command only — \"implement X\" as free text almost always triggers a freehand build instead of the Bridge plan→code→PR→CI pipeline).\n- **Flags:** `--auto` skip the approval gates (e.g. auto-commit/push).\n\n**9. Full Automation**\n- **What it does:** Drives the whole chain end-to-end: idea → ticket(s) → review each → spawn worktrees to implement.\n- **When it's useful:** (Automation) When you want to go from a raw idea to in-progress implementation with minimal hands-on steps.\n- **How to use it:** `/full-automation <idea>` (command only — creates tickets, spawns worktrees, and carries scheduling/`--max-children` flags that free text can't).\n- **Flags:** `--require-approval` toggle the approval gates, full automation runs end to end by default.\n\n**10. Idea to Ticket**\n- **What it does:** Turns a one-line idea into a Jira Task/Spike (or an Epic plus child tickets), with research, duplicate detection, and a critique pass built in.\n- **When it's useful:** (Refinement | Automation) When you have a rough idea and want a fully-formed, uploaded ticket without the manual draft-and-refine loop.\n- **How to use it:** `/idea-to-ticket <idea>`\n\n### Tier 3 — Now and then\n\nThese features are useful once in a while, but you probably won't need them everyday.\n\n**1. Reimplement Ticket**\n- **What it does:** Pulls in new context/attachments since the last pass and implements small follow-up changes on an already-built ticket.\n- **When it's useful:** (Implementation) After review feedback or new screenshots, when you need a targeted second pass rather than a fresh build.\n- **How to use it:** `/reimplement-ticket BAPI-123`\n\n**2. Run Tests**\n- **What it does:** Runs the unit and E2E suites and autonomously triages/fixes failures (via the test-correction agent).\n- **When it's useful:** (Implementation) After making changes, to confirm everything passes and auto-fix straightforward breakages.\n- **How to use it:** `/run-tests` (`--unit-only`, `--skip-e2e`)\n\n**3. Plan Epic**\n- **What it does:** Decomposes a large epic into sub-tasks with a structured exploration doc for each.\n- **When it's useful:** (Architecture | Refinement) When a feature is too big for one ticket and you need it broken down and scoped.\n- **How to use it:** `/plan-epic <epic>` — *\"Decompose the epic 'migrate PayPal token storage off Custom Objects' into sub-tasks with an exploration doc for each.\"*\n\n**4. Update Ticket**\n- **What it does:** Synthesizes a ticket's clarifying answers and critique into a rewritten description and pushes it to Jira.\n- **When it's useful:** (Refinement) After review, to fold the resolved questions and fixes back into the ticket itself.\n- **How to use it:** `/update-ticket BAPI-123` (command only — does a full overwrite of the live Jira description; \"update\" as free text is both vague and hard to reverse).\n\n**5. Get Ticket**\n- **What it does:** Retrieves the full details of a Jira ticket (summary, status, description, etc.).\n- **When it's useful:** (Refinement | Implementation) Any time you want the agent to read a ticket before acting on it.\n- **How to use it:** ask your agent — *\"Pull up BAPI-123 and show me its description, status, and acceptance criteria.\"*\n\n**6. Write Comment**\n- **What it does:** Posts a comment on a Jira ticket (markdown; long ones can attach as a file).\n- **When it's useful:** (Refinement | Implementation) To leave context, status, or a decision trail on the ticket.\n- **How to use it:** ask your agent — *\"Post a comment on BAPI-123: blocked on the expired Atlassian token — will retry after it's rotated.\"*\n\n**7. Download / Upload Attachment**\n- **What it does:** Pulls files off a Jira ticket to disk, or attaches a local file to a ticket.\n- **When it's useful:** (Refinement | Implementation) When a ticket has design files/logs you need locally, or you want to attach output back to it.\n- **How to use it:** ask your agent — *\"Download the design mockups attached to BAPI-123 into my docs folder.\"* / *\"Attach build-log.txt to BAPI-123.\"*\n\n**8. Learn Repository**\n- **What it does:** Researches and documents the repo's architecture, testing, review, and correctness standards, then saves them to Bridge for future agents.\n- **When it's useful:** (Setup/Learning) When onboarding a new repo, or after big changes, so Bridge's agents follow your conventions.\n- **How to use it:** `/learn-repository`\n\n**9. Teach Bridge**\n- **What it does:** Takes a plain-English instruction, figures out which standards field it belongs to, and merges it in (admin only).\n- **When it's useful:** (Setup/Learning) When you notice the agents missing a convention and want to correct it in one sentence.\n- **How to use it:** `/teach-bridge <teaching>` — *\"Teach Bridge: always use data-testid selectors in E2E tests.\"*\n\n### Operational commands\n\nWorkflow commands you'll reach for during implementation and CI, beyond the tiers above:\n\n| Command | What it does |\n|---|---|\n| `/code-ticket PROJ-123` | Download the implementation plan and questions, then execute the plan inline |\n| `/commit-ticket PROJ-123` | Stage, commit, and push changes; transition Jira status; post a smoke-test comment |\n| `/create-pr PROJ-123` | Commit staged changes and open a pull request |\n| `/check-ci [PROJ-123]` | Monitor CI checks for the current branch, triage failures, apply fixes, and report results |\n| `/parse-repository` | Queue a background job to index the repository for Bridge AI agents |\n| `/check-parse-status` | Check whether a background repository parse job is still running |\n| `/scan-tickets` | Sync recently-updated Jira tickets and backfill workflow timestamps |\n\n> Commands are designed for Claude Code. Other editors may support slash commands differently — check your editor's documentation for how to invoke prompt files.\n\n### Extra Capabilities\n\nBehind-the-scenes capabilities an agent gains from the MCP tools — mostly invoked automatically by the commands above, rarely requested by name:\n\n- **Ship a PR end-to-end:** commit & push, open a pull request, transition the Jira status, and discover/poll CI checks (powers `commit-ticket`, `create-pr`, `check-ci`, `implement-ticket`).\n- **Architecture plan** for a ticket (design-level guidance, separate from the implementation plan).\n- **Index the codebase** so Bridge's agents can reason about it: queue/parse the repo, check parse status, regenerate the directory map.\n- **Read & tune project config/standards:** list/read/update config fields, fetch project standards, and the per-topic `learn-*` commands that populate them.\n- **Ticket lifecycle bookkeeping:** track tickets and backfill workflow-state timestamps (`scan-tickets`), search across tickets, read comments, list attachments.\n- **Pipeline machinery:** list/inspect pipeline recipes and run/resume/list/delete pipeline runs (the engine under the orchestration commands).\n- **Decision page** generation for capturing human review decisions as structured data.\n- **Connectivity & identity checks:** ping Bridge, check your role, resolve the local docs directory.\n- **Retrieve any generated artifact** (`get_*` for plans, critiques, questions, brainstorms, research, architecture) without regenerating it.\n- **Tiered-section execution telemetry** recording (internal measurement).\n\n## Salesforce B2C Commerce (SFCC) Tools\n\nSalesforce's official MCP server, `@salesforce/b2c-dx-mcp`, covers developer-experience tasks — cartridge deploy, step debugging, docs search, logs/MRT, and SCAPI Custom API scaffolding. As of its current toolset it has no way to *read* the platform's object model, custom objects, or site configuration — exactly the context an AI coding agent needs to write correct Commerce code and to inspect a sandbox before touching it. Bridge's SFCC tools install side-by-side with `b2c-dx-mcp` (they don't duplicate its surface) and close that gap with **read-only OCAPI Data API introspection** of system objects, custom object definitions, and site preferences.\n\n**v1 is read-only and developer-sandbox-only** — no writes, and non-sandbox instances are rejected. Credentials stay local (in `dw.json` or `SFCC_*` env vars) and are never sent to Bridge.\n\n<details>\n<summary><strong>Setup</strong></summary>\n\nThe two diagnostic tools (`sfcc_setup_status`, `check_permissions`) are always available. The seven read tools must be enabled with a profile (step 3).\n\n**Prerequisites:** a running SFCC **sandbox** and its hostname, plus an Account Manager API client (`client-id` + `client-secret`).\n\n**1. Set the repo `version` config field** to your SFCC project type — one of `sfra | pwakit | sitegenesis | storefrontnext | hybrid`. The call-time gate reads this; a non-SFCC value blocks every SFCC tool except `sfcc_setup_status`. Set it via your normal config path, the `config_field` MCP tool (operation `update`, field `version`), or the `/teach-bridge` skill.\n\n**2. Provide SFCC credentials.** Create a `dw.json` in your project root:\n\n```json\n{\n \"hostname\": \"zzzz-001.sandbox.us01.dx.commercecloud.salesforce.com\",\n \"client-id\": \"<account-manager-client-id>\",\n \"client-secret\": \"<account-manager-client-secret>\"\n}\n```\n\nAccepted key spellings: `hostname`/`host`, `client-id`/`clientId`/`client_id`, `client-secret`/`clientSecret`/`client_secret`. Prefer a single config — a multi-entry `configs[]` array forces you to pass an explicit `instance` on every call. `dw.json` is auto-added to git exclude and must never be committed. Alternatively, export `SFCC_HOSTNAME` / `SFCC_CLIENT_ID` / `SFCC_CLIENT_SECRET` in the MCP server environment.\n\n**3. Enable the read tools.** Add `sfcc` to `BRIDGE_MCP_PROFILE` in the MCP server `env` block (it is comma-separated; `full` also works), then **restart the MCP client**:\n\n```json\n\"env\": { \"BRIDGE_MCP_PROFILE\": \"sfcc\" }\n```\n\nWithout this, only the diagnostic tools are registered.\n\n**4. Verify and grant OCAPI access.** Ask your agent to run `sfcc_setup_status` (expect all checks ✓), then `check_permissions`. If it reports HTTP 401/403, it prints the exact OCAPI Settings JSON to paste in Business Manager (**Administration → Site Development → Open Commerce API Settings → Data API** tab); save it there and re-run. Restart the MCP client after any credential or env change — a running session does not pick them up.\n\n</details>\n\n### Tools\n\nAll SFCC tools are read-only and target a developer sandbox. Oversized responses are automatically saved to `BAPI_DOCS_DIR/sfcc/` and previewed inline.\n\n**Diagnostics** (always available, no profile needed)\n- `sfcc_setup_status` — report on every prerequisite: Bridge API key, repo name, `version` config, `dw.json` presence/uniqueness, and AM token acquisition.\n- `check_permissions` — probe OCAPI access via `GET /system_object_definitions`; on 401/403, print the exact OCAPI Settings JSON to paste in Business Manager (split read-only vs. write/import grants).\n\n**System object model** (needs the `sfcc` profile)\n- `system_object_list` — list system object types (Product, Order, Customer, …).\n- `system_object_get` — fetch one type's definition, optionally with its full attribute definitions (`expand_attribute_definitions`).\n- `system_object_attribute_search` — search a type's attribute definitions; prefer this over a full dump when hunting a specific `c_` custom attribute.\n\n**Custom object definitions** (needs the `sfcc` profile)\n- `custom_object_definition_attributes_get` — fetch attribute definitions for a known custom object type. OCAPI cannot enumerate custom object types, so `object_type` must already be known.\n- `custom_object_definition_attribute_search` — search attribute definitions within a known custom object type. Read-only — creating a custom object *type* isn't possible via OCAPI; that's a future v2 metadata-import capability.\n- `custom_object_definition_attribute_create` — **write** (sandbox only): create an attribute definition on a known custom object type via `PUT /custom_object_definitions/{type}/attribute_definitions/{id}`. TYPE creation is never attempted (the type must pre-exist). Echoes paste-ready OCAPI grant JSON on 403.\n- `custom_object_definition_attribute_update` — **write** (sandbox only): update an attribute definition via an ETag-conditional `PATCH …/attribute_definitions/{id}`; surfaces 409/412 conflicts and echoes grant JSON on 403.\n\n**Site preferences** (needs the `sfcc` profile; sandbox only)\n- `site_preference_get` — read a preference group's effective preferences.\n- `site_preference_search` — search/filter preferences within a group.\n- `site_preference_values_set` — **write** (sandbox only): set custom preference values via `PATCH /site_preferences/preference_groups/{group}/sandbox` with a flat map of `c_`-prefixed ids to string/number/boolean/string[] values. A bad group returns 404 `CustomPreferenceGroupNotFoundException`; echoes grant JSON on 403.\n\n## CLI Subcommands\n\nBeyond `--init` / `--upgrade`, the package ships operational subcommands of the **single `bridge-api-mcp-server` bin** (not separate binaries) — so they travel with the package to every consumer. See [Usage Documentation → Start Tickets](#tier-1--regularly-useful) for *when* to use `start-tickets`; this section is the full CLI reference.\n\n### `start-tickets`\n\nSpawns one Worktrunk worktree + selected-agent session per Jira ticket and backs the `/start-tickets` slash command. The agent defaults to **Claude Code** (`claude`) and is configurable via `--agent`.\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets [flags] KEY [KEY ...]\n```\n\n| Flag | Default | Meaning |\n|---|---|---|\n| `--agent claude\\|cursor-agent` | `claude` | Agent command to launch in each worktree |\n| `--terminal terminal\\|iterm` | auto-detect via `$TERM_PROGRAM` | Override the macOS terminal app (honored on macOS only) |\n| `--dry-run` | off | Print intended actions; create no worktrees, open no tabs (any OS) |\n| `--branch KEY=BRANCH` | `feature/<KEY>` | Use a custom branch for that ticket (repeatable) |\n| `--base-branch <BRANCH>` | `main` | Cut new worktrees from `<BRANCH>` and refresh `origin/<BRANCH>` instead of `main` |\n| `--no-refresh-main` | off (the configured base branch is refreshed) | Skip refresh of the configured base branch (default `main`). Historical flag name preserved for backward compatibility — despite the name, it now skips refresh of whatever `--base-branch` resolves to. |\n| `--max-parallel N` | `3` | Max worktrees created concurrently |\n| `--conductor` | off | Opt into the Conductor system (per-worker `BAPI_CONDUCTOR_*` env + Claude hook injection, a supervisor peer tab, and the `check_messages` message-relay prompt). **Default off** — a plain run spawns `cd <worktree> && <agent> '/implement-ticket <KEY>'`. |\n| `-h`, `--help` | — | Show usage |\n\nEach `KEY` must match `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). The CLI creates/switches each worktree up front (throttled by `--max-parallel`), then opens one tab/session per successful worktree running the selected agent's `'/implement-ticket <KEY>'` — `claude '/implement-ticket <KEY>'` by default, or `cursor-agent '/implement-ticket <KEY>'` with `--agent cursor-agent`. The `/implement-ticket <KEY>` prompt is unchanged for both agents. To launch Cursor Agent instead of Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\n**Difficulty-based model routing.** Before launching each agent, the CLI selects an implementation **model tier** from the ticket's `difficulty` (1-2 → cheap, 3-5 → basic, 6+ → premium) and injects it as a `--model` flag at the spawn boundary. The Python backend returns only the coarse tier (`GET /jira/tickets/{KEY}/model-tier`, computing + caching difficulty on demand); this CLI alone maps a tier to the agent-specific alias (`claude`: `haiku`/`sonnet`/`opus`; `cursor-agent`: version-suffixed strings validated against `cursor-agent --list-models`). It is gated per repo by `difficulty_model_routing_enabled` (default **ON**) with an optional `difficulty_model_tier_overrides` JSON map (tier → alias). Routing is **fail-open**: missing credentials, an evaluation failure/timeout, a backend `fallback`, an invalid/unavailable alias, an unadvertised Cursor model, or an agent without `--model` support all omit `--model` (the agent uses its default) and surface a per-ticket warning rather than failing the spawn. `--dry-run` does **not** fetch tiers or inject `--model`.\n\n**Conductor observability (opt-in via `--conductor`, BAPI-394).** Conductor is **off by default**. With `--conductor`, a run mints a conductor `run_id`, emits events into the local ledger (`~/.config/bridge/events.db`), opens a supervisor peer tab, and (for Claude Code) injects a secret-free lifecycle hook into each worktree; it also sets `BRIDGE_MCP_PROFILE=conductor` so workers get the conductor MCP tools. Observability is best-effort — a conductor failure never blocks a spawn, and `--dry-run` performs no conductor side effects. (Epic-tick dispatch always enables conductor internally.) See **[CONDUCTOR.md](./CONDUCTOR.md)** for the full observability, git-hook, done-gate, and auto-merge reference.\n\n**Cross-platform spawning.** The CLI routes spawning per platform; `--dry-run` previews the platform-correct command form on any OS. An unsupported `process.platform` (not `darwin`/`win32`/`linux`) fails fast with a clear \"unsupported platform\" message.\n\n- **macOS** — opens a Terminal.app or iTerm tab via `osascript`.\n- **Windows** — creates worktrees with **`git-wt`** (Worktrunk's winget alias) and opens a tab via **Windows Terminal (`wt.exe new-tab`)**, falling back to **`Start-Process powershell.exe`** when Windows Terminal is absent. Requires **Git for Windows / Git Bash** (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash). The Worktrunk binary (`git-wt`) and the tab launcher (`wt.exe`) are resolved independently and never conflated.\n- **Linux** — creates one detached **tmux** session per ticket (pane kept open after the agent exits); attach with `tmux attach -t <session>`. A missing `tmux` produces a clear, actionable error.\n\nPer-OS prerequisites: macOS `wt`, `git`, `osascript`; Windows `git-wt`, Git for Windows / Git Bash, Windows Terminal or PowerShell; Linux `wt`, `git`, `tmux`. Set `BAPI_WORKTRUNK_BIN` to override the Worktrunk executable name/path for nonstandard installs (`doctor` honors it too). The read-only `doctor` subcommand (below) additionally surfaces a missing `uv` — Worktrunk's `pre-start` hook runs `uv`, but live preflight does not check it — and the selected agent's command; run `doctor --agent cursor-agent` to also check `cursor-agent` (it prints `cursor-agent login` as an informational auth reminder).\n\n### `doctor`\n\nThe package also ships a strictly **read-only** `doctor` subcommand that diagnoses the `start-tickets` prerequisites for the current OS without changing anything:\n\n```\nnpx -y @bridge_gpt/mcp-server doctor [--agent <name>]\n```\n\nIt is **read-only**: it never installs anything, modifies your system, adds an npm `postinstall`, spawns a terminal, or starts the MCP server, and there is no `--fix`. For each prerequisite it prints found/missing and, when missing, the exact per-OS install command **as a manual instruction you run yourself**. The checked set is the `start-tickets` preflight prerequisites **plus `uv`** **plus the selected agent's command** (`claude` by default, or `cursor-agent` with `--agent cursor-agent`). The Worktrunk binary is probed via the resolved name (honoring `BAPI_WORKTRUNK_BIN`), not a hard-coded one. **Exit code:** `0` when all required prerequisites are present, non-zero when any is missing or the platform is unsupported. A failing `start-tickets` preflight now hints you to run `doctor` for an actionable diagnostics report.\n\n### Conductor (epic & multi-agent orchestration)\n\nConductor is an **opt-in, off-by-default** layer for epic supervision, inter-agent messaging, done-gate evaluation, local git-hook event producers, and conditional auto-merge. Its full reference — `conductor install-git-hooks`, the `conductor_done_gate` and `conductor_auto_merge_enabled` config fields, and the observability stream — lives in **[CONDUCTOR.md](./CONDUCTOR.md)**.\n\n## Custom Pipelines\n\nYou can create your own pipelines by adding JSON files to `.bridge/pipelines/`. Running `--init` scaffolds this directory with a `README.md` and an example pipeline to get you started.\n\nThe easiest way to write a custom pipeline is to describe what you want to automate to your AI coding agent and have it draft the JSON for you. The schema is straightforward, and agents like Claude Code understand it well — just describe the steps you want, and the agent will produce a working pipeline file.\n\n**What you can build:**\n\n- Any sequence of Bridge MCP tool calls and free-form agent tasks\n- Parameterized workflows using variables (e.g., `{ticket_key}`)\n- Approval gates that pause for user confirmation before sensitive steps\n- Per-step error handling — halt immediately or warn and continue\n\n**Ideas for custom pipelines:**\n\n- A standup pipeline that fetches your open tickets and summarizes their status\n- A ticket triage pipeline that runs critiques on a batch of new tickets\n- A pre-merge checklist that runs tests, checks linting, and posts a summary comment\n\n**Step types:**\n\n| Type | What it does |\n|---|---|\n| `mcp_call` | Calls an MCP tool with the given params |\n| `agent_task` | Gives the AI a free-form instruction (inline or from a file in `.bridge/instructions/`) |\n\nVariables are declared in the `variables` array and referenced as `{variable_name}` in params and instructions. Each step supports `on_error: \"halt\"` (default) or `\"warn_and_continue\"`, and `requires_approval: true` to pause before execution.\n\n**System variables:**\n\nTwo variables are automatically available in every pipeline without declaring them:\n\n| Variable | What it does |\n|---|---|\n| `{provider}` | Routes AI generation to a specific LLM provider (`openai`, `anthropic`, or `gemini`). Pass it through to any `request_*` tool param to control which provider handles that step. Omit it (or leave it empty) to use the project default. |\n| `{second_opinion}` | Runs AI generation through a different provider than the default, acting as a cross-check. Set to `\"auto\"` to let Bridge pick the second provider automatically. When set, it takes precedence over `{provider}`. Use this when you want two independent AI perspectives on the same task — for example, running clarifying questions and a critique twice (once with each provider) produces better results than a single pass. |\n\nSee `.bridge/pipelines/README.md` for the full schema reference.\n\nIf a custom pipeline has the same key as a built-in pipeline, the custom version takes precedence (a warning is logged at startup).\n\n## Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `BAPI_BASE_URL` | Yes | `https://bridgegpt-api.com` | Bridge API base URL |\n| `BAPI_REPO_NAME` | Yes | _(none)_ | Jira project/repository identifier configured in Bridge API |\n| `BAPI_API_KEY` | Yes | _(none)_ | API key obtained from the Bridge API setup UI |\n| `BAPI_PROJECT_ROOT` | No | _(auto-set by --init)_ | Absolute path to project root. Anchors `BAPI_DOCS_DIR` and `BAPI_PIPELINES_DIR` resolution |\n| `BAPI_DOCS_DIR` | No | `docs/tmp` | Local directory for saving plans, critiques, and research reports |\n| `BAPI_PIPELINES_DIR` | No | `.bridge/pipelines` | Directory for user-defined custom pipeline JSON files |\n| `BAPI_WORKTRUNK_BIN` | No | `wt` (`git-wt` on Windows) | Override the Worktrunk executable name/path used by `start-tickets` for nonstandard installs |\n| `BAPI_TMUX_SESSION` | No | `bridge-start-tickets` | Override the tmux session-name prefix used by `start-tickets` on Linux |\n| `BAPI_MCP_UPGRADE_ADVICE_ENABLED` | No | _(enabled)_ | MCP-local opt-out for proactively surfacing upgrade advice in pipeline recipe preambles. Set to `false`/`0`/`no`/`off`/`disabled` to suppress. Disabling it does **not** change the `/jira/ping` response or server-side upgrade computation — it only gates the recipe-preamble convention |\n| `CURSOR_API_KEY` | No | _(none)_ | API key used to authenticate `cursor-agent` when launching `start-tickets`/scheduled runs with `--agent cursor-agent`. Not needed for the default Claude Code agent |\n| `BRIDGE_MCP_PROFILE` | No | `core` | Startup-time tool registration profile — a **comma-separated** list of groups controlling which tool groups are registered when the server starts. `core` is always implicitly included. Valid groups: `core` (default — normal coding tools only), `conductor` (+ 8 conductor/event/supervisor tools), `pipeline-authoring` (+ 5 pipeline run/admin tools — `get_pipeline_recipe` is NOT gated; it stays in `core` because the recipe-driven slash commands depend on it), `sfcc` (+ the 7 heavy SFCC read tools — see [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools); the `sfcc_setup_status`/`check_permissions` diagnostics are always registered regardless), and `full` (shortcut that expands to every group). Example: `sfcc,conductor`. Unknown, blank, or malformed tokens are dropped (falling back to `core`). Dynamic mid-session switching via `tools/list_changed` is unsupported — groups are resolved once at process startup. Conductor/epic sessions launched via `start-tickets --conductor` automatically inject `BRIDGE_MCP_PROFILE=conductor` at the spawn boundary; a normal `start-tickets` run stays on `core`. |\n\n## Worktree credentials and the `mcp-invoke` shim\n\nWhen `start-tickets` creates a git worktree, it provisions a Bridge API MCP\nregistration into that worktree so Claude Code (`.mcp.json`) and Cursor\n(`.cursor/mcp.json`) can reach the server immediately. These registrations are\n**secret-free**: they contain no `env` block and no API key. Instead they point\nat an internal subcommand of the published single CLI bin, `mcp-invoke`:\n\n```bash\nnpx -y @bridge_gpt/mcp-server@<VERSION> mcp-invoke --target bapi --project-root <ABS_WORKTREE_PATH>\n```\n\n`mcp-invoke` is not a separate binary — it is a positional subcommand of\n`bridge-api-mcp-server`. It resolves the repo identity from the absolute\n`--project-root` (the committed `.bridge/config` manifest, falling back to the\ngit common dir), resolves credentials from the home-directory credential store,\nand then spawns the real MCP server with that environment.\n\n### Credential store\n\nCredentials live outside the repository, keyed by `bapi:<repo_name>`:\n\n```json\n{\n \"bapi:<repo_name>\": {\n \"BAPI_API_KEY\": \"...\"\n }\n}\n```\n\nResolution order:\n\n1. `BAPI_API_KEY` in the parent environment (overrides the file entirely).\n2. `$XDG_CONFIG_HOME/bridge/credentials.json`, else `~/.config/bridge/credentials.json`.\n3. `~/.bridge/credentials.json` (only when the primary path is absent).\n\nOn POSIX systems, lock the file down so only you can read it:\n\n```bash\nchmod 600 ~/.config/bridge/credentials.json\n```\n\n`mcp-invoke` warns (but continues) if the file is group/world-readable, and it\nnever creates or initializes the credential file for you.\n\n### Populating the credential store\n\nThe same store also backs the shell-spawned `start-tickets` CLI (its\ndifficulty→model routing runs in a Bash process that cannot see an `env` block in\n`.mcp.json` / `.cursor/mcp.json`), so the store must hold the key for routing to\nwork. Two supported paths write it for you:\n\n- **Install-time upsert.** `/install-bridge`'s final stage persists the validated\n routing credential into `~/.config/bridge/credentials.json` (target\n `bapi:<repo>`) via the `persist_routing_credential` MCP tool — the tool resolves\n the key inside the MCP process and writes the store, so no secret crosses the\n wire.\n- **One-shot migration.** If a key currently lives only in `.mcp.json` /\n `.cursor/mcp.json`, migrate it into the user-scoped store with the consent-gated\n command (a compatibility aid, not a live fallback):\n\n ```bash\n npx -y @bridge_gpt/mcp-server credentials migrate-agent-config [--write-credentials]\n ```\n\n Run it without `--write-credentials` to preview; add the flag to write the store.\n\n## Reference\n\nThe full surface, for when you need the complete enumeration. Day-to-day, use [Usage Documentation](#usage-documentation) instead — you don't call MCP tools directly; you ask your AI assistant to perform a task, or compose tools into a pipeline.\n\n### MCP tools\n\nThe server exposes **59 documented tools** (enumerated below). What's actually registered in a session depends on `BRIDGE_MCP_PROFILE`: the default `core` profile loads a trimmed subset, and the conductor/pipeline-authoring/SFCC tools are added only under their respective profiles (see [Environment Variables](#environment-variables)). Async AI tools follow a request/get pattern: call the `request_*` tool to kick off generation, then the matching `get_*` tool to retrieve the result (or pass `wait_for_result: true` to poll automatically).\n\n- **Connectivity & identity** — `ping`, `get_my_role`, `get_docs_dir`\n- **Jira tickets** — `get_tickets`, `get_ticket`, `create_ticket`, `update_ticket_description`, `add_comment`, `get_comments`\n- **Attachments** — `attachment` (operations: `upload`, `download`, `list`)\n- **AI generation (request/get)** — `request_plan_generation`/`get_plan`, `request_architecture`/`get_architecture`, `create_doc`/`get_doc` (design docs by `doc_type`: tdd/fsd/prd), `request_prd`/`get_prd`, `request_clarifying_questions`/`get_clarifying_questions`, `request_ticket_critique`/`get_ticket_critique`, `request_ticket_review`, `request_reimplement_context`/`get_reimplement_context`, `request_brainstorm`/`get_brainstorm`, `request_deep_research`/`get_deep_research`\n- **Other AI** — `second_opinion`, `generate_image`, `generate_decision_page`, `visual_diff` (deterministic pixel diff of a rendered URL vs a design comp)\n- **Ticket lifecycle** — `track_ticket`, `update_ticket_state`, `get_ticket_state`\n- **Jira status** — `get_jira_transitions`, `update_jira_status`, `resolve_target_status`\n- **Repository & CI** — `parse_repository`, `get_parse_status`, `regenerate_directory_map`, `create_pull_request`, `resolve_ci_checks`, `poll_ci_checks`\n- **Pipelines & automation** — `list_pipelines`, `get_pipeline_recipe`, `run_pipeline`, `resume_pipeline`, `list_pipeline_runs`, `delete_pipeline_run`, `run_full_automation`, `resume_full_automation`\n- **Config** — `get_project_standards`, `config_field` (operations: `get`, `update`, `list`)\n\n### Bundled pipelines\n\nPipelines are declarative, multi-step workflows your AI agent executes step-by-step — each a JSON recipe chaining MCP tool calls and free-form agent tasks, with variable substitution, per-step error handling, and optional approval gates. You can also write your own (see [Custom Pipelines](#custom-pipelines)).\n\n| Pipeline | Description | Invoke with |\n|---|---|---|\n| `implement-ticket` | Generate a plan, execute the implementation, commit, open a PR, and monitor CI | `/implement-ticket PROJ-123` |\n| `review-ticket` | Full ticket quality review: clarifying questions + critique plus an automatic second-opinion pass, then evaluation and decision capture. The backend owns round orchestration; pass `--rounds=1` for a single-pass review or `--rounds=2` to force the full second-opinion review, or omit `--rounds` to let the backend decide adaptively. | `/review-ticket PROJ-123` |\n| `idea-to-ticket` | Turn an idea into a Jira Task/Spike (or Epic + children) with research, dedup, and critique | `/idea-to-ticket \"<idea>\"` |\n| `plan-epic` | Decompose an epic into sub-tasks with a structured exploration doc for each | `/plan-epic \"<epic>\"` |\n| `full-automation` | Chain: idea → ticket(s) → review each → spawn worktrees to implement | `/full-automation \"<idea>\"` |\n| `pr-ticket` | Commit changes and open a pull request | `/create-pr PROJ-123` |\n| `check-ci-ticket` | Commit, open a PR, then monitor CI checks until they pass or fail | `/check-ci PROJ-123` |\n| `learn-repository` | Analyze codebase architecture, testing, review, and documentation standards, then upload to Bridge | `/learn-repository` |\n\n### Pipeline response envelope\n\n`run_pipeline`, `resume_pipeline`, and `list_pipeline_runs` share a unified envelope keyed on `status`:\n\n- `completed` — terminal success; `results` holds per-step output.\n- `needs_agent_task` — the orchestrator paused. Read `instruction`, perform the task, then call `resume_pipeline` with `pipeline_run_id` and a string `agent_result`.\n- `failed` — terminal failure. `error_code` is one of `VALIDATION`, `NOT_FOUND`, `EXPIRED`, `REPO_MISMATCH`, `TOOL_ERROR`.\n\nPaused runs auto-expire after an idle TTL (default 24 hours; override with `ttl_seconds`). The TTL is reset on every state transition. List output is metadata-only — it never includes resolved recipes, params, instructions, results, or agent outputs.\n";
@@ -5,7 +5,10 @@
5
5
  * the current `deps.cwd`. Intentionally does NOT create Worktrunk worktrees,
6
6
  * provision MCP registrations, run venv hooks, or perform difficulty/model-tier
7
7
  * routing. One terminal tab per ticket is spawned with `/review-ticket <KEY>
8
- * [--auto] --rounds=<1|2>` as the agent prompt.
8
+ * [--auto] [--rounds=<1|2>]` as the agent prompt. `--rounds` is OMITTED by
9
+ * default so the backend's difficulty-adaptive review policy chooses the shape;
10
+ * it is only forwarded when the caller passes an explicit `--rounds`/`--review`
11
+ * round override.
9
12
  *
10
13
  * Optionally supports a supervisor-driven epic-dispatch path: when an
11
14
  * `EpicDispatchIdentity` is threaded through `ReviewTicketsOptions.epic`, each
@@ -31,7 +34,7 @@ export function getReviewTicketsUsage() {
31
34
  "",
32
35
  "Flags:",
33
36
  " --auto Auto-approve all review gates (global, applies to all tickets)",
34
- " --rounds=1|2 Review depth: 1=single-pass, 2=full two-pass (default: 2)",
37
+ " --rounds=1|2 Review depth: 1=single-pass, 2=full two-pass (default: adaptive — backend routes by difficulty)",
35
38
  " --review KEY=auto,rounds=1 Per-ticket review mode override (repeatable)",
36
39
  " --agent claude|cursor-agent Agent command to launch in each tab (default: claude)",
37
40
  " --model VALUE Model alias to pass as --model to the agent (optional passthrough)",
@@ -396,11 +399,14 @@ export function parseReviewTicketsArgs(argv) {
396
399
  export function resolveEffectiveReviewMode(key, options) {
397
400
  const override = options.reviewOverrides[key];
398
401
  const auto = override?.auto !== undefined ? override.auto : options.auto;
402
+ // Rounds is tri-state: a per-ticket override wins, then the global --rounds,
403
+ // then `undefined` (adaptive — the default). `undefined` means no --rounds is
404
+ // forwarded, so the backend's difficulty-adaptive policy chooses the shape.
399
405
  const rounds = override?.rounds !== undefined
400
406
  ? override.rounds
401
407
  : options.rounds !== undefined
402
408
  ? options.rounds
403
- : 2;
409
+ : undefined;
404
410
  return { auto, rounds };
405
411
  }
406
412
  // ---------------------------------------------------------------------------
@@ -411,10 +417,13 @@ export function resolveEffectiveReviewMode(key, options) {
411
417
  // ---------------------------------------------------------------------------
412
418
  export function buildReviewTicketPrompt(key, mode, base = NO_BASE_CONTEXT) {
413
419
  const autoFlag = mode.auto ? " --auto" : "";
420
+ // Omit --rounds entirely when adaptive (mode.rounds === undefined) so the
421
+ // backend routes by difficulty; only forward it for an explicit 1/2 override.
422
+ const roundsFlag = mode.rounds !== undefined ? ` --rounds=${mode.rounds}` : "";
414
423
  const baseBranchFlag = base.baseBranch ? ` --base-branch=${base.baseBranch}` : "";
415
424
  const baseShaFlag = base.baseSha ? ` --base-sha=${base.baseSha}` : "";
416
425
  const noRefreshFlag = base.noRefreshBase ? " --no-refresh-base" : "";
417
- return `/review-ticket ${key}${autoFlag} --rounds=${mode.rounds}${baseBranchFlag}${baseShaFlag}${noRefreshFlag}`;
426
+ return `/review-ticket ${key}${autoFlag}${roundsFlag}${baseBranchFlag}${baseShaFlag}${noRefreshFlag}`;
418
427
  }
419
428
  export function buildPosixReviewAgentShellCommand(agent, key, mode, cwd, modelAlias, base = NO_BASE_CONTEXT) {
420
429
  const prompt = buildReviewTicketPrompt(key, mode, base);
@@ -662,8 +671,9 @@ export function formatReviewTicketsSummaryReport(rows) {
662
671
  const lines = ["Summary:"];
663
672
  for (const row of rows) {
664
673
  const modelPart = row.modelAlias ? row.modelAlias : "default";
674
+ const roundsPart = row.rounds !== undefined ? String(row.rounds) : "adaptive";
665
675
  const runIdPart = row.runId ? ` run_id=${row.runId}` : "";
666
- lines.push(`${row.key} auto=${row.auto} rounds=${row.rounds} agent=${row.agentName} model=${modelPart} status=${row.status}${runIdPart}`);
676
+ lines.push(`${row.key} auto=${row.auto} rounds=${roundsPart} agent=${row.agentName} model=${modelPart} status=${row.status}${runIdPart}`);
667
677
  }
668
678
  const failedRows = rows.filter((r) => r.status === "spawn-failed" || r.status === "already-dispatched");
669
679
  if (failedRows.length > 0) {
@@ -1,11 +1,16 @@
1
1
  /**
2
- * SFCC AM-OAuth2 token manager and thin OCAPI Data API GET client.
2
+ * SFCC AM-OAuth2 token manager and thin OCAPI Data API client (GET/POST/PUT/PATCH).
3
3
  *
4
4
  * Token refresh is single-flight per instance (keyed Map<instanceId, Promise>).
5
5
  * On a 401, invalidate-once + retry-once — no infinite loops.
6
+ * On a 429, bounded deterministic exponential backoff — never retries forever.
7
+ * All verbs route through the shared `ocapiRequest` helper so token handling,
8
+ * response parsing, ETag capture, write-fault mapping, and 429 backoff are
9
+ * identical across methods.
6
10
  * SDK types are intentionally NOT exported from this module.
7
11
  */
8
12
  import { DEFAULT_OCAPI_VERSION, AM_TOKEN_URL } from "./config.js";
13
+ import { mapOcapiWriteFault, buildSyntheticIfMatchRequiredBody } from "./ocapi-write-faults.js";
9
14
  // ---------------------------------------------------------------------------
10
15
  // Single-flight token mutex
11
16
  // ---------------------------------------------------------------------------
@@ -67,37 +72,138 @@ export function invalidateAmToken(credentials) {
67
72
  tokenCache.delete(credentials.hostname);
68
73
  }
69
74
  // ---------------------------------------------------------------------------
70
- // OCAPI Data API GET client
75
+ // Private transport helpers
71
76
  // ---------------------------------------------------------------------------
77
+ /** Build the canonical OCAPI Data API URL for a path. */
78
+ function buildOcapiUrl(hostname, ocapiVersion, path) {
79
+ const baseUrl = `https://${hostname}/s/-/dw/data/${ocapiVersion}`;
80
+ return `${baseUrl}${path.startsWith("/") ? path : "/" + path}`;
81
+ }
82
+ /** Safely read a JSON body from a Response; returns null when absent/unparseable. */
83
+ async function parseOcapiResponse(resp) {
84
+ try {
85
+ return await resp.json();
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
72
91
  /**
73
- * Issue a GET request against the OCAPI Data API.
92
+ * Capture response headers as a lowercased plain object and pull out the ETag.
93
+ * The ETag string is preserved exactly, including surrounding quotes if SFCC
94
+ * sends a quoted value.
95
+ */
96
+ function captureOcapiHeaders(resp) {
97
+ const headers = {};
98
+ // Real fetch always provides a Headers object; guard anyway so header-less
99
+ // responses (e.g. test doubles) degrade gracefully rather than throwing.
100
+ const respHeaders = resp.headers;
101
+ if (respHeaders && typeof respHeaders.forEach === "function") {
102
+ respHeaders.forEach((value, key) => {
103
+ headers[key.toLowerCase()] = value;
104
+ });
105
+ return { headers, etag: respHeaders.get("etag") };
106
+ }
107
+ return { headers, etag: null };
108
+ }
109
+ /** Promise-based sleep used for backoff delays. */
110
+ function sleep(ms) {
111
+ return new Promise((resolve) => setTimeout(resolve, ms));
112
+ }
113
+ /** Maximum backoff we will honor for a single 429 retry, regardless of Retry-After. */
114
+ const MAX_RETRY_AFTER_MS = 5000;
115
+ /**
116
+ * Parse a `Retry-After` header value into milliseconds.
74
117
  *
75
- * Base URL form: `https://{hostname}/s/-/dw/data/{version}/{path}`.
118
+ * Supports both the numeric "delay-seconds" form and the HTTP-date form.
119
+ * Returns `undefined` when the header is absent or malformed.
120
+ */
121
+ function parseRetryAfterMs(headerValue) {
122
+ if (!headerValue)
123
+ return undefined;
124
+ const trimmed = headerValue.trim();
125
+ if (trimmed === "")
126
+ return undefined;
127
+ // Numeric delay-seconds form.
128
+ if (/^\d+$/.test(trimmed)) {
129
+ return Number(trimmed) * 1000;
130
+ }
131
+ // HTTP-date form.
132
+ const dateMs = Date.parse(trimmed);
133
+ if (!Number.isNaN(dateMs)) {
134
+ const delta = dateMs - Date.now();
135
+ return delta > 0 ? delta : 0;
136
+ }
137
+ return undefined;
138
+ }
139
+ /** Deterministic exponential backoff schedule (ms) for successive 429 retries. */
140
+ const BACKOFF_SCHEDULE_MS = [250, 500, 1000];
141
+ /**
142
+ * Perform a fetch with bounded retries for `429 Too Many Requests`.
76
143
  *
77
- * On a 401 response: invalidates the cached token once and retries once —
78
- * never loops.
144
+ * Uses deterministic exponential backoff (250ms, 500ms, 1000ms). When the
145
+ * response carries a valid `Retry-After`, that delay is preferred but capped at
146
+ * `MAX_RETRY_AFTER_MS` so a hostile/large header can never hang the MCP call.
147
+ * Retries are bounded by the schedule length — it never loops forever.
79
148
  */
80
- export async function ocapiGet(path, credentials, ocapiVersion = DEFAULT_OCAPI_VERSION) {
149
+ async function fetchWith429Backoff(url, init) {
150
+ let resp = await fetch(url, init);
151
+ for (let attempt = 0; attempt < BACKOFF_SCHEDULE_MS.length; attempt++) {
152
+ if (resp.status !== 429)
153
+ return resp;
154
+ const retryAfterHeader = resp.headers && typeof resp.headers.get === "function"
155
+ ? resp.headers.get("retry-after")
156
+ : null;
157
+ const retryAfter = parseRetryAfterMs(retryAfterHeader);
158
+ const backoff = retryAfter !== undefined
159
+ ? Math.min(retryAfter, MAX_RETRY_AFTER_MS)
160
+ : BACKOFF_SCHEDULE_MS[attempt];
161
+ await sleep(backoff);
162
+ resp = await fetch(url, init);
163
+ }
164
+ // Return whatever we have after the final attempt (may still be 429).
165
+ return resp;
166
+ }
167
+ /**
168
+ * Shared authenticated OCAPI request helper.
169
+ *
170
+ * Handles: AM-token acquisition (single-flight via getAmToken), 401
171
+ * invalidate-once + retry-once, 429 bounded backoff per physical fetch,
172
+ * response parsing, header/ETag capture, write-fault mapping, and PUT/PATCH
173
+ * create/update outcome surfacing. All exported verbs delegate here.
174
+ */
175
+ async function ocapiRequest(method, path, body, credentials, ocapiVersion, extraHeaders) {
81
176
  const doRequest = async () => {
82
177
  const token = await getAmToken(credentials);
83
- const baseUrl = `https://${credentials.hostname}/s/-/dw/data/${ocapiVersion}`;
84
- const url = `${baseUrl}${path.startsWith("/") ? path : "/" + path}`;
85
- const resp = await fetch(url, {
86
- method: "GET",
178
+ const url = buildOcapiUrl(credentials.hostname, ocapiVersion, path);
179
+ const init = {
180
+ method,
87
181
  headers: {
88
182
  Authorization: `Bearer ${token}`,
89
183
  "Content-Type": "application/json",
184
+ ...(extraHeaders ?? {}),
90
185
  },
91
- });
186
+ };
187
+ if (body !== undefined) {
188
+ init.body = JSON.stringify(body);
189
+ }
190
+ const resp = await fetchWith429Backoff(url, init);
92
191
  const status = resp.status;
93
- let body = null;
94
- try {
95
- body = await resp.json();
192
+ const respBody = await parseOcapiResponse(resp);
193
+ const { headers, etag } = captureOcapiHeaders(resp);
194
+ const result = { ok: resp.ok, status, body: respBody, headers, etag };
195
+ // Success outcome for write verbs.
196
+ if ((method === "PUT" || method === "PATCH") && resp.ok) {
197
+ if (status === 201)
198
+ result.outcome = "created";
199
+ else if (status === 200)
200
+ result.outcome = "updated";
96
201
  }
97
- catch {
98
- body = null;
202
+ // Structured write-fault mapping for error statuses.
203
+ if (!resp.ok) {
204
+ result.fault = mapOcapiWriteFault(status, respBody);
99
205
  }
100
- return { ok: resp.ok, status, body };
206
+ return result;
101
207
  };
102
208
  const first = await doRequest();
103
209
  if (first.status === 401) {
@@ -108,44 +214,80 @@ export async function ocapiGet(path, credentials, ocapiVersion = DEFAULT_OCAPI_V
108
214
  return first;
109
215
  }
110
216
  // ---------------------------------------------------------------------------
111
- // OCAPI Data API POST client
217
+ // OCAPI Data API verbs
112
218
  // ---------------------------------------------------------------------------
113
219
  /**
114
- * Issue a POST request against the OCAPI Data API.
220
+ * Issue a GET request against the OCAPI Data API.
115
221
  *
116
222
  * Base URL form: `https://{hostname}/s/-/dw/data/{version}/{path}`.
223
+ * Callers receive `etag`/`headers` when the response provides them. On a 401:
224
+ * invalidates the cached token once and retries once — never loops.
225
+ */
226
+ export async function ocapiGet(path, credentials, ocapiVersion = DEFAULT_OCAPI_VERSION) {
227
+ return ocapiRequest("GET", path, undefined, credentials, ocapiVersion);
228
+ }
229
+ /**
230
+ * Issue a POST request against the OCAPI Data API.
117
231
  *
118
- * On a 401 response: invalidates the cached token once and retries once —
119
- * never loops.
232
+ * Base URL form: `https://{hostname}/s/-/dw/data/{version}/{path}`.
233
+ * On a 401: invalidates the cached token once and retries once — never loops.
120
234
  */
121
235
  export async function ocapiPost(path, body, credentials, ocapiVersion = DEFAULT_OCAPI_VERSION) {
122
- const doRequest = async () => {
123
- const token = await getAmToken(credentials);
124
- const baseUrl = `https://${credentials.hostname}/s/-/dw/data/${ocapiVersion}`;
125
- const url = `${baseUrl}${path.startsWith("/") ? path : "/" + path}`;
126
- const resp = await fetch(url, {
127
- method: "POST",
128
- headers: {
129
- Authorization: `Bearer ${token}`,
130
- "Content-Type": "application/json",
131
- },
132
- body: JSON.stringify(body),
133
- });
134
- const status = resp.status;
135
- let respBody = null;
136
- try {
137
- respBody = await resp.json();
138
- }
139
- catch {
140
- respBody = null;
141
- }
142
- return { ok: resp.ok, status, body: respBody };
143
- };
144
- const first = await doRequest();
145
- if (first.status === 401) {
146
- // Invalidate-once + retry-once no looping.
147
- invalidateAmToken(credentials);
148
- return doRequest();
236
+ return ocapiRequest("POST", path, body, credentials, ocapiVersion);
237
+ }
238
+ /**
239
+ * Issue a PUT request against the OCAPI Data API.
240
+ *
241
+ * Transport-only: this function MUST NOT construct SFCC resource payloads —
242
+ * callers pass a fully-formed `body`. Surfaces the create/update distinction
243
+ * via `outcome` ("created" for 201, "updated" for 200). Shares AM-token
244
+ * single-flight, 401 retry-once, and 429 backoff with the other verbs.
245
+ */
246
+ export async function ocapiPut(path, body, credentials, ocapiVersion = DEFAULT_OCAPI_VERSION) {
247
+ return ocapiRequest("PUT", path, body, credentials, ocapiVersion);
248
+ }
249
+ /**
250
+ * Issue an ETag-conditional PATCH against the OCAPI Data API.
251
+ *
252
+ * Transport-only: callers pass a fully-formed `body`. Performs the required
253
+ * GET-then-`If-Match` round trip:
254
+ * 1. GET the current representation to capture its ETag.
255
+ * 2. If the GET fails, return that result (with any mapped fault) — no PATCH.
256
+ * 3. If the GET succeeds but returns no ETag, synthesize a
257
+ * `409 IfMatchRequiredException` result rather than issuing an
258
+ * unconditional PATCH.
259
+ * 4. Otherwise PATCH with `If-Match: <etag>`. A stale ETag surfaces from SFCC
260
+ * as `412 InvalidIfMatchException` through the shared fault mapping.
261
+ * Successful `200` responses are surfaced as `outcome: "updated"`.
262
+ */
263
+ export async function ocapiPatch(path, body, credentials, ocapiVersion = DEFAULT_OCAPI_VERSION) {
264
+ const getResult = await ocapiGet(path, credentials, ocapiVersion);
265
+ // Do not attempt PATCH when the GET failed — return it with any mapped fault.
266
+ if (!getResult.ok) {
267
+ return getResult;
149
268
  }
150
- return first;
269
+ const etag = getResult.etag;
270
+ if (etag === null || etag === undefined || etag.trim() === "") {
271
+ const syntheticBody = buildSyntheticIfMatchRequiredBody(path);
272
+ return {
273
+ ok: false,
274
+ status: 409,
275
+ body: syntheticBody,
276
+ fault: mapOcapiWriteFault(409, syntheticBody),
277
+ };
278
+ }
279
+ return ocapiRequest("PATCH", path, body, credentials, ocapiVersion, { "If-Match": etag });
280
+ }
281
+ /**
282
+ * Issue a direct (unconditional) PATCH against the OCAPI Data API.
283
+ *
284
+ * Transport-only: callers pass a fully-formed `body`. Unlike `ocapiPatch`,
285
+ * this performs NO GET-then-If-Match round trip. Use it for OCAPI resources
286
+ * whose PATCH does not require optimistic-concurrency (e.g. site-preference
287
+ * value writes, where a GET preflight would not yield a usable ETag and would
288
+ * force a spurious `409 IfMatchRequired`). Shares AM-token single-flight, 401
289
+ * retry-once, 429 backoff, and write-result parsing with the other verbs.
290
+ */
291
+ export async function ocapiPatchDirect(path, body, credentials, ocapiVersion = DEFAULT_OCAPI_VERSION) {
292
+ return ocapiRequest("PATCH", path, body, credentials, ocapiVersion);
151
293
  }