@bridge_gpt/mcp-server 0.2.57 → 0.2.59
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 +112 -113
- package/build/agent-registry.js +15 -0
- package/build/agent-utils.js +16 -8
- package/build/claude-user-config-doctor.js +1 -35
- package/build/codex-bridge-profile.js +5 -1
- package/build/codex-launch-scope.js +13 -2
- package/build/commands.generated.js +5 -6
- package/build/conductor/bridge-api-client.js +149 -0
- package/build/conductor/bring-up-facts.js +1 -1
- package/build/conductor/claude-hook.js +16 -16
- package/build/conductor/doctor.js +24 -19
- package/build/conductor/epic-implementer-cli.js +38 -5
- package/build/conductor/epic-reconcile.js +1 -1
- package/build/conductor/epic-runtime.js +10 -10
- package/build/conductor/errors.js +3 -3
- package/build/conductor/file-scope-guard.js +4 -4
- package/build/conductor/git-ci-types.js +1 -1
- package/build/conductor/git-inspection.js +2 -2
- package/build/conductor/install-doctor.js +6 -6
- package/build/conductor/merge-ledger.js +2 -2
- package/build/conductor/pr-ci-producer.js +6 -6
- package/build/conductor/pr-review-producer.js +2 -2
- package/build/conductor/producer-ledger.js +2 -0
- package/build/conductor/readiness-sections.js +7 -7
- package/build/conductor/readiness.js +2 -2
- package/build/conductor/recovery-cli.js +243 -12
- package/build/conductor/recovery-operations.js +56 -4
- package/build/conductor/repair-contract.js +38 -4
- package/build/conductor/spec-review-producer.js +6 -6
- package/build/conductor/store.js +10 -10
- package/build/conductor/supervisor-config.js +14 -14
- package/build/conductor/supervisor-ledger.js +2 -2
- package/build/conductor/supervisor-message-relay.js +2 -2
- package/build/conductor/worker-ledger-cli.js +19 -19
- package/build/conductor-bundle-artifacts.js +2 -2
- package/build/cursor-agent-progress.js +72 -0
- package/build/doctor.js +20 -296
- package/build/drive-epic.js +65 -71
- package/build/epic-implementer-bin.js +22 -17
- package/build/epic-implementer-claude-hook-bin.js +4 -2
- package/build/epic-implementer-env.js +155 -0
- package/build/epic-integration-pr.js +2 -2
- package/build/epic-shared/bridge-client.js +773 -0
- package/build/epic-shared/cut-protocol.js +392 -0
- package/build/executor/env.js +19 -8
- package/build/executor/heartbeat.js +1 -1
- package/build/executor/job-errors.js +3 -3
- package/build/executor/job-runner.js +1 -1
- package/build/executor/merge-job.js +35 -3
- package/build/executor/observation.js +1 -1
- package/build/executor/preflight.js +1 -1
- package/build/executor/prompt-spec.js +1 -1
- package/build/executor/reconcile-overlap-job.js +10 -130
- package/build/executor/reconcile-overlap-types.js +1 -1
- package/build/executor/runner.js +1 -1
- package/build/implement-epic/cli.js +1 -1
- package/build/index.js +187 -224
- package/build/init.js +20 -28
- package/build/install-bridge-conductor.js +7 -7
- package/build/install-bridge.js +203 -10
- package/build/install-doctor.js +1 -1
- package/build/mcp-host-entry-adapter.js +6 -0
- package/build/mcp-host-targets.js +2 -0
- package/build/mcp-invoke.js +5 -5
- package/build/mcp-profile.js +2 -2
- package/build/mcp-provisioning.js +2 -2
- package/build/mcp-server-invocation.js +3 -3
- package/build/merge-pull-request.js +15 -0
- package/build/model-routing-host.js +25 -0
- package/build/model-routing-preferences.js +99 -0
- package/build/pipelines.generated.js +11 -11
- package/build/plan-epic-implementer-eligibility.js +215 -0
- package/build/plan-execution-ledger.js +8 -0
- package/build/plane/member-roster.js +5 -5
- package/build/plane/supervisor.js +1 -1
- package/build/plane/types.js +5 -5
- package/build/readiness-check.js +1 -1
- package/build/readme.generated.js +1 -1
- package/build/review-tickets.js +4 -4
- package/build/setup-epic.js +8 -8
- package/build/start-tickets-conductor.js +32 -32
- package/build/start-tickets-prereqs.js +4 -9
- package/build/start-tickets.js +494 -98
- package/build/version.generated.js +3 -3
- package/docs/CONDUCTOR.md +69 -18
- package/docs/install/mcp-tool-integrations.md +2 -1
- package/package.json +2 -2
- package/pipelines/implement-ticket.json +2 -2
- package/pipelines/plan-epic.json +2 -2
- package/pipelines/review-ticket.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
The Bridge MCP is an end-to-end accelerator for shipping code within SFCC, powered by [Bridge API](https://bridgegpt-api.com). Works with Claude Code, Github Copilot, Cursor, Windsurf, and OpenAI Codex.
|
|
4
4
|
|
|
5
|
+
## Personal model routing preferences
|
|
6
|
+
|
|
7
|
+
Create `~/.config/bridge/model-routing.json` to choose ordered providers for
|
|
8
|
+
ticket review, Second Opinion, and Council without changing shared project
|
|
9
|
+
configuration. The file is personal and uncommitted, contains no credentials,
|
|
10
|
+
and is reread for every request. The repository key must exactly match Bridge's
|
|
11
|
+
`repo_name`:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"version": 1,
|
|
16
|
+
"defaults": {
|
|
17
|
+
"review_ticket": ["gemini", "anthropic"],
|
|
18
|
+
"second_opinion": ["anthropic", "gemini"],
|
|
19
|
+
"council": ["anthropic", "gemini"]
|
|
20
|
+
},
|
|
21
|
+
"repositories": {
|
|
22
|
+
"my-repo": { "council": ["gemini"] }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A repository entry replaces the machine entry only for a feature key it
|
|
28
|
+
contains. Remove a key or set it to `[]` to restore automatic selection for
|
|
29
|
+
that feature; an empty repository entry suppresses the machine entry. Delete
|
|
30
|
+
the file to clear every preference. Malformed, unreadable, or unsafe files are
|
|
31
|
+
ignored with a bounded local warning. Keep the file owned by your user and not
|
|
32
|
+
writable by your group or others.
|
|
33
|
+
|
|
34
|
+
Only the exact initialized MCP client names `codex` and `claude-code`, or their
|
|
35
|
+
installer markers, identify the Codex and Claude Code families. Cursor, GitHub
|
|
36
|
+
Copilot, and unlisted clients remain neutral. Automatic Codex requests prefer
|
|
37
|
+
eligible Anthropic then Gemini; Claude Code requests prefer eligible OpenAI
|
|
38
|
+
then Gemini. Explicit per-call choices override this personal order but must
|
|
39
|
+
still pass model support, project restrictions, and feature entitlement. A
|
|
40
|
+
populated project Allowed Providers list can reduce an automatic request or
|
|
41
|
+
refuse an incompatible explicit request.
|
|
42
|
+
|
|
5
43
|
## Install
|
|
6
44
|
|
|
7
45
|
From your **project root**, run:
|
|
@@ -242,13 +280,16 @@ npx -y @bridge_gpt/mcp-server doctor
|
|
|
242
280
|
`doctor` is strictly read-only. It reports what it found — configs, registrations,
|
|
243
281
|
credential availability, prerequisites — and changes nothing.
|
|
244
282
|
|
|
245
|
-
**Codex approval profile.** When selecting Codex during `install`,
|
|
246
|
-
|
|
283
|
+
**Codex approval profile.** When selecting Codex during interactive `install`,
|
|
284
|
+
press Enter to accept the default Yes at the profile prompt, or answer `n`/`no`
|
|
285
|
+
to decline. Pass `--tools=codex --codex-profile=on` in a scripted install. Bridge
|
|
247
286
|
creates `$CODEX_HOME/bridge.config.toml` (default `~/.codex/bridge.config.toml`)
|
|
248
287
|
with approval settings for the tools used by its review and implementation
|
|
249
288
|
workflows. Bridge-spawned Codex workers then select it automatically, including
|
|
250
289
|
the fresh implementation session after review. Pass `--codex-profile=off` to
|
|
251
|
-
decline; the installer leaves any existing profile content intact.
|
|
290
|
+
decline; the installer leaves any existing profile content intact. On package
|
|
291
|
+
upgrade, the next Bridge-spawned worker refreshes an older, unmodified managed
|
|
292
|
+
profile before launch. If a profile
|
|
252
293
|
already contains user-authored or conflicting content, the installer leaves it
|
|
253
294
|
untouched and reports that workers cannot use it. Run `doctor --agent codex` to
|
|
254
295
|
inspect the current status. To use the profile in a Codex session you open
|
|
@@ -365,7 +406,7 @@ later boot.
|
|
|
365
406
|
"mcpServers": {
|
|
366
407
|
"bridge": {
|
|
367
408
|
"command": "npx",
|
|
368
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
409
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.59", "serve"],
|
|
369
410
|
"env": {
|
|
370
411
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
371
412
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -387,7 +428,7 @@ later boot.
|
|
|
387
428
|
"bridge": {
|
|
388
429
|
"type": "stdio",
|
|
389
430
|
"command": "npx",
|
|
390
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
431
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.59", "serve"],
|
|
391
432
|
"env": {
|
|
392
433
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
393
434
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -409,7 +450,7 @@ later boot.
|
|
|
409
450
|
"bridge": {
|
|
410
451
|
"type": "stdio",
|
|
411
452
|
"command": "npx",
|
|
412
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
453
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.59", "serve"],
|
|
413
454
|
"env": {
|
|
414
455
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
415
456
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -432,7 +473,7 @@ Windsurf only supports global MCP configuration.
|
|
|
432
473
|
"mcpServers": {
|
|
433
474
|
"bridge": {
|
|
434
475
|
"command": "npx",
|
|
435
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
476
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.59", "serve"],
|
|
436
477
|
"env": {
|
|
437
478
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
438
479
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -451,7 +492,7 @@ Windsurf only supports global MCP configuration.
|
|
|
451
492
|
```toml
|
|
452
493
|
[mcp_servers.bridge]
|
|
453
494
|
command = "npx"
|
|
454
|
-
args = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
495
|
+
args = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.59", "serve"]
|
|
455
496
|
|
|
456
497
|
[mcp_servers.bridge.env]
|
|
457
498
|
BAPI_BASE_URL = "https://bridgegpt-api.com"
|
|
@@ -564,7 +605,7 @@ The tools worth knowing for most tickets.
|
|
|
564
605
|
- **What it does:** Creates one git worktree per ticket and spawns an agent session in each to implement them in parallel.
|
|
565
606
|
- **When it's useful:** (Implementation | Automation) When you're ready to start building one or more refined tickets concurrently.
|
|
566
607
|
- **How to use it:** `/start-tickets BAPI-248 BAPI-250` (see [CLI Subcommands](#cli-subcommands) for the full flag table and cross-platform behavior).
|
|
567
|
-
- **Flags:** `--auto` skip the approval gates · `--base-branch <branch>` branch off something other than the default · `--workflow implement|review-and-implement` selects which slash command each spawned worktree runs · `--rounds=1|2` forwarded to the review phase, valid only with `--workflow review-and-implement` · `--tier cheap|basic|premium`
|
|
608
|
+
- **Flags:** `--auto` skip the approval gates · `--base-branch <branch>` branch off something other than the default · `--workflow implement|review-and-implement` selects which slash command each spawned worktree runs · `--rounds=1|2` forwarded to the review phase, valid only with `--workflow review-and-implement` · `--tier cheap|basic|premium` strict manual model-routing override · `--auto-tier cheap|basic|premium` machine-carried raw tier for chained automation (posture-adjusted; used internally by the `/review-and-implement` handoff), mutually exclusive with `--tier`.
|
|
568
609
|
|
|
569
610
|
**3. Review and Start**
|
|
570
611
|
- **What it does:** Spawns one worktree per ticket; each session reviews the ticket inline and, after a per-ticket proceed/halt gate, hands off to a **fresh implementation session** that reuses the same worktree — review and implementation run in two separate agent contexts, not one shared session.
|
|
@@ -664,43 +705,49 @@ Useful once in a while.
|
|
|
664
705
|
- **How to use it:** Ask your agent — "Post a comment on BAPI-123: blocked on the expired Atlassian token — will retry after it is rotated."
|
|
665
706
|
- **Flags:** A long comment can be attached as a file instead of inlined.
|
|
666
707
|
|
|
667
|
-
**6.
|
|
708
|
+
**6. Report MCP Bug**
|
|
709
|
+
- **What it does:** Reports a Bridge MCP failure; after receipt, a response and apology will follow. Never include secrets, PII, or proprietary code.
|
|
710
|
+
- **When it's useful:** (Support) When a Bridge MCP tool misbehaves and you need to report it.
|
|
711
|
+
- **How to use it:** Ask your agent to report the failed MCP tool, with no secrets, PII, or proprietary code.
|
|
712
|
+
- **Flags:** Critical reports trigger an immediate page; successful reports receive a response and apology follow-up.
|
|
713
|
+
|
|
714
|
+
**7. Read Comments**
|
|
668
715
|
- **What it does:** Reads the comment thread on a ticket.
|
|
669
716
|
- **When it's useful:** (Refinement | Implementation) When the discussion on a ticket matters and you want the agent to read it before acting.
|
|
670
717
|
- **How to use it:** Ask your agent — "Read the comments on BAPI-123 and summarize what was decided."
|
|
671
718
|
- **Flags:** None.
|
|
672
719
|
|
|
673
|
-
**
|
|
720
|
+
**8. Ticket Attachments**
|
|
674
721
|
- **What it does:** Downloads files from a ticket to your disk, or attaches a local file to a ticket.
|
|
675
722
|
- **When it's useful:** (Refinement | Implementation) When a ticket has design files or logs you need locally, or you want to attach output back to it.
|
|
676
723
|
- **How to use it:** Ask your agent — "Download the design mockups attached to BAPI-123 into my docs folder," or "Attach build-log.txt to BAPI-123."
|
|
677
724
|
- **Flags:** Choose the direction (download from the ticket, or upload to it) and, for a download, where the files should land.
|
|
678
725
|
|
|
679
|
-
**
|
|
726
|
+
**9. Estimate Epic**
|
|
680
727
|
- **What it does:** Estimates an epic, or an explicit group of tickets you name.
|
|
681
728
|
- **When it's useful:** (Architecture | Refinement) When you need a sizing pass across an epic, or across a set of tickets you name explicitly.
|
|
682
729
|
- **How to use it:** `/estimate-epic BAPI-123`
|
|
683
730
|
- **Flags:** Pass an epic key, or an explicit list of ticket keys to estimate as one group.
|
|
684
731
|
|
|
685
|
-
**
|
|
732
|
+
**10. Regression Check**
|
|
686
733
|
- **What it does:** Runs the deterministic regression-reviewer (lightweight mode) against a proposed code change and reports its blast radius — which real call-sites, tests, mocks, or config the change does and doesn't account for.
|
|
687
734
|
- **When it's useful:** (Correctness) Before merging a change, to see which real callers, tests, or config it leaves unaccounted for.
|
|
688
735
|
- **How to use it:** `/regression-check` reviews the working tree against `HEAD` by default; `/regression-check --diff main...HEAD` reviews an explicit range.
|
|
689
736
|
- **Flags:** `--diff <range>` review a specific diff/PR range instead of the working tree vs. `HEAD` · `--symbols a,b,c` review a not-yet-diffed planned change by naming its symbols directly.
|
|
690
737
|
|
|
691
|
-
**
|
|
738
|
+
**11. Scan Test Coverage**
|
|
692
739
|
- **What it does:** Scans recently shipped tickets from git history and reports which features have or could gain integration tests, and which can only be smoke tested.
|
|
693
740
|
- **When it's useful:** (Testing) Periodically, to find recently shipped features still missing integration-test coverage and how each could be smoke tested instead.
|
|
694
741
|
- **How to use it:** `/scan-test-coverage` runs incrementally from the last recorded marker in `docs/test-coverage/STATE.md`.
|
|
695
742
|
- **Flags:** `--since=YYYY-MM-DD` override the window start date · `--full` ignore the marker and use a 6-month lookback · `--limit=N` cap the number of features investigated.
|
|
696
743
|
|
|
697
|
-
**
|
|
744
|
+
**12. Estimate Ticket**
|
|
698
745
|
- **What it does:** One-ticket shortcut over the existing epic estimation capability: estimates a single Jira ticket via the same `estimate_epic` tool.
|
|
699
746
|
- **When it's useful:** (Estimation) When you want one ticket's estimate without typing out an epic key or an explicit ticket list.
|
|
700
747
|
- **How to use it:** `/estimate-ticket BAPI-123`
|
|
701
748
|
- **Flags:** `--allow-partial` the sole optional flag.
|
|
702
749
|
|
|
703
|
-
**
|
|
750
|
+
**13. Draft Ticket**
|
|
704
751
|
- **What it does:** Delegates to the Jira Ticket Writer to create only a local Markdown draft — it does not create or update a Jira issue.
|
|
705
752
|
- **When it's useful:** (Refinement) When you want a reviewable local ticket draft before committing to an upload workflow.
|
|
706
753
|
- **How to use it:** `/draft-ticket <description>`
|
|
@@ -789,16 +836,37 @@ Repository and ticket housekeeping commands, beyond the tiers above. Implementat
|
|
|
789
836
|
| `/parse-repository` | Queue a background job to index the repository for Bridge AI agents |
|
|
790
837
|
| `/scan-tickets` | Sync recently-updated Jira tickets and backfill workflow timestamps |
|
|
791
838
|
|
|
792
|
-
> **GitHub Copilot modes.**
|
|
793
|
-
>
|
|
794
|
-
>
|
|
795
|
-
>
|
|
796
|
-
>
|
|
797
|
-
>
|
|
798
|
-
>
|
|
799
|
-
>
|
|
800
|
-
>
|
|
801
|
-
>
|
|
839
|
+
> **GitHub Copilot modes.** Bridge supports Copilot workflow commands in two
|
|
840
|
+
> VS Code chat modes only. The recorded live results live in
|
|
841
|
+
> [`smoke-test/COPILOT-COMMAND-SMOKE.md`](smoke-test/COPILOT-COMMAND-SMOKE.md),
|
|
842
|
+
> which is the authoritative discovery contract.
|
|
843
|
+
>
|
|
844
|
+
> | Copilot mode | Artifact Bridge writes | How you invoke it |
|
|
845
|
+
> |---|---|---|
|
|
846
|
+
> | VS Code Local agent | `.github/prompts/<command>.prompt.md` | `/<command>`, for example `/explore-ticket` |
|
|
847
|
+
> | VS Code Agent Host | `.github/skills/<command>/SKILL.md` (a separately verified project skill; prompt files are not loaded in this mode) | `/<command>` |
|
|
848
|
+
> | Copilot CLI, Copilot cloud coding agent | None | Out of scope |
|
|
849
|
+
>
|
|
850
|
+
> - **When they are written.** `--init` writes both artifacts for every packaged
|
|
851
|
+
> command whenever the project has a `.vscode/` directory. `.vscode/` is only
|
|
852
|
+
> Bridge's VS Code project heuristic; it does not prove that GitHub Copilot is
|
|
853
|
+
> installed or enabled.
|
|
854
|
+
> - **Input.** A prompt file reads its input from `${input:task}`. Text supplied
|
|
855
|
+
> after the slash command takes precedence when present, and the agent asks
|
|
856
|
+
> you for the task when both are empty.
|
|
857
|
+
> - **Tools.** Bridge MCP tools come from the generated `.vscode/mcp.json`
|
|
858
|
+
> registration. Prompt files carry no `tools` frontmatter restriction.
|
|
859
|
+
> - **Reserved names.** `.github/prompts/<packaged-command>.prompt.md` and
|
|
860
|
+
> `.github/skills/<packaged-command>/SKILL.md` are Bridge-managed. Rerunning
|
|
861
|
+
> init overwrites a changed file at one of those paths and reports
|
|
862
|
+
> `Overwritten (content changed)`. Files with any other name are never
|
|
863
|
+
> modified.
|
|
864
|
+
> - **Agent definitions are separate.** `.github/agents/*.agent.md` holds agent
|
|
865
|
+
> definitions, not reusable workflow commands.
|
|
866
|
+
> - **If a command is not discovered.** Open VS Code's Chat Customizations
|
|
867
|
+
> diagnostics and confirm the workspace is trusted. If Agent Host is recorded
|
|
868
|
+
> as `UNSUPPORTED_DISCOVERY`, switch the chat session to the Local agent and
|
|
869
|
+
> use the `.github/prompts/` prompt files instead.
|
|
802
870
|
|
|
803
871
|
### Extra Capabilities
|
|
804
872
|
|
|
@@ -1001,14 +1069,15 @@ npx -y @bridge_gpt/mcp-server start-tickets [flags] KEY [KEY ...]
|
|
|
1001
1069
|
| `--agent claude\|cursor-agent` | `claude` | Agent command to launch in each worktree |
|
|
1002
1070
|
| `--workflow implement\|review-and-implement` | `implement` | Slash command each spawned worktree runs. `review-and-implement` reviews the ticket, then hands approval to a fresh implementation session in the same worktree. `--auto` applies to both phases. |
|
|
1003
1071
|
| `--rounds 1\|2` | unset | Review round count forwarded to the review phase. Review-only — valid only with `--workflow review-and-implement`. |
|
|
1004
|
-
| `--tier cheap\|basic\|premium` | unset (difficulty routing) |
|
|
1072
|
+
| `--tier cheap\|basic\|premium` | unset (difficulty routing) | Strict manual model-routing override. Bypasses **only** the per-ticket difficulty/tier lookup (`GET /jira/tickets/{KEY}/model-tier`) and applies this one tier to every ticket exactly as supplied, with no per-session posture adjustment; Claude/Cursor use their registry aliases and repository overrides, while Codex uses the authenticated Bridge catalog. It is **not** a raw `--model` alias and never carries an API key or credential. A malformed value fails open to premium routing when its alias is available. Mutually exclusive with `--auto-tier`. |
|
|
1073
|
+
| `--auto-tier cheap\|basic\|premium` | unset | Internal/advanced machine-carried raw backend tier for chained automation (BAPI-1236). Bypasses the per-ticket lookup like `--tier`, but remains subject to the launching session's own `maximize_subscription` downshift. Used by the `/review-and-implement` handoff to reuse the review-time raw tier snapshot without silently skipping that downshift. Mutually exclusive with `--tier`. |
|
|
1005
1074
|
| `--terminal terminal\|iterm` | auto-detect via `$TERM_PROGRAM` | Override the macOS terminal app (honored on macOS only) |
|
|
1006
1075
|
| `--dry-run` | off | Print intended actions; create no worktrees, open no tabs (any OS) |
|
|
1007
1076
|
| `--branch KEY=BRANCH` | `feature/<KEY>` | Use a custom branch for that ticket (repeatable) |
|
|
1008
1077
|
| `--base-branch <BRANCH>` | `main` | Cut new worktrees from `<BRANCH>` and refresh `origin/<BRANCH>` instead of `main` |
|
|
1009
1078
|
| `--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. |
|
|
1010
1079
|
| `--max-parallel N` | `3` | Max worktrees created concurrently |
|
|
1011
|
-
| `--conductor` | off | Opt into the Conductor system (per-worker `
|
|
1080
|
+
| `--conductor` | off | Opt into the Conductor system (per-worker `BAPI_EPIC_IMPLEMENTER_*` env + Claude hook injection, lifecycle ledger events, and a supervisor peer tab). **Default off** — a plain run spawns `cd <worktree> && <agent> '/implement-ticket <KEY>'`. |
|
|
1012
1081
|
| `-h`, `--help` | — | Show usage |
|
|
1013
1082
|
|
|
1014
1083
|
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. Claude is the default. Cursor with `--auto` runs headlessly after a bounded authentication/readiness check; manual Cursor runs remain interactive for human review gates. To launch Cursor Agent instead of Claude Code:
|
|
@@ -1021,7 +1090,11 @@ npx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248
|
|
|
1021
1090
|
|
|
1022
1091
|
For Codex, `GET /jira/codex-model-catalog?repo_name=...` is the sole tier-to-model authority. Bridge derives its three bare CLI IDs and SHA-256 mapping revision at request time from `MODEL_REGISTRY`; the launcher fetches and validates that catalog once per invocation. Repository `difficulty_model_tier_overrides` do not apply to Codex. A failed access, config, tier, catalog, revision, or alias check omits `--model` and launches the configured Codex default. The catalog expresses **Bridge provider policy only**: it cannot confirm that the local Codex login or subscription can use a model. The CLI has no supported non-interactive entitlement probe, so routing emits that limitation as a diagnostic and never uses Bridge's OpenAI API key as proof of local access.
|
|
1023
1092
|
|
|
1024
|
-
**
|
|
1093
|
+
**Maximize-subscription posture (BAPI-1224, extended by BAPI-1236).** The per-repository `optimization_posture` config field (`quality` | `speed` | `maximize_subscription`; unset/NULL, malformed, or unreadable all resolve to `quality`) is read once per invocation, isolated from the routing-config fetch above, and applied to a tier resolved by either the automatic per-ticket difficulty lookup **or** a carried `--auto-tier` raw snapshot — both paths share the same posture resolution and downshift call site. Only `maximize_subscription` downshifts that tier one level — `premium -> basic`, `basic -> cheap`, `cheap -> cheap` (the fixed floor) — before alias resolution; `quality`, `speed`, and every degraded posture outcome preserve current (Quality) routing unchanged. Claude and Cursor still resolve the downshifted tier through their unmodified registry aliases, and Codex still indexes the fetched catalog (`catalog.models[effectiveTier]`) — no maximize-specific alias table exists anywhere. An explicit `--tier` override remains an exact manual override (the posture is never even read on that path), and the harness-local premium fallback used when no usable tier is available stays on `premium` regardless of posture. A degraded posture read never fails a spawn; it appends a secret-free `optimization-posture-unavailable` warning to affected rows and stamps at most one de-duplicated diagnostic per invocation (stderr-only, never Sentry). A separate, additive `[model-routing]` stderr line (never stdout) reports the source/baseline/posture/adjustment/effective-tier/alias for every successfully baseline-routed row, across all three routing paths (`--tier`, `--auto-tier`, and the automatic difficulty lookup).
|
|
1094
|
+
|
|
1095
|
+
**Coarse `--tier` override.** Passing `--tier cheap|basic|premium` bypasses only the per-ticket difficulty lookup and applies that tier to every ticket exactly as supplied — posture is never read on this path. Claude/Cursor use the same alias resolution as normal routing; Codex uses the fetched catalog. It is never a raw model alias, and no API key or credential belongs in the spawned command. A malformed tier requests the premium fallback when its alias is available; otherwise the agent default launches.
|
|
1096
|
+
|
|
1097
|
+
**Machine-carried `--auto-tier` handoff (BAPI-1236).** Passing `--auto-tier cheap|basic|premium` also bypasses the per-ticket difficulty lookup, but — unlike `--tier` — the carried tier IS routed through the launching session's own `maximize_subscription` downshift, exactly once. A malformed, empty, or repeated `--auto-tier` value is a hard parser error (no fail-open sentinel), and `--tier`/`--auto-tier` are mutually exclusive regardless of order. `/review-and-implement` uses this flag, never `--tier`, to hand its review-time raw tier snapshot to the fresh implementation session — so a `maximize_subscription` repo still gets the downshift on the chained handoff instead of silently skipping it.
|
|
1025
1098
|
|
|
1026
1099
|
**Conductor observability (opt-in via `--conductor`, BAPI-394).** Conductor is **off by default**. With `--conductor`, a run mints a conductor `run_id`, emits events into the local ledger (`~/.config/bridge/events.db`), opens a supervisor peer tab, and (for Claude Code) injects a secret-free lifecycle hook into each worktree; it also sets `BRIDGE_MCP_PROFILE=conductor` so workers get the conductor MCP tools. Observability is best-effort — a conductor failure never blocks a spawn, and `--dry-run` performs no conductor side effects. (Epic-tick dispatch always enables conductor internally.) See **[CONDUCTOR.md](./docs/CONDUCTOR.md)** for the full observability, git-hook, done-gate, and auto-merge reference.
|
|
1027
1100
|
|
|
@@ -1077,12 +1150,12 @@ your project can actually run:
|
|
|
1077
1150
|
npx -y @bridge_gpt/mcp-server drive-epic <EPIC>
|
|
1078
1151
|
```
|
|
1079
1152
|
|
|
1080
|
-
You are never asked to choose.
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1153
|
+
You are never asked to choose. `drive-epic` routes to exactly one conductor, the
|
|
1154
|
+
v2 engine — one transition authority per epic is what keeps an epic from wedging.
|
|
1155
|
+
Readiness green routes to the v2 bootstrap below (pass `--plan-file`
|
|
1156
|
+
and `drive-epic` runs it for you); readiness not green reports that the v2 engine
|
|
1157
|
+
is not ready to drive this epic — there is no alternative conductor to fall back
|
|
1158
|
+
to — and prints no conductor invocation. If readiness is **unknown** — unreachable,
|
|
1086
1159
|
unauthorized, or malformed — it escalates and prints no conductor invocation at
|
|
1087
1160
|
all, because an unknown owner is not the same as a not-ready one. No branch,
|
|
1088
1161
|
including every error path, ever offers you two paths.
|
|
@@ -1197,80 +1270,6 @@ Once the plan is approved, the **server-side reconciler** picks the run up withi
|
|
|
1197
1270
|
npx -y @bridge_gpt/mcp-server executor --repo <name>
|
|
1198
1271
|
```
|
|
1199
1272
|
|
|
1200
|
-
### `implement-epic`
|
|
1201
|
-
|
|
1202
|
-
The deterministic half of the `/conduct-epic` loop: it owns the epic branch, a
|
|
1203
|
-
versioned local checkpoint, a per-epic lock, and the read-only probes the loop
|
|
1204
|
-
decides on. It never creates or mutates an `epic_run` — that is the server-side
|
|
1205
|
-
v2 reconciler's job, and `init` refuses to start when one is already active.
|
|
1206
|
-
This is EPIC-LEVEL orchestration, distinct from the ticket-level
|
|
1207
|
-
`implement-ticket` command.
|
|
1208
|
-
|
|
1209
|
-
```
|
|
1210
|
-
npx -y @bridge_gpt/mcp-server implement-epic <verb> [flags]
|
|
1211
|
-
```
|
|
1212
|
-
|
|
1213
|
-
**Verbs**
|
|
1214
|
-
|
|
1215
|
-
| Verb | Flags |
|
|
1216
|
-
| --- | --- |
|
|
1217
|
-
| `init <EPIC>` | `--tickets K1,K2,…` (required), `--base-branch <b>`, `--checkpoint-path <p>`, `--dry-run`, `--json` |
|
|
1218
|
-
| `status <EPIC>` | `--json` (required), `--checkpoint-path <p>` |
|
|
1219
|
-
| `checkpoint set <EPIC>` | `--ticket <KEY>` (required), `--field <name> <value>` (repeatable), `--journal "<line>"`, `--checkpoint-path <p>` |
|
|
1220
|
-
| `finish <EPIC>` | `--checkpoint-path <p>`, `--json` |
|
|
1221
|
-
| `spawn <EPIC>` | `--ticket <KEY>` and `--prompt-file <path>` (required), `--agent claude\|cursor-agent`, `--checkpoint-path <p>`, `--json` |
|
|
1222
|
-
|
|
1223
|
-
**Local state.** Everything lives *outside* the repository, under
|
|
1224
|
-
`~/.config/bridge/conduct/<repo>/` (honoring `XDG_CONFIG_HOME`), so it resolves
|
|
1225
|
-
identically from the main checkout and from any worktree and can never be
|
|
1226
|
-
committed by an agent running `git add`:
|
|
1227
|
-
|
|
1228
|
-
| Path | Purpose |
|
|
1229
|
-
| --- | --- |
|
|
1230
|
-
| `<EPIC>.json` | the version-1 checkpoint (file `0600`, directory `0700`) |
|
|
1231
|
-
| `<EPIC>.json.prev` | the previous valid checkpoint, retained on every write |
|
|
1232
|
-
| `<EPIC>.lock` | the per-epic lock |
|
|
1233
|
-
| `<EPIC>/prompts/<KEY>-<kind>-<n>.md` | prompt files the caller writes for `spawn` |
|
|
1234
|
-
|
|
1235
|
-
`status` prints the resolved `checkpoint_path`. To unpark a run a human edits the
|
|
1236
|
-
checkpoint (`needs_human` → `null`, plus the ticket's `status`/counters);
|
|
1237
|
-
`last_seen_head`, `ci_last_poll`, and `lock` are observational and are never
|
|
1238
|
-
hand-edited.
|
|
1239
|
-
|
|
1240
|
-
**`init` runs ONE preflight** that reports *every* failure in a single pass and
|
|
1241
|
-
writes nothing unless all of them pass: `gh auth status`; Worktrunk resolves
|
|
1242
|
-
(honoring `BAPI_WORKTRUNK_BIN`); Bridge credentials resolve; `auto_merge_enabled`
|
|
1243
|
-
is on — or is turned on by PUTting the *complete* effective config back with just
|
|
1244
|
-
that flag flipped, which prints a line beginning `announced:`; at least one
|
|
1245
|
-
required CI check exists (an empty required set would make the done gate pass
|
|
1246
|
-
vacuously); no active server-side `epic_run` for the key; the lock is free or its
|
|
1247
|
-
owner is provably dead; the base branch exists on `origin` after `git fetch`; and
|
|
1248
|
-
the indexed-branch override is either absent or this epic's own — a re-`init`
|
|
1249
|
-
after a crash is accepted and its `original_base_branch` becomes the default base,
|
|
1250
|
-
while a *foreign* override is refused by name. `resolve-ci-checks` is called
|
|
1251
|
-
exactly once either way, because that call is what warms the `poll-ci-checks`
|
|
1252
|
-
cache the first `status` depends on. Only then does `init` push
|
|
1253
|
-
`epic/<EPIC>` to `origin` at the fetched base tip (no local checkout), repoint the
|
|
1254
|
-
index, write the checkpoint, and take the lock. `--dry-run` prints the validated
|
|
1255
|
-
plan and mutates nothing. A second `init` refuses with `already initialized`.
|
|
1256
|
-
|
|
1257
|
-
**Failure posture is split on purpose.** In `status`, each probe fails *open*: a
|
|
1258
|
-
`gh`, CI, review, or parse failure leaves that sub-object `null`, adds an entry to
|
|
1259
|
-
`probe_errors`, and the command still exits `0` with a complete object — the loop
|
|
1260
|
-
must be able to read its own checkpoint during a GitHub outage. Everything else
|
|
1261
|
-
fails *closed*: a corrupt or wrong-version checkpoint makes every verb but `init`
|
|
1262
|
-
exit non-zero **without rewriting it**, and `checkpoint set`, `spawn`, and
|
|
1263
|
-
`finish` refuse a lock held by another live process. `status` never takes the lock.
|
|
1264
|
-
|
|
1265
|
-
**Exit codes.** `0` on success — including a missing checkpoint
|
|
1266
|
-
(`checkpoint_exists: false`) and an idempotent second `finish`. Non-zero on any
|
|
1267
|
-
other failure, with a one-line reason on stderr. With `--json`, stdout is exactly
|
|
1268
|
-
one JSON object carrying `ok`.
|
|
1269
|
-
|
|
1270
|
-
**Credentials** resolve only from `BAPI_API_KEY` or the user-scoped
|
|
1271
|
-
`bapi:<repo>` credential target, travel only in the `X-API-Key` header, and never
|
|
1272
|
-
appear in a command argument, in stdout/stderr, or in a journal line.
|
|
1273
|
-
|
|
1274
1273
|
### Conductor (epic & multi-agent orchestration)
|
|
1275
1274
|
|
|
1276
1275
|
Conductor is an **opt-in, off-by-default** layer for epic supervision, inter-agent messaging, done-gate evaluation, local git-hook event producers, and conditional auto-merge. Its full reference — the v2 architecture (server-side reconciler + local executor), `setup-epic`, `epic-implementer install-git-hooks`, the supervisor `done_gate_config` / `auto_merge_enabled` settings, and the observability stream — lives in **[CONDUCTOR.md](./docs/CONDUCTOR.md)**. (The event-ledger CLI was renamed from `conductor` to `epic-implementer`; the retained `conductor` bin name is now a fixed migration pointer.)
|
|
@@ -1562,14 +1561,14 @@ The full surface, for when you need the complete enumeration. Day-to-day, use [U
|
|
|
1562
1561
|
|
|
1563
1562
|
### MCP tools
|
|
1564
1563
|
|
|
1565
|
-
The authoritative tool catalog covers **
|
|
1564
|
+
The authoritative tool catalog covers **77 tools** (enumerated below). What's actually registered in a session depends on `BRIDGE_MCP_PROFILE`: the default `core` profile loads a trimmed subset, and the conductor/pipeline-authoring/SFCC tools are added only under their respective profiles (see [Environment Variables](#environment-variables)). Async AI tools follow a request/get pattern: call the `request_*` tool to kick off generation, then the matching `get_*` tool to retrieve the result (or pass `wait_for_result: true` to poll automatically).
|
|
1566
1565
|
|
|
1567
1566
|
- **Connectivity & identity** — `ping` (its JSON also carries `docs_dir`, `role`, and `customer_type`)
|
|
1568
1567
|
- **Team & access** — `invite_member` (admin-only; mints a scoped access key for a teammate on an already-configured project — the plaintext key is shown exactly once)
|
|
1569
1568
|
- **Jira tickets** — `get_tickets`, `get_ticket`, `create_ticket`, `update_ticket_description`, `add_comment`, `get_comments`
|
|
1570
|
-
- **Attachments** — `attachment` (operations: `upload`, `download`, `list`)
|
|
1569
|
+
- **Attachments** — `attachment` (operations: `upload`, `download`, `list`, `delete`); `publish_reviewed_attachment` appends only a captured review's fixed Markdown copy during auto review, leaving older Jira attachments intact.
|
|
1571
1570
|
- **AI generation (request/get)** — `request_plan_generation`/`get_plan`, `create_doc`/`get_doc` (design docs by `doc_type`: tdd/architecture/fsd/prd, where `architecture` is an alias of `tdd`), `request_ticket_review` (writes both `get_clarifying_questions` and `get_ticket_critique`), `request_reimplement_context`/`get_reimplement_context`, `request_council`/`get_council`, `request_deep_research`/`get_deep_research`
|
|
1572
|
-
- **Bounded waits** — `wait_for_ticket_review`, `wait_for_ci_checks`. Each
|
|
1571
|
+
- **Bounded waits** — `wait_for_ticket_review`, `wait_for_ci_checks`. Each returns `state: "ready" | "pending" | "error"`; review waits default to 20 s and CI waits default to 240 s (both accept up to 240 s explicitly). `pending` is a normal, resumable result you call again on, never a failure. They exist so an agent can wait without a foreground `sleep` and without binding a long server-side operation to the 900 s MCP client deadline. `wait_for_ci_checks` reports only that checks are **terminal** — deciding pass or fail stays with the caller.
|
|
1573
1572
|
- **Other AI** — `second_opinion`, `generate_image`, `generate_decision_page`, `visual_diff` (deterministic pixel diff of a rendered URL vs a design comp)
|
|
1574
1573
|
- **Ticket lifecycle** — `track_ticket`, `update_ticket_state`, `get_ticket_state`, `get_ticket_state_tree` (live repo-wide lifecycle + dependency tree; read-only, no mutation parameter)
|
|
1575
1574
|
- **Jira status** — `update_jira_status` (pass `status: "auto"` to resolve the configured post-PR status server-side)
|
|
@@ -1584,7 +1583,7 @@ Pipelines are declarative, multi-step workflows your AI agent executes step-by-s
|
|
|
1584
1583
|
| Pipeline | Description | Invoke with |
|
|
1585
1584
|
|---|---|---|
|
|
1586
1585
|
| `implement-ticket` | Generate a plan, execute the implementation, commit, open a PR, and monitor CI | `/implement-ticket PROJ-123` |
|
|
1587
|
-
| `review-ticket` | Full ticket quality review: clarifying questions + critique plus an automatic second-opinion pass, then evaluation and decision capture. The backend owns round orchestration; pass `--rounds=1` for a single-pass review or `--rounds=2` to force the full second-opinion review, or omit `--rounds` to let the backend decide adaptively. Internal atomic pipeline invoked by `/review-tickets` (inline for one key, spawned per tab for several) and by `/review-and-implement`, `/review-and-start`, `/start-tickets
|
|
1586
|
+
| `review-ticket` | Full ticket quality review: clarifying questions + critique plus an automatic second-opinion pass, then evaluation and decision capture. The backend owns round orchestration; pass `--rounds=1` for a single-pass review or `--rounds=2` to force the full second-opinion review, or omit `--rounds` to let the backend decide adaptively. Internal atomic pipeline invoked by `/review-tickets` (inline for one key, spawned per tab for several) and by `/review-and-implement`, `/review-and-start`, and `/start-tickets`. | `/review-tickets PROJ-123` |
|
|
1588
1587
|
| `idea-to-ticket` | Turn an idea into a Jira Task/Spike (or Epic + children) with research, dedup, and critique | `/idea-to-ticket "<idea>"` |
|
|
1589
1588
|
| `plan-epic` | Decompose an epic into sub-tasks with a structured exploration doc for each | `/plan-epic "<epic>"` |
|
|
1590
1589
|
| `full-automation` | Client-side wrapper: runs the `idea-to-ticket` recipe, then hands the created keys to `/review-and-start --auto` | `/idea-to-pr "<idea>"` |
|
package/build/agent-registry.js
CHANGED
|
@@ -24,6 +24,21 @@ export function isValidModelAlias(value) {
|
|
|
24
24
|
export function isModelTier(value) {
|
|
25
25
|
return value === "cheap" || value === "basic" || value === "premium";
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* BAPI-1224 — downshift one model tier: `premium -> basic`, `basic -> cheap`,
|
|
29
|
+
* `cheap -> cheap` (the fixed floor). Total and exhaustive: no permissive
|
|
30
|
+
* default branch, so extending `ModelTier` forces an explicit decision here.
|
|
31
|
+
*/
|
|
32
|
+
export function downshiftModelTier(tier) {
|
|
33
|
+
switch (tier) {
|
|
34
|
+
case "premium":
|
|
35
|
+
return "basic";
|
|
36
|
+
case "basic":
|
|
37
|
+
return "cheap";
|
|
38
|
+
case "cheap":
|
|
39
|
+
return "cheap";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
27
42
|
/**
|
|
28
43
|
* The registry: the ONLY place mapping an agent name to its command/spec. Seeded
|
|
29
44
|
* with `claude` (default), `cursor-agent`, and `codex`. `as const satisfies` keeps
|
package/build/agent-utils.js
CHANGED
|
@@ -115,10 +115,18 @@ export function translateAgentToCopilot(frontmatter, body) {
|
|
|
115
115
|
* command catalog.
|
|
116
116
|
*/
|
|
117
117
|
function commandDescription(source, commandName) {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
const lines = source.split(/\r?\n/).map((candidate) => candidate.trim());
|
|
119
|
+
// Some commands open with host-parsed YAML frontmatter; its delimiters and
|
|
120
|
+
// keys are metadata, not a summary, so drop the block before scanning.
|
|
121
|
+
if (lines[0] === "---") {
|
|
122
|
+
const close = lines.indexOf("---", 1);
|
|
123
|
+
if (close > 0)
|
|
124
|
+
lines.splice(0, close + 1);
|
|
125
|
+
}
|
|
126
|
+
const line = lines.find((candidate) => candidate.length > 0 &&
|
|
127
|
+
candidate !== "$ARGUMENTS" &&
|
|
128
|
+
!candidate.startsWith("#") &&
|
|
129
|
+
!/^[-*_\s]+$/.test(candidate));
|
|
122
130
|
return line ?? `Run the Bridge ${commandName} workflow.`;
|
|
123
131
|
}
|
|
124
132
|
function commandBodyForPrompt(source) {
|
|
@@ -136,17 +144,17 @@ function commandBodyForSkill(source) {
|
|
|
136
144
|
export function translateCommandToCopilot(filename, source) {
|
|
137
145
|
const commandName = filename.replace(/\.md$/, "");
|
|
138
146
|
const description = commandDescription(source, commandName);
|
|
147
|
+
// No `tools:` frontmatter: Bridge MCP tools reach Copilot through the
|
|
148
|
+
// generated `.vscode/mcp.json` registration, not a prompt-level restriction.
|
|
139
149
|
const prompt = [
|
|
140
150
|
"---",
|
|
141
151
|
`description: ${quoteYamlValue(description)}`,
|
|
142
152
|
"argument-hint: \"[task or ticket key]\"",
|
|
143
|
-
"tools:",
|
|
144
|
-
" - bridge/*",
|
|
145
153
|
"---",
|
|
146
154
|
"",
|
|
147
155
|
`# Bridge ${commandName} workflow`,
|
|
148
156
|
"",
|
|
149
|
-
"The
|
|
157
|
+
"The workflow input is `${input:task}`. When text is supplied after the slash command, that text takes precedence; if neither contains input, ask the user for the task before continuing. Follow the canonical workflow below.",
|
|
150
158
|
"",
|
|
151
159
|
commandBodyForPrompt(source),
|
|
152
160
|
].join("\n");
|
|
@@ -160,7 +168,7 @@ export function translateCommandToCopilot(filename, source) {
|
|
|
160
168
|
"",
|
|
161
169
|
`# Bridge ${commandName} workflow`,
|
|
162
170
|
"",
|
|
163
|
-
"Use the text supplied after this slash command as the workflow input. Follow the canonical workflow below.",
|
|
171
|
+
"Use the text supplied after this slash command as the workflow input; if no text was supplied, ask the user for the task before continuing. Follow the canonical workflow below.",
|
|
164
172
|
"",
|
|
165
173
|
commandBodyForSkill(source),
|
|
166
174
|
].join("\n");
|
|
@@ -52,21 +52,6 @@ const CLAUDE_USER_CONFIG_FILENAME = ".claude.json";
|
|
|
52
52
|
export function resolveClaudeUserConfigPath(homeDirectory, platform) {
|
|
53
53
|
return pathApiForProvisioningPlatform(platform).join(homeDirectory, CLAUDE_USER_CONFIG_FILENAME);
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
56
|
-
* RETIRED (BAPI-790) — this override is now behaviorally inert.
|
|
57
|
-
*
|
|
58
|
-
* It used to downgrade a confirmed shadowing collision from a refusal to a loud
|
|
59
|
-
* warning. There is no refusal left to downgrade: strict flag-scoped MCP loading
|
|
60
|
-
* means an operator registration cannot shadow a worker, so the finding is always
|
|
61
|
-
* advisory and setting this variable changes nothing.
|
|
62
|
-
*
|
|
63
|
-
* The constant is kept exported, and {@link isClaudeMcpShadowingOverrideEnabled}
|
|
64
|
-
* with it, so an operator who still has it set in a service unit or shell profile
|
|
65
|
-
* gets no error — and so the name stays greppable to whoever wonders why it stopped
|
|
66
|
-
* mattering. Nothing in the policy consults it. Do not reintroduce a branch on it
|
|
67
|
-
* without re-establishing that shadowing can affect a worker again.
|
|
68
|
-
*/
|
|
69
|
-
export const CONDUCTOR_ALLOW_CLAUDE_MCP_SHADOWING_ENV = "BAPI_CONDUCTOR_ALLOW_CLAUDE_MCP_SHADOWING";
|
|
70
55
|
/** A plain, non-array object. */
|
|
71
56
|
function asRecord(value) {
|
|
72
57
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -262,27 +247,8 @@ export async function inspectClaudeUserConfigForMcpShadowing(input, deps) {
|
|
|
262
247
|
return { findings, diagnostics };
|
|
263
248
|
}
|
|
264
249
|
// ---------------------------------------------------------------------------
|
|
265
|
-
//
|
|
250
|
+
// Shared policy
|
|
266
251
|
// ---------------------------------------------------------------------------
|
|
267
|
-
/** Documented truthy override values (trimmed, case-insensitive). */
|
|
268
|
-
const OVERRIDE_TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
269
|
-
/**
|
|
270
|
-
* True only for a trimmed, case-insensitive `1`, `true`, `yes`, or `on`. Absent,
|
|
271
|
-
* blank, `0`, `false`, `no`, `off`, and anything else are disabled.
|
|
272
|
-
*
|
|
273
|
-
* RETIRED (BAPI-790): no policy branches on this any more — see
|
|
274
|
-
* {@link CONDUCTOR_ALLOW_CLAUDE_MCP_SHADOWING_ENV}. Retained as a pure predicate
|
|
275
|
-
* so the parsing rule stays documented and testable rather than being deleted and
|
|
276
|
-
* re-guessed if the override is ever revived.
|
|
277
|
-
*/
|
|
278
|
-
export function isClaudeMcpShadowingOverrideEnabled(env) {
|
|
279
|
-
const raw = env?.[CONDUCTOR_ALLOW_CLAUDE_MCP_SHADOWING_ENV];
|
|
280
|
-
if (typeof raw !== "string")
|
|
281
|
-
return false;
|
|
282
|
-
return OVERRIDE_TRUTHY.has(raw.trim().toLowerCase());
|
|
283
|
-
}
|
|
284
|
-
/** The exact wording every override path emits, pinned by tests. */
|
|
285
|
-
export const CLAUDE_MCP_SHADOWING_OVERRIDE_WARNING = `Proceeding anyway because ${CONDUCTOR_ALLOW_CLAUDE_MCP_SHADOWING_ENV} is set`;
|
|
286
252
|
/** Human-readable scope labels (fixed strings, never derived from the config). */
|
|
287
253
|
const SCOPE_LABELS = {
|
|
288
254
|
user: "user scope (mcpServers)",
|
|
@@ -13,6 +13,7 @@ export const BRIDGE_WORKFLOW_TOOLS = [
|
|
|
13
13
|
"ping", "get_ticket", "get_pipeline_recipe", "request_ticket_review",
|
|
14
14
|
"wait_for_ticket_review", "fresh_base", "generate_decision_page",
|
|
15
15
|
"capture_review_decisions", "request_plan_generation", "get_plan",
|
|
16
|
+
"attachment", "publish_reviewed_attachment",
|
|
16
17
|
"get_phase_context", "record_phase_result", "create_pull_request",
|
|
17
18
|
"update_jira_status", "config_field", "resolve_ci_checks", "poll_ci_checks",
|
|
18
19
|
"wait_for_ci_checks",
|
|
@@ -76,8 +77,11 @@ export async function inspectBridgeCodexProfile(home, readFile) {
|
|
|
76
77
|
return { status: "declined", key };
|
|
77
78
|
if (profile === null || !("hash" in parsed) || typeof parsed.hash !== "string")
|
|
78
79
|
return { status: "conflict", key };
|
|
79
|
-
if (
|
|
80
|
+
if (fingerprint(profile) !== parsed.hash)
|
|
80
81
|
return { status: "conflict", key };
|
|
82
|
+
if (profile !== renderBridgeCodexProfile(key)) {
|
|
83
|
+
return { status: profile.startsWith(MARKER) ? "stale" : "conflict", key };
|
|
84
|
+
}
|
|
81
85
|
return { status: "active", key };
|
|
82
86
|
}
|
|
83
87
|
export async function configureBridgeCodexProfile(home, choice, deps) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readBridgeConfig } from "./bridge-config.js";
|
|
3
3
|
import { codexBridgeTableNames } from "./mcp-host-config.js";
|
|
4
4
|
import { codexHome } from "./codex-home.js";
|
|
5
|
-
import { codexProfilePaths, inspectBridgeCodexProfile, BRIDGE_CODEX_PROFILE } from "./codex-bridge-profile.js";
|
|
5
|
+
import { codexProfilePaths, configureBridgeCodexProfile, inspectBridgeCodexProfile, BRIDGE_CODEX_PROFILE } from "./codex-bridge-profile.js";
|
|
6
6
|
import { buildMcpServerEntriesForManifest, normalizeWorktreePathForRegistration, } from "./mcp-provisioning.js";
|
|
7
7
|
import { MCP_SERVER_NAME } from "./mcp-identity.js";
|
|
8
8
|
/** A missing Bridge target is a launch failure, never a fallback to user-scoped config. */
|
|
@@ -39,7 +39,18 @@ export async function resolveCodexLaunchScope(root, deps, codexConfigPath = code
|
|
|
39
39
|
}
|
|
40
40
|
const bridgeKey = registeredNames[0] ?? MCP_SERVER_NAME;
|
|
41
41
|
const home = codexHome(deps.env ?? process.env, deps.homedir?.());
|
|
42
|
-
|
|
42
|
+
let profile = await inspectBridgeCodexProfile(home, deps.readFile);
|
|
43
|
+
if (profile.status === "stale") {
|
|
44
|
+
try {
|
|
45
|
+
profile = await configureBridgeCodexProfile(home, "enable", deps);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { ok: false, error: "Codex MCP scope unavailable: could not refresh the managed Bridge approval profile." };
|
|
49
|
+
}
|
|
50
|
+
if (profile.status !== "active") {
|
|
51
|
+
return { ok: false, error: "Codex MCP scope unavailable: managed Bridge approval profile refresh failed." };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
43
54
|
const argv = ["-C", normalized.path];
|
|
44
55
|
if (profile.status === "active" && profile.key === bridgeKey)
|
|
45
56
|
argv.push("--profile", BRIDGE_CODEX_PROFILE);
|