@bridge_gpt/mcp-server 0.2.4 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -371,6 +371,7 @@ npx -y @bridge_gpt/mcp-server start-tickets [flags] KEY [KEY ...]
371
371
  | `--base-branch <BRANCH>` | `main` | Cut new worktrees from `<BRANCH>` and refresh `origin/<BRANCH>` instead of `main` |
372
372
  | `--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. |
373
373
  | `--max-parallel N` | `3` | Max worktrees created concurrently |
374
+ | `--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>'`. |
374
375
  | `-h`, `--help` | — | Show usage |
375
376
 
376
377
  Each `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:
@@ -381,7 +382,7 @@ npx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248
381
382
 
382
383
  **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`.
383
384
 
384
- **Conductor observability (BAPI-394).** A real run mints a single conductor `run_id` and emits one canonical `run.started` event into the local conductor ledger (`~/.config/bridge/events.db`), attributing each worker by `worker_id`, ticket key, and worktree path. When the selected agent is **Claude Code**, the CLI also injects a conductor lifecycle hook into each created worktree's `.claude/settings.local.json` (preserving any existing hooks) so the spawned session streams local `run.started` / `run.stopped` / `agent.notification` (and, with `BAPI_CONDUCTOR_ENABLE_PRE_TOOL_USE=1`, `tool.intent`) events. Per-worker conductor identity is passed only via secret-free environment scoped to that one terminal/tab/session — no credentials are ever placed in the env, hook command, or run metadata. Override the gate/supervisor labels with `BAPI_CONDUCTOR_GATE_NAME` / `BAPI_CONDUCTOR_SUPERVISOR_MODE`. Inspect the stream with `conductor doctor`. Observability is best-effort: a conductor failure never blocks or aborts a spawn, and `--dry-run` performs no conductor side effects.
385
+ **Conductor observability (opt-in via `--conductor`, BAPI-394).** Conductor is **off by default** — without `--conductor` no `BAPI_CONDUCTOR_*` env, supervisor tab, or message-relay prompt is produced. With `--conductor`, a run mints a single conductor `run_id` and emits one canonical `run.started` event into the local conductor ledger (`~/.config/bridge/events.db`), attributing each worker by `worker_id`, ticket key, and worktree path, and opens a supervisor peer tab. When the selected agent is **Claude Code**, the CLI also injects a conductor lifecycle hook into each created worktree's `.claude/settings.local.json` (preserving any existing hooks) so the spawned session streams local `run.started` / `run.stopped` / `agent.notification` (and, with `BAPI_CONDUCTOR_ENABLE_PRE_TOOL_USE=1`, `tool.intent`) events. Per-worker conductor identity is passed only via secret-free environment scoped to that one terminal/tab/session — no credentials are ever placed in the env, hook command, or run metadata. Override the gate/supervisor labels with `BAPI_CONDUCTOR_GATE_NAME` / `BAPI_CONDUCTOR_SUPERVISOR_MODE`. Inspect the stream with `conductor doctor`. Observability is best-effort: a conductor failure never blocks or aborts a spawn, and `--dry-run` performs no conductor side effects. (Epic-tick dispatch always enables conductor internally, independent of this flag.)
385
386
 
386
387
  **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.
387
388
 
@@ -23,7 +23,7 @@ export const COMMANDS = {
23
23
  "review-tickets.md": "---\nschedulable: true\ninteractive: true\narguments: {\"positionals\":[{\"name\":\"ticketKeys\",\"type\":\"string\",\"required\":true,\"variadic\":true}],\"flags\":[{\"name\":\"auto\",\"flag\":\"--auto\",\"type\":\"boolean\"},{\"name\":\"rounds\",\"flag\":\"--rounds\",\"type\":\"string\"},{\"name\":\"review\",\"flag\":\"--review\",\"type\":\"string\",\"repeatable\":true},{\"name\":\"agent\",\"flag\":\"--agent\",\"type\":\"string\"},{\"name\":\"model\",\"flag\":\"--model\",\"type\":\"string\"},{\"name\":\"maxParallel\",\"flag\":\"--max-parallel\",\"type\":\"string\"},{\"name\":\"dryRun\",\"flag\":\"--dry-run\",\"type\":\"boolean\"}]}\n---\n\n# Review Tickets: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes one or more Jira ticket keys and invokes the packaged `@bridge_gpt/mcp-server` CLI subcommand `review-tickets`, which opens one terminal tab per ticket running the selected agent with `/review-ticket <KEY> [--auto] --rounds=<1|2>`. Unlike `/start-tickets`, it creates no Worktrunk worktrees and does not require `wt`, `git-wt`, or `git` — it only needs the terminal launcher prerequisite for your OS.\n\n---\n\n# Instructions\n\n## Stage 0 — Parse Arguments and Connectivity Check\n\n1. **Parse `$ARGUMENTS`** to extract ticket keys, review modes, and pass-through flags:\n\n - **Ticket keys**: every whitespace-separated token matching `[A-Z]+-[0-9]+` (e.g., `BAPI-1`). If zero keys are found, stop immediately and display:\n ```\n No ticket keys found. Expected one or more keys like BAPI-1.\n Usage: /review-tickets [flags] KEY [KEY ...]\n ```\n\n - **Review mode interpretation** (per ticket or global):\n - `auto` or `--auto` → per-ticket or global auto-approve flag.\n - `single-pass`, `one-pass`, `rounds=1`, or `--rounds=1` → `rounds=1`.\n - `full`, `two-pass`, `rounds=2`, `--rounds=2`, or omitted rounds → `rounds=2`.\n - `--auto` and `--rounds` are independent: both may apply to the same ticket.\n\n - **Homogeneous modes**: when all tickets share the same auto and rounds values, translate into global `--auto` (if all auto) and `--rounds=1|2` (if all rounds are the same).\n\n - **Heterogeneous modes**: when different tickets have different auto or rounds values, translate into repeatable `--review KEY=auto,rounds=N` overrides. Do NOT set global `--auto` when only some tickets are auto-approved.\n\n - **Pass-through flags**: collect `--dry-run`, `--max-parallel N`, `--agent claude|cursor-agent`, and `--model VALUE` if supplied, and forward verbatim to the CLI.\n\n2. **Connectivity check**: Call the `ping` MCP tool. If it fails or does not return `\"status\": \"ok\"`, stop immediately and display:\n ```\n Connectivity check failed. Please verify:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that BAPI_BASE_URL is set and the server is reachable\n - Check that BAPI_API_KEY is valid\n - Check that BAPI_REPO_NAME matches a configured repository\n ```\n\n## Stage 1 — Invoke the Packaged CLI\n\nUse the **Bash tool** to invoke exactly one CLI invocation:\n\n```\nnpx -y @bridge_gpt/mcp-server review-tickets [--auto] [--rounds=1|2] [--review KEY=auto,rounds=N ...] [--agent <name>] [--model <alias>] [--max-parallel N] [--dry-run] KEY [KEY ...]\n```\n\n- `review-tickets` runs all tabs from the current repository cwd — it creates no worktrees.\n- The command never runs `wt`, `git-wt`, or `git` — only the terminal launcher is required.\n- Prerequisites: macOS `osascript`, Windows `wt.exe` or PowerShell, Linux `tmux`.\n\nPass through the CLI's stdout and stderr verbatim. If the CLI exits non-zero, treat it as a critical failure and report the exit code and error output.\n\n## Stage 2 — Final Report\n\nOnce the CLI exits 0, parse its `Summary:` lines (each shaped like `KEY auto=<true|false> rounds=<1|2> agent=<agent> model=<alias|default> status=<status>`) and render as a markdown table:\n\n```\n| Ticket | Auto | Rounds | Agent | Model | Status |\n|----------|-------|--------|--------|---------|---------|\n| BAPI-1 | false | 2 | claude | default | spawned |\n| BAPI-2 | true | 1 | claude | default | spawned |\n```\n\nRender any CLI `Warnings:` lines below the table. If there were none, omit the warnings section.\n",
24
24
  "run-tests.md": "Run the project's full test suite (unit and E2E) using the project-configured test stacks, triage failures, fix test-code issues, and produce a structured health-check report.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command discovers how to run tests by reading per-project configuration from the Bridge API, not from hardcoded paths. Stages run only when the project has the corresponding stack configured.\n\n## Stage 0 — Argument Parsing and Setup\n\n1. **Parse `$ARGUMENTS`** for optional flags. Supported flags:\n - `--skip-e2e` — skip the E2E test stage even if an E2E stack is configured (e.g., when no local server is running)\n - `--unit-only` — shorthand that implies `--skip-e2e`\n\n Resolve flags to boolean variables:\n - Start with: `run_unit = true`, `run_e2e = true`\n - If `--unit-only` is present: set `run_e2e = false`\n - If `--skip-e2e` is present: set `run_e2e = false`\n - Unknown flags: note them in the final report as \"Unrecognized flag ignored\" but do not fail\n\n2. **Generate a run timestamp** using the current date and time in `YYYY-MM-DD-HH-MM` format (e.g., `2026-03-10-14-35`). Store this as `run_timestamp`. Both output documents will use this value.\n\nThis stage has no failure conditions — proceed to Stage 1.\n\n## Stage 1 — Resolve Project Config via MCP\n\nRead the per-project test setup from the Bridge database. Every subsequent stage is driven by what these calls return.\n\n1. **Resolve docs directory**: Call the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n2. **Read unit-test stack**: Call the `get_config_field` MCP tool with `field_name` set to `unit_testing_stack`. Store the returned value as `unit_stack` (may be null/empty).\n\n3. **Read unit-test instructions**: Call the `get_config_field` MCP tool with `field_name` set to `unit_testing_instructions`. Store the returned value as `unit_instructions` (may be null/empty).\n\n4. **Read E2E stack**: Call the `get_config_field` MCP tool with `field_name` set to `e2e_testing_stack`. Store as `e2e_stack`.\n\n5. **Read E2E instructions**: Call the `get_config_field` MCP tool with `field_name` set to `e2e_testing_instructions`. Store as `e2e_instructions`.\n\n6. **Compute configuration booleans**:\n - `unit_configured` = `true` if either `unit_stack` or `unit_instructions` is a non-empty string; otherwise `false`\n - `e2e_configured` = `true` if either `e2e_stack` or `e2e_instructions` is a non-empty string; otherwise `false`\n\n7. **Create the output directory**:\n ```\n mkdir -p {docs_dir}/testing/\n ```\n If this fails, stop immediately and report: `Cannot create output directory {docs_dir}/testing/ — check permissions.`\n\nIf any MCP call fails (e.g., the API is unreachable or returns 4xx/5xx), stop immediately and report which call failed. Do not fall back to hardcoded commands — the whole point of this command is that test setup lives in config.\n\n## Stage 2 — Unit / Standard Tests\n\nIf `run_unit` is `false`, skip this stage and record: `Unit tests: SKIPPED — run_unit was set to false (this should not happen in normal use; report as a bug).`\n\nIf `unit_configured` is `false`, skip and record:\n```\nUnit tests: SKIPPED — no unit_testing_stack or unit_testing_instructions configured for this repo. Configure via /learn-unit-testing or the project setup UI before running /run-tests.\n```\n\nOtherwise:\n\n1. Read `unit_instructions` carefully. It is the source of truth for **how to run unit tests in this repo** — runner binary, paths, environment activation, sub-suites (if the project distinguishes \"unit\" from \"integration\", both belong in this stage), and any flags. Pair it with `unit_stack` (a short label, e.g., `Pytest`, `Jest + React Testing Library`) for context.\n\n2. **Derive the test command(s)**: Extract the literal shell commands the instructions describe. If the instructions describe multiple sub-suites (e.g., a fast unit batch and a slower integration batch), plan to run each as a **separate batch** in the order described. Do not invent runners or paths that the instructions do not mention.\n\n3. **If the instructions do not specify any runnable command**, skip and record:\n ```\n Unit tests: SKIPPED — unit_testing_instructions does not describe how to invoke tests; please update via /learn-unit-testing.\n ```\n\n4. **Run each batch sequentially** in the terminal. **Continue to the next batch even if the current one has failures.** Capture the full output of each batch, including the runner's summary line (e.g., `47 passed, 3 failed in 12.4s` or `Tests: 5 failed, 22 passed`).\n\n5. For each failing test, apply the **Triage Logic** (below), then record the result.\n\n## Stage 3 — E2E Tests\n\nIf `run_e2e` is `false`, skip this stage and record: `E2E tests: SKIPPED — --skip-e2e or --unit-only flag was set.`\n\nIf `e2e_configured` is `false`, skip and record:\n```\nE2E tests: SKIPPED — no e2e_testing_stack or e2e_testing_instructions configured (the project may not have an E2E suite).\n```\n\nOtherwise:\n\n1. Read `e2e_instructions`. It is the source of truth for the E2E runner, spec paths, browser config, and any prerequisites. Pair with `e2e_stack` for context.\n\n2. **Detect server prerequisites**: If `e2e_instructions` indicates that a local server must be running (look for explicit cues such as \"server\", \"running\", \"localhost\", \"started\", \"dev server\", a URL, or a port number) and describes a readiness check, perform that check exactly as described. If the instructions describe a server prerequisite but do not describe a check, attempt the check the instructions imply (e.g., curl the URL the instructions mention) and skip the stage if it fails:\n ```\n E2E tests: SKIPPED — e2e_testing_instructions describe a server prerequisite that wasn't met. Start the server per the instructions and re-run.\n ```\n\n3. **Derive the test command(s)** from the instructions, including any spec-directory batching the instructions specify.\n\n4. **If the instructions do not specify any runnable command**, skip and record:\n ```\n E2E tests: SKIPPED — e2e_testing_instructions does not describe how to invoke tests; please update via /learn-e2e-testing.\n ```\n\n5. **Run each batch sequentially** in the terminal. **Continue to the next batch even if the current one has failures.** Capture the full output and summary line of each batch.\n\n6. For each failing test, apply the **Triage Logic** (below), then record the result.\n\n## Triage Logic\n\nFor every failing test, examine the test file and the code it tests. Classify as ONE of the following:\n\n### TEST-CODE ISSUE — fix it directly\n\nClassify as a test-code issue if ANY of the following applies:\n- The test asserts against a hardcoded value that no longer matches current behavior (outdated mock data)\n- The test imports or calls a function that was renamed, moved, or removed\n- The test asserts on a response field that was restructured\n- The test expects a specific error message string that has since changed\n- A fixture references a removed table column, model field, or schema member\n\n**Action**: Apply a minimal, targeted fix to the test file only. Then re-run just that failing test, using the runner described in the relevant instructions field (`unit_instructions` for unit-test failures, `e2e_instructions` for E2E failures). Adapt the runner invocation that the instructions provide to target a single test, following whatever convention the instructions or stack idiomatically use.\n\nIf the re-run **still fails** after your fix, do not make further edits — escalate to implementation-code issue instead and revert your change.\n\n### IMPLEMENTATION-CODE ISSUE (or UNCERTAIN) — document, do not fix\n\nClassify as an implementation issue if ANY of the following applies:\n- The production function raises an unexpected exception\n- A handler returns the wrong status code or response shape for a documented behavior\n- Business logic produces incorrect output that the test correctly asserts against\n- You are not confident the test is wrong\n\n**Action**: Do NOT modify any file outside the test directories described in `unit_testing_instructions` / `e2e_testing_instructions`. When in doubt about whether a path is test-only, treat it as production code and escalate. Record the failure in the implementation-issues document for the user to triage.\n\n## Stage 4 — Write Output Documents\n\n### Document 1: Test Run Report (always write this)\n\nWrite to: `{docs_dir}/testing/test-run-{run_timestamp}.md`\n\n```markdown\n# Test Run: {run_timestamp}\n\n## Configuration\n- Unit stack: {unit_stack or \"not configured\"}\n- E2E stack: {e2e_stack or \"not configured\"}\n- Unit tests: RUN | SKIPPED — (reason)\n- E2E tests: RUN | SKIPPED — (reason)\n\n## Unit Tests\n**Stack**: {unit_stack or \"not configured\"}\n**Result**: X passed, Y failed (sum across batches)\n\n### Batch 1: `<command>`\n**Result**: X passed, Y failed\n**Fixes applied**:\n- `path/to/test_file`: brief description of what was fixed\n- (or \"none\" if no fixes were needed)\n\n### Batch 2: `<command>`\n...\n\n**Failures escalated as implementation issues**: N\n\n## E2E Tests\n**Stack**: {e2e_stack or \"not configured\"}\n**Result**: X passed, Y failed (sum across batches)\n\n### Batch 1: `<command>`\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### Batch 2: `<command>`\n...\n\n**Failures escalated as implementation issues**: N\n\n## Overall Summary\n- Total test fixes applied: N\n- Suspected implementation issues found: N\n- Implementation issues document: {docs_dir}/testing/implementation-issues-{run_timestamp}.md\n (or \"not created — no issues found\")\n```\n\n### Document 2: Implementation Issues (only write if issues were found)\n\nIf at least one failure was escalated as an implementation-code issue, write to:\n`{docs_dir}/testing/implementation-issues-{run_timestamp}.md`\n\n```markdown\n# Suspected Implementation Issues: {run_timestamp}\n\nThese test failures were NOT fixed. They may indicate bugs in production code.\nA developer should investigate each item before merging.\n\n## Issue 1\n- **Test**: `path/to/test_file::test_function_name`\n- **Tier**: unit | e2e\n- **Failure message**: (paste the key assertion or exception line)\n- **Why not fixed**: (brief reasoning, e.g., \"production function raises KeyError on valid input\")\n\n## Issue 2\n...\n```\n\nIf no implementation issues were found, do NOT create this file.\n\n## Final Output\n\nAfter writing all documents, print this summary:\n\n```\nTest run complete: {run_timestamp}\nReport saved to: {docs_dir}/testing/test-run-{run_timestamp}.md\nImplementation issues: {docs_dir}/testing/implementation-issues-{run_timestamp}.md (if applicable)\nNo suspected implementation issues found. (if none)\n```\n",
25
25
  "scan-tickets.md": "$ARGUMENTS\n\n---\n\n# Instructions\n\nSynchronize recently-updated Jira tickets with the local `tickets` database table and backfill missing workflow state timestamps. Perform all work directly in the main thread.\n\n## Stage 0 — Parse Arguments and Calculate Date\n\n1. Read the value of `$ARGUMENTS`. If it is empty, whitespace-only, or not a valid integer, default `months_back` to `3`. If it contains multiple tokens, extract only the first token and attempt to parse it as an integer. If parsing fails, default to `3`.\n\n2. Calculate `updated_since` by subtracting `months_back` months from today's date. Format the result as `YYYY-MM-DD`. Example: if today is 2026-03-07 and `months_back` is 3, then `updated_since` is 2025-12-07.\n\n3. Display the parsed values: \"Scanning tickets updated since {updated_since} (months_back = {months_back})\"\n\n4. Initialize the following tracking variables:\n - `tickets_scanned` = 0 (total tickets fetched from Jira)\n - `newly_tracked` = 0 (tickets inserted into database for the first time)\n - `state_updated_list` = [] (list of objects with ticket key and fields updated)\n - `warnings` = [] (list of warning strings for any per-ticket failures)\n\n## Stage 1 — Fetch All Tickets from Jira\n\n1. Initialize an empty list `all_tickets` and set `offset` to `0`.\n\n2. Enter a pagination loop:\n - Call the `get_tickets` MCP tool with: `updated_since` set to the calculated date, `limit` set to `100`, and `offset` set to the current offset value.\n - Parse the JSON response. The response contains a `tickets` array of ticket objects. Each ticket object has a `ticket_number` field (the Jira key, e.g., `BAPI-42`), along with `summary`, `status`, `issue_type`, `assignee`, and `updated_at`.\n - Append all tickets from the response's `tickets` array to `all_tickets`.\n - If the number of tickets returned in this page equals `100`, increment `offset` by `100` and repeat the loop.\n - If fewer than `100` tickets are returned, exit the loop.\n\n3. Set `tickets_scanned` to the length of `all_tickets`.\n\n4. Display: \"Fetched {tickets_scanned} tickets from Jira. Processing...\"\n\n5. If the `get_tickets` call fails at any point during pagination, **stop** and report the error. Do not proceed to Stage 2.\n\n## Stage 2 — Track Each Ticket\n\n1. Iterate over each ticket in `all_tickets`. For each ticket:\n - Call the `track_ticket` MCP tool with `ticket_number` set to the ticket's `ticket_number` field. If the ticket object includes a `summary` field, pass it as the `description` parameter.\n - Inspect the response message. If the response indicates the ticket was newly created/inserted (look for words like \"created\" or \"inserted\" in the message, as opposed to \"already exists\" or \"updated\"), increment `newly_tracked` by 1.\n - If the `track_ticket` call fails for this ticket, add a warning to the `warnings` list (e.g., \"Warning: Failed to track ticket {ticket_number}: {error}\") and **continue** to the next ticket. Do not abort the scan.\n\n2. Display a brief progress indicator every 25 tickets, e.g., \"Tracked {N} of {tickets_scanned} tickets...\"\n\n## Stage 3 — Detect and Backfill Workflow State\n\nDisplay: \"Checking workflow state for {tickets_scanned} tickets...\"\n\nIterate over each ticket in `all_tickets`. For each ticket (referenced by its `ticket_number` field), perform the following sub-steps. Wrap the entire per-ticket block in error handling: if the `get_ticket_state` call or the subsequent `update_ticket_state` call fails for a ticket, add a warning to `warnings` and continue to the next ticket.\n\n**Sub-step 4a — Retrieve current state**: Call the `get_ticket_state` MCP tool with `ticket_number` set to the ticket's key. The response contains:\n\n- Five timestamp fields (each is a timestamp string or null): `clarify_called`, `clarify_answered`, `critique_called`, `critique_answered`, `plan_generated`\n- Three boolean artifact flags: `has_clarifying_questions`, `has_critique`, `has_plan`\n\nIf the call returns a 404 or any error, add a warning to `warnings` and continue to the next ticket.\n\n**Sub-step 4b — Build fields_to_update list**: Initialize an empty `fields_to_update` list, then apply the following rules:\n\n- If `has_clarifying_questions` is `true` AND `clarify_called` is null -> add `\"clarify_called\"` to `fields_to_update`\n- If `has_clarifying_questions` is `true` AND `clarify_answered` is null -> add `\"clarify_answered\"` to `fields_to_update`\n- If `has_critique` is `true` AND `critique_called` is null -> add `\"critique_called\"` to `fields_to_update`\n- If `has_critique` is `true` AND `critique_answered` is null -> add `\"critique_answered\"` to `fields_to_update`\n- If `has_plan` is `true` AND `plan_generated` is null -> add `\"plan_generated\"` to `fields_to_update`\n\n**Sub-step 4c — Call update_ticket_state if needed**: If `fields_to_update` is non-empty, call the `update_ticket_state` MCP tool with `ticket_number` set to the ticket's key and `fields` set to the `fields_to_update` array. If this succeeds, add an entry to `state_updated_list` recording the ticket key and the list of fields that were set. If `update_ticket_state` fails, add a warning to `warnings` and continue.\n\nDisplay a progress indicator every 25 tickets that includes the current ticket key, e.g., \"Checked state for {TICKET-KEY} ({N} of {tickets_scanned} tickets)\"\n\n## Stage 4 — Report Summary\n\n1. Calculate `state_updated_count` as the length of `state_updated_list`.\n\n2. Display the summary:\n\n ```\n **Scan complete**\n\n * Tickets scanned: {tickets_scanned}\n * Newly tracked: {newly_tracked}\n * State updated: {state_updated_count}\n ```\n\n3. If `state_updated_list` is non-empty, display a section titled \"Updated tickets:\" with one bullet per ticket showing the ticket key and the comma-separated list of fields that were set. Example:\n\n ```\n Updated tickets:\n * BAPI-101: clarify_called, clarify_answered\n * BAPI-105: critique_called, critique_answered, plan_generated\n ```\n\n4. If the `warnings` list is non-empty, display a section titled \"Warnings:\" listing each warning string as a bullet. Example:\n\n ```\n Warnings:\n * Warning: Failed to track ticket BAPI-99: Connection timeout\n * Warning: State query failed for BAPI-112: SQL error\n ```\n\n5. If there are no warnings, do not display the \"Warnings:\" section.\n",
26
- "start-tickets.md": "---\nschedulable: true\narguments: {\"positionals\":[{\"name\":\"ticketKeys\",\"type\":\"string\",\"required\":true,\"variadic\":true}],\"flags\":[{\"name\":\"auto\",\"flag\":\"--auto\",\"type\":\"boolean\"},{\"name\":\"agent\",\"flag\":\"--agent\",\"type\":\"string\"},{\"name\":\"baseBranch\",\"flag\":\"--base-branch\",\"type\":\"string\"},{\"name\":\"maxParallel\",\"flag\":\"--max-parallel\",\"type\":\"string\"},{\"name\":\"dryRun\",\"flag\":\"--dry-run\",\"type\":\"boolean\"}]}\n---\n\n# Start Tickets: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes one or more Jira ticket keys (e.g., `BAPI-248 BAPI-250`) and invokes the packaged `@bridge_gpt/mcp-server` CLI subcommand `start-tickets`, which creates a Worktrunk worktree for each key and opens one tab/session per worktree running the **selected agent** — Claude Code (`claude`) by default, or Cursor Agent (`cursor-agent`) via `--agent` — in a macOS Terminal/iTerm tab, a Windows Terminal tab (or PowerShell fallback window), or a detached Linux tmux session, chosen automatically by platform. It replaces Parts 2–5 of `docs/claude/parallel-worktrees.md` with a single command.\n\nBecause the orchestration ships inside the `@bridge_gpt/mcp-server` npm package (not a repo-local script), this command works for every consumer — including projects that installed the package via `--init`.\n\nStage 0 and Stage 1 are critical (stop on failure). Stage 2 is non-critical (per-ticket enrichment failures fall back to the default branch and continue). Stage 3 is critical (propagate the packaged CLI's exit code).\n\n---\n\n# Instructions\n\nYou are executing a 4-stage pipeline that spawns N parallel Worktrunk worktrees and selected-agent sessions (Claude Code by default) via the packaged CLI. Execute all stages in sequence directly in the main thread.\n\n## Stage 0 — Argument Parsing and Connectivity Check\n\n1. **Parse `$ARGUMENTS`** into ticket keys, pass-through flags, and branch overrides:\n - **Ticket keys**: every whitespace-separated token matching `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). If zero keys are found, stop immediately and display:\n\n ```\n No ticket keys found in arguments. Expected one or more keys like BAPI-248.\n Usage: /start-tickets [flags] <KEY> [KEY ...] (e.g., /start-tickets BAPI-248 BAPI-250)\n ```\n\n - **Pass-through flags**: collect any of `--agent <name>` (and the equals form `--agent=<name>`), `--terminal terminal|iterm`, `--dry-run`, `--auto`, `--no-refresh-main`, `--base-branch <branch>` (and the equals form `--base-branch=<branch>`), and `--max-parallel N` that the user supplied. These are forwarded verbatim to the CLI in Stage 3. `--auto` makes each spawned agent run `/implement-ticket <KEY> --auto` (hands-off implementation); omit it to keep the implementation agents interactive.\n - **Selected agent**: track a `selected_agent` variable that defaults to `claude`. If the user passed `--agent <name>` / `--agent=<name>`, validate the value against the supported agents `claude` and `cursor-agent`, set `selected_agent` to it, and reject any other (malformed/unsupported) `--agent` value before proceeding. The agent is not auto-detected from the host editor — the user selects it explicitly (default `claude`).\n - **User-supplied base branch**: track a `user_supplied_base_branch` boolean that defaults to `false`. If the user passed `--base-branch <branch>` or `--base-branch=<branch>`, set the boolean to `true` and capture the value. A user-supplied `--base-branch` value **takes precedence** over any value resolved from Bridge API config in Stage 2. Validate the user-supplied value before proceeding: after trimming surrounding whitespace it must be non-empty, at most 255 characters, must not start with `-`, and must not contain ASCII control characters (`0x00`–`0x1F` or `0x7F`); reject any malformed value with a clear error.\n - **User branch overrides**: collect any user-supplied repeatable `--branch KEY=BRANCH` flags. A user-provided override always takes precedence over Stage 2 enrichment for that key.\n - Reject malformed input before proceeding: if a token looks like a flag but is not one of the supported flags, or a ticket key does not match `[A-Z]+-[0-9]+`, or a `--branch` value is not `KEY=BRANCH`, or `--agent` names an agent other than `claude`/`cursor-agent`, or `--base-branch` fails the validation rules above, stop and report the malformed argument.\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or does not return `\"status\": \"ok\"`, stop immediately and display:\n\n ```\n Connectivity check failed. Please verify:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that BAPI_BASE_URL is set and the server is reachable\n - Check that BAPI_API_KEY is valid\n - Check that BAPI_REPO_NAME matches a configured repository\n ```\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 1.\n\n## Stage 1 — Acknowledge CLI Pre-flight\n\nThe packaged CLI runs its own per-platform pre-flight checks and then fetches `origin` and fast-forwards the local **configured base branch** (the value resolved in Stage 2 below, or `main` when none is configured) from `origin/<base>` so the new worktrees are based on an up-to-date base. The historical flag `--no-refresh-main` still controls this behavior — the flag name is preserved for backward compatibility, but it now skips refresh of whatever base branch resolves (default `main`). The required commands depend on the OS:\n\n- **macOS**: `wt`, `git`, `osascript`.\n- **Windows**: `git-wt`, `git`, Git for Windows / Git Bash (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash), and Windows Terminal **or** PowerShell.\n- **Linux**: `wt`, `git`, `tmux`.\n\nOn **Windows** the Worktrunk binary is `git-wt` (its winget alias), which is a different tool from Windows Terminal's `wt.exe`: the CLI uses `git-wt` to **create worktrees** and `wt.exe` to **open a tab**, and never conflates the two. On **Linux** the CLI opens one detached `tmux` session per ticket (a window is added if that ticket's session already exists); attach later with `tmux attach -t <session>`. An unsupported OS (not macOS/Windows/Linux) fails fast with a clear \"unsupported platform\" message.\n\nThis stage simply notes that the CLI will fail fast if any prerequisite is missing or if local `main` has diverged from `origin/main` — you do not need to verify anything separately here, and you must not run any pre-flight commands yourself. When the CLI's pre-flight fails it now hints the user to run the read-only diagnostics command `npx -y @bridge_gpt/mcp-server doctor`, which reports found/missing for every prerequisite on the current OS — the pre-flight set plus `uv` plus the selected agent's command — and prints the manual install command for each missing one. `doctor` is strictly read-only and never installs anything; never run install commands automatically on the user's behalf. The CLI does not call any Bridge API tools; all credential-bearing work (branch enrichment in Stage 2) stays in this command. Proceed to Stage 2.\n\nThe packaged CLI also performs **secret-free Bridge API MCP provisioning** inside each created worktree: synchronously after the worktree is created and **before the agent tab/session is opened**, it writes both `.mcp.json` (Claude Code) and `.cursor/mcp.json` (Cursor) pointing at the `mcp-invoke` shim. These registrations are **secret-free** — they contain no `env` block and no API key, because the shim resolves credentials at runtime. If a spawned agent (or difficulty→model routing) reports missing Bridge API credentials, fix it by rerunning `/install-bridge` (its final stage persists the routing credential), by running `npx -y @bridge_gpt/mcp-server credentials migrate-agent-config --write-credentials` to migrate a key that lives only in `.mcp.json` / `.cursor/mcp.json`, or by adding a `bapi:<repo>` entry to the user-scoped credentials file (`~/.config/bridge/credentials.json`) — never by putting `BAPI_API_KEY` into the worktree `.mcp.json` or `.cursor/mcp.json` (that env is invisible to the Bash-spawned CLI).\n\nThis stage is **critical** in the sense that the CLI will abort if its pre-flight fails; you will see the error in Stage 3's output and must surface it.\n\n## Stage 2 — Resolve Base Branch + Enrich Branch Names (best-effort)\n\n### Stage 2a — Resolve configured `base_branch`\n\nThe CLI must be told which branch to cut new worktrees from. Resolution order:\n\n1. If `user_supplied_base_branch` from Stage 0 is `true`, **skip the config-field lookup entirely** and use the user-supplied value. The user's explicit `--base-branch` always wins; never call `get_config_field` for `base_branch` in that case.\n2. Otherwise, call the `get_config_field` MCP tool with `field_name` set to `base_branch` (do not pass any other parameters; the tool resolves the repository from the MCP server's configured `BAPI_REPO_NAME`).\n3. Parse the response. Treat the result as the **configured base branch** only when the response is a JSON object whose `value` field is a non-empty string after trimming surrounding whitespace.\n4. Treat **all** of the following as \"unset\" — emit a single-line warning like `Warning: base_branch is unset; CLI will default to main` and **omit** the `--base-branch` flag entirely from the Stage 3 command (the CLI's own default is `main`):\n - `value` is `null`.\n - `value` is an empty string or a whitespace-only string.\n - The endpoint returns HTTP `400` (invalid field — happens before the registry includes `base_branch`).\n - The tool returns a network error, timeout, or non-JSON parse failure.\n - Any other lookup failure.\n5. When the configured value is usable, capture it in a `resolved_base_branch` variable. **Do not** stop the pipeline on a lookup failure; fall through to the CLI default.\n\nWhen forwarding `resolved_base_branch` into the Bash invocation in Stage 3, **shell-escape it safely**: replace every literal single quote `'` in the value with the four-character sequence `'\\''`, then wrap the entire resulting string in single quotes (so the final argument looks like `'<escaped-value>'`). This is the standard POSIX single-quote escaping rule and is **mandatory** because `base_branch` is admin-configurable data that gets interpolated into a Bash command string; any unescaped single quote would otherwise break out of the surrounding quotes. Pass `--base-branch '<escaped-value>'` to the CLI as a single argv element — never expand the value unquoted into the command line.\n\n### Stage 2b — Enrich Branch Names\n\nBranch enrichment happens here, in the command, **before** invoking the CLI — the `get_ticket` MCP tool runs inside the MCP server process, which holds the Bridge API credentials the shell-spawned CLI does not have. For each parsed ticket key that does **not** already have a user-provided `--branch` override:\n\n1. Call the `get_ticket` MCP tool with `ticket_number` set to the key and `save_locally` set to `false`.\n2. From the response, extract the `summary` field. Slugify it: lowercase the string, replace every run of non-alphanumeric characters (`[^a-z0-9]+`) with a single dash `-`, trim leading and trailing dashes, and truncate to at most `40` characters (cutting at a dash boundary if possible).\n3. The enriched branch name is `feature/<KEY>-<slug>`. Example: `BAPI-248` with summary `\"Add PR rating pre-evaluation step\"` becomes `feature/BAPI-248-add-pr-rating-pre-evaluation-step` (trimmed at 40 chars).\n4. If the `get_ticket` call fails for a particular key (404, network error, missing summary) or produces an empty slug, emit a single-line warning like `Warning: could not enrich BAPI-248, falling back to feature/BAPI-248` and let the CLI apply its default `feature/<KEY>` for that key only. Do NOT stop the pipeline.\n5. Build a list of `--branch <KEY>=<BRANCH>` arguments — one entry per key whose enrichment succeeded — and merge it with any user-provided overrides from Stage 0. **Do not** call `get_ticket` for keys that already have a user-provided override; those overrides win.\n\nThis stage is **non-critical** — warnings are acceptable, the pipeline continues with the fallback default for any key that fails. Do not call the Bridge API from the CLI itself; the CLI never has credentials.\n\n## Stage 3 — Invoke the Packaged CLI\n\nUse the **Bash tool** to invoke the packaged CLI. Build the command line as:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets <pass-through-flags> <base-branch-flag> <branch-overrides> <ticket-keys>\n```\n\nWhere:\n- `<pass-through-flags>` are the supported flags collected in Stage 0 (`--agent`, `--terminal`, `--dry-run`, `--auto`, `--no-refresh-main`, `--max-parallel`), forwarded verbatim. Forward `--agent <name>` only if the user supplied it; otherwise omit it and the CLI defaults to `claude`. Forward `--auto` only if the user supplied it.\n- `<base-branch-flag>` is `--base-branch '<escaped-value>'` (single-quoted using the Stage 2a escaping rule) **only when** the user supplied `--base-branch` in Stage 0 **or** Stage 2a's `get_config_field` lookup returned a non-empty configured value. When the configured value is unset / lookup fails / user did not supply one, **omit this flag entirely** so the CLI's own default (`main`) takes effect.\n- `<branch-overrides>` is the list of `--branch KEY=BRANCH` flags assembled in Stage 2 (enrichment results merged with user overrides; omit any key whose enrichment failed and had no user override).\n- `<ticket-keys>` is the original list of ticket keys parsed in Stage 0, space-separated and in the original order.\n\nExample for two tickets after successful enrichment, throttled to 2 concurrent worktrees:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets \\\n --max-parallel 2 \\\n --branch BAPI-248=feature/BAPI-248-add-pr-rating-pre-evaluation-step \\\n --branch BAPI-250=feature/BAPI-250-deep-research-durability \\\n BAPI-248 BAPI-250\n```\n\nExample launching Cursor Agent instead of the default Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\nExample cutting worktrees from a non-`main` base (either user-supplied via `--base-branch develop` in Stage 0 or resolved from Bridge API config in Stage 2a):\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --base-branch develop BAPI-248\n```\n\nPass through the CLI's stdout and stderr to the user verbatim. If the CLI exits non-zero, treat that as a critical failure: report the exit code and the CLI's error output, and stop.\n\nThis stage is **critical** — propagate any non-zero exit from the packaged CLI.\n\n## Stage 4 — Final Report\n\nOnce the CLI exits 0, parse its `Summary` section (one stable line per ticket in the form `KEY branch=BRANCH status=STATUS`, with an optional trailing `path=PATH`) and reformat it as a markdown table:\n\n```\n| Ticket | Branch | Status |\n|----------|-----------------------------------------------------|----------|\n| BAPI-248 | feature/BAPI-248-add-pr-rating-pre-evaluation-step | spawned |\n| BAPI-250 | feature/BAPI-250-deep-research-durability | spawned |\n```\n\nStatus values are `dry-run`, `spawned`, `create-failed`, and `spawn-failed`. End the report with the worktree-first explanation, rendered for the tracked `selected_agent`. When `selected_agent` is `claude` (the default):\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`claude '/implement-ticket <KEY>'` inside its already-created worktree, which launches\nClaude Code with the starter prompt as its first message. Switch to each tab — or on\nLinux run `tmux attach -t <session>` — to monitor.\n```\n\nWhen `selected_agent` is `cursor-agent`, render the same explanation but with the Cursor handoff — do **not** claim it launches Claude Code:\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`cursor-agent '/implement-ticket <KEY>'` inside its already-created worktree, which\nlaunches Cursor Agent with the starter prompt as its first message. Switch to each\ntab — or on Linux run `tmux attach -t <session>` — to monitor.\n```\n\nThe `/implement-ticket <KEY>` prompt is identical for both agents; only the launched command differs. When start-tickets was invoked with `--auto`, the spawned prompt is `/implement-ticket <KEY> --auto` (the implementation pipeline runs hands-off, without approval gates).\n\nIf the CLI reported any `create-failed` or `spawn-failed` statuses, or Stage 2 emitted any enrichment warnings, list them under a `Warnings:` heading at the bottom of the report. If there were none, omit that section.\n\nSee `docs/claude/parallel-worktrees.md` for the deep-dive runbook and the Worktrunk verification result behind this worktree-first model.\n\n## Difficulty-Based Implementation-Model Routing\n\nBefore launching the interactive agent for each ticket, the packaged CLI selects an\nimplementation **model tier** from the ticket's `difficulty` rating (1-10) and injects\nit as a `--model` flag at the agent spawn boundary. This happens entirely inside the\nCLI — it is **not** part of the server-side `/implement-ticket` recipe, because the\nmodel an interactive agent session uses is fixed at the moment the process is launched.\n\n- **Tier ladder (fixed):** `difficulty 1-2 → cheap`, `3-5 → basic`, `6+ → premium`.\n- **Separation of concerns:** the Python backend returns only the coarse tier\n (`cheap`/`basic`/`premium`) via `GET /jira/tickets/{KEY}/model-tier`; difficulty is\n computed on demand and cached when absent. The TypeScript CLI alone maps a tier to\n the agent-specific model alias (`claude`: `haiku`/`sonnet`/`opus`; `cursor-agent`:\n version-suffixed strings validated against `cursor-agent --list-models`).\n- **Per-repo config:**\n - `difficulty_model_routing_enabled` — boolean, **default ON**. Set to `false` to\n disable routing for a repo (the CLI then omits `--model`).\n - `difficulty_model_tier_overrides` — a JSON object mapping a tier name to a model\n alias (e.g. `{\"premium\": \"opus\"}`), **not** raw CLI arguments. Only `cheap`,\n `basic`, and `premium` keys are accepted; aliases must match `^[A-Za-z0-9._:-]+$`.\n- **Fail-open:** routing never aborts a spawn. Credential, network, config, or\n no-tier routing failures **assume a hard ticket and default to the premium/Opus\n tier** when the selected agent supports a valid premium alias; routing being\n disabled (`difficulty_model_routing_enabled = false`) or an agent that does not\n support `--model` instead omit `--model` so the agent runs on its own default\n model. Each degraded case is surfaced as exactly one secret-free, per-ticket\n routing-diagnostic line, never a hard failure.\n\n### Model routing credential\n\nDifficulty→model routing needs Bridge API credentials, and the shell-spawned\n`start-tickets` CLI is a **different runtime surface** from the MCP server: a\n`BAPI_API_KEY` that lives only in `.mcp.json` / `.cursor/mcp.json` is visible to\nthe MCP server but **not** to the Bash-spawned CLI, so routing silently degrades.\nThe durable source of truth both runtimes can resolve is the user-scoped store\n`~/.config/bridge/credentials.json`, keyed `bapi:<repo>`. If a routing-diagnostic\nline reports the credential is missing (e.g. difficulty resolves as `?`), fix it\nby any one of:\n\n1. Rerun `/install-bridge` — its final stage now persists the validated routing\n credential into `~/.config/bridge/credentials.json` via the\n `persist_routing_credential` tool.\n2. Migrate a key that lives **only** in `.mcp.json` / `.cursor/mcp.json` into the\n user-scoped store with the consent-gated, one-shot command:\n\n ```\n npx -y @bridge_gpt/mcp-server credentials migrate-agent-config --write-credentials\n ```\n\n3. Manually add `BAPI_API_KEY` under the `bapi:<repo>` target in the user-scoped\n store `~/.config/bridge/credentials.json`.\n\nNever put `BAPI_API_KEY` into a worktree `.mcp.json` / `.cursor/mcp.json` as a fix —\nthat env is invisible to the spawned CLI.\n\n## Conductor observability (BAPI-394)\n\nA real `start-tickets` run mints a single conductor `run_id` and attributes each\nworker's lifecycle events by `worker_id`, ticket key, and worktree path. When the\nselected agent is **Claude Code**, the CLI injects a conductor lifecycle hook into\neach created worktree's `.claude/settings.local.json` so the spawned session emits\nlocal `run.started` / `run.stopped` / `agent.notification` (and, when\n`BAPI_CONDUCTOR_ENABLE_PRE_TOOL_USE=1`, `tool.intent`) events into the local\nconductor ledger. These hooks apply **only** when the selected agent is Claude\nCode; other agents (e.g. `cursor-agent`) still participate in the run-level\n`run.started` event but receive no per-worktree Claude hook. Inspect the ledger\nwith the `conductor` CLI (e.g. `conductor doctor`). Conductor observability is\nbest-effort and never blocks or aborts a spawn.\n\nWhen the selected agent is **Claude Code**, each worker is also launched with an\nexplicit instruction to call the `check_messages` MCP tool at checkpoints, so the\nsupervisor can pass it typed guidance mid-run (BAPI-397). Delivery is\n**cooperative** — the worker polls and acknowledges messages and they are never\ninjected into a running session.\n",
26
+ "start-tickets.md": "---\nschedulable: true\narguments: {\"positionals\":[{\"name\":\"ticketKeys\",\"type\":\"string\",\"required\":true,\"variadic\":true}],\"flags\":[{\"name\":\"auto\",\"flag\":\"--auto\",\"type\":\"boolean\"},{\"name\":\"agent\",\"flag\":\"--agent\",\"type\":\"string\"},{\"name\":\"baseBranch\",\"flag\":\"--base-branch\",\"type\":\"string\"},{\"name\":\"maxParallel\",\"flag\":\"--max-parallel\",\"type\":\"string\"},{\"name\":\"dryRun\",\"flag\":\"--dry-run\",\"type\":\"boolean\"}]}\n---\n\n# Start Tickets: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes one or more Jira ticket keys (e.g., `BAPI-248 BAPI-250`) and invokes the packaged `@bridge_gpt/mcp-server` CLI subcommand `start-tickets`, which creates a Worktrunk worktree for each key and opens one tab/session per worktree running the **selected agent** — Claude Code (`claude`) by default, or Cursor Agent (`cursor-agent`) via `--agent` — in a macOS Terminal/iTerm tab, a Windows Terminal tab (or PowerShell fallback window), or a detached Linux tmux session, chosen automatically by platform. It replaces Parts 2–5 of `docs/claude/parallel-worktrees.md` with a single command.\n\nBecause the orchestration ships inside the `@bridge_gpt/mcp-server` npm package (not a repo-local script), this command works for every consumer — including projects that installed the package via `--init`.\n\nStage 0 and Stage 1 are critical (stop on failure). Stage 2 is non-critical (per-ticket enrichment failures fall back to the default branch and continue). Stage 3 is critical (propagate the packaged CLI's exit code).\n\n---\n\n# Instructions\n\nYou are executing a 4-stage pipeline that spawns N parallel Worktrunk worktrees and selected-agent sessions (Claude Code by default) via the packaged CLI. Execute all stages in sequence directly in the main thread.\n\n## Stage 0 — Argument Parsing and Connectivity Check\n\n1. **Parse `$ARGUMENTS`** into ticket keys, pass-through flags, and branch overrides:\n - **Ticket keys**: every whitespace-separated token matching `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). If zero keys are found, stop immediately and display:\n\n ```\n No ticket keys found in arguments. Expected one or more keys like BAPI-248.\n Usage: /start-tickets [flags] <KEY> [KEY ...] (e.g., /start-tickets BAPI-248 BAPI-250)\n ```\n\n - **Pass-through flags**: collect any of `--agent <name>` (and the equals form `--agent=<name>`), `--terminal terminal|iterm`, `--dry-run`, `--auto`, `--no-refresh-main`, `--base-branch <branch>` (and the equals form `--base-branch=<branch>`), and `--max-parallel N` that the user supplied. These are forwarded verbatim to the CLI in Stage 3. `--auto` makes each spawned agent run `/implement-ticket <KEY> --auto` (hands-off implementation); omit it to keep the implementation agents interactive.\n - **Selected agent**: track a `selected_agent` variable that defaults to `claude`. If the user passed `--agent <name>` / `--agent=<name>`, validate the value against the supported agents `claude` and `cursor-agent`, set `selected_agent` to it, and reject any other (malformed/unsupported) `--agent` value before proceeding. The agent is not auto-detected from the host editor — the user selects it explicitly (default `claude`).\n - **User-supplied base branch**: track a `user_supplied_base_branch` boolean that defaults to `false`. If the user passed `--base-branch <branch>` or `--base-branch=<branch>`, set the boolean to `true` and capture the value. A user-supplied `--base-branch` value **takes precedence** over any value resolved from Bridge API config in Stage 2. Validate the user-supplied value before proceeding: after trimming surrounding whitespace it must be non-empty, at most 255 characters, must not start with `-`, and must not contain ASCII control characters (`0x00`–`0x1F` or `0x7F`); reject any malformed value with a clear error.\n - **User branch overrides**: collect any user-supplied repeatable `--branch KEY=BRANCH` flags. A user-provided override always takes precedence over Stage 2 enrichment for that key.\n - Reject malformed input before proceeding: if a token looks like a flag but is not one of the supported flags, or a ticket key does not match `[A-Z]+-[0-9]+`, or a `--branch` value is not `KEY=BRANCH`, or `--agent` names an agent other than `claude`/`cursor-agent`, or `--base-branch` fails the validation rules above, stop and report the malformed argument.\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or does not return `\"status\": \"ok\"`, stop immediately and display:\n\n ```\n Connectivity check failed. Please verify:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that BAPI_BASE_URL is set and the server is reachable\n - Check that BAPI_API_KEY is valid\n - Check that BAPI_REPO_NAME matches a configured repository\n ```\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 1.\n\n## Stage 1 — Acknowledge CLI Pre-flight\n\nThe packaged CLI runs its own per-platform pre-flight checks and then fetches `origin` and fast-forwards the local **configured base branch** (the value resolved in Stage 2 below, or `main` when none is configured) from `origin/<base>` so the new worktrees are based on an up-to-date base. The historical flag `--no-refresh-main` still controls this behavior — the flag name is preserved for backward compatibility, but it now skips refresh of whatever base branch resolves (default `main`). The required commands depend on the OS:\n\n- **macOS**: `wt`, `git`, `osascript`.\n- **Windows**: `git-wt`, `git`, Git for Windows / Git Bash (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash), and Windows Terminal **or** PowerShell.\n- **Linux**: `wt`, `git`, `tmux`.\n\nOn **Windows** the Worktrunk binary is `git-wt` (its winget alias), which is a different tool from Windows Terminal's `wt.exe`: the CLI uses `git-wt` to **create worktrees** and `wt.exe` to **open a tab**, and never conflates the two. On **Linux** the CLI opens one detached `tmux` session per ticket (a window is added if that ticket's session already exists); attach later with `tmux attach -t <session>`. An unsupported OS (not macOS/Windows/Linux) fails fast with a clear \"unsupported platform\" message.\n\nThis stage simply notes that the CLI will fail fast if any prerequisite is missing or if local `main` has diverged from `origin/main` — you do not need to verify anything separately here, and you must not run any pre-flight commands yourself. When the CLI's pre-flight fails it now hints the user to run the read-only diagnostics command `npx -y @bridge_gpt/mcp-server doctor`, which reports found/missing for every prerequisite on the current OS — the pre-flight set plus `uv` plus the selected agent's command — and prints the manual install command for each missing one. `doctor` is strictly read-only and never installs anything; never run install commands automatically on the user's behalf. The CLI does not call any Bridge API tools; all credential-bearing work (branch enrichment in Stage 2) stays in this command. Proceed to Stage 2.\n\nThe packaged CLI also performs **secret-free Bridge API MCP provisioning** inside each created worktree: synchronously after the worktree is created and **before the agent tab/session is opened**, it writes both `.mcp.json` (Claude Code) and `.cursor/mcp.json` (Cursor) pointing at the `mcp-invoke` shim. These registrations are **secret-free** — they contain no `env` block and no API key, because the shim resolves credentials at runtime. If a spawned agent (or difficulty→model routing) reports missing Bridge API credentials, fix it by rerunning `/install-bridge` (its final stage persists the routing credential), by running `npx -y @bridge_gpt/mcp-server credentials migrate-agent-config --write-credentials` to migrate a key that lives only in `.mcp.json` / `.cursor/mcp.json`, or by adding a `bapi:<repo>` entry to the user-scoped credentials file (`~/.config/bridge/credentials.json`) — never by putting `BAPI_API_KEY` into the worktree `.mcp.json` or `.cursor/mcp.json` (that env is invisible to the Bash-spawned CLI).\n\nThis stage is **critical** in the sense that the CLI will abort if its pre-flight fails; you will see the error in Stage 3's output and must surface it.\n\n## Stage 2 — Resolve Base Branch + Enrich Branch Names (best-effort)\n\n### Stage 2a — Resolve configured `base_branch`\n\nThe CLI must be told which branch to cut new worktrees from. Resolution order:\n\n1. If `user_supplied_base_branch` from Stage 0 is `true`, **skip the config-field lookup entirely** and use the user-supplied value. The user's explicit `--base-branch` always wins; never call `get_config_field` for `base_branch` in that case.\n2. Otherwise, call the `get_config_field` MCP tool with `field_name` set to `base_branch` (do not pass any other parameters; the tool resolves the repository from the MCP server's configured `BAPI_REPO_NAME`).\n3. Parse the response. Treat the result as the **configured base branch** only when the response is a JSON object whose `value` field is a non-empty string after trimming surrounding whitespace.\n4. Treat **all** of the following as \"unset\" — emit a single-line warning like `Warning: base_branch is unset; CLI will default to main` and **omit** the `--base-branch` flag entirely from the Stage 3 command (the CLI's own default is `main`):\n - `value` is `null`.\n - `value` is an empty string or a whitespace-only string.\n - The endpoint returns HTTP `400` (invalid field — happens before the registry includes `base_branch`).\n - The tool returns a network error, timeout, or non-JSON parse failure.\n - Any other lookup failure.\n5. When the configured value is usable, capture it in a `resolved_base_branch` variable. **Do not** stop the pipeline on a lookup failure; fall through to the CLI default.\n\nWhen forwarding `resolved_base_branch` into the Bash invocation in Stage 3, **shell-escape it safely**: replace every literal single quote `'` in the value with the four-character sequence `'\\''`, then wrap the entire resulting string in single quotes (so the final argument looks like `'<escaped-value>'`). This is the standard POSIX single-quote escaping rule and is **mandatory** because `base_branch` is admin-configurable data that gets interpolated into a Bash command string; any unescaped single quote would otherwise break out of the surrounding quotes. Pass `--base-branch '<escaped-value>'` to the CLI as a single argv element — never expand the value unquoted into the command line.\n\n### Stage 2b — Enrich Branch Names\n\nBranch enrichment happens here, in the command, **before** invoking the CLI — the `get_ticket` MCP tool runs inside the MCP server process, which holds the Bridge API credentials the shell-spawned CLI does not have. For each parsed ticket key that does **not** already have a user-provided `--branch` override:\n\n1. Call the `get_ticket` MCP tool with `ticket_number` set to the key and `save_locally` set to `false`.\n2. From the response, extract the `summary` field. Slugify it: lowercase the string, replace every run of non-alphanumeric characters (`[^a-z0-9]+`) with a single dash `-`, trim leading and trailing dashes, and truncate to at most `40` characters (cutting at a dash boundary if possible).\n3. The enriched branch name is `feature/<KEY>-<slug>`. Example: `BAPI-248` with summary `\"Add PR rating pre-evaluation step\"` becomes `feature/BAPI-248-add-pr-rating-pre-evaluation-step` (trimmed at 40 chars).\n4. If the `get_ticket` call fails for a particular key (404, network error, missing summary) or produces an empty slug, emit a single-line warning like `Warning: could not enrich BAPI-248, falling back to feature/BAPI-248` and let the CLI apply its default `feature/<KEY>` for that key only. Do NOT stop the pipeline.\n5. Build a list of `--branch <KEY>=<BRANCH>` arguments — one entry per key whose enrichment succeeded — and merge it with any user-provided overrides from Stage 0. **Do not** call `get_ticket` for keys that already have a user-provided override; those overrides win.\n\nThis stage is **non-critical** — warnings are acceptable, the pipeline continues with the fallback default for any key that fails. Do not call the Bridge API from the CLI itself; the CLI never has credentials.\n\n## Stage 3 — Invoke the Packaged CLI\n\nUse the **Bash tool** to invoke the packaged CLI. Build the command line as:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets <pass-through-flags> <base-branch-flag> <branch-overrides> <ticket-keys>\n```\n\nWhere:\n- `<pass-through-flags>` are the supported flags collected in Stage 0 (`--agent`, `--terminal`, `--dry-run`, `--auto`, `--no-refresh-main`, `--max-parallel`), forwarded verbatim. Forward `--agent <name>` only if the user supplied it; otherwise omit it and the CLI defaults to `claude`. Forward `--auto` only if the user supplied it.\n- `<base-branch-flag>` is `--base-branch '<escaped-value>'` (single-quoted using the Stage 2a escaping rule) **only when** the user supplied `--base-branch` in Stage 0 **or** Stage 2a's `get_config_field` lookup returned a non-empty configured value. When the configured value is unset / lookup fails / user did not supply one, **omit this flag entirely** so the CLI's own default (`main`) takes effect.\n- `<branch-overrides>` is the list of `--branch KEY=BRANCH` flags assembled in Stage 2 (enrichment results merged with user overrides; omit any key whose enrichment failed and had no user override).\n- `<ticket-keys>` is the original list of ticket keys parsed in Stage 0, space-separated and in the original order.\n\nExample for two tickets after successful enrichment, throttled to 2 concurrent worktrees:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets \\\n --max-parallel 2 \\\n --branch BAPI-248=feature/BAPI-248-add-pr-rating-pre-evaluation-step \\\n --branch BAPI-250=feature/BAPI-250-deep-research-durability \\\n BAPI-248 BAPI-250\n```\n\nExample launching Cursor Agent instead of the default Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\nExample cutting worktrees from a non-`main` base (either user-supplied via `--base-branch develop` in Stage 0 or resolved from Bridge API config in Stage 2a):\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --base-branch develop BAPI-248\n```\n\nPass through the CLI's stdout and stderr to the user verbatim. If the CLI exits non-zero, treat that as a critical failure: report the exit code and the CLI's error output, and stop.\n\nThis stage is **critical** — propagate any non-zero exit from the packaged CLI.\n\n## Stage 4 — Final Report\n\nOnce the CLI exits 0, parse its `Summary` section (one stable line per ticket in the form `KEY branch=BRANCH status=STATUS`, with an optional trailing `path=PATH`) and reformat it as a markdown table:\n\n```\n| Ticket | Branch | Status |\n|----------|-----------------------------------------------------|----------|\n| BAPI-248 | feature/BAPI-248-add-pr-rating-pre-evaluation-step | spawned |\n| BAPI-250 | feature/BAPI-250-deep-research-durability | spawned |\n```\n\nStatus values are `dry-run`, `spawned`, `create-failed`, and `spawn-failed`. End the report with the worktree-first explanation, rendered for the tracked `selected_agent`. When `selected_agent` is `claude` (the default):\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`claude '/implement-ticket <KEY>'` inside its already-created worktree, which launches\nClaude Code with the starter prompt as its first message. Switch to each tab — or on\nLinux run `tmux attach -t <session>` — to monitor.\n```\n\nWhen `selected_agent` is `cursor-agent`, render the same explanation but with the Cursor handoff — do **not** claim it launches Claude Code:\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`cursor-agent '/implement-ticket <KEY>'` inside its already-created worktree, which\nlaunches Cursor Agent with the starter prompt as its first message. Switch to each\ntab — or on Linux run `tmux attach -t <session>` — to monitor.\n```\n\nThe `/implement-ticket <KEY>` prompt is identical for both agents; only the launched command differs. When start-tickets was invoked with `--auto`, the spawned prompt is `/implement-ticket <KEY> --auto` (the implementation pipeline runs hands-off, without approval gates).\n\nIf the CLI reported any `create-failed` or `spawn-failed` statuses, or Stage 2 emitted any enrichment warnings, list them under a `Warnings:` heading at the bottom of the report. If there were none, omit that section.\n\nSee `docs/claude/parallel-worktrees.md` for the deep-dive runbook and the Worktrunk verification result behind this worktree-first model.\n\n## Difficulty-Based Implementation-Model Routing\n\nBefore launching the interactive agent for each ticket, the packaged CLI selects an\nimplementation **model tier** from the ticket's `difficulty` rating (1-10) and injects\nit as a `--model` flag at the agent spawn boundary. This happens entirely inside the\nCLI — it is **not** part of the server-side `/implement-ticket` recipe, because the\nmodel an interactive agent session uses is fixed at the moment the process is launched.\n\n- **Tier ladder (fixed):** `difficulty 1-2 → cheap`, `3-5 → basic`, `6+ → premium`.\n- **Separation of concerns:** the Python backend returns only the coarse tier\n (`cheap`/`basic`/`premium`) via `GET /jira/tickets/{KEY}/model-tier`; difficulty is\n computed on demand and cached when absent. The TypeScript CLI alone maps a tier to\n the agent-specific model alias (`claude`: `haiku`/`sonnet`/`opus`; `cursor-agent`:\n version-suffixed strings validated against `cursor-agent --list-models`).\n- **Per-repo config:**\n - `difficulty_model_routing_enabled` — boolean, **default ON**. Set to `false` to\n disable routing for a repo (the CLI then omits `--model`).\n - `difficulty_model_tier_overrides` — a JSON object mapping a tier name to a model\n alias (e.g. `{\"premium\": \"opus\"}`), **not** raw CLI arguments. Only `cheap`,\n `basic`, and `premium` keys are accepted; aliases must match `^[A-Za-z0-9._:-]+$`.\n- **Fail-open:** routing never aborts a spawn. Credential, network, config, or\n no-tier routing failures **assume a hard ticket and default to the premium/Opus\n tier** when the selected agent supports a valid premium alias; routing being\n disabled (`difficulty_model_routing_enabled = false`) or an agent that does not\n support `--model` instead omit `--model` so the agent runs on its own default\n model. Each degraded case is surfaced as exactly one secret-free, per-ticket\n routing-diagnostic line, never a hard failure.\n\n### Model routing credential\n\nDifficulty→model routing needs Bridge API credentials, and the shell-spawned\n`start-tickets` CLI is a **different runtime surface** from the MCP server: a\n`BAPI_API_KEY` that lives only in `.mcp.json` / `.cursor/mcp.json` is visible to\nthe MCP server but **not** to the Bash-spawned CLI, so routing silently degrades.\nThe durable source of truth both runtimes can resolve is the user-scoped store\n`~/.config/bridge/credentials.json`, keyed `bapi:<repo>`. If a routing-diagnostic\nline reports the credential is missing (e.g. difficulty resolves as `?`), fix it\nby any one of:\n\n1. Rerun `/install-bridge` — its final stage now persists the validated routing\n credential into `~/.config/bridge/credentials.json` via the\n `persist_routing_credential` tool.\n2. Migrate a key that lives **only** in `.mcp.json` / `.cursor/mcp.json` into the\n user-scoped store with the consent-gated, one-shot command:\n\n ```\n npx -y @bridge_gpt/mcp-server credentials migrate-agent-config --write-credentials\n ```\n\n3. Manually add `BAPI_API_KEY` under the `bapi:<repo>` target in the user-scoped\n store `~/.config/bridge/credentials.json`.\n\nNever put `BAPI_API_KEY` into a worktree `.mcp.json` / `.cursor/mcp.json` as a fix —\nthat env is invisible to the spawned CLI.\n\n## Conductor observability (opt-in via `--conductor`, BAPI-394)\n\nConductor is **opt-in**. By default `start-tickets` spawns the plain\n`cd <worktree> && <agent> '/implement-ticket <KEY> [--auto]'` — no\n`BAPI_CONDUCTOR_*` env, no supervisor window, and no message-relay instruction.\nPass `--conductor` (e.g. `/start-tickets --conductor BAPI-123`) to enable the\nConductor system below.\n\nWith `--conductor`, a run mints a single conductor `run_id` and attributes each\nworker's lifecycle events by `worker_id`, ticket key, and worktree path, and a\nsupervisor peer tab is opened. When the selected agent is **Claude Code**, the CLI\ninjects a conductor lifecycle hook into each created worktree's\n`.claude/settings.local.json` so the spawned session emits local `run.started` /\n`run.stopped` / `agent.notification` (and, when\n`BAPI_CONDUCTOR_ENABLE_PRE_TOOL_USE=1`, `tool.intent`) events into the local\nconductor ledger. These hooks apply **only** when the selected agent is Claude\nCode; other agents (e.g. `cursor-agent`) still participate in the run-level\n`run.started` event but receive no per-worktree Claude hook. Inspect the ledger\nwith the `conductor` CLI (e.g. `conductor doctor`). Conductor observability is\nbest-effort and never blocks or aborts a spawn.\n\nAlso under `--conductor`, each worker is launched with an explicit instruction to\ncall the `check_messages` MCP tool at checkpoints, so the supervisor can pass it\ntyped guidance mid-run (BAPI-397). Delivery is **cooperative** — the worker polls\nand acknowledges messages and they are never injected into a running session.\n(Epic-tick dispatch always runs with conductor enabled, independent of this\nuser-facing flag.)\n",
27
27
  "teach-bridge.md": "Update a Bridge API configuration field via a natural-language teaching.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command takes a natural-language teaching (e.g., \"use data-testid selectors in Playwright tests\") and updates the appropriate Bridge API configuration field. The teaching is auto-classified to the correct field, merged with existing content as actionable AI instructions, and uploaded after user confirmation.\n\n`$ARGUMENTS` is required — it is the teaching text. If `$ARGUMENTS` is empty, show:\n\n```\nUsage: /teach-bridge <teaching>\n\nExamples:\n /teach-bridge use data-testid selectors in Playwright tests\n /teach-bridge always validate input DTOs with Pydantic before passing to service layer\n /teach-bridge prefer composition over inheritance for service classes\n```\n\nIf any stage fails, stop immediately and report which stage failed and why.\n\n## Stage 0 — Preflight\n\n1. **Validate arguments**: If `$ARGUMENTS` is empty or contains only whitespace, display the usage instructions above and stop.\n\n2. **Admin check**: Call the `get_my_role` MCP tool (no parameters). Inspect the response:\n - If `role` is `\"admin\"` OR `source` is `\"legacy\"`: proceed normally.\n - Otherwise: stop immediately and display:\n ```\n Admin access required. Your API key has role \"<role>\" (source: <source>).\n Only admin keys and legacy shared keys can update configuration fields.\n Contact your project administrator to request admin access.\n ```\n\nIf this stage fails, stop immediately and report the error. Do not proceed to Stage 1.\n\n## Stage 1 — Classify\n\n1. **List available fields**: Call the `list_config_fields` MCP tool (no parameters). This returns all available configuration field names with descriptions.\n\n2. **Evaluate the teaching**: Compare the user's teaching (`$ARGUMENTS`) against each field's description to determine which field it applies to.\n\n3. **Handle classification outcomes**:\n - **Clear single match**: If one field is clearly the best target, proceed to Stage 2 with that field.\n - **Multiple plausible matches**: If 2-3 fields are equally plausible, present them to the user with their descriptions and ask which one to update. Wait for user input before proceeding.\n - **No confident match**: If you cannot confidently map the teaching to any field, ask the user to elaborate or specify which field they intend. Wait for user input before proceeding.\n\n## Stage 2 — Merge\n\n1. **Read current value**: Call the `get_config_field` MCP tool with `field_name` set to the selected field from Stage 1. Capture the current value, description, and examples from the response.\n\n2. **Draft the update**:\n - **If the field is currently null or empty**: Compose initial content from the teaching. Rephrase the user's input as imperative, agent-facing instructions (e.g., convert \"I want you to use data-testid\" to \"Always use `data-testid` attributes for Playwright element locators\"). Do not use the user's exact conversational text.\n - **If the field has existing content**: Merge the teaching into the existing value at the most appropriate location. Rephrase as imperative, agent-facing instructions. Preserve the existing structure and formatting.\n\n3. **Handle contradictions**: If the teaching contradicts existing instructions in the field, present both the existing instruction and the new teaching side-by-side and ask the user which should take precedence. Wait for user input before proceeding.\n\n## Stage 3 — Confirm and Upload\n\n1. **Show the proposed update**: Display to the user:\n - **Field**: The name of the field being updated\n - **Change summary**: A brief description of what was added or changed\n - **Full proposed value**: The complete new value for the field (not just the diff)\n\n2. **Wait for confirmation**: Ask the user to confirm, request edits, or abort.\n\n3. **On confirmation**: Call the `update_config_field` MCP tool with:\n - `field_name`: the selected field name\n - `value`: the full merged value (pass inline, do not use `file_path`)\n\n Display a success message confirming the update.\n\n4. **On rejection**: Ask the user what they'd like to change. If they provide edits, revise the proposed value and show it again. If they abort, stop without making any changes.\n",
28
28
  "upgrade-bridge.md": "# Upgrade Bridge\n\n$ARGUMENTS\n\nUse this command to upgrade (or update) the Bridge API MCP — the\n`@bridge_gpt/mcp-server` package, also called the bridge-api MCP — to the latest\npublished version. This is the action behind the ping tool's advice to \"tell\nyour local agent 'upgrade bridge'\".\n\n---\n\n# Instructions\n\nRun the existing packaged upgrade flow. Do not edit files, install anything by\nhand, or invent a new subcommand — just drive the upgrade CLI and report what it\ndid.\n\n## Step 1 — Run the upgrade command\n\nFrom the **project root**, run exactly:\n\n```\nnpx -y @bridge_gpt/mcp-server --upgrade\n```\n\nThis upgrades/updates the installed `@bridge_gpt/mcp-server` (the bridge-api MCP)\nand re-scaffolds the slash commands.\n\n## Step 2 — Report the result\n\n- If the CLI reports a version change, report it in the CLI's\n `oldVersion -> newVersion` form (e.g. `0.1.17 -> 0.1.19`), mirroring the\n `runUpgradeCli` output.\n- If the CLI reports that no upgrade was needed (the installed version is already\n the latest), report `Already up-to-date.` exactly.\n\n## Step 3 — Handle failures\n\nIf the command fails (non-zero exit or an error in its output), **stop** and\nreport the CLI error verbatim. Do not retry blindly or attempt manual edits to\nwork around it.\n\n## Final Report\n\nReport whether the bridge-api MCP was upgraded (with the\n`oldVersion -> newVersion` transition), was already current (`Already up-to-date.`),\nor failed (with the CLI error).\n"
29
29
  };
@@ -503,6 +503,12 @@ export async function buildProductionEpicRuntimeDeps(epicKey) {
503
503
  throw new Error(`dispatchSeam called before fetchPlan for epic ${ek} ticket ${tk}; cachedPlanVersion is 0`);
504
504
  }
505
505
  const kind = automationMap.get(tk) ?? "start-tickets";
506
+ // Operator dry-run: when BAPI_CONDUCTOR_DISPATCH_DRY_RUN=1, dispatch resolves
507
+ // the spawn command + model routing but opens NO terminal, creates NO worktree,
508
+ // and opens NO PR (start-tickets/review-tickets dryRun). The full loop dispatch
509
+ // path (ready-set → claim key → dispatch → correlate run_id) still exercises;
510
+ // dry-run rows carry no runId, so a synthetic one is substituted for correlation.
511
+ const dispatchDryRun = process.env.BAPI_CONDUCTOR_DISPATCH_DRY_RUN === "1";
506
512
  const identity = {
507
513
  epic_key: ek,
508
514
  epic_run_id: ek,
@@ -516,7 +522,7 @@ export async function buildProductionEpicRuntimeDeps(epicKey) {
516
522
  keys: [tk],
517
523
  epic: identity,
518
524
  agentName: "claude",
519
- dryRun: false,
525
+ dryRun: dispatchDryRun,
520
526
  maxParallel: 1,
521
527
  auto: true,
522
528
  reviewOverrides: {},
@@ -531,18 +537,28 @@ export async function buildProductionEpicRuntimeDeps(epicKey) {
531
537
  keys: [tk],
532
538
  epic: identity,
533
539
  agentName: "claude",
534
- dryRun: false,
540
+ dryRun: dispatchDryRun,
535
541
  autoApprove: true,
536
542
  maxParallel: 1,
537
543
  refreshMain: false,
538
544
  branchOverrides: {},
539
545
  baseBranch: "main",
546
+ // Epic dispatch always uses the Conductor system (epic-tick coordinates
547
+ // workers through it), so the message-relay instruction must be present
548
+ // on epic-dispatched worker prompts regardless of the user-facing
549
+ // `--conductor` default.
550
+ conductorEnabled: true,
540
551
  });
541
552
  if (!result.ok) {
542
553
  throw new Error(`start-tickets dispatch failed: ${result.error}`);
543
554
  }
544
555
  runId = result.rows[0]?.runId;
545
556
  }
557
+ if (!runId && dispatchDryRun) {
558
+ // Dry-run dispatch produced no real run_id; substitute a synthetic id so the
559
+ // idempotent correlate step still runs and the dispatch row is observable.
560
+ runId = `dry-run:${identity.dispatch_key}`;
561
+ }
546
562
  if (!runId) {
547
563
  throw new Error(`dispatch returned no runId for ticket ${tk}`);
548
564
  }
@@ -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\nMCP server for [Bridge API](https://bridgegpt-api.com) — exposes Jira integration endpoints as MCP tools for AI coding agents. Works with Claude Code, VS Code/Copilot, Cursor, Windsurf, and OpenAI Codex.\n\n> **New here?** Jump to [Usage Documentation](#usage-documentation) for what you can actually do with Bridge, grouped by how often you'll reach for it.\n\n## Getting Started\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\n\nTo upgrade to the latest version and refresh all scaffolded artifacts in one step:\n\n```bash\nnpx -y @bridge_gpt/mcp-server --upgrade\n```\n\nThis runs `npm i @bridge_gpt/mcp-server@latest`, prints a before/after version summary, then re-runs the full `--init` scaffolding flow to update your slash commands, 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## 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\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## 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| `-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 (BAPI-394).** A real run mints a single conductor `run_id` and emits one canonical `run.started` event into the local conductor ledger (`~/.config/bridge/events.db`), attributing each worker by `worker_id`, ticket key, and worktree path. When the selected agent is **Claude Code**, the CLI also injects a conductor lifecycle hook into each created worktree's `.claude/settings.local.json` (preserving any existing hooks) so the spawned session streams local `run.started` / `run.stopped` / `agent.notification` (and, with `BAPI_CONDUCTOR_ENABLE_PRE_TOOL_USE=1`, `tool.intent`) events. Per-worker conductor identity is passed only via secret-free environment scoped to that one terminal/tab/session — no credentials are ever placed in the env, hook command, or run metadata. Override the gate/supervisor labels with `BAPI_CONDUCTOR_GATE_NAME` / `BAPI_CONDUCTOR_SUPERVISOR_MODE`. Inspect the stream with `conductor doctor`. Observability is best-effort: a conductor failure never blocks or aborts a spawn, and `--dry-run` performs no conductor side effects.\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 install-git-hooks` (BAPI-395)\n\nInstalls local git hooks that opportunistically emit conductor git/PR/CI events into the local ledger:\n\n```\nconductor install-git-hooks [--json]\n```\n\nThe installed hooks are **local, unversioned, opportunistic, and bypassable**: they live in the worktree's git hooks directory (resolved via `git rev-parse --git-common-dir`), insert only a clearly-delimited managed block (preserving any existing user hook content), launch the producer **detached in the background** so a commit or ref update is never blocked, and tolerate every failure (`|| true`). A directory that is not a git worktree, or an existing hook that looks binary/unsafe, is left untouched and reported as a **degraded optional capability** — never a fatal error. The hooks installed are `post-commit` (emits `git.commit_created`) and `reference-transaction` (emits `worktree.changed` for committed ref updates).\n\nMissing hooks do **not** prevent PR/CI gate evaluation — `conductor doctor` reads hook presence and managed-snippet status **read-only** (a new `git hooks` section / `git_hooks` JSON object alongside the ledger report), and the `wait_for_done_gate` MCP tool drives CI polling and gate evaluation regardless of whether hooks are installed.\n\n#### `conductor_done_gate` config\n\nThe per-repo `conductor_done_gate` config field (read through the existing config-field route) defines the v1 done gate. It supports exactly one condition, `required_ci_checks_green`:\n\n```json\n{\n \"enabled\": true,\n \"conditions\": [\n { \"type\": \"required_ci_checks_green\", \"required_checks\": [\"build\", \"test\"] }\n ]\n}\n```\n\n`gate.met` is emitted (exactly once per `repo + pr_number + head_sha + effective config`) only when every listed required check is present, complete, and green for the bound PR head SHA. The gate **fails closed**: an unset, disabled (`enabled` not strictly `true`), malformed, empty, or unsupported config emits no `gate.met`.\n\n#### `conductor_auto_merge_enabled` config (C6 conditional auto-merge)\n\nWhen a worker's PR meets the done gate (`gate.met`), the supervisor can autonomously merge it — but **only** when the repo has explicitly opted in. The per-repo `conductor_auto_merge_enabled` config field (read through the same config-field route as `conductor_done_gate`) is the opt-in switch:\n\n```json\n{ \"enabled\": true }\n```\n\nA bare JSON boolean (`true`) is also accepted. **Auto-merge is disabled by default.** Behavior:\n\n- **Disabled / unset / malformed → dry-run.** Anything other than `true` or `{\"enabled\": true}` — including unset, `false`, `{\"enabled\": false}`, or any malformed value — fails **closed**: the supervisor records a `merge.dry_run` event and **no PR is ever merged**.\n- **Enabled → autonomous merge** when the gate is met and the deterministic guards pass.\n- **Kill-switch.** Set `conductor_auto_merge_enabled` to `false` or remove the field to immediately stop autonomous merges. The protected merge endpoint **independently re-enforces** the flag, so even a conductor that calls it cannot merge while the flag is off.\n\nMerge authority is **deterministic code, never an LLM**. The deterministic guards, all bound to **PR number + expected head SHA (never a branch name)**:\n\n- the per-repo enablement flag (off → dry-run),\n- the PR is still open,\n- the merge is bound to the PR number plus the expected head SHA — **head-SHA drift between gate evaluation and merge aborts the merge**,\n- required CI checks are **revalidated green immediately before merge**.\n\nIdempotency is crash-safe and race-safe: a TTL lease keyed by the deterministic action key `merge:{repo}:{pr}:{head_sha}:{gate}` is claimed before acting, and an existing `merge.succeeded` for that key is terminal — the supervisor never double-merges across a crash/restart or two racing instances. The conductor never holds VCS write credentials: it calls the protected Bridge API endpoint `POST /vcs/pull-requests/{pr_number}/merge`, which owns the privileged merge, and records the returned `merge.dry_run` / `merge.attempted` / `merge.succeeded` / `merge.failed` / `merge.pending_approval` events into the local ledger. `merge.failed` is **retryable** (a drifted head SHA produces a new action key); `merge.pending_approval` is **nonterminal** — the worker remains active until a human redeems the approval token and the server returns `merge.succeeded`. **`merge.succeeded` is the only terminal merge event.** The local SQLite conductor store uses schema version 5 (BAPI-413) to accommodate the `merge.pending_approval` type in the `events.type` CHECK constraint.\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## Smoke testing\n\nThe package ships a canonical, **opt-in** in-host smoke-test runbook at\n`smoke-test/SMOKE-TEST.md`. An AI agent running inside your host (Claude Code,\nCursor, Codex, Windsurf, or VS Code/Copilot) executes it to verify that the MCP\nserver actually works end-to-end *inside that host* — it calls the real tools and\nrecords a PASS/FAIL verdict for each one in a markdown report.\n\n- `smoke-test/SMOKE-TEST.md` **ships with the npm package** and is the\n **canonical** source of truth for the smoke test.\n- The smoke test **adds no MCP tool** and **does not change the registered\n tool surface** (the server still registers its existing 62 tools).\n- It is **opt-in**: default `--init` **does not scaffold `/smoke-test-mcp`**, so\n consumer command palettes are not polluted.\n\n### Running it\n\nYou have two options:\n\n1. **Copy the opt-in command manually.** Copy the packaged command stub into your\n host's command directory, then invoke `/smoke-test-mcp`:\n\n ```bash\n # Claude Code\n cp node_modules/@bridge_gpt/mcp-server/smoke-test/smoke-test-mcp.md .claude/commands/smoke-test-mcp.md\n # Cursor\n cp node_modules/@bridge_gpt/mcp-server/smoke-test/smoke-test-mcp.md .cursor/commands/smoke-test-mcp.md\n ```\n\n2. **Open the runbook directly.** Alternatively, open\n `smoke-test/SMOKE-TEST.md` and ask the host agent to execute it.\n\nReports are written to `<BAPI_DOCS_DIR>/smoke-test/REPORT-<host>-<timestamp>.md`.\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\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 registers **62 tools**. 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** — `list_attachments`, `upload_attachment`, `download_attachment`\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`\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`, `list_config_fields`, `get_config_field`, `update_config_field`\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\nMCP server for [Bridge API](https://bridgegpt-api.com) — exposes Jira integration endpoints as MCP tools for AI coding agents. Works with Claude Code, VS Code/Copilot, Cursor, Windsurf, and OpenAI Codex.\n\n> **New here?** Jump to [Usage Documentation](#usage-documentation) for what you can actually do with Bridge, grouped by how often you'll reach for it.\n\n## Getting Started\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\n\nTo upgrade to the latest version and refresh all scaffolded artifacts in one step:\n\n```bash\nnpx -y @bridge_gpt/mcp-server --upgrade\n```\n\nThis runs `npm i @bridge_gpt/mcp-server@latest`, prints a before/after version summary, then re-runs the full `--init` scaffolding flow to update your slash commands, 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## 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\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## 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** — without `--conductor` no `BAPI_CONDUCTOR_*` env, supervisor tab, or message-relay prompt is produced. With `--conductor`, a run mints a single conductor `run_id` and emits one canonical `run.started` event into the local conductor ledger (`~/.config/bridge/events.db`), attributing each worker by `worker_id`, ticket key, and worktree path, and opens a supervisor peer tab. When the selected agent is **Claude Code**, the CLI also injects a conductor lifecycle hook into each created worktree's `.claude/settings.local.json` (preserving any existing hooks) so the spawned session streams local `run.started` / `run.stopped` / `agent.notification` (and, with `BAPI_CONDUCTOR_ENABLE_PRE_TOOL_USE=1`, `tool.intent`) events. Per-worker conductor identity is passed only via secret-free environment scoped to that one terminal/tab/session — no credentials are ever placed in the env, hook command, or run metadata. Override the gate/supervisor labels with `BAPI_CONDUCTOR_GATE_NAME` / `BAPI_CONDUCTOR_SUPERVISOR_MODE`. Inspect the stream with `conductor doctor`. Observability is best-effort: a conductor failure never blocks or aborts a spawn, and `--dry-run` performs no conductor side effects. (Epic-tick dispatch always enables conductor internally, independent of this flag.)\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 install-git-hooks` (BAPI-395)\n\nInstalls local git hooks that opportunistically emit conductor git/PR/CI events into the local ledger:\n\n```\nconductor install-git-hooks [--json]\n```\n\nThe installed hooks are **local, unversioned, opportunistic, and bypassable**: they live in the worktree's git hooks directory (resolved via `git rev-parse --git-common-dir`), insert only a clearly-delimited managed block (preserving any existing user hook content), launch the producer **detached in the background** so a commit or ref update is never blocked, and tolerate every failure (`|| true`). A directory that is not a git worktree, or an existing hook that looks binary/unsafe, is left untouched and reported as a **degraded optional capability** — never a fatal error. The hooks installed are `post-commit` (emits `git.commit_created`) and `reference-transaction` (emits `worktree.changed` for committed ref updates).\n\nMissing hooks do **not** prevent PR/CI gate evaluation — `conductor doctor` reads hook presence and managed-snippet status **read-only** (a new `git hooks` section / `git_hooks` JSON object alongside the ledger report), and the `wait_for_done_gate` MCP tool drives CI polling and gate evaluation regardless of whether hooks are installed.\n\n#### `conductor_done_gate` config\n\nThe per-repo `conductor_done_gate` config field (read through the existing config-field route) defines the v1 done gate. It supports exactly one condition, `required_ci_checks_green`:\n\n```json\n{\n \"enabled\": true,\n \"conditions\": [\n { \"type\": \"required_ci_checks_green\", \"required_checks\": [\"build\", \"test\"] }\n ]\n}\n```\n\n`gate.met` is emitted (exactly once per `repo + pr_number + head_sha + effective config`) only when every listed required check is present, complete, and green for the bound PR head SHA. The gate **fails closed**: an unset, disabled (`enabled` not strictly `true`), malformed, empty, or unsupported config emits no `gate.met`.\n\n#### `conductor_auto_merge_enabled` config (C6 conditional auto-merge)\n\nWhen a worker's PR meets the done gate (`gate.met`), the supervisor can autonomously merge it — but **only** when the repo has explicitly opted in. The per-repo `conductor_auto_merge_enabled` config field (read through the same config-field route as `conductor_done_gate`) is the opt-in switch:\n\n```json\n{ \"enabled\": true }\n```\n\nA bare JSON boolean (`true`) is also accepted. **Auto-merge is disabled by default.** Behavior:\n\n- **Disabled / unset / malformed → dry-run.** Anything other than `true` or `{\"enabled\": true}` — including unset, `false`, `{\"enabled\": false}`, or any malformed value — fails **closed**: the supervisor records a `merge.dry_run` event and **no PR is ever merged**.\n- **Enabled → autonomous merge** when the gate is met and the deterministic guards pass.\n- **Kill-switch.** Set `conductor_auto_merge_enabled` to `false` or remove the field to immediately stop autonomous merges. The protected merge endpoint **independently re-enforces** the flag, so even a conductor that calls it cannot merge while the flag is off.\n\nMerge authority is **deterministic code, never an LLM**. The deterministic guards, all bound to **PR number + expected head SHA (never a branch name)**:\n\n- the per-repo enablement flag (off → dry-run),\n- the PR is still open,\n- the merge is bound to the PR number plus the expected head SHA — **head-SHA drift between gate evaluation and merge aborts the merge**,\n- required CI checks are **revalidated green immediately before merge**.\n\nIdempotency is crash-safe and race-safe: a TTL lease keyed by the deterministic action key `merge:{repo}:{pr}:{head_sha}:{gate}` is claimed before acting, and an existing `merge.succeeded` for that key is terminal — the supervisor never double-merges across a crash/restart or two racing instances. The conductor never holds VCS write credentials: it calls the protected Bridge API endpoint `POST /vcs/pull-requests/{pr_number}/merge`, which owns the privileged merge, and records the returned `merge.dry_run` / `merge.attempted` / `merge.succeeded` / `merge.failed` / `merge.pending_approval` events into the local ledger. `merge.failed` is **retryable** (a drifted head SHA produces a new action key); `merge.pending_approval` is **nonterminal** — the worker remains active until a human redeems the approval token and the server returns `merge.succeeded`. **`merge.succeeded` is the only terminal merge event.** The local SQLite conductor store uses schema version 5 (BAPI-413) to accommodate the `merge.pending_approval` type in the `events.type` CHECK constraint.\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## Smoke testing\n\nThe package ships a canonical, **opt-in** in-host smoke-test runbook at\n`smoke-test/SMOKE-TEST.md`. An AI agent running inside your host (Claude Code,\nCursor, Codex, Windsurf, or VS Code/Copilot) executes it to verify that the MCP\nserver actually works end-to-end *inside that host* — it calls the real tools and\nrecords a PASS/FAIL verdict for each one in a markdown report.\n\n- `smoke-test/SMOKE-TEST.md` **ships with the npm package** and is the\n **canonical** source of truth for the smoke test.\n- The smoke test **adds no MCP tool** and **does not change the registered\n tool surface** (the server still registers its existing 62 tools).\n- It is **opt-in**: default `--init` **does not scaffold `/smoke-test-mcp`**, so\n consumer command palettes are not polluted.\n\n### Running it\n\nYou have two options:\n\n1. **Copy the opt-in command manually.** Copy the packaged command stub into your\n host's command directory, then invoke `/smoke-test-mcp`:\n\n ```bash\n # Claude Code\n cp node_modules/@bridge_gpt/mcp-server/smoke-test/smoke-test-mcp.md .claude/commands/smoke-test-mcp.md\n # Cursor\n cp node_modules/@bridge_gpt/mcp-server/smoke-test/smoke-test-mcp.md .cursor/commands/smoke-test-mcp.md\n ```\n\n2. **Open the runbook directly.** Alternatively, open\n `smoke-test/SMOKE-TEST.md` and ask the host agent to execute it.\n\nReports are written to `<BAPI_DOCS_DIR>/smoke-test/REPORT-<host>-<timestamp>.md`.\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\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 registers **62 tools**. 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** — `list_attachments`, `upload_attachment`, `download_attachment`\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`\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`, `list_config_fields`, `get_config_field`, `update_config_field`\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";
@@ -54,7 +54,7 @@
54
54
  * unit-testable on Linux CI without spawning real commands or terminals.
55
55
  */
56
56
  import { execFile } from "child_process";
57
- import { readFile, writeFile, mkdir, stat } from "fs/promises";
57
+ import { readFile, writeFile, mkdir, mkdtemp, stat } from "fs/promises";
58
58
  import os from "node:os";
59
59
  import path from "path";
60
60
  import { VERSION } from "./version.generated.js";
@@ -105,6 +105,7 @@ export function getStartTicketsUsage() {
105
105
  " --base-branch BRANCH Cut new worktrees from BRANCH and refresh origin/BRANCH (default: main)",
106
106
  " --no-refresh-main Skip refresh of the configured base branch (default main); historical name retained for backward compatibility",
107
107
  " --max-parallel N Max worktrees to create concurrently (default: 3)",
108
+ " --conductor Enable the Conductor system: per-worker hook injection + BAPI_CONDUCTOR_* env, a supervisor peer tab, and check_messages message-relay polling (default: off — a plain run just spawns cd <worktree> && <agent> '/implement-ticket <KEY>')",
108
109
  " -h, --help Show this help",
109
110
  "",
110
111
  "Environment:",
@@ -114,12 +115,14 @@ export function getStartTicketsUsage() {
114
115
  " BAPI_CONDUCTOR_SUPERVISOR_MODE Conductor supervisor mode (default: auto when --auto, else interactive)",
115
116
  " BAPI_CONDUCTOR_ENABLE_PRE_TOOL_USE Set 1/true to also register a PreToolUse conductor hook",
116
117
  "",
117
- "Conductor observability:",
118
- " Real Claude Code workers launched by start-tickets receive per-worktree conductor",
119
- " hook injection (into .claude/settings.local.json) and emit local lifecycle events",
120
- " into the conductor ledger. Each real run mints one run_id and attributes worker",
121
- " events by worker_id, ticket key, and worktree path. Inspect the ledger with the",
122
- " `conductor` CLI.",
118
+ "Conductor observability (opt-in via --conductor):",
119
+ " With --conductor, real Claude Code workers launched by start-tickets receive",
120
+ " per-worktree conductor hook injection (into .claude/settings.local.json) and emit",
121
+ " local lifecycle events into the conductor ledger. Each such run mints one run_id",
122
+ " and attributes worker events by worker_id, ticket key, and worktree path, and a",
123
+ " supervisor peer tab is opened. Without --conductor none of this happens. Inspect",
124
+ " the ledger with the `conductor` CLI. The BAPI_CONDUCTOR_* env vars above apply",
125
+ " only when --conductor is set.",
123
126
  "",
124
127
  "Prerequisites:",
125
128
  " macOS wt, git, osascript",
@@ -145,6 +148,7 @@ export function parseStartTicketsArgs(argv) {
145
148
  let maxParallelRaw;
146
149
  let agentName = DEFAULT_AGENT_NAME;
147
150
  let baseBranch = "main";
151
+ let conductorEnabled = false;
148
152
  const branchEntries = [];
149
153
  const keys = [];
150
154
  for (let i = 0; i < argv.length; i++) {
@@ -254,6 +258,10 @@ export function parseStartTicketsArgs(argv) {
254
258
  autoApprove = true;
255
259
  continue;
256
260
  }
261
+ if (arg === "--conductor") {
262
+ conductorEnabled = true;
263
+ continue;
264
+ }
257
265
  if (arg === "--no-refresh-main") {
258
266
  refreshMain = false;
259
267
  continue;
@@ -329,7 +337,7 @@ export function parseStartTicketsArgs(argv) {
329
337
  }
330
338
  return {
331
339
  status: "ok",
332
- options: { keys, terminal, dryRun, autoApprove, refreshMain, maxParallel, branchOverrides, agentName, baseBranch },
340
+ options: { keys, terminal, dryRun, autoApprove, refreshMain, maxParallel, branchOverrides, agentName, baseBranch, conductorEnabled },
333
341
  };
334
342
  }
335
343
  /** Returns an error string for an unsafe branch name, or null when valid. */
@@ -397,7 +405,7 @@ export function getDefaultSpawnTerminalTabForPlatform(platform) {
397
405
  * are always honored). Returns a structured error for unsupported platforms;
398
406
  * never throws.
399
407
  */
400
- export function resolveStartTicketsPlatformConfig(deps, agent, autoApprove = false) {
408
+ export function resolveStartTicketsPlatformConfig(deps, agent, autoApprove = false, conductorEnabled = false) {
401
409
  if (!isSupportedStartTicketsPlatform(deps.platform)) {
402
410
  return { ok: false, error: unsupportedPlatformMessage(deps.platform) };
403
411
  }
@@ -407,7 +415,7 @@ export function resolveStartTicketsPlatformConfig(deps, agent, autoApprove = fal
407
415
  config: {
408
416
  platform,
409
417
  worktrunkBinary: resolveWorktrunkBinary(platform, deps.env),
410
- buildAgentShellCommand: (key, worktreePath, modelAlias) => buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias),
418
+ buildAgentShellCommand: (key, worktreePath, modelAlias) => buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled),
411
419
  spawnTerminalTab: deps.spawnTerminalTab,
412
420
  },
413
421
  };
@@ -503,6 +511,10 @@ export function createDefaultStartTicketsDeps() {
503
511
  cwd: process.cwd(),
504
512
  // The platform router is the single source of truth for spawner selection.
505
513
  spawnTerminalTab: getDefaultSpawnTerminalTabForPlatform(process.platform),
514
+ // Deliver each worker command via a launch-script file rather than embedding
515
+ // a multi-KB command in the terminal spawn (robust on macOS AppleScript /
516
+ // Windows wt.exe paths). Tests omit this seam to keep the legacy inline path.
517
+ writeWorkerLaunchScript: defaultWriteWorkerLaunchScript,
506
518
  };
507
519
  return deps;
508
520
  }
@@ -839,16 +851,22 @@ export function buildConductorMessageRelayLaunchInstruction() {
839
851
  /**
840
852
  * The starter prompt handed to the selected agent. Identical for every agent.
841
853
  * When `autoApprove` is set, the implementation agent runs hands-off
842
- * (`/implement-ticket <KEY> --auto`) — used by full-automation chains. The
843
- * conductor message-relay polling instruction is appended after the slash command
844
- * (BAPI-397) so launched workers actually poll `check_messages`.
854
+ * (`/implement-ticket <KEY> --auto`) — used by full-automation chains.
855
+ *
856
+ * The conductor message-relay polling instruction (BAPI-397) is appended after
857
+ * the slash command ONLY when `conductorEnabled` is set (the `--conductor`
858
+ * flag). A plain run returns the bare `/implement-ticket <KEY> [--auto]` so the
859
+ * worker is not told to poll `check_messages` for a conductor that is not
860
+ * running.
845
861
  */
846
862
  export function buildAgentPrompt(key, opts = {}) {
847
863
  // `modelAlias` is accepted for signature consistency only — the model is
848
864
  // injected as a `--model` flag (see buildAgentInvocationArgv), never embedded
849
865
  // in the prompt text.
850
866
  const command = `/implement-ticket ${key}${opts.autoApprove ? " --auto" : ""}`;
851
- return `${command} ${buildConductorMessageRelayLaunchInstruction()}`;
867
+ return opts.conductorEnabled
868
+ ? `${command} ${buildConductorMessageRelayLaunchInstruction()}`
869
+ : command;
852
870
  }
853
871
  /**
854
872
  * Build the ordered argv for an agent invocation:
@@ -887,13 +905,13 @@ export function buildAgentInvocation(agent, prompt, quote, modelAlias) {
887
905
  }
888
906
  }
889
907
  /** POSIX agent shell command: `cd '<path>' && <agent> [--model '<alias>'] '<prompt>'`. */
890
- export function buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias) {
891
- const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove }), (p) => `'${shSquoteInner(p)}'`, modelAlias);
908
+ export function buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias, conductorEnabled = false) {
909
+ const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove, conductorEnabled }), (p) => `'${shSquoteInner(p)}'`, modelAlias);
892
910
  return `cd '${shSquoteInner(worktreePath)}' && ${invocation}`;
893
911
  }
894
912
  /** PowerShell agent shell command: `Set-Location -LiteralPath '<path>'; <agent> [--model '<alias>'] '<prompt>'`. */
895
- export function buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias) {
896
- const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove }), powershellSquote, modelAlias);
913
+ export function buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias, conductorEnabled = false) {
914
+ const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove, conductorEnabled }), powershellSquote, modelAlias);
897
915
  return `Set-Location -LiteralPath ${powershellSquote(worktreePath)}; ${invocation}`;
898
916
  }
899
917
  /**
@@ -901,12 +919,13 @@ export function buildPowerShellAgentShellCommand(agent, key, worktreePath, autoA
901
919
  * platform. PowerShell on Windows; POSIX everywhere else (incl. the unsupported
902
920
  * dry-run fallback). The selected `agent` (never a module-level constant)
903
921
  * determines the launched command. An optional validated `modelAlias` is
904
- * injected as `--model` at the spawn boundary.
922
+ * injected as `--model` at the spawn boundary. `conductorEnabled` appends the
923
+ * BAPI-397 message-relay instruction to the prompt (opt-in via `--conductor`).
905
924
  */
906
- export function buildAgentShellCommand(agent, key, worktreePath, platform = "darwin", autoApprove = false, modelAlias) {
925
+ export function buildAgentShellCommand(agent, key, worktreePath, platform = "darwin", autoApprove = false, modelAlias, conductorEnabled = false) {
907
926
  if (platform === "win32")
908
- return buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias);
909
- return buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias);
927
+ return buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias, conductorEnabled);
928
+ return buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias, conductorEnabled);
910
929
  }
911
930
  // ---------------------------------------------------------------------------
912
931
  // Spawned-terminal titling (shared across platforms)
@@ -1183,6 +1202,92 @@ export async function spawnUnsupportedPlatformTerminalTab(deps, _terminal, _shel
1183
1202
  return { ok: false, error: unsupportedPlatformMessage(deps.platform) };
1184
1203
  }
1185
1204
  // ---------------------------------------------------------------------------
1205
+ // Per-worker launch script (robust spawn delivery)
1206
+ // ---------------------------------------------------------------------------
1207
+ /*
1208
+ * Why launch via a script file instead of embedding the command in the spawn?
1209
+ *
1210
+ * Each worker command grew from ~100 chars to >1 KB once the conductor identity
1211
+ * env (BAPI-394) and the message-relay launch instruction (BAPI-397) were added.
1212
+ * On macOS that whole string is escaped into an AppleScript literal and injected
1213
+ * via a `keystroke "t"` (Cmd-T) new-tab dance — a delivery path that is
1214
+ * length-, quoting-, and timing-sensitive (a long command racing a not-yet-ready
1215
+ * new-tab shell can land mangled / unexecuted). Windows (`wt.exe`) additionally
1216
+ * has to escape `;` to stop its own arg-splitter from truncating the command.
1217
+ *
1218
+ * Writing the full command to a tiny script and handing the terminal a short
1219
+ * `source <path>` runner sidesteps all of it: the spawned payload is a constant
1220
+ * ~40 chars regardless of how large the underlying command is, and the env +
1221
+ * `cd` + agent invocation live verbatim in the file. The runner is `source`d
1222
+ * (not exec'd) so the tab is left in the worktree after the agent exits, exactly
1223
+ * like the legacy inline path.
1224
+ */
1225
+ /** Filesystem-safe per-key launch-script basename fragment. */
1226
+ export function sanitizeKeyForLaunchScript(key) {
1227
+ const cleaned = key.replace(/[^A-Za-z0-9._-]/g, "_");
1228
+ return cleaned.length > 0 ? cleaned : "worker";
1229
+ }
1230
+ /**
1231
+ * Script body for the resolved platform. POSIX scripts carry a `bash` shebang
1232
+ * (harmless when the file is `source`d); Windows scripts are plain PowerShell.
1233
+ * A trailing newline keeps the final statement well-formed.
1234
+ */
1235
+ export function buildLaunchScriptContent(platform, fullCommand) {
1236
+ if (platform === "win32")
1237
+ return `${fullCommand}\n`;
1238
+ return `#!/usr/bin/env bash\n${fullCommand}\n`;
1239
+ }
1240
+ /** The short, escaping-immune runner the terminal executes: `source <path>`. */
1241
+ export function buildLaunchScriptRunnerCommand(platform, scriptPath) {
1242
+ // `.` (POSIX) / `.` (PowerShell) dot-source the script into the tab's shell so
1243
+ // `cd` persists and the user is left in the worktree once the agent exits.
1244
+ if (platform === "win32")
1245
+ return `. ${powershellSquote(scriptPath)}`;
1246
+ return `. '${shSquoteInner(scriptPath)}'`;
1247
+ }
1248
+ /** Default real-filesystem launch-script writer (used in production). */
1249
+ export const defaultWriteWorkerLaunchScript = async ({ platform, key, content, }) => {
1250
+ const parent = path.join(os.tmpdir(), "bridge-start-tickets");
1251
+ await mkdir(parent, { recursive: true });
1252
+ // Atomically allocate a UNIQUE per-write subdirectory. Two workers can share a
1253
+ // sanitized key (a duplicate CLI arg like `BAPI-1 BAPI-1`, or two keys that
1254
+ // collide after sanitization); a fixed `launch-<key>.<ext>` path would let the
1255
+ // second write silently clobber the first before either tab `source`s it,
1256
+ // leaving the first worker running the second's command. `mkdtemp` gives each
1257
+ // write its own directory (collision-proof across workers AND concurrent
1258
+ // processes), so the readable per-key basename can stay for debuggability.
1259
+ const dir = await mkdtemp(path.join(parent, "w-"));
1260
+ const ext = platform === "win32" ? "ps1" : "sh";
1261
+ const file = path.join(dir, `launch-${sanitizeKeyForLaunchScript(key)}.${ext}`);
1262
+ // 0600: the file carries no secrets (conductor env is secret-free) but there is
1263
+ // no reason to make it world-readable; `source` only needs read.
1264
+ await writeFile(file, content, { mode: 0o600 });
1265
+ return file;
1266
+ };
1267
+ /**
1268
+ * Resolve the command actually handed to the terminal spawner for one worker.
1269
+ * When `deps.writeWorkerLaunchScript` is provided, the full command is persisted
1270
+ * to a script and a short `source <path>` runner is returned; if writing fails
1271
+ * for any reason the original inline command is returned (fail-open — a launch
1272
+ * never aborts because a temp file could not be written). When the seam is
1273
+ * absent, the inline command is returned unchanged (legacy behaviour).
1274
+ */
1275
+ export async function materializeWorkerLaunchCommand(deps, key, fullCommand) {
1276
+ if (!deps.writeWorkerLaunchScript)
1277
+ return fullCommand;
1278
+ try {
1279
+ const scriptPath = await deps.writeWorkerLaunchScript({
1280
+ platform: deps.platform,
1281
+ key,
1282
+ content: buildLaunchScriptContent(deps.platform, fullCommand),
1283
+ });
1284
+ return buildLaunchScriptRunnerCommand(deps.platform, scriptPath);
1285
+ }
1286
+ catch {
1287
+ return fullCommand;
1288
+ }
1289
+ }
1290
+ // ---------------------------------------------------------------------------
1186
1291
  // Tab spawning across created worktrees
1187
1292
  // ---------------------------------------------------------------------------
1188
1293
  /**
@@ -1204,7 +1309,11 @@ export async function spawnTabsForCreatedWorktrees(deps, rows, terminal, buildSh
1204
1309
  // (BAPI-394). No-op when the row carries no conductorEnv (dry-run, non-Claude
1205
1310
  // agents, or conductor disabled). Never mutates process/global env.
1206
1311
  const shellCommand = injectConductorEnvIntoShellCommand(deps.platform, baseShellCommand, row.conductorEnv);
1207
- const result = await deps.spawnTerminalTab(deps, terminal, shellCommand, {
1312
+ // Deliver the (potentially multi-KB) command via a launch-script file so the
1313
+ // terminal spawn payload stays tiny and escaping-immune. Fail-open / no-op
1314
+ // when no writer seam is configured (see materializeWorkerLaunchCommand).
1315
+ const runnableCommand = await materializeWorkerLaunchCommand(deps, row.key, shellCommand);
1316
+ const result = await deps.spawnTerminalTab(deps, terminal, runnableCommand, {
1208
1317
  key: row.key,
1209
1318
  worktreePath: row.path,
1210
1319
  });
@@ -1234,12 +1343,12 @@ export function buildDryRunResults(keys, overrides) {
1234
1343
  * PowerShell on Windows, `wt` + POSIX on macOS/Linux, and a non-throwing `wt` +
1235
1344
  * POSIX fallback for unsupported platforms.
1236
1345
  */
1237
- export function getDryRunPlatformDetails(agent, platform = process.platform, env = process.env, autoApprove = false) {
1346
+ export function getDryRunPlatformDetails(agent, platform = process.platform, env = process.env, autoApprove = false, conductorEnabled = false) {
1238
1347
  return {
1239
1348
  worktrunkBinary: resolveWorktrunkBinary(platform, env),
1240
1349
  // The builder accepts an optional resolved modelAlias; the dry-run caller
1241
1350
  // now passes the previewed tier's alias so `--model` shows in the preview.
1242
- buildAgentShellCommand: (key, worktreePath, modelAlias) => buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias),
1351
+ buildAgentShellCommand: (key, worktreePath, modelAlias) => buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled),
1243
1352
  };
1244
1353
  }
1245
1354
  /**
@@ -1268,8 +1377,8 @@ export function buildDryRunMcpProvisioningLines(worktreePath, platform = process
1268
1377
  * the secret-free MCP provisioning preview. Pure platform formatting only — no
1269
1378
  * preflight, no routing failures.
1270
1379
  */
1271
- export function buildDryRunDetailLines(agent, key, branch, platform = process.platform, env = process.env, baseBranch = "main", autoApprove = false, modelAlias = null) {
1272
- const { worktrunkBinary, buildAgentShellCommand: build } = getDryRunPlatformDetails(agent, platform, env, autoApprove);
1380
+ export function buildDryRunDetailLines(agent, key, branch, platform = process.platform, env = process.env, baseBranch = "main", autoApprove = false, modelAlias = null, conductorEnabled = false) {
1381
+ const { worktrunkBinary, buildAgentShellCommand: build } = getDryRunPlatformDetails(agent, platform, env, autoApprove, conductorEnabled);
1273
1382
  const wtArgs = buildWtSwitchArgs(branch, false, baseBranch);
1274
1383
  const agentInvocation = build(key, "<worktree-path>", modelAlias);
1275
1384
  return [
@@ -2175,7 +2284,7 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
2175
2284
  const preflight = await runPreflight(deps, options);
2176
2285
  if (!preflight.ok)
2177
2286
  return { ok: false, error: preflight.error };
2178
- const platformConfig = resolveStartTicketsPlatformConfig(deps, agent, options.autoApprove);
2287
+ const platformConfig = resolveStartTicketsPlatformConfig(deps, agent, options.autoApprove, options.conductorEnabled ?? false);
2179
2288
  if (!platformConfig.ok)
2180
2289
  return { ok: false, error: platformConfig.error };
2181
2290
  const refresh = await refreshBaseBranch(deps, {
@@ -2342,7 +2451,7 @@ export async function runStartTicketsCli(argv, overrides = {}) {
2342
2451
  const branch = resolveBranchForTicket(key, options.branchOverrides);
2343
2452
  const routedRow = routedByKey.get(key);
2344
2453
  const modelAlias = routedRow?.modelAlias ?? null;
2345
- for (const line of buildDryRunDetailLines(agent, key, branch, deps.platform, deps.env, options.baseBranch, options.autoApprove, modelAlias)) {
2454
+ for (const line of buildDryRunDetailLines(agent, key, branch, deps.platform, deps.env, options.baseBranch, options.autoApprove, modelAlias, options.conductorEnabled ?? false)) {
2346
2455
  log(line);
2347
2456
  }
2348
2457
  log(`DRY-RUN: model routing: ${formatModelRoutingLine(routedRow ?? { key, branch, status: "dry-run" }, agent)}`);
@@ -2361,12 +2470,19 @@ export async function runStartTicketsCli(argv, overrides = {}) {
2361
2470
  const result = await orchestrate(deps, options, {
2362
2471
  modelRoutingLog: log,
2363
2472
  modelRoutingWarningLog: errorLog,
2364
- // BAPI-394: opt the packaged CLI into conductor observability. Supplying the
2365
- // context seam activates the conductor stage inside orchestrate; the other
2366
- // two seams fall back to their real module implementations.
2367
- createConductorContext: createStartTicketsConductorContext,
2368
- provisionConductorHooksForRows,
2369
- emitStartTicketsRunStarted,
2473
+ // Conductor is OPT-IN (BAPI-394): the three seams are supplied ONLY when the
2474
+ // user passes `--conductor`. Supplying `createConductorContext` activates the
2475
+ // conductor stage inside orchestrate (env injection + supervisor tab); the
2476
+ // other two seams fall back to their real module implementations. Omitting
2477
+ // them (the default) leaves rows untouched, so a plain run spawns the bare
2478
+ // `cd <worktree> && <agent> '/implement-ticket <KEY>'`.
2479
+ ...(options.conductorEnabled
2480
+ ? {
2481
+ createConductorContext: createStartTicketsConductorContext,
2482
+ provisionConductorHooksForRows,
2483
+ emitStartTicketsRunStarted,
2484
+ }
2485
+ : {}),
2370
2486
  });
2371
2487
  if (!result.ok) {
2372
2488
  errorLog(`Error: ${result.error}`);
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.4";
2
+ export const VERSION = "0.2.9";
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.4",
3
+ "version": "0.2.9",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
8
+ "mcp-server": "./build/index.js",
8
9
  "bridge-api-mcp-server": "./build/index.js",
9
10
  "conductor": "./build/conductor-bin.js",
10
11
  "conductor-claude-hook": "./build/conductor-claude-hook-bin.js"
@@ -27,6 +28,7 @@
27
28
  "start": "node build/index.js",
28
29
  "test": "node --test build/pipeline-utils.test.js build/update-check.test.js build/cli-upgrade.test.js build/decision-page-schema.test.js build/decision-page-template.test.js build/bundle-pipelines.test.js build/instructions-contract.test.js build/pipeline-orchestrator-persistence.test.js build/pipeline-orchestrator-execution.test.js build/pipeline-orchestrator-integration.test.js build/index-static.test.js build/index-resolvers.test.js build/index-project-root.test.js build/index-pipelines.test.js build/index.test.js build/bridge-config.test.js build/credential-store.test.js build/agent-config-credential-migration.test.js build/mcp-invoke.test.js build/mcp-provisioning.test.js build/third-party-mcp-targets.test.js build/git-ignore-utils.test.js build/credential-materialization.test.js build/mcp-registration-doctor.test.js build/secret-safety.test.js build/start-tickets.test.js build/review-tickets.test.js build/start-tickets-base-branch.test.js build/agent-registry.test.js build/agent-registry.model-routing.test.js build/start-tickets.shell-model-routing.test.js build/start-tickets.bridge-api-model-routing.test.js build/start-tickets.tier-fetch-model-routing.test.js build/start-tickets.resolve-model-routing.test.js build/start-tickets.orchestrate-model-routing.test.js build/start-tickets.routing-diagnostics.test.js build/start-tickets-repo.test.js build/start-tickets-credential-invariants.static.test.js build/credentials-cli.test.js build/start-tickets-prereqs.test.js build/doctor.test.js build/resolveUploadAttachment.test.js build/package-static.test.js build/chain-utils.test.js build/chain-orchestrator.test.js build/scheduler-backends/types.test.js build/scheduler-backends/escaping.test.js build/scheduler-backends/launchd.test.js build/scheduler-backends/task-scheduler.test.js build/scheduler-backends/systemd-user.test.js build/scheduler-backends/at-fallback.test.js build/scheduler-backends/index.test.js build/command-catalog.test.js build/scheduled-prompt.test.js build/agent-launchers/claude.test.js build/agent-launchers/cursor.test.js build/agent-launchers/index.test.js build/schedule-store.test.js build/schedule-run.test.js build/agent-capabilities/cli.test.js build/agent-capabilities/runner.test.js build/agent-capabilities/probes.test.js build/agent-capabilities/reporter.test.js build/conductor/taxonomy-and-errors.test.js build/conductor/redaction-normalization.test.js build/conductor/claude-hook.test.js build/conductor/git-ci-types.test.js build/conductor/done-gate.test.js build/conductor/git-ci-taxonomy-payload.test.js build/conductor/bridge-api-client.test.js build/conductor/plan.test.js build/conductor/producer-ledger.test.js build/conductor/git-producer.test.js build/conductor/git-hooks.test.js build/conductor/store-migration.test.js build/conductor/pr-discovery.test.js build/conductor/pr-ci-producer.test.js build/conductor/doctor.test.js build/conductor/index-poll-ci-producer.test.js build/start-tickets-conductor.test.js build/start-tickets-conductor.spawn.test.js build/conductor/supervisor-config.test.js build/conductor/supervisor-ledger.test.js build/conductor/supervisor-state-reducer.test.js build/conductor/supervisor-housekeeping-projection.test.js build/conductor/supervisor-escalation.test.js build/conductor/supervisor-judgment.test.js build/conductor/supervisor-judgment-python-adapter.test.js build/conductor/supervisor-runtime.test.js build/conductor/supervisor-store-projection.test.js build/conductor/supervisor-cli.test.js build/conductor/supervisor-start-tickets.test.js build/conductor/supervisor-message-relay.test.js build/conductor/supervisor-state-message-events.test.js build/conductor/store-message-relay.test.js build/start-tickets-message-relay.test.js build/conductor/merge-ledger.test.js build/conductor/supervisor-merge.test.js build/conductor/bridge-api-merge-client.test.js build/conductor/bridge-api-epic-client.test.js build/conductor/supervisor-merge-runtime-state.test.js build/conductor/epic-state.test.js build/conductor/epic-reconcile.test.js build/conductor/epic-runtime.test.js build/conductor/epic-tick-sequence.test.js build/conductor/epic-runtime-post-action.test.js && node --experimental-test-module-mocks --test build/index-heavy-read-truncation.test.js build/index-artifacts.test.js build/index-brainstorm-filenames.test.js build/index-output-path.test.js build/index-generate-decision-page.test.js build/index-generate-decision-page.integration.test.js build/conductor/paths.test.js build/conductor/store-lifecycle.test.js build/conductor/store-queries.test.js build/conductor/tools.test.js build/conductor/cli.test.js build/conductor/security-regressions.test.js build/conductor/git-inspection.test.js build/conductor/tools-done-gate.test.js build/conductor/cli-git-hooks.test.js",
29
30
  "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/start-tickets.integration.test.js build/integration/doctor.integration.test.js build/integration/agent-capabilities.integration.test.js build/integration/conductor-producer.integration.test.js build/integration/conductor-message-relay.integration.test.js",
31
+ "test:smoke": "node --test build/integration/packaged-cli-smoke.test.js",
30
32
  "prepublishOnly": "node scripts/bundle-assets.js && npm run build && node scripts/verify-shebang.cjs"
31
33
  },
32
34
  "dependencies": {