@bridge_gpt/mcp-server 0.2.54 → 0.2.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +38 -54
  2. package/build/agent-guidance.generated.js +3 -0
  3. package/build/agent-launchers/claude-executor-adapter.js +3 -0
  4. package/build/agent-notes.js +178 -0
  5. package/build/agent-registry.js +5 -2
  6. package/build/agent-utils.js +58 -0
  7. package/build/agents.generated.js +1 -1
  8. package/build/codex-skill-adapter.js +55 -0
  9. package/build/commands.generated.js +5 -4
  10. package/build/conductor/bridge-api-client.js +199 -6
  11. package/build/conductor/bring-up-facts.js +187 -0
  12. package/build/conductor/claude-hook.js +7 -5
  13. package/build/conductor/cli.js +28 -0
  14. package/build/conductor/doctor.js +80 -9
  15. package/build/conductor/epic-implementer-cli.js +1298 -0
  16. package/build/conductor/epic-runtime.js +1 -1
  17. package/build/conductor/errors.js +2 -2
  18. package/build/conductor/git-ci-types.js +1 -1
  19. package/build/conductor/git-hooks.js +28 -14
  20. package/build/conductor/install-doctor.js +11 -5
  21. package/build/conductor/readiness-cli.js +10 -10
  22. package/build/conductor/readiness-sections.js +58 -9
  23. package/build/conductor/readiness.js +37 -6
  24. package/build/conductor/recovery-cli.js +289 -10
  25. package/build/conductor/recovery-operations.js +125 -2
  26. package/build/conductor/repair-contract.js +58 -0
  27. package/build/conductor/store.js +2 -2
  28. package/build/conductor/supervisor-runtime.js +1 -1
  29. package/build/conductor-bin.js +2 -139
  30. package/build/conductor-claude-hook-bin.js +2 -2
  31. package/build/conductor-claude-hook-removed-stub-bin.js +31 -0
  32. package/build/conductor-removed-stub-bin.js +30 -0
  33. package/build/docs.generated.js +1 -1
  34. package/build/doctor.js +77 -17
  35. package/build/drive-epic.js +541 -115
  36. package/build/epic-implementer-bin.js +145 -0
  37. package/build/epic-implementer-bundle-cli.js +264 -0
  38. package/build/epic-implementer-claude-hook-bin.js +3 -0
  39. package/build/epic-integration-pr.js +5 -3
  40. package/build/executor/env.js +6 -0
  41. package/build/executor/interrupted-worktree.js +60 -0
  42. package/build/executor/job-errors.js +45 -0
  43. package/build/executor/job-runner.js +274 -9
  44. package/build/executor/job-types.js +25 -9
  45. package/build/executor/merge-tree-classifier.js +171 -0
  46. package/build/executor/reconcile-overlap-governance.js +129 -0
  47. package/build/executor/reconcile-overlap-job.js +989 -0
  48. package/build/executor/reconcile-overlap-types.js +14 -0
  49. package/build/executor/spawn-job-driver.js +1 -0
  50. package/build/executor/types.js +2 -0
  51. package/build/executor/worker-finalization.js +25 -2
  52. package/build/executor/worker-guard-hook.js +15 -7
  53. package/build/implement-epic/bridge-client.js +773 -0
  54. package/build/implement-epic/checkpoint-store.js +542 -0
  55. package/build/implement-epic/cli.js +3158 -0
  56. package/build/implement-epic/cut-protocol.js +392 -0
  57. package/build/implement-epic/lock.js +302 -0
  58. package/build/implement-epic/pr-state.js +286 -0
  59. package/build/implement-epic/spawn.js +113 -0
  60. package/build/index.js +586 -138
  61. package/build/init.js +72 -8
  62. package/build/install-bridge-conductor.js +5 -5
  63. package/build/install-bridge.js +403 -70
  64. package/build/mcp-host-config.js +22 -60
  65. package/build/mcp-host-entry-adapter.js +18 -0
  66. package/build/mcp-host-targets.js +1 -21
  67. package/build/merge-pull-request.js +1 -1
  68. package/build/pipelines.generated.js +7 -7
  69. package/build/plan-epic-conductor-eligibility.js +1 -1
  70. package/build/plane/cli.js +36 -5
  71. package/build/plane/preflight.js +128 -12
  72. package/build/plane/shutdown.js +4 -4
  73. package/build/readiness-check.js +3 -3
  74. package/build/readme.generated.js +1 -1
  75. package/build/run-unit-tests-launcher.js +1 -1
  76. package/build/setup-epic.js +69 -31
  77. package/build/start-tickets-conductor.js +8 -7
  78. package/build/version.generated.js +3 -3
  79. package/build/worker-guard-hook-bin.js +1 -1
  80. package/docs/CONDUCTOR.md +8 -6
  81. package/package.json +5 -3
package/README.md CHANGED
@@ -32,8 +32,7 @@ account — it will ask.
32
32
  `--api-key` or `BAPI_API_KEY` — the installer writes that store, it does not read
33
33
  it back.
34
34
  - Writes outside your project root only when you pick a host whose configuration is
35
- global: OpenAI Codex (`~/.codex/config.toml`) and GitHub Copilot CLI
36
- (`~/.copilot/mcp-config.json`).
35
+ global: OpenAI Codex (`~/.codex/config.toml`).
37
36
 
38
37
  **Prerequisites**
39
38
 
@@ -41,8 +40,8 @@ account — it will ask.
41
40
  - **A project directory** — run the command from the folder your editor opens: your
42
41
  repository root, the one containing `.git`. No `package.json` is required — SFCC
43
42
  cartridge repos, Python, Go, Rust, and other non-Node projects work the same way.
44
- - **An MCP-capable editor or CLI**: Claude Code, GitHub Copilot in VS Code, GitHub
45
- Copilot CLI, Cursor, Windsurf, or OpenAI Codex.
43
+ - **An MCP-capable editor or CLI**: Claude Code, GitHub Copilot in VS Code, Cursor,
44
+ Windsurf, or OpenAI Codex.
46
45
  - **No Bridge account needed.** The installer can create one for you from just an
47
46
  email address.
48
47
 
@@ -87,13 +86,13 @@ npx -y @bridge_gpt/mcp-server install
87
86
  a token:
88
87
 
89
88
  ```
90
- 1. Yes, I have received a token
91
- 2. No, I need one
89
+ 1. I need a token
90
+ 2. I already have a token
92
91
  ```
93
92
 
94
- - Choose **2** if you have nothing yet. It asks for your email address and a name
93
+ - Choose **1** if you have nothing yet. It asks for your email address and a name
95
94
  for your new Bridge project, then creates both for you.
96
- - Choose **1** if someone gave you a token — either a Bridge API key or an invite
95
+ - Choose **2** if someone gave you a token — either a Bridge API key or an invite
97
96
  code. Paste it at the hidden prompt; you do not have to say which kind it is,
98
97
  because the installer recognizes it. Nothing is echoed as you type.
99
98
 
@@ -120,8 +119,8 @@ include a host the installer can launch, the run has to be on an interactive
120
119
  terminal, and you have to accept the consent prompt (*"Bridge can configure and set
121
120
  up this project for you automatically. Open a `<tool>` session to do that now?
122
121
  (Y/n)"*). Claude Code is the only selection that launches on its own. A
123
- Cursor-only, Copilot, Copilot CLI, Codex, or Windsurf selection, a non-interactive
124
- run, or a declined prompt all print the command to continue by hand instead. Pass
122
+ Cursor-only, Copilot, Codex, or Windsurf selection, a non-interactive run, or a
123
+ declined prompt all print the command to continue by hand instead. Pass
125
124
  `--agent claude` or `--agent cursor-agent` to override the decision outright.
126
125
 
127
126
  **7. Follow the next step the session shows you, if it shows one.** The installer
@@ -143,7 +142,7 @@ Want to see what would happen without changing anything? Add `--dry-run`.
143
142
 
144
143
  | Path | What it is | Commit it? |
145
144
  |---|---|---|
146
- | `.claude/commands/`, `.cursor/commands/` | The slash commands your editor runs | Yes |
145
+ | `.claude/commands/`, `.cursor/commands/`, `.github/prompts/`, `.github/skills/` | The generated workflow commands for supported agent hosts | Yes |
147
146
  | `.claude/agents/` and editor equivalents | Agent definitions used by those commands | Yes |
148
147
  | `.bridge/config` | Your project manifest — the repository name and which MCP targets to provision. Deliberately secret-free | Yes |
149
148
  | `.bridge/pipelines/`, `.bridge/instructions/` | Editable pipeline definitions | Yes |
@@ -173,8 +172,8 @@ Cursor's agent instead of Claude Code.
173
172
 
174
173
  **Selecting Windsurf prints instructions instead of writing config.** Windsurf's
175
174
  global `mcp_config.json` is never modified automatically; the installer reports the
176
- entry for you to paste yourself. Codex and Copilot CLI *are* written automatically,
177
- even though their files are global too.
175
+ entry for you to paste yourself. Codex *is* written automatically, even though its
176
+ file is global too.
178
177
 
179
178
  **Your key is stored for the tools that read the store.** The MCP server and the
180
179
  shell-spawned tooling (`start-tickets` and its model routing) resolve it from
@@ -261,7 +260,7 @@ Creates a brand-new Bridge project for that address and your first admin key in
261
260
  command. No account, no key, and no invite needed beforehand. The address labels
262
261
  your new workspace and may receive a setup message; delivery is best-effort, so
263
262
  nothing waits on it. The email is visible as you type (it is not a secret) and is
264
- never written to a log. This is the same route as answering **2** at the prompt.
263
+ never written to a log. This is the same route as answering **1** at the prompt.
265
264
 
266
265
  **You were sent an invite code.**
267
266
 
@@ -300,7 +299,7 @@ created or spent.
300
299
  | `--invite [code]` | Redeem an invite code. Omit the value for the hidden prompt (recommended) |
301
300
  | `--api-key <key>` | Use an existing Bridge API key |
302
301
  | `--repo <name>` | Name the registered repository instead of resolving or asking for it |
303
- | `--tools <list>` | Configure specific MCP hosts without the picker. Accepted IDs are exactly `claude-code`, `cursor`, `copilot-vscode`, `copilot-cli`, `codex`, and `windsurf` (e.g. `claude-code,cursor`); any other value is a parse error |
302
+ | `--tools <list>` | Configure specific MCP hosts without the picker. Accepted IDs are exactly `claude-code`, `cursor`, `copilot-vscode`, `codex`, and `windsurf` (e.g. `claude-code,cursor`); any other value is a parse error |
304
303
  | `--agent claude\|cursor-agent` | Which agent to open for the final configuration step. **No default** — without this flag the agent is derived from the hosts you selected, and an explicit value always wins, including for a host you did not select |
305
304
  | `--dry-run` | Preview every step without writing, contacting Bridge, resolving or prompting for a credential, or opening anything. Genuinely inert: it returns before the project-root prompt, before the repository is resolved, and before any tool-selection prompt, so a value it cannot know locally (an unresolved repository name, an unselected tool) is shown as **not yet known** rather than guessed |
306
305
  | `--force` | Overwrite an existing stored key without asking |
@@ -352,7 +351,7 @@ later boot.
352
351
  "mcpServers": {
353
352
  "bridge": {
354
353
  "command": "npx",
355
- "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.54", "serve"],
354
+ "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.55", "serve"],
356
355
  "env": {
357
356
  "BAPI_BASE_URL": "https://bridgegpt-api.com",
358
357
  "BAPI_REPO_NAME": "your-repo",
@@ -366,7 +365,7 @@ later boot.
366
365
  </details>
367
366
 
368
367
  <details>
369
- <summary><strong>VS Code / Copilot (.vscode/mcp.json)</strong></summary>
368
+ <summary><strong>GitHub Copilot (.vscode/mcp.json)</strong></summary>
370
369
 
371
370
  ```json
372
371
  {
@@ -374,7 +373,7 @@ later boot.
374
373
  "bridge": {
375
374
  "type": "stdio",
376
375
  "command": "npx",
377
- "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.54", "serve"],
376
+ "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.55", "serve"],
378
377
  "env": {
379
378
  "BAPI_BASE_URL": "https://bridgegpt-api.com",
380
379
  "BAPI_REPO_NAME": "your-repo",
@@ -396,33 +395,7 @@ later boot.
396
395
  "bridge": {
397
396
  "type": "stdio",
398
397
  "command": "npx",
399
- "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.54", "serve"],
400
- "env": {
401
- "BAPI_BASE_URL": "https://bridgegpt-api.com",
402
- "BAPI_REPO_NAME": "your-repo",
403
- "BAPI_API_KEY": "your-api-key",
404
- "BAPI_DOCS_DIR": "docs/tmp"
405
- }
406
- }
407
- }
408
- }
409
- ```
410
- </details>
411
-
412
- <details>
413
- <summary><strong>GitHub Copilot CLI (~/.copilot/mcp-config.json)</strong></summary>
414
-
415
- Copilot CLI reads a single global file. The installer writes this one for you when
416
- you select `copilot-cli`; the shape below is what it produces.
417
-
418
- ```json
419
- {
420
- "mcpServers": {
421
- "bridge": {
422
- "type": "local",
423
- "command": "npx",
424
- "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.54", "serve"],
425
- "tools": ["*"],
398
+ "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.55", "serve"],
426
399
  "env": {
427
400
  "BAPI_BASE_URL": "https://bridgegpt-api.com",
428
401
  "BAPI_REPO_NAME": "your-repo",
@@ -445,7 +418,7 @@ Windsurf only supports global MCP configuration.
445
418
  "mcpServers": {
446
419
  "bridge": {
447
420
  "command": "npx",
448
- "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.54", "serve"],
421
+ "args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.55", "serve"],
449
422
  "env": {
450
423
  "BAPI_BASE_URL": "https://bridgegpt-api.com",
451
424
  "BAPI_REPO_NAME": "your-repo",
@@ -464,7 +437,7 @@ Windsurf only supports global MCP configuration.
464
437
  ```toml
465
438
  [mcp_servers.bridge]
466
439
  command = "npx"
467
- args = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.54", "serve"]
440
+ args = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.55", "serve"]
468
441
 
469
442
  [mcp_servers.bridge.env]
470
443
  BAPI_BASE_URL = "https://bridgegpt-api.com"
@@ -802,7 +775,16 @@ Repository and ticket housekeeping commands, beyond the tiers above. Implementat
802
775
  | `/parse-repository` | Queue a background job to index the repository for Bridge AI agents |
803
776
  | `/scan-tickets` | Sync recently-updated Jira tickets and backfill workflow timestamps |
804
777
 
805
- > Commands are designed for Claude Code. Other editors may support slash commands differently — check your editor's documentation for how to invoke prompt files.
778
+ > **GitHub Copilot modes.** On VS Code's Local agent, Bridge provisions one
779
+ > `.github/prompts/<command>.prompt.md` prompt per packaged command, including
780
+ > `/explore-ticket`. On VS Code Agent Host, prompt files are not loaded, so the
781
+ > installer provisions the same canonical command as a project skill at
782
+ > `.github/skills/<command>/SKILL.md`; invoke that skill by its slash-command
783
+ > name. Bridge does not claim Copilot CLI or cloud-agent workflow support from
784
+ > this scaffold alone. If either artifact is not discovered, use VS Code's Chat
785
+ > Customizations diagnostics, confirm the workspace is trusted, and invoke the
786
+ > workflow manually with its full instructions rather than assuming the slash
787
+ > command is available.
806
788
 
807
789
  ### Extra Capabilities
808
790
 
@@ -1176,7 +1158,7 @@ default, or, when it declares none, as an explicit recorded `no_ci_gate` mode.
1176
1158
  > refusal rather than a silent winner.
1177
1159
 
1178
1160
  An unattended run has two one-time **repository** prerequisites — consent
1179
- (`unattended_conductor_allowed`) and a verified notify webhook default — plus
1161
+ (`unattended_epic_implementer_allowed`) and a verified notify webhook default — plus
1180
1162
  confirmed default-branch review/CI workflows. Each is refused by name **before any
1181
1163
  run row is created** (`target_not_allowed`, `webhook_unverified`,
1182
1164
  `repository_readiness_unconfirmed`), and `drive-epic` readiness and `doctor` both
@@ -1190,7 +1172,7 @@ prerequisite.
1190
1172
  > half alone would 422 every unattended run at `POST /jira/epic-runs/runs`.
1191
1173
  > Deploying only the server half is safe and inert: it narrows two refusals and adds
1192
1174
  > a first-approval stamp, and **rewrites no existing run**. No database migration and
1193
- > no new environment variable is introduced — `config_projects.unattended_conductor_allowed`
1175
+ > no new environment variable is introduced — `config_projects.unattended_epic_implementer_allowed`
1194
1176
  > and `epic_supervisor_setup.done_gate_config` already exist and are read as-is.
1195
1177
 
1196
1178
  Once the plan is approved, the **server-side reconciler** picks the run up within ~30s. To execute claimed jobs on your machine, run `executor`:
@@ -1199,15 +1181,17 @@ Once the plan is approved, the **server-side reconciler** picks the run up withi
1199
1181
  npx -y @bridge_gpt/mcp-server executor --repo <name>
1200
1182
  ```
1201
1183
 
1202
- ### `conduct-epic`
1184
+ ### `implement-epic`
1203
1185
 
1204
1186
  The deterministic half of the `/conduct-epic` loop: it owns the epic branch, a
1205
1187
  versioned local checkpoint, a per-epic lock, and the read-only probes the loop
1206
1188
  decides on. It never creates or mutates an `epic_run` — that is the server-side
1207
1189
  v2 reconciler's job, and `init` refuses to start when one is already active.
1190
+ This is EPIC-LEVEL orchestration, distinct from the ticket-level
1191
+ `implement-ticket` command.
1208
1192
 
1209
1193
  ```
1210
- npx -y @bridge_gpt/mcp-server conduct-epic <verb> [flags]
1194
+ npx -y @bridge_gpt/mcp-server implement-epic <verb> [flags]
1211
1195
  ```
1212
1196
 
1213
1197
  **Verbs**
@@ -1273,9 +1257,9 @@ appear in a command argument, in stdout/stderr, or in a journal line.
1273
1257
 
1274
1258
  ### Conductor (epic & multi-agent orchestration)
1275
1259
 
1276
- 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`, `conductor install-git-hooks`, the supervisor `done_gate_config` / `auto_merge_enabled` settings, and the observability stream — lives in **[CONDUCTOR.md](./docs/CONDUCTOR.md)**.
1260
+ 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.)
1277
1261
 
1278
- > The v1 `conductor epic-tick` command is **frozen** — it throws on every invocation. There is nothing to schedule locally, and `conductor doctor` flags any epic-tick schedule left over from an earlier release so you can cancel it.
1262
+ > The v1 `epic-implementer epic-tick` command is **frozen** — it throws on every invocation. There is nothing to schedule locally, and `epic-implementer doctor` flags any epic-tick schedule left over from an earlier release so you can cancel it.
1279
1263
 
1280
1264
  ## Custom Pipelines
1281
1265
 
@@ -0,0 +1,3 @@
1
+ // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
+ // This file is produced by scripts/bundle-agent-guidance.js from mcp_server/agent-guidance.md
3
+ export const AGENT_GUIDANCE = "### Get Project Standards (`get_project_standards`)\n\n**When to use it:** (Implementation | Refinement) Before writing or reviewing code, so the agent applies this project's recorded coding standards instead of generic defaults.\n\n**How to use it:** Call the `get_project_standards` MCP tool directly — it takes no required input and returns the project's recorded standards text.\n\n### Second Opinion (`second_opinion`)\n\n**When to use it:** (Architecture | Refinement | Implementation) Any time you want a quick sanity check on a plan, draft, or decision from a fresh perspective.\n\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 production.\"\n\n### Council (`request_council`)\n\n**When to use it:** (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 still needs figuring out before a real ticket exists, general for a quick brief-driven pass before the repository is indexed.\n\n**How to use it:** `/council <question>`\n\n### Review Ticket (`request_ticket_review`)\n\n**When to use it:** (Refinement) Right after a ticket is drafted, before anyone starts building — to surface gaps and tighten it.\n\n**How to use it:** `/review-tickets BAPI-123` (command only — \"review\" as free text is easily mistaken for a freehand agent review). A single key reviews inline in the current session — no terminal tab, no worktree. For several tickets at once, `/review-tickets BAPI-123 BAPI-456` opens one terminal tab per ticket and reviews them in parallel with no worktrees; every review flag applies, and `--review KEY=auto,rounds=N` sets per-ticket overrides.";
@@ -345,6 +345,9 @@ export function createClaudeExecutorAdapter(spec, deps = {}) {
345
345
  if (input.workerBranch !== undefined) {
346
346
  envOptions.workerBranch = input.workerBranch;
347
347
  }
348
+ if (input.declaredTouchedFiles !== undefined) {
349
+ envOptions.declaredTouchedFiles = input.declaredTouchedFiles;
350
+ }
348
351
  return {
349
352
  executable: spec.command,
350
353
  argv: buildClaudeArgv(input.prompt, input.modelAlias, input.mcpConfigPath, input.posture),
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Marker-scoped, opt-in cross-platform agent notes writer (BAPI-1207).
3
+ *
4
+ * Creates or refreshes a Bridge-owned region inside a consumer project's
5
+ * `AGENTS.md` (shared by Cursor, Copilot VS Code, Codex, and Claude Code) and,
6
+ * for Claude Code specifically, a pointer region inside `CLAUDE.md` — so a
7
+ * Claude-only selection never leaves a dangling `@AGENTS.md` reference. Every
8
+ * other byte in either file — content a person wrote by hand outside the
9
+ * marker pair — is preserved exactly.
10
+ *
11
+ * This module performs I/O only through the injected `readFile`/`writeFile`/
12
+ * `mkdir` seams, mirroring the rest of the install-flow dependency-injection
13
+ * convention (see `InstallBridgeDeps` in `install-bridge.ts`), and does no
14
+ * platform detection, credential handling, `.gitignore` mutation, or scaffold
15
+ * bookkeeping of its own — those stay the caller's responsibility.
16
+ */
17
+ import path from "path";
18
+ /** The runtime marker pair this module owns inside a target notes file. */
19
+ export const BRIDGE_MCP_GUIDANCE_BEGIN_MARKER = "<!-- BEGIN BRIDGE MCP GUIDANCE -->";
20
+ export const BRIDGE_MCP_GUIDANCE_END_MARKER = "<!-- END BRIDGE MCP GUIDANCE -->";
21
+ /** Platforms whose shared notes file is AGENTS.md. Claude Code also gets CLAUDE.md. */
22
+ const AGENTS_MD_PLATFORMS = new Set([
23
+ "cursor",
24
+ "copilot-vscode",
25
+ "codex",
26
+ "claude-code",
27
+ ]);
28
+ /**
29
+ * Pure projection from a selected-platform set to the physical notes files
30
+ * that selection implies. Never touches the filesystem or re-detects
31
+ * platforms — the caller supplies the already-selected set.
32
+ */
33
+ export function resolveAgentNotesTargets(selectedPlatforms) {
34
+ const targets = new Set();
35
+ const unsupported = new Set();
36
+ for (const platform of selectedPlatforms) {
37
+ if (platform === "windsurf") {
38
+ unsupported.add(platform);
39
+ continue;
40
+ }
41
+ if (AGENTS_MD_PLATFORMS.has(platform)) {
42
+ targets.add("AGENTS.md");
43
+ }
44
+ // Claude Code additionally gets a CLAUDE.md pointer — never a substitute
45
+ // for AGENTS.md, since @AGENTS.md would otherwise dangle.
46
+ if (platform === "claude-code") {
47
+ targets.add("CLAUDE.md");
48
+ }
49
+ }
50
+ const ordered = [];
51
+ if (targets.has("AGENTS.md"))
52
+ ordered.push("AGENTS.md");
53
+ if (targets.has("CLAUDE.md"))
54
+ ordered.push("CLAUDE.md");
55
+ return { targets: ordered, unsupportedPlatforms: [...unsupported] };
56
+ }
57
+ /** The one-line pointer note CLAUDE.md's owned region carries (BAPI-1207). */
58
+ const CLAUDE_NOTES_POINTER_NOTE = "Bridge MCP usage guidance is maintained in AGENTS.md — see the section above.";
59
+ function buildClaudeOwnedBody() {
60
+ return `@AGENTS.md\n\n${CLAUDE_NOTES_POINTER_NOTE}`;
61
+ }
62
+ function countOccurrences(text, marker) {
63
+ return text.split(marker).length - 1;
64
+ }
65
+ /**
66
+ * Locate the existing owned region in *text*, or classify why it cannot be
67
+ * located safely. A malformed topology (a missing half, a duplicate, reversed
68
+ * order, or a marker not on its own line) is reported rather than repaired —
69
+ * this module never appends a second region next to one it cannot parse.
70
+ */
71
+ function scanMarkers(text) {
72
+ const beginCount = countOccurrences(text, BRIDGE_MCP_GUIDANCE_BEGIN_MARKER);
73
+ const endCount = countOccurrences(text, BRIDGE_MCP_GUIDANCE_END_MARKER);
74
+ if (beginCount === 0 && endCount === 0)
75
+ return { kind: "absent" };
76
+ if (beginCount === 0 || endCount === 0) {
77
+ return { kind: "malformed", reason: "missing half of the marker pair" };
78
+ }
79
+ if (beginCount > 1)
80
+ return { kind: "malformed", reason: "duplicate BEGIN marker" };
81
+ if (endCount > 1)
82
+ return { kind: "malformed", reason: "duplicate END marker" };
83
+ const beginIndex = text.indexOf(BRIDGE_MCP_GUIDANCE_BEGIN_MARKER);
84
+ const endIndex = text.indexOf(BRIDGE_MCP_GUIDANCE_END_MARKER);
85
+ if (endIndex < beginIndex)
86
+ return { kind: "malformed", reason: "markers are reversed" };
87
+ const afterBegin = beginIndex + BRIDGE_MCP_GUIDANCE_BEGIN_MARKER.length;
88
+ if (text[afterBegin] !== "\n") {
89
+ return { kind: "malformed", reason: "BEGIN marker is not on its own line" };
90
+ }
91
+ const contentStart = afterBegin + 1;
92
+ const lineStart = text.lastIndexOf("\n", endIndex - 1) + 1;
93
+ if (text.slice(lineStart, endIndex).trim().length > 0) {
94
+ return { kind: "malformed", reason: "END marker is not on its own line" };
95
+ }
96
+ return { kind: "present", contentStart, contentEnd: endIndex };
97
+ }
98
+ /** Replace only the bytes between valid existing markers; preserve everything else. */
99
+ function replaceRegion(text, contentStart, contentEnd, ownedBody) {
100
+ const normalized = ownedBody.trim() + "\n";
101
+ return text.slice(0, contentStart) + normalized + text.slice(contentEnd);
102
+ }
103
+ /**
104
+ * Append one fresh owned region at EOF, using the minimal separator needed
105
+ * for a clean markdown boundary. The existing prefix is never normalized —
106
+ * only the amount of new whitespace inserted before the region varies.
107
+ */
108
+ function appendRegion(existingText, ownedBody) {
109
+ const region = `${BRIDGE_MCP_GUIDANCE_BEGIN_MARKER}\n${ownedBody.trim()}\n${BRIDGE_MCP_GUIDANCE_END_MARKER}\n`;
110
+ if (existingText.length === 0)
111
+ return region;
112
+ if (existingText.endsWith("\n\n"))
113
+ return existingText + region;
114
+ if (existingText.endsWith("\n"))
115
+ return existingText + "\n" + region;
116
+ return existingText + "\n\n" + region;
117
+ }
118
+ function isEnoent(err) {
119
+ return (typeof err === "object" &&
120
+ err !== null &&
121
+ "code" in err &&
122
+ err.code === "ENOENT");
123
+ }
124
+ async function writeOneTarget(target, options) {
125
+ const filePath = path.join(options.cwd, target);
126
+ let existing;
127
+ try {
128
+ existing = await options.readFile(filePath);
129
+ }
130
+ catch (err) {
131
+ if (isEnoent(err)) {
132
+ existing = "";
133
+ }
134
+ else {
135
+ return { target, status: "failed", reason: "could not read the existing file" };
136
+ }
137
+ }
138
+ const ownedBody = target === "AGENTS.md" ? options.guidance : buildClaudeOwnedBody();
139
+ const scan = scanMarkers(existing);
140
+ let merged;
141
+ if (scan.kind === "malformed") {
142
+ return {
143
+ target,
144
+ status: "failed",
145
+ reason: `existing marker region is malformed (${scan.reason})`,
146
+ };
147
+ }
148
+ else if (scan.kind === "present") {
149
+ merged = replaceRegion(existing, scan.contentStart, scan.contentEnd, ownedBody);
150
+ }
151
+ else {
152
+ merged = appendRegion(existing, ownedBody);
153
+ }
154
+ if (merged === existing) {
155
+ return { target, status: "skipped", reason: "content already up to date" };
156
+ }
157
+ try {
158
+ await options.mkdir(path.dirname(filePath), { recursive: true });
159
+ await options.writeFile(filePath, merged, { encoding: "utf-8" });
160
+ }
161
+ catch {
162
+ return { target, status: "failed", reason: "could not write the file" };
163
+ }
164
+ return { target, status: "written" };
165
+ }
166
+ /**
167
+ * Create or refresh the Bridge-owned notes region in every target the
168
+ * selected platforms resolve to. Each target is handled independently — a
169
+ * failure on one never rolls back or blocks a write already made to another.
170
+ */
171
+ export async function writeAgentNotes(options) {
172
+ const { targets, unsupportedPlatforms } = resolveAgentNotesTargets(options.selectedPlatforms);
173
+ const files = [];
174
+ for (const target of targets) {
175
+ files.push(await writeOneTarget(target, options));
176
+ }
177
+ return { files, unsupportedPlatforms };
178
+ }
@@ -44,8 +44,11 @@ export const AGENT_REGISTRY = {
44
44
  supportsModelOverride: true,
45
45
  modelFlag: "--model",
46
46
  // Claude family aliases float to the latest release and never drift, so they
47
- // can be validated against a static allowlist.
48
- tierModels: { cheap: "haiku", basic: "sonnet", premium: "opus" },
47
+ // can be validated against a static allowlist. The `cheap` tier deliberately
48
+ // maps to `sonnet` (not `haiku`): even the lowest-difficulty tickets route to
49
+ // sonnet. `haiku` stays in the allowlist so a per-repo
50
+ // `difficulty_model_tier_overrides` may still opt back into it.
51
+ tierModels: { cheap: "sonnet", basic: "sonnet", premium: "opus" },
49
52
  staticModelAliasAllowlist: ["haiku", "sonnet", "opus"],
50
53
  // BAPI-781: the reference executor adapter. `managedCarriers` is EMPTY and
51
54
  // `passthroughs` names only the operator's own OAuth variable — Bridge
@@ -108,3 +108,61 @@ export function translateAgentToCopilot(frontmatter, body) {
108
108
  const yaml = lines.join("\n");
109
109
  return `---\n${yaml}\n---\n${body}`;
110
110
  }
111
+ /**
112
+ * Return the first meaningful line as a short, deterministic Copilot summary.
113
+ * Canonical command files deliberately have no required frontmatter, so their
114
+ * opening prose is the only source we can use without maintaining a second
115
+ * command catalog.
116
+ */
117
+ function commandDescription(source, commandName) {
118
+ const line = source
119
+ .split(/\r?\n/)
120
+ .map((candidate) => candidate.trim())
121
+ .find((candidate) => candidate.length > 0 && candidate !== "$ARGUMENTS" && !candidate.startsWith("#"));
122
+ return line ?? `Run the Bridge ${commandName} workflow.`;
123
+ }
124
+ function commandBodyForPrompt(source) {
125
+ return source.replaceAll("$ARGUMENTS", "${input:task}");
126
+ }
127
+ function commandBodyForSkill(source) {
128
+ return source.replaceAll("$ARGUMENTS", "the text supplied after this slash command");
129
+ }
130
+ /**
131
+ * Translate one packaged command into the two Copilot artifact formats Bridge
132
+ * supports today. Prompt files are for VS Code's Local agent; project skills
133
+ * are the Agent Host replacement. Both remain derived from the same canonical
134
+ * command body, never from a hand-maintained Copilot copy.
135
+ */
136
+ export function translateCommandToCopilot(filename, source) {
137
+ const commandName = filename.replace(/\.md$/, "");
138
+ const description = commandDescription(source, commandName);
139
+ const prompt = [
140
+ "---",
141
+ `description: ${quoteYamlValue(description)}`,
142
+ "argument-hint: \"[task or ticket key]\"",
143
+ "tools:",
144
+ " - bridge/*",
145
+ "---",
146
+ "",
147
+ `# Bridge ${commandName} workflow`,
148
+ "",
149
+ "The user input is available as `${input:task}`. Follow the canonical workflow below.",
150
+ "",
151
+ commandBodyForPrompt(source),
152
+ ].join("\n");
153
+ const skill = [
154
+ "---",
155
+ `name: ${commandName}`,
156
+ `description: ${quoteYamlValue(description)}`,
157
+ "argument-hint: \"[task or ticket key]\"",
158
+ "disable-model-invocation: true",
159
+ "---",
160
+ "",
161
+ `# Bridge ${commandName} workflow`,
162
+ "",
163
+ "Use the text supplied after this slash command as the workflow input. Follow the canonical workflow below.",
164
+ "",
165
+ commandBodyForSkill(source),
166
+ ].join("\n");
167
+ return { prompt, skill };
168
+ }