@bridge_gpt/mcp-server 0.2.68 → 0.2.70

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 (166) hide show
  1. package/README.md +226 -320
  2. package/build/agent-launchers/claude-executor-adapter.js +17 -4
  3. package/build/agents.generated.js +4 -4
  4. package/build/artifact-diff.js +110 -0
  5. package/build/bin-entrypoint.js +36 -0
  6. package/build/bridge-permissions.js +650 -98
  7. package/build/check-update-cli.js +74 -0
  8. package/build/claude-review-workflow-drift.js +3 -3
  9. package/build/claude-settings.js +84 -2
  10. package/build/cli-help.js +7 -1
  11. package/build/codex-bridge-profile.js +1 -1
  12. package/build/codex-home.js +9 -1
  13. package/build/codex-launcher-pin.js +598 -0
  14. package/build/commands.generated.js +8 -8
  15. package/build/conductor/bridge-api-client.js +598 -19
  16. package/build/conductor/bring-up-facts.js +9 -9
  17. package/build/conductor/doctor.js +466 -18
  18. package/build/conductor/epic-child-continuation.js +54 -3
  19. package/build/conductor/epic-implementer-cli.js +132 -34
  20. package/build/conductor/epic-runtime.js +2 -2
  21. package/build/conductor/epic-state.js +1 -1
  22. package/build/conductor/hook-runtime-qualification.js +1 -1
  23. package/build/conductor/install-doctor.js +4 -2
  24. package/build/conductor/launch-need-probes.js +337 -0
  25. package/build/conductor/local-merge.js +241 -56
  26. package/build/conductor/merge-credential.js +172 -0
  27. package/build/conductor/merge-execution-eligibility.js +1 -1
  28. package/build/conductor/merged-child-check.js +95 -0
  29. package/build/conductor/plan-trim.js +30 -0
  30. package/build/conductor/pr-ci-producer.js +1 -1
  31. package/build/conductor/readiness-cli.js +11 -6
  32. package/build/conductor/readiness-prerequisites.js +41 -0
  33. package/build/conductor/readiness-sections.js +345 -55
  34. package/build/conductor/readiness.js +99 -87
  35. package/build/conductor/recovery-cli.js +606 -21
  36. package/build/conductor/recovery-operations.js +306 -7
  37. package/build/conductor/repair-contract.js +47 -2
  38. package/build/conductor/repository-workflow-remedy.js +8 -6
  39. package/build/conductor/stored-merge-credential.js +76 -0
  40. package/build/conductor/stored-merge-token-probe.js +28 -0
  41. package/build/conductor/temporary-plan.js +50 -0
  42. package/build/conductor/wait-run-cli.js +759 -0
  43. package/build/conductor/worker-database-doctor-inspector.js +49 -0
  44. package/build/conductor-bin.js +2 -2
  45. package/build/conductor-claude-hook-bin.js +2 -2
  46. package/build/conductor-claude-hook-removed-stub-bin.js +2 -5
  47. package/build/conductor-removed-stub-bin.js +2 -5
  48. package/build/connect-github-api.js +33 -14
  49. package/build/credential-store.js +356 -13
  50. package/build/docs.generated.js +27 -14
  51. package/build/doctor.js +203 -38
  52. package/build/drive-epic-help.js +212 -0
  53. package/build/drive-epic.js +1840 -373
  54. package/build/epic-ci-workflow-drift.js +77 -0
  55. package/build/epic-ci-workflow-install.js +183 -0
  56. package/build/epic-ci-workflow.js +157 -0
  57. package/build/epic-contract-context.js +326 -0
  58. package/build/epic-implementer-bin.js +131 -120
  59. package/build/epic-implementer-claude-hook-bin.js +2 -2
  60. package/build/escalation-reachability.js +134 -0
  61. package/build/executor/cleanup-report.js +99 -0
  62. package/build/executor/cli.js +12 -1
  63. package/build/executor/completion-contract.js +68 -11
  64. package/build/executor/completion-supervision.js +99 -0
  65. package/build/executor/delivery-outcome.js +179 -0
  66. package/build/executor/deps.js +3 -0
  67. package/build/executor/env.js +71 -0
  68. package/build/executor/failure-reason-codes.js +126 -0
  69. package/build/executor/heartbeat.js +38 -0
  70. package/build/executor/http-client.js +115 -4
  71. package/build/executor/integration-job.js +298 -5
  72. package/build/executor/job-errors.js +36 -6
  73. package/build/executor/job-runner.js +720 -36
  74. package/build/executor/likely-tests.js +36 -2
  75. package/build/executor/merge-job.js +145 -2
  76. package/build/executor/observation.js +73 -4
  77. package/build/executor/permissions.js +193 -97
  78. package/build/executor/preflight.js +14 -3
  79. package/build/executor/process-containment.js +28 -2
  80. package/build/executor/prompt-spec.js +50 -5
  81. package/build/executor/rebase-finalization.js +38 -3
  82. package/build/executor/reconcile-overlap-job.js +35 -7
  83. package/build/executor/required-work-ledger.js +236 -81
  84. package/build/executor/resume-pre-spawn.js +38 -1
  85. package/build/executor/runner.js +91 -3
  86. package/build/executor/test-clock.js +8 -0
  87. package/build/executor/types.js +37 -2
  88. package/build/executor/worker-cli-launcher.js +259 -0
  89. package/build/executor/worker-command.js +41 -4
  90. package/build/executor/worker-database-target.js +91 -0
  91. package/build/executor/worker-database.js +908 -0
  92. package/build/executor/worker-finalization.js +106 -0
  93. package/build/executor/worker-guard-hook.js +203 -0
  94. package/build/executor/worker-resources.js +47 -0
  95. package/build/executor/worker-verification-guidance.js +15 -2
  96. package/build/executor/worktree.js +303 -15
  97. package/build/executor-launch.js +11 -1
  98. package/build/generated-assets-doctor.js +9 -1
  99. package/build/generated-assets-health.js +13 -4
  100. package/build/generated-assets-legacy.js +39 -0
  101. package/build/generated-assets-paths.js +12 -1
  102. package/build/generated-assets-repair.js +4 -1
  103. package/build/generated-assets-test-support.js +12 -0
  104. package/build/github-remote.js +33 -0
  105. package/build/github-sign-in.js +62 -0
  106. package/build/index.js +1214 -465
  107. package/build/init.js +39 -1
  108. package/build/install-bridge-conductor-gate.js +78 -0
  109. package/build/install-bridge-conductor-setup.js +413 -0
  110. package/build/install-bridge-conductor.js +1226 -267
  111. package/build/install-bridge.js +629 -350
  112. package/build/launcher-config-inspection.js +184 -19
  113. package/build/mcp-host-targets.js +4 -0
  114. package/build/mcp-identity.js +7 -0
  115. package/build/mcp-install-state.js +8 -2
  116. package/build/merge-pull-request.js +6 -0
  117. package/build/merge-workflow-install.js +763 -345
  118. package/build/phase-result-artifacts.js +75 -20
  119. package/build/pipeline-orchestrator.js +247 -4
  120. package/build/pipelines.generated.js +35 -35
  121. package/build/plan-discovery.js +15 -2
  122. package/build/plan-execution-ledger.js +306 -17
  123. package/build/plan-phase-routing.js +48 -13
  124. package/build/plane/cli.js +105 -51
  125. package/build/plane/defaults.js +3 -0
  126. package/build/plane/plane-freshness.js +294 -0
  127. package/build/plane/schema-compatibility.js +137 -0
  128. package/build/plane/shutdown-deps.js +38 -0
  129. package/build/plane/shutdown.js +83 -8
  130. package/build/plane/terminal-run-cleanup.js +303 -0
  131. package/build/printed-remedy-commands.js +26 -4
  132. package/build/readiness-check.js +6 -2
  133. package/build/readiness-prerequisites.generated.js +532 -0
  134. package/build/readme.generated.js +1 -1
  135. package/build/session-upgrade-notice.js +86 -0
  136. package/build/setup-epic.js +400 -30
  137. package/build/shared-resources.generated.js +5 -0
  138. package/build/start-tickets.js +43 -15
  139. package/build/test-command-detection.js +247 -0
  140. package/build/test-command-validation.js +73 -0
  141. package/build/testing-config-client.js +150 -0
  142. package/build/ticket-delivery-artifacts.js +258 -0
  143. package/build/ticket-delivery-enrollment.js +306 -0
  144. package/build/ticket-delivery.js +205 -0
  145. package/build/ticket-review-summary.js +14 -0
  146. package/build/tool-call-advisory.js +162 -0
  147. package/build/tool-surface-gating.js +28 -89
  148. package/build/update-check.js +97 -16
  149. package/build/update-status.js +61 -71
  150. package/build/upgrade-advice.js +15 -11
  151. package/build/upgrade-cli.js +360 -20
  152. package/build/v2-readiness-requirements.js +552 -0
  153. package/build/version.generated.js +3 -3
  154. package/build/worker-guard-hook-bin.js +2 -2
  155. package/build/worktree-core.js +22 -7
  156. package/docs/BRIDGE-DIRECTORY-README.md +48 -0
  157. package/docs/EPIC-IMPLEMENTER-INSTALL.md +12 -867
  158. package/docs/EPIC-IMPLEMENTER.md +104 -63
  159. package/docs/epic-implementer/getting-started.md +131 -0
  160. package/docs/epic-implementer/self-hosting.md +133 -0
  161. package/package.json +3 -3
  162. package/pipelines/idea-to-ticket.json +1 -1
  163. package/pipelines/implement-ticket.json +8 -8
  164. package/pipelines/review-ticket.json +2 -2
  165. package/design-assets/favicon/favicon-32x32.png +0 -0
  166. package/design-assets/just-logo-rough-draft.png +0 -0
package/build/doctor.js CHANGED
@@ -36,6 +36,9 @@ import { modelRoutingPreferencesPath, readModelRoutingPreference } from "./model
36
36
  import { inspectExecutorAgentAdapter, inspectionForFailedResolution, } from "./agent-launchers/executor-adapter-inspection.js";
37
37
  import { resolveExecutorAgentAdapter } from "./agent-launchers/executor-adapter-registry.js";
38
38
  import { parseDefaultOnEnvFlag } from "./env-flags.js";
39
+ import { checkForUpdate, isPlainReleaseVersion } from "./update-check.js";
40
+ import { getNoticeDecision, isUnattendedSession, UPDATE_CHECK_ENABLED_ENV, } from "./session-upgrade-notice.js";
41
+ import { formatUpdateAdvice } from "./upgrade-advice.js";
39
42
  import { createBridgeApiUrls } from "./bridge-api-urls.js";
40
43
  import { probeToolSurface } from "./tool-surface-gating.js";
41
44
  import { readStoredBindingState, resolveBapiCredentials } from "./credential-store.js";
@@ -45,7 +48,7 @@ import { validateHttpBaseUrl } from "./base-url.js";
45
48
  import { getLiveRepositoryConductors, getParseDispatcherHealth, } from "./epic-shared/bridge-client.js";
46
49
  import { resolveRequiredStartTicketsRepoName } from "./start-tickets-repo.js";
47
50
  import { collectWorkerContainmentDiagnostic, formatWorkerContainmentDiagnosticReport, } from "./worker-containment-diagnostic.js";
48
- import { BRIDGE_PACKAGE_NAME, describeLauncherReason, DUPLICATE_REGISTRATION_GUIDANCE, inspectLauncherConfigs, parseLauncherPin as parseSharedLauncherPin, } from "./launcher-config-inspection.js";
51
+ import { BRIDGE_PACKAGE_NAME, describeLauncherReason, DUPLICATE_REGISTRATION_GUIDANCE, inspectLauncherConfigs, isExactVersion, isPrerequisiteFailure, parseLauncherPin as parseSharedLauncherPin, } from "./launcher-config-inspection.js";
49
52
  import { MCP_SERVER_NAME, MCP_PACKAGE_NAME, } from "./mcp-identity.js";
50
53
  import { DEFAULT_AGENT_NAME, resolveAgentSpec, isAgentName, formatValidAgentNames, } from "./agent-registry.js";
51
54
  import { getDoctorPrereqDescriptors, probePrerequisite, } from "./start-tickets-prereqs.js";
@@ -53,6 +56,7 @@ import { resolveProfiles } from "./mcp-profile.js";
53
56
  import { executorLaunchdDirForHome, executorSystemdDirForHome, executorIdFromLaunchdFilename, executorIdFromSystemdFilename, inspectExecutorServiceArtifact, inspectExecutorServiceDurability, EXECUTOR_SERVICE_BASE_URL_ENV, } from "./executor/index.js";
54
57
  import { DEFAULT_BAPI_BASE_URL } from "./executor/credentials.js";
55
58
  import { detectClaudeLogin, formatClaudeLoginAdvisory } from "./claude-login.js";
59
+ import { checkGithubSignIn, githubSignInRemedy, } from "./github-sign-in.js";
56
60
  import { validateBranchName } from "./base-ref.js";
57
61
  import { readPlaneManifest } from "./plane/manifest.js";
58
62
  import { validateTicketKey } from "./ticket-key-utils.js";
@@ -74,9 +78,9 @@ export function getDoctorUsage() {
74
78
  ` npx -y ${MCP_PACKAGE_NAME} doctor --stale-branch <KEY> [--stale-branch <KEY> ...] \\`,
75
79
  " --stale-branch-base <ref> [--branch KEY=BRANCH ...]",
76
80
  "",
77
- // BAPI-669 (U9b): `doctor` is the general Bridge diagnostic command, not a
78
- // start-tickets-only one — and its report leads with install status, so the
79
- // usage text describes it in that order too.
81
+ // `doctor` is the general Bridge diagnostic command, not a start-tickets-only
82
+ // one — and its report leads with install status, so the usage text
83
+ // describes it in that order too.
80
84
  `${DOCTOR_REPORT_TITLE}. It only checks your environment and prints manual`,
81
85
  "install instructions — it does not install anything, modify your system, or",
82
86
  "start the MCP server.",
@@ -85,16 +89,23 @@ export function getDoctorUsage() {
85
89
  "Flags:",
86
90
  " --agent claude|cursor-agent|codex Agent to include in the prerequisite check (default: claude)",
87
91
  " --stale-branch KEY Add a ticket to the read-only stale-ticket-branch check (repeatable); requires --stale-branch-base",
88
- " --stale-branch-base REF The base ref that check measures ancestry against, e.g. epic/BAPI-902 (required with --stale-branch; never defaulted)",
92
+ " --stale-branch-base REF The base ref that check measures ancestry against, e.g. epic/PROJ-902 (required with --stale-branch; never defaulted)",
89
93
  " --branch KEY=BRANCH Diagnose BRANCH instead of feature/KEY for that ticket (repeatable); same override rule start-tickets uses",
94
+ " --advanced Also show Python and self-host diagnostics: the `uv` check and the",
95
+ " local plane credential section. Without it doctor runs no `uv`,",
96
+ " Alembic, or Python check, so it works on a repository in any language.",
90
97
  " -h, --help Show this help",
91
98
  "",
99
+ "A 'Getting started' section follows Install status: your Claude sign-in (it reads only",
100
+ "~/.claude.json) and your GitHub CLI sign-in (one `gh auth status`, ten seconds at most).",
101
+ "Each one that is not ready names its fix. It never changes the exit code.",
102
+ "",
92
103
  "The report opens with an advisory 'Install status' section (easy-install done",
93
104
  "criteria): repo identity, credential resolution, server connectivity,",
94
105
  "bootstrap-field completeness, integration credentials, and repository-indexing",
95
106
  "state. It performs read-only GETs only and never affects the exit code.",
96
107
  "",
97
- "Next comes an advisory 'Project identity and root' section (BAPI-1318): whether",
108
+ "Next comes an advisory 'Project identity and root' section: whether",
98
109
  "each BAPI_PROJECT_ROOT (from the environment and the project MCP configs) names",
99
110
  "a real project folder the server will use, and whether .bridge/config's",
100
111
  "repo_name matches each project MCP config's BAPI_REPO_NAME. It only reads",
@@ -102,7 +113,7 @@ export function getDoctorUsage() {
102
113
  "",
103
114
  "After that come the start-tickets prerequisite checks (for the current OS):",
104
115
  "the start-tickets preflight prerequisites plus",
105
- "uv, the selected agent's command, Bridge API credential resolution, and",
116
+ "the selected agent's command (and uv with --advanced), Bridge API credential resolution, and",
106
117
  "worktree MCP registration reachability, and a read-only check of whether the",
107
118
  `Claude user config (~/.claude.json) registers a '${MCP_SERVER_NAME}' MCP server that`,
108
119
  "could shadow the registration provisioned into a worker worktree. That check",
@@ -113,14 +124,8 @@ export function getDoctorUsage() {
113
124
  "migrate a credential, use /install-bridge or the `credentials` subcommand —",
114
125
  "doctor stays strictly read-only.",
115
126
  "",
116
- // BAPI-815/R4: the old text here promised a 'Worker config isolation",
117
- // (conductor executor)' section naming a CLAUDE_CONFIG_DIR strategy. BAPI-790
118
- // replaced per-job config-directory isolation with --strict-mcp-config argv
119
- // scoping, and BAPI-791 deleted that doctor section together with the whole
120
- // worker-auth strategy vocabulary. The section it described exists in no
121
- // doctor output, so the claim is removed rather than reworded.
122
- "It includes an advisory 'Worker containment' section (BAPI-1019) reporting the",
123
- "DECLARED posture a conductor worker runs under: that the executor strips this",
127
+ "It includes an advisory 'Worker containment' section reporting the DECLARED",
128
+ "posture an Epic Implementer worker runs under: that the executor strips this",
124
129
  "checkout's .env* files from a worker's worktree as the last step before spawn",
125
130
  "(refusing to spawn with ContractError.WorkerEnvFilePresent when it cannot",
126
131
  "confirm they are gone), that a worker's database environment names a target",
@@ -129,14 +134,13 @@ export function getDoctorUsage() {
129
134
  "reports constants rather than measurements, so it never claims a live check",
130
135
  "ran and never changes the exit code.",
131
136
  "",
132
- "MCP containment is still not reported here. A conductor worker is scoped",
133
- "at spawn time with `--strict-mcp-config --mcp-config <file>`, and the",
134
- "executor asserts that scope per spawn from the worker's own system/init",
135
- "event — failing the job with ContractError.McpSurfaceMismatch on a confirmed",
136
- "leak. That per-spawn assertion, not a doctor health line, is the containment",
137
- "proof; see docs/claude/worker-config-isolation-drill.md.",
137
+ "MCP containment is still not reported here. A worker is scoped at spawn time",
138
+ "with `--strict-mcp-config --mcp-config <file>`, and the executor asserts that",
139
+ "scope per spawn from the worker's own system/init event — failing the job with",
140
+ "ContractError.McpSurfaceMismatch on a confirmed leak. That per-spawn assertion,",
141
+ "not a doctor health line, is the containment proof.",
138
142
  "",
139
- "It also includes an advisory 'Local CLI launcher' section (BAPI-815): whether",
143
+ "It also includes an advisory 'Local CLI launcher' section: whether",
140
144
  "the BAPI_MCP_CLI override is set, and whether the launcher it names can be",
141
145
  "executed. When the override is unset, NO launcher probe runs at all. When it",
142
146
  "is set, doctor runs that launcher exactly once with a single appended",
@@ -146,7 +150,7 @@ export function getDoctorUsage() {
146
150
  "is rejected without running anything. The whole section is advisory: a",
147
151
  "missing, malformed, or unrunnable override never changes the exit code.",
148
152
  "",
149
- "It also includes an advisory 'MCP tool surface' section (BAPI-641): what",
153
+ "It also includes an advisory 'MCP tool surface' section: what",
150
154
  "dynamic capability gating would advertise for this repo. It performs at most",
151
155
  "one read-only GET to /jira/mcp/tool-surface (none under the kill switch) and",
152
156
  "is advisory/fail-open — a timeout or malformed response is reported as",
@@ -154,7 +158,7 @@ export function getDoctorUsage() {
154
158
  "ignore notifications/tools/list_changed must reconnect or start a new MCP",
155
159
  "session to observe surface changes; no project MCP config change is required.",
156
160
  "",
157
- "It also includes an advisory 'Executor provisioning' section (BAPI-688): the",
161
+ "It also includes an advisory 'Executor provisioning' section: the",
158
162
  "generated executor service units on this host. It enumerates them read-only by",
159
163
  "fixed convention — ~/Library/LaunchAgents/com.bridge-gpt.executor.*.plist on",
160
164
  "macOS, ~/.config/systemd/user/bridge-gpt-executor-*.service on Linux (Windows",
@@ -165,19 +169,20 @@ export function getDoctorUsage() {
165
169
  "is advisory: missing, malformed, or credential-less units never change the exit code.",
166
170
  "",
167
171
  "It also includes an advisory '/drive-epic-run permission rules' section",
168
- "(BAPI-1398): whether the project permissions.allow rules and the optional user",
169
- "autoMode entries that install writes are PRESENT, MISSING, or DRIFTED (for",
172
+ "whether the project permissions.allow rules and the optional user",
173
+ "autoMode entries (added through /drive-epic-run or the bridge-permissions",
174
+ "subcommand, never by install) are PRESENT, MISSING, or DRIFTED (for",
170
175
  "example after the BAPI_MCP_CLI launcher changed). It reads .claude/settings.local.json,",
171
176
  "~/.claude/settings.json, and .bridge/bridge-permissions.json, writes nothing,",
172
177
  "and never changes the exit code.",
173
178
  "",
174
- "Conductor ledger / native-module diagnostics (the SQLite ledger's native",
179
+ "Local ledger / native-module diagnostics (the SQLite ledger's native",
175
180
  "binding load status and Node-version skew) live under a separate command:",
176
181
  " epic-implementer doctor",
177
182
  "That command is likewise strictly read-only — it does not install, rebuild,",
178
183
  "migrate, or write ledger files.",
179
184
  "",
180
- "It also includes an OPT-IN advisory 'Stale ticket branches' section (BAPI-948),",
185
+ "It also includes an OPT-IN advisory 'Stale ticket branches' section,",
181
186
  "and only when you pass --stale-branch: for each named ticket it reports whether",
182
187
  "that ticket's branch would be REFUSED by the stale-branch guard that",
183
188
  "`start-tickets --guard-stale-branch` (and --conductor, which implies it) runs —",
@@ -191,7 +196,7 @@ export function getDoctorUsage() {
191
196
  "",
192
197
  "The 'operational advisories' section also reports RETIRED local drive helpers",
193
198
  "(~/.local/bin/bapi-runtime-up, ~/.local/bin/bapi-drive) when they are still on",
194
- "this machine (BAPI-1102). That is a PATH-PRESENCE check only: doctor does not",
199
+ "this machine. That is a PATH-PRESENCE check only: doctor does not",
195
200
  "run, read, source, chmod, or delete either file, and cannot remove them for you",
196
201
  "— they are outside this repository, so their removal is an operator step. The",
197
202
  "row is advisory and never changes the exit code.",
@@ -218,6 +223,7 @@ export function parseDoctorArgs(argv) {
218
223
  const staleBranchKeys = [];
219
224
  let staleBranchBase = null;
220
225
  const staleBranchOverrides = {};
226
+ let advanced = false;
221
227
  for (let i = 0; i < argv.length; i++) {
222
228
  const arg = argv[i];
223
229
  if (arg === "--fix" || arg.startsWith("--fix=")) {
@@ -296,6 +302,10 @@ export function parseDoctorArgs(argv) {
296
302
  staleBranchOverrides[overrideKey] = branchName;
297
303
  continue;
298
304
  }
305
+ if (arg === "--advanced") {
306
+ advanced = true;
307
+ continue;
308
+ }
299
309
  if (arg === "--agent" || arg.startsWith("--agent=")) {
300
310
  let value;
301
311
  if (arg.startsWith("--agent=")) {
@@ -350,7 +360,13 @@ export function parseDoctorArgs(argv) {
350
360
  }
351
361
  return {
352
362
  status: "ok",
353
- options: { agentName, staleBranchKeys, staleBranchBase, staleBranchOverrides },
363
+ options: {
364
+ agentName,
365
+ staleBranchKeys,
366
+ staleBranchBase,
367
+ staleBranchOverrides,
368
+ ...(advanced ? { advanced: true } : {}),
369
+ },
354
370
  };
355
371
  }
356
372
  /**
@@ -359,13 +375,19 @@ export function parseDoctorArgs(argv) {
359
375
  * missing earlier prerequisite never hides a later one. Returns a structured
360
376
  * unsupported-platform result (no probes) for unsupported OSes; never throws.
361
377
  */
362
- export async function collectDoctorResults(deps, agentName) {
378
+ export async function collectDoctorResults(deps, agentName, options = { advanced: true }) {
363
379
  // agentName is type-guaranteed valid by the parser; resolve is defensive.
364
380
  const agent = resolveAgentSpec(agentName) ?? resolveAgentSpec(DEFAULT_AGENT_NAME);
365
381
  const descriptorsResult = getDoctorPrereqDescriptors(deps.platform, deps.env, agent);
366
382
  if (!descriptorsResult.ok) {
367
383
  return { ok: false, unsupported: true, error: descriptorsResult.error };
368
384
  }
385
+ // BAPI-1461: the default view drops the Python/self-host descriptors BEFORE any
386
+ // probe runs, so a non-Python repository never spawns `uv`. The descriptor
387
+ // table itself is unchanged; `--advanced` keeps them.
388
+ const descriptors = options.advanced === true
389
+ ? descriptorsResult.descriptors
390
+ : descriptorsResult.descriptors.filter((d) => !ADVANCED_ONLY_DESCRIPTOR_IDS.has(d.id));
369
391
  // Augment with read-only filesystem deps so the doctor-only credential and
370
392
  // worktree-MCP-registration probes can run. These are intentionally NOT part
371
393
  // of StartTicketsDeps (live preflight never reads files); doctor injects them
@@ -381,11 +403,93 @@ export async function collectDoctorResults(deps, agentName) {
381
403
  homedir: injected.homedir ?? os.homedir,
382
404
  };
383
405
  const results = [];
384
- for (const descriptor of descriptorsResult.descriptors) {
406
+ for (const descriptor of descriptors) {
385
407
  results.push(await probePrerequisite(probeDeps, descriptor));
386
408
  }
387
409
  return { ok: true, results };
388
410
  }
411
+ /**
412
+ * BAPI-1461 — doctor-only descriptors shown only with `--advanced`, matched by
413
+ * their stable descriptor id. `uv` serves bridge-api's own Python tooling, not a
414
+ * customer repository.
415
+ */
416
+ export const ADVANCED_ONLY_DESCRIPTOR_IDS = new Set(["uv"]);
417
+ /** The production checker, bound so it can neither read nor populate the cache. */
418
+ export function createDoctorVersionChecker(env) {
419
+ return () => checkForUpdate({ env, readCache: false, writeCache: false });
420
+ }
421
+ /**
422
+ * Collect the version diagnostic. Never throws. The lookup is skipped when update
423
+ * checks are disabled or the session is unattended; a null, rejected, or malformed
424
+ * result is `unavailable`. Only a reliable fresh (or override) result reaches the
425
+ * shared notice decision, and only then are project pins evaluated.
426
+ */
427
+ export async function collectVersionDiagnostic(deps) {
428
+ try {
429
+ if (!parseDefaultOnEnvFlag(deps.env[UPDATE_CHECK_ENABLED_ENV]) || isUnattendedSession(deps.env)) {
430
+ return { status: "not-checked" };
431
+ }
432
+ let result = null;
433
+ try {
434
+ result = await deps.checkLatest();
435
+ }
436
+ catch {
437
+ result = null;
438
+ }
439
+ if (!result || typeof result !== "object" || !isPlainReleaseVersion(result.latestVersion)) {
440
+ return { status: "unavailable" };
441
+ }
442
+ const latest = result.latestVersion;
443
+ const authoritative = { known: true, latest };
444
+ const upgrade = getNoticeDecision(deps.env, VERSION, authoritative).show;
445
+ const stalePins = [];
446
+ try {
447
+ const shared = await inspectLauncherConfigs({
448
+ cwd: deps.cwd,
449
+ targetVersion: VERSION,
450
+ readFile: deps.readFile,
451
+ });
452
+ for (const found of shared) {
453
+ if (!found.filePresent || !found.bridgeEntryPresent)
454
+ continue;
455
+ if (found.shape !== "standard" || isPrerequisiteFailure(found))
456
+ continue;
457
+ if (!isExactVersion(found.currentVersion))
458
+ continue;
459
+ const pin = found.currentVersion;
460
+ if (getNoticeDecision(deps.env, pin, authoritative).show) {
461
+ stalePins.push({ relPath: found.relPath, pin });
462
+ }
463
+ }
464
+ }
465
+ catch {
466
+ // Pin diagnostics are advisory; the installed-runtime line still renders.
467
+ }
468
+ return { status: "known", latest, upgrade, stalePins };
469
+ }
470
+ catch {
471
+ return { status: "unavailable" };
472
+ }
473
+ }
474
+ /** The `Bridge MCP version` lines, or none when no diagnostic was collected. */
475
+ export function formatDoctorVersionLines(diagnostic) {
476
+ if (!diagnostic)
477
+ return [];
478
+ if (diagnostic.status === "not-checked") {
479
+ return [`Bridge MCP version: installed ${VERSION}, latest: not checked`];
480
+ }
481
+ if (diagnostic.status === "unavailable") {
482
+ return [`Bridge MCP version: installed ${VERSION}, latest: unavailable`];
483
+ }
484
+ const lines = [`Bridge MCP version: installed ${VERSION}, latest ${diagnostic.latest}`];
485
+ if (diagnostic.upgrade)
486
+ lines.push(` ${formatUpdateAdvice(VERSION, diagnostic.latest)}`);
487
+ // Stale project pins are subordinate to the runtime line above.
488
+ for (const { relPath, pin } of diagnostic.stalePins) {
489
+ lines.push(` ${relPath} pin: ${formatUpdateAdvice(pin, diagnostic.latest)}`);
490
+ }
491
+ return lines;
492
+ }
389
493
  /**
390
494
  * The BROAD report header: title plus the run's platform / agent / profile
391
495
  * metadata (BAPI-669, U9b). Split out from the prerequisite body so `runDoctorCli`
@@ -393,7 +497,10 @@ export async function collectDoctorResults(deps, agentName) {
393
497
  * ordering change is the whole point, and it must not require rewriting the
394
498
  * prerequisite formatting below.
395
499
  */
396
- export function buildDoctorReportHeader(platform, agent) {
500
+ export function buildDoctorReportHeader(platform, agent,
501
+ // A DEFAULT value, not a plain optional marker, so the function's declared arity
502
+ // stays two: the header takes no dependency seam and does no I/O.
503
+ versionDiagnostic = undefined) {
397
504
  const activeGroups = Array.from(resolveProfiles(process.env.BRIDGE_MCP_PROFILE)).join(", ");
398
505
  return [
399
506
  DOCTOR_REPORT_TITLE,
@@ -404,6 +511,7 @@ export function buildDoctorReportHeader(platform, agent) {
404
511
  // never blank and never omitted. Neither line feeds launcher-pin drift,
405
512
  // which stays a `VERSION`-only comparison.
406
513
  `Package version: ${VERSION}`,
514
+ ...formatDoctorVersionLines(versionDiagnostic),
407
515
  `Build commit: ${BUILD_COMMIT}`,
408
516
  `Platform: ${platform}`,
409
517
  `Selected agent: ${agent.name} (command: ${agent.command})`,
@@ -1696,6 +1804,24 @@ export function formatOperationalAdvisoryReport(diagnostic) {
1696
1804
  lines.push(" runs NO command probe, repairs no scheduler, and never changes the exit code.");
1697
1805
  return lines.join("\n");
1698
1806
  }
1807
+ /**
1808
+ * BAPI-1461 — render the "Getting started" section: the Claude sign-in and the
1809
+ * GitHub CLI sign-in, each ready or with its one fix. Both are needed for a
1810
+ * first epic; neither line carries any value read from disk or from `gh`.
1811
+ */
1812
+ export function formatGettingStartedReport(claude, github) {
1813
+ const lines = ["", "Getting started (needed for your first epic)"];
1814
+ lines.push(claude.detected
1815
+ ? " ready Claude sign-in"
1816
+ : " needs action Claude sign-in — run `claude login`");
1817
+ const remedy = githubSignInRemedy(github);
1818
+ lines.push(remedy === null
1819
+ ? " ready GitHub sign-in"
1820
+ : github === "unavailable"
1821
+ ? ` could not verify GitHub sign-in — ${remedy}`
1822
+ : ` needs action GitHub sign-in — ${remedy}`);
1823
+ return lines.join("\n");
1824
+ }
1699
1825
  /** Collect the permission-rules inspection. Reads only; never writes. */
1700
1826
  export async function collectBridgePermissionsDiagnostic(deps) {
1701
1827
  return inspectBridgePermissions({ cwd: deps.cwd, homeDir: deps.homeDir, env: deps.env }, { readFile: deps.readFile });
@@ -1736,10 +1862,10 @@ export function formatBridgePermissionsDiagnosticReport(inspection) {
1736
1862
  else {
1737
1863
  lines.push(...formatPermissionScope("project permissions.allow", inspection.project));
1738
1864
  }
1739
- lines.push(...formatPermissionScope("user autoMode entries", inspection.user, "Optional: install adds them only after you say yes."));
1865
+ lines.push(...formatPermissionScope("user autoMode entries", inspection.user, `Optional: /drive-epic-run offers to add them, or run \`npx -y ${MCP_PACKAGE_NAME} bridge-permissions\`.`));
1740
1866
  const healthy = inspection.project.state === "present" && inspection.user.state === "present";
1741
1867
  if (!healthy) {
1742
- lines.push(` To repair: re-run \`npx -y ${MCP_PACKAGE_NAME} install\` in a terminal (it lists every rule it writes).`);
1868
+ lines.push(` To repair the project rules: re-run \`npx -y ${MCP_PACKAGE_NAME} install\` in a terminal (it lists every rule it writes).`);
1743
1869
  lines.push(` To remove Bridge's entries: \`npx -y ${MCP_PACKAGE_NAME} install --remove-bridge-permissions\`.`);
1744
1870
  }
1745
1871
  lines.push(` Bridge's ownership record: ${BRIDGE_PERMISSIONS_MANIFEST_RELPATH}. Advisory only; never changes the exit code.`);
@@ -1875,7 +2001,8 @@ export async function runDoctorCli(argv, overrides = {}) {
1875
2001
  }
1876
2002
  const deps = overrides.deps ?? createDefaultStartTicketsDeps();
1877
2003
  const agent = resolveAgentSpec(parsed.options.agentName) ?? resolveAgentSpec(DEFAULT_AGENT_NAME);
1878
- const collection = await collectDoctorResults(deps, parsed.options.agentName);
2004
+ const advanced = parsed.options.advanced === true;
2005
+ const collection = await collectDoctorResults(deps, parsed.options.agentName, { advanced });
1879
2006
  // ---- Section order (BAPI-669, U9c) ----
1880
2007
  // Broad header → Install status → Project identity and root (BAPI-1318) →
1881
2008
  // start-tickets prerequisites → launcher cache → MCP tool surface. Install status renders FIRST and UNCONDITIONALLY: it answers
@@ -1885,7 +2012,26 @@ export async function runDoctorCli(argv, overrides = {}) {
1885
2012
  // always-first is both simpler and equivalent. The `installStatus: false` override
1886
2013
  // remains a dependency-injection-only seam for hermetic tests; a normal CLI
1887
2014
  // invocation never takes it.
1888
- log(buildDoctorReportHeader(deps.platform, agent).join("\n"));
2015
+ // BAPI-1446: the version diagnostic is advisory and read-only (fresh lookup, no
2016
+ // cache read or write, no spawn). A failure leaves the header exactly as it was.
2017
+ let versionDiagnostic;
2018
+ if (overrides.version !== false && (overrides.deps === undefined || overrides.version?.checkLatest)) {
2019
+ try {
2020
+ const versionEnv = overrides.version?.env ?? deps.env;
2021
+ versionDiagnostic = await collectVersionDiagnostic({
2022
+ env: versionEnv,
2023
+ cwd: overrides.version?.cwd ?? overrides.launcherProbe?.cwd ?? deps.cwd,
2024
+ readFile: overrides.version?.readFile ??
2025
+ overrides.launcherProbe?.readFile ??
2026
+ ((p) => readFile(p, "utf-8")),
2027
+ checkLatest: overrides.version?.checkLatest ?? createDoctorVersionChecker(versionEnv),
2028
+ });
2029
+ }
2030
+ catch {
2031
+ versionDiagnostic = undefined;
2032
+ }
2033
+ }
2034
+ log(buildDoctorReportHeader(deps.platform, agent, versionDiagnostic).join("\n"));
1889
2035
  // Advisory install-status section (easy-install done criteria). Read-only GETs
1890
2036
  // only; any failure degrades to WARN/SKIP lines inside the section, and any
1891
2037
  // unexpected throw renders the sanitized fallback rather than dropping the
@@ -1914,6 +2060,24 @@ export async function runDoctorCli(argv, overrides = {}) {
1914
2060
  log(formatInstallStatusFallbackReport());
1915
2061
  }
1916
2062
  }
2063
+ // BAPI-1461: "Getting started" — the two sign-ins a first epic needs, right
2064
+ // after Install status. Read-only (one file read, one bounded `gh auth status`)
2065
+ // and advisory: it never changes the exit code. With injected deps it runs only
2066
+ // when its own seam is supplied, so a faked machine never reaches real `gh`.
2067
+ if (overrides.signIn !== false && (overrides.deps === undefined || overrides.signIn !== undefined)) {
2068
+ try {
2069
+ const injectedFs = deps;
2070
+ const claude = await detectClaudeLogin({
2071
+ homedir: overrides.signIn?.homedir ?? injectedFs.homedir ?? os.homedir,
2072
+ readFile: overrides.signIn?.readFile ?? injectedFs.readFile ?? ((p) => readFile(p, "utf-8")),
2073
+ });
2074
+ const github = await checkGithubSignIn(overrides.signIn?.runGh);
2075
+ log(formatGettingStartedReport(claude, github));
2076
+ }
2077
+ catch {
2078
+ log(formatGettingStartedReport({ detected: false }, "unavailable"));
2079
+ }
2080
+ }
1917
2081
  // Advisory "Project identity and root" section (BAPI-1318). Read-only: it
1918
2082
  // validates each explicit BAPI_PROJECT_ROOT the way the server does and
1919
2083
  // compares .bridge/config's repo_name with each project MCP config. Placed
@@ -2176,7 +2340,8 @@ export async function runDoctorCli(argv, overrides = {}) {
2176
2340
  // every section above, never affects the exit code: `plane up` enforces these
2177
2341
  // same invariants at the point where they actually block, and an ordinary MCP
2178
2342
  // user who never runs a conductor plane should not see a failing doctor.
2179
- if (overrides.planeCredentials !== false) {
2343
+ // BAPI-1461: a self-host diagnostic, so it runs only with --advanced.
2344
+ if (advanced && overrides.planeCredentials !== false) {
2180
2345
  try {
2181
2346
  const injectedFs = deps;
2182
2347
  const planeDeps = {