@bridge_gpt/mcp-server 0.2.41 → 0.2.42
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 +10 -10
- package/build/agent-capabilities/cli.js +2 -1
- package/build/agent-launchers/claude-executor-adapter.js +17 -4
- package/build/claude-user-config-doctor.js +42 -11
- package/build/cli-release.js +2 -1
- package/build/commands.generated.js +4 -4
- package/build/conduct-epic/bridge-client.js +354 -113
- package/build/conduct-epic/checkpoint-store.js +17 -0
- package/build/conduct-epic/cli.js +752 -99
- package/build/conduct-epic/cut-protocol.js +327 -0
- package/build/conduct-epic/spawn.js +14 -2
- package/build/conductor/bridge-api-client.js +27 -1
- package/build/conductor/cli.js +46 -1
- package/build/conductor/doctor.js +101 -16
- package/build/conductor/epic-reconcile.js +72 -19
- package/build/conductor/epic-runtime.js +15 -3
- package/build/conductor/errors.js +47 -0
- package/build/conductor/git-hooks.js +205 -11
- package/build/conductor/install-doctor.js +230 -1
- package/build/conductor/local-merge.js +130 -28
- package/build/conductor/tools.js +32 -3
- package/build/conductor/worker-ledger-cli.js +27 -1
- package/build/conductor-bin.js +15 -15
- package/build/credentials-cli.js +3 -2
- package/build/doctor.js +107 -41
- package/build/executor/cli.js +48 -1
- package/build/executor/env.js +21 -0
- package/build/executor/index-scope.js +39 -0
- package/build/executor/job-log-registry.js +69 -0
- package/build/executor/job-runner.js +148 -26
- package/build/executor/live-worker-registry.js +83 -0
- package/build/executor/observation.js +167 -6
- package/build/executor/platform.js +147 -3
- package/build/executor/process.js +58 -14
- package/build/executor/runner.js +235 -48
- package/build/executor/test-clock.js +3 -2
- package/build/index-scope-contract.js +96 -0
- package/build/index.js +153 -204
- package/build/init.js +83 -22
- package/build/install-bridge-conductor.js +323 -14
- package/build/install-bridge.js +202 -38
- package/build/install-doctor.js +23 -9
- package/build/install-reexec.js +2 -1
- package/build/launcher-config-inspection.js +83 -22
- package/build/mcp-host-config.js +331 -67
- package/build/mcp-host-targets.js +45 -21
- package/build/mcp-identity.js +92 -0
- package/build/mcp-install-state.js +94 -1
- package/build/mcp-invoke.js +2 -1
- package/build/mcp-provisioning.js +45 -12
- package/build/mcp-registration-doctor.js +35 -13
- package/build/mcp-server-invocation.js +4 -2
- package/build/merge-pull-request.js +208 -9
- package/build/pipelines.generated.js +3 -3
- package/build/plane/defaults.js +4 -1
- package/build/plane/preflight.js +81 -10
- package/build/plane/test-fakes.js +9 -1
- package/build/readme.generated.js +1 -1
- package/build/regression-check.js +3 -2
- package/build/review-tickets.js +8 -7
- package/build/run-unit-tests-launcher.js +74 -1
- package/build/schedule-run.js +3 -2
- package/build/setup-epic.js +453 -78
- package/build/sfcc/tool-wrapper.js +15 -0
- package/build/start-tickets-prereqs.js +11 -6
- package/build/start-tickets.js +91 -85
- package/build/update-check.js +3 -2
- package/build/upgrade-advice.js +2 -1
- package/build/upgrade-cli.js +50 -18
- package/build/version.generated.js +1 -1
- package/docs/CONDUCTOR.md +22 -0
- package/docs/install/mcp-tool-integrations.md +19 -3
- package/package.json +2 -2
|
@@ -23,9 +23,13 @@
|
|
|
23
23
|
* filesystem write, install-state write, and write-consent prompt. (It still
|
|
24
24
|
* asks the base-URL confirmation above, because its doctor GET is a network
|
|
25
25
|
* call that reveals the key to the resolved host.)
|
|
26
|
-
* 3. **
|
|
27
|
-
* consent to write
|
|
28
|
-
*
|
|
26
|
+
* 3. **One consent per write surface.** Confirming the resolved base URL is NOT
|
|
27
|
+
* consent to write; consent to overwrite a workflow file is not consent to
|
|
28
|
+
* edit host MCP configuration; and neither is consent to install Git hooks.
|
|
29
|
+
* The rule is per-SURFACE, not a fixed count — BAPI-775 added two more write
|
|
30
|
+
* surfaces (host MCP configs, managed Git hooks) and each asks for itself, so
|
|
31
|
+
* counting consents was never the invariant. A missing prompt seam (no TTY)
|
|
32
|
+
* is a decline everywhere, never a default-yes.
|
|
29
33
|
* 4. **The executor leg is never claimed as provisioned by local success.**
|
|
30
34
|
* AMENDED BY BAPI-779 (R-1): this command can now write the service unit
|
|
31
35
|
* *and* start it. What did not change — and must not — is where readiness
|
|
@@ -36,6 +40,20 @@
|
|
|
36
40
|
* otherwise. Local lifecycle success and server observation are reported as
|
|
37
41
|
* two separate facts, and the second is never derived from the first.
|
|
38
42
|
*
|
|
43
|
+
* The two local out-of-the-box phases (BAPI-775) run between the workflow
|
|
44
|
+
* scaffold and setup-epic verification, each behind its own consent:
|
|
45
|
+
*
|
|
46
|
+
* tool visibility — merge the `conductor` token into EXISTING supported host
|
|
47
|
+
* MCP entries (`.mcp.json`, `.vscode/mcp.json`, `.cursor/mcp.json` only,
|
|
48
|
+
* never Codex or Copilot), then print the restart advisory, because
|
|
49
|
+
* `ACTIVE_GROUPS` is frozen at MCP server startup.
|
|
50
|
+
* local observability — install the managed conductor Git hooks, then report
|
|
51
|
+
* native ledger loadability. The ledger probe is READ-ONLY, so it needs no
|
|
52
|
+
* consent and runs whether hook consent was accepted or declined.
|
|
53
|
+
*
|
|
54
|
+
* Neither can produce `complete`: they provision LOCAL capability, and the
|
|
55
|
+
* terminal outcome still turns only on the live server-observed executor claim.
|
|
56
|
+
*
|
|
39
57
|
* The executor phase (BAPI-779) is opt-in via `--executor-id` and runs behind
|
|
40
58
|
* TWO independent consents, per property 3: writing the unit is not consent to
|
|
41
59
|
* start a persistent daemon. Its shape is:
|
|
@@ -57,6 +75,9 @@
|
|
|
57
75
|
*/
|
|
58
76
|
import { bootstrapConductorSupervisorDefaults, ConductorBridgeApiError, fetchConductorReadiness, } from "./conductor/bridge-api-client.js";
|
|
59
77
|
import { CONDUCTOR_OPERATOR_RUNBOOK_POINTER, EXECUTOR_PROVISIONING_GUIDANCE, conductorInstallDoctorExitCode, conductorInstallDoctorHasFatal, formatConductorInstallDoctorReport, } from "./conductor/install-doctor.js";
|
|
78
|
+
import { describeNativeLedgerAvailability, } from "./conductor/doctor.js";
|
|
79
|
+
import { CONDUCTOR_BIN_UNRESOLVED_ERROR, MANAGED_HOOK_NAMES, } from "./conductor/git-hooks.js";
|
|
80
|
+
import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
|
|
60
81
|
import { CLAUDE_REVIEW_WORKFLOW_RELPATH, renderClaudeReviewWorkflow, } from "./claude-review-workflow.js";
|
|
61
82
|
import { formatExecutorInstallPreflight, } from "./executor/install-preflight.js";
|
|
62
83
|
import { buildExecutorServiceStartPlan, formatExecutorServiceStartFailure, } from "./executor/service-lifecycle.js";
|
|
@@ -101,9 +122,11 @@ export function getInstallBridgeConductorUsage() {
|
|
|
101
122
|
" --branches a,b claude-review workflow: PR branch triggers",
|
|
102
123
|
" --skip-workflow Never offer the claude-review workflow",
|
|
103
124
|
" --dry-run Diagnose and preview only. Performs NO writes:",
|
|
104
|
-
" no POST, no file write, no
|
|
105
|
-
" no
|
|
106
|
-
"
|
|
125
|
+
" no POST, no file write, no host MCP config write,",
|
|
126
|
+
" no Git hook write, no install-state or capability",
|
|
127
|
+
" write, no service-unit write, no launchctl/systemctl",
|
|
128
|
+
" or other lifecycle command, and no write- or",
|
|
129
|
+
" start-consent prompt.",
|
|
107
130
|
" Still asks the base-URL confirmation below first —",
|
|
108
131
|
" its doctor readiness GET carries the repo API key.",
|
|
109
132
|
" -h, --help Show this help",
|
|
@@ -113,6 +136,22 @@ export function getInstallBridgeConductorUsage() {
|
|
|
113
136
|
"production when unset). The bootstrap POST then asks its own, separate write",
|
|
114
137
|
"consent. Without a TTY there is no consent, so the command aborts.",
|
|
115
138
|
"",
|
|
139
|
+
"Tool visibility (its own consent): adds the `conductor` token to the",
|
|
140
|
+
"BRIDGE_MCP_PROFILE of the Bridge entry in host MCP configurations that",
|
|
141
|
+
"ALREADY have one. Automatic merging covers .mcp.json, .vscode/mcp.json, and",
|
|
142
|
+
".cursor/mcp.json only — never Codex or Copilot, which are neither inspected",
|
|
143
|
+
"nor changed. No file is ever created: when no supported config carries a",
|
|
144
|
+
"a Bridge entry, the run says so and points at the base `install` command.",
|
|
145
|
+
"The MCP server captures its active groups at startup, so a merge activates on",
|
|
146
|
+
"the next launch — restart your MCP client to see the conductor tools.",
|
|
147
|
+
"",
|
|
148
|
+
"Local observability (a SEPARATE consent): installs or updates this",
|
|
149
|
+
"repository's managed conductor Git hooks (post-commit,",
|
|
150
|
+
"reference-transaction) and reports each hook's action. It then probes native",
|
|
151
|
+
"ledger loadability, which is READ-ONLY and therefore runs with or without",
|
|
152
|
+
"hook consent; a degraded ledger reports the module, NODE_MODULE_VERSION, and",
|
|
153
|
+
"failure kind, and never triggers an install, rebuild, or migration.",
|
|
154
|
+
"",
|
|
116
155
|
"Executor provisioning: `executor install-service` still generates a unit and",
|
|
117
156
|
"nothing else, and remains available for operators who want the file only.",
|
|
118
157
|
"",
|
|
@@ -503,6 +542,216 @@ async function runExecutorProvisioningPhase(deps, executor, plans, access) {
|
|
|
503
542
|
deps.log(" executor_ready is NOT claimed — only a live server observation can produce it.");
|
|
504
543
|
return null;
|
|
505
544
|
}
|
|
545
|
+
// ---------------------------------------------------------------------------
|
|
546
|
+
// Local out-of-the-box phases (BAPI-775)
|
|
547
|
+
// ---------------------------------------------------------------------------
|
|
548
|
+
/**
|
|
549
|
+
* The exact restart advisory the SFCC profile merge prints (`index.ts`), reused
|
|
550
|
+
* verbatim in shape for the conductor token.
|
|
551
|
+
*
|
|
552
|
+
* `ACTIVE_GROUPS` is frozen when the MCP server process starts, so a merged
|
|
553
|
+
* profile changes nothing about the session that is already running. Printing
|
|
554
|
+
* this after EVERY non-empty merge — including an idempotent one where the token
|
|
555
|
+
* was already present — is deliberate: an operator whose second run silently
|
|
556
|
+
* said nothing would reasonably conclude the tools were already visible.
|
|
557
|
+
*/
|
|
558
|
+
export function formatConductorRestartAdvisory(mergedProfile) {
|
|
559
|
+
return (`⚠️ Conductor profile updated: BRIDGE_MCP_PROFILE is now set to \`${mergedProfile}\` ` +
|
|
560
|
+
"in your local MCP config file(s). The running MCP server captured its active groups " +
|
|
561
|
+
"at startup, so this activates on the next MCP server launch — restart your MCP " +
|
|
562
|
+
"client to gain access to the conductor tools.");
|
|
563
|
+
}
|
|
564
|
+
/** Remediation printed when no supported host config carries a Bridge entry. */
|
|
565
|
+
export const CONDUCTOR_PROFILE_EMPTY_MERGE_REMEDIATION = `run \`npx ${MCP_PACKAGE_NAME} install\` to create and register the host MCP entry, ` +
|
|
566
|
+
"then re-run `install conductor`.";
|
|
567
|
+
/** Remediation printed whenever the managed conductor hooks were not installed. */
|
|
568
|
+
export const CONDUCTOR_HOOKS_MANUAL_REMEDIATION = "re-run `install conductor` and accept the local-observability consent, or run " +
|
|
569
|
+
"`conductor install-git-hooks` directly.";
|
|
570
|
+
/**
|
|
571
|
+
* Phase: tool visibility.
|
|
572
|
+
*
|
|
573
|
+
* Adds the `conductor` token to EXISTING supported host MCP entries behind its
|
|
574
|
+
* own consent, then prints the restart advisory. It never creates a host config
|
|
575
|
+
* — the merge helper's never-create rule is what keeps a declined or
|
|
576
|
+
* unconfigured repository from acquiring files it did not ask for.
|
|
577
|
+
*/
|
|
578
|
+
async function runConductorToolVisibilityPhase(deps, local) {
|
|
579
|
+
const consented = await confirm(deps, "Update existing supported host MCP configurations (.mcp.json, .vscode/mcp.json, " +
|
|
580
|
+
".cursor/mcp.json) to add the `conductor` profile? [y/N]: ");
|
|
581
|
+
if (!consented) {
|
|
582
|
+
deps.log(" declined — no host MCP configuration and no install state were written.");
|
|
583
|
+
return {
|
|
584
|
+
manualActions: [
|
|
585
|
+
"tool visibility: re-run `install conductor` and accept the profile consent to " +
|
|
586
|
+
"make the conductor MCP tools visible.",
|
|
587
|
+
],
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
let mergedProfile;
|
|
591
|
+
try {
|
|
592
|
+
mergedProfile = await local.mergeProfileToken();
|
|
593
|
+
}
|
|
594
|
+
catch {
|
|
595
|
+
// The thrown value is discarded: it can carry an absolute config path.
|
|
596
|
+
deps.errorLog(" the host MCP profile could not be updated.");
|
|
597
|
+
return { manualActions: [], fatal: true };
|
|
598
|
+
}
|
|
599
|
+
if (mergedProfile === "") {
|
|
600
|
+
deps.log(" no supported host MCP configuration carries a Bridge entry, so nothing " +
|
|
601
|
+
"was written (this command never creates one).");
|
|
602
|
+
return {
|
|
603
|
+
manualActions: [
|
|
604
|
+
`tool visibility: ${CONDUCTOR_PROFILE_EMPTY_MERGE_REMEDIATION}`,
|
|
605
|
+
],
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
// Which configs actually carry the token now. Derived from the READ-ONLY
|
|
609
|
+
// inspector rather than guessed from the merged profile string, which says
|
|
610
|
+
// nothing about which files it came from.
|
|
611
|
+
let carrying = [];
|
|
612
|
+
try {
|
|
613
|
+
const inspection = await local.inspectProfileToken();
|
|
614
|
+
carrying = inspection.configs
|
|
615
|
+
.filter((c) => c.state === "token-present")
|
|
616
|
+
.map((c) => c.path);
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
// A failed read-back is not a failed merge. The advisory below still prints.
|
|
620
|
+
}
|
|
621
|
+
deps.log(carrying.length > 0
|
|
622
|
+
? ` updated: ${carrying.join(", ")}`
|
|
623
|
+
: " the conductor profile was merged into your supported host MCP config(s).");
|
|
624
|
+
// Coverage honesty: the merge path writes these three files and no others.
|
|
625
|
+
deps.log(" Codex and Copilot host configuration was NOT inspected or changed.");
|
|
626
|
+
deps.log(` ${formatConductorRestartAdvisory(mergedProfile)}`);
|
|
627
|
+
const recorded = await local.recordCapability("conductor_tool_visibility");
|
|
628
|
+
if (!recorded.ok) {
|
|
629
|
+
// Advisory: install state is a diagnostic aid. A recording failure does not
|
|
630
|
+
// invalidate the host-config change that just landed.
|
|
631
|
+
deps.errorLog(` note: install-state not updated (${recorded.error ?? "unknown"})`);
|
|
632
|
+
}
|
|
633
|
+
return { manualActions: [] };
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Phase: local observability.
|
|
637
|
+
*
|
|
638
|
+
* Installs the managed conductor Git hooks behind its OWN consent — never
|
|
639
|
+
* inferred from the base-URL, bootstrap, workflow, profile-token, or executor
|
|
640
|
+
* consents — and then always reports native ledger loadability, which is
|
|
641
|
+
* read-only and therefore needs no consent at all.
|
|
642
|
+
*/
|
|
643
|
+
async function runConductorLocalObservabilityPhase(deps, local) {
|
|
644
|
+
const manualActions = [];
|
|
645
|
+
let fatal = false;
|
|
646
|
+
const consented = await confirm(deps, "Install or update this repository's managed conductor Git hooks " +
|
|
647
|
+
"(post-commit, reference-transaction)? [y/N]: ");
|
|
648
|
+
if (!consented) {
|
|
649
|
+
deps.log(" declined — no Git hook and no install state were written.");
|
|
650
|
+
manualActions.push(`local observability: ${CONDUCTOR_HOOKS_MANUAL_REMEDIATION}`);
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
let result = null;
|
|
654
|
+
try {
|
|
655
|
+
result = await local.installGitHooks();
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
// Generic by design: the caught value can carry a hooks-directory path.
|
|
659
|
+
// The phase is marked fatal but does NOT return here — the read-only
|
|
660
|
+
// ledger report below still owes the operator its answer.
|
|
661
|
+
deps.errorLog(" the conductor Git hooks could not be installed.");
|
|
662
|
+
manualActions.push(`local observability: ${CONDUCTOR_HOOKS_MANUAL_REMEDIATION}`);
|
|
663
|
+
fatal = true;
|
|
664
|
+
}
|
|
665
|
+
if (result !== null && !result.ok) {
|
|
666
|
+
// BAPI-772's fail-loud case: nothing was created, merged, or chmodded, and
|
|
667
|
+
// `reason` is already a sanitized sentence. This one IS a command failure —
|
|
668
|
+
// a hook that cannot resolve its binary is a dead hook, and the whole point
|
|
669
|
+
// of that ticket is that it no longer gets written silently.
|
|
670
|
+
deps.errorLog(result.error === CONDUCTOR_BIN_UNRESOLVED_ERROR
|
|
671
|
+
? ` hook installation refused — ${result.reason}`
|
|
672
|
+
: " hook installation refused.");
|
|
673
|
+
manualActions.push(`local observability: ${CONDUCTOR_HOOKS_MANUAL_REMEDIATION}`);
|
|
674
|
+
fatal = true;
|
|
675
|
+
}
|
|
676
|
+
else if (result !== null && result.ok) {
|
|
677
|
+
if (!result.is_worktree) {
|
|
678
|
+
deps.log(" not a git worktree, so no managed hook was installed (degraded optional capability).");
|
|
679
|
+
manualActions.push("local observability: run `install conductor` from a git worktree to install the " +
|
|
680
|
+
"managed conductor hooks.");
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
for (const hook of result.installed) {
|
|
684
|
+
// The per-hook warning is rendered beside its action, not merely
|
|
685
|
+
// counted: a hook reported as `created` while carrying "could not
|
|
686
|
+
// chmod +x" reads as provisioned unless the warning is visible, and
|
|
687
|
+
// that warning is exactly what makes it unusable.
|
|
688
|
+
deps.log(hook.warning === undefined
|
|
689
|
+
? ` ${hook.name}: ${hook.action}`
|
|
690
|
+
: ` ${hook.name}: ${hook.action} — warning: ${hook.warning}`);
|
|
691
|
+
}
|
|
692
|
+
// Sanitized: the installer's own warnings are secret-free sentences, and
|
|
693
|
+
// hook CONTENT, env, and caught errors are never rendered.
|
|
694
|
+
for (const warning of result.warnings)
|
|
695
|
+
deps.log(` warning: ${warning}`);
|
|
696
|
+
// Completeness is checked against the managed-hook ROSTER, not against
|
|
697
|
+
// "at least one hook was installed": a partial install that wrote only
|
|
698
|
+
// post-commit and warned about nothing would otherwise be recorded as
|
|
699
|
+
// provisioned observability, and the missing reference-transaction hook
|
|
700
|
+
// is precisely the one that stops half the events from ever being
|
|
701
|
+
// emitted.
|
|
702
|
+
const usable = (name) => {
|
|
703
|
+
const hook = result.installed.find((h) => h.name === name);
|
|
704
|
+
return hook !== undefined && hook.action !== "skipped" && hook.warning === undefined;
|
|
705
|
+
};
|
|
706
|
+
const complete = MANAGED_HOOK_NAMES.every((name) => usable(name)) && result.warnings.length === 0;
|
|
707
|
+
if (complete) {
|
|
708
|
+
const recorded = await local.recordCapability("conductor_local_observability");
|
|
709
|
+
if (!recorded.ok) {
|
|
710
|
+
deps.errorLog(` note: install-state not updated (${recorded.error ?? "unknown"})`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
// A skipped or warned hook is NOT provisioned local observability, and
|
|
715
|
+
// recording the capability here would make install state claim
|
|
716
|
+
// something a `conductor doctor` run would immediately contradict.
|
|
717
|
+
deps.log(" the managed hooks are not fully usable, so local observability was not recorded.");
|
|
718
|
+
manualActions.push(`local observability: ${CONDUCTOR_HOOKS_MANUAL_REMEDIATION}`);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
// The ledger probe runs whether hook consent was accepted or DECLINED: it is
|
|
724
|
+
// strictly read-only, so it needs no consent, and its result is exactly what
|
|
725
|
+
// an operator needs to know before a hook can produce an event. It also runs
|
|
726
|
+
// before any fail-loud return below, so a hook-binary refusal never costs the
|
|
727
|
+
// operator this diagnostic.
|
|
728
|
+
let inspection = null;
|
|
729
|
+
try {
|
|
730
|
+
inspection = await local.probeNativeLedger();
|
|
731
|
+
}
|
|
732
|
+
catch {
|
|
733
|
+
inspection = null;
|
|
734
|
+
}
|
|
735
|
+
if (inspection === null) {
|
|
736
|
+
deps.log(" ledger loadability could not be probed on this host.");
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
const availability = describeNativeLedgerAvailability(inspection);
|
|
740
|
+
if (availability.status === "available") {
|
|
741
|
+
deps.log(" ledger available — the native binding loads.");
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
// Never fatal, and never an install/rebuild/migration attempt: the module,
|
|
745
|
+
// ABI, and sanitized failure kind are what make an npm optional-dependency
|
|
746
|
+
// build skip diagnosable.
|
|
747
|
+
deps.log(` ledger degraded — ${availability.module} did not load ` +
|
|
748
|
+
`(NODE_MODULE_VERSION ${availability.nodeModulesAbi}, failure: ${availability.failureKind}).`);
|
|
749
|
+
manualActions.push(`local observability: reinstall ${availability.module} for this Node runtime to ` +
|
|
750
|
+
"restore the local conductor event ledger.");
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return { manualActions, fatal };
|
|
754
|
+
}
|
|
506
755
|
/**
|
|
507
756
|
* Run the nested conductor installer. Returns the process exit code.
|
|
508
757
|
*/
|
|
@@ -542,7 +791,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
542
791
|
const access = accessResult.ok ? accessResult.access : null;
|
|
543
792
|
const accessError = accessResult.ok ? undefined : accessResult.error;
|
|
544
793
|
// ---- Phase 1: unified doctor, BEFORE every write -----------------------
|
|
545
|
-
deps.log("Phase 1/
|
|
794
|
+
deps.log("Phase 1/8 — running the unified conductor doctor (read-only)…");
|
|
546
795
|
const firstReport = await deps.runDoctor({
|
|
547
796
|
access,
|
|
548
797
|
accessError,
|
|
@@ -571,6 +820,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
571
820
|
// effect — provisioning a service that cannot run is worse than refusing —
|
|
572
821
|
// and before the dry-run return, so `--dry-run` reports the same refusal.
|
|
573
822
|
const executor = deps.executorProvisioning;
|
|
823
|
+
const local = deps.localProvisioning;
|
|
574
824
|
const executorRequested = Boolean(executor && options.executorId);
|
|
575
825
|
let executorPlans = null;
|
|
576
826
|
if (executor && executorRequested) {
|
|
@@ -638,6 +888,18 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
638
888
|
// would actually happen. Still zero writes: no unit writer, no
|
|
639
889
|
// install-state recorder, no lifecycle runner, and no consent prompt has
|
|
640
890
|
// been reached on this branch.
|
|
891
|
+
// BAPI-775: named as SEPARATE consents so the preview cannot read as one
|
|
892
|
+
// blanket approval. Nothing below this line invokes the profile merger, the
|
|
893
|
+
// hook installer, the capability recorder, or the phase's ledger probe —
|
|
894
|
+
// this branch returns before every one of them.
|
|
895
|
+
deps.log(local
|
|
896
|
+
? " Tool visibility: would ask its OWN consent before adding the `conductor` " +
|
|
897
|
+
"profile to existing .mcp.json / .vscode/mcp.json / .cursor/mcp.json entries."
|
|
898
|
+
: " Tool visibility: skipped (this caller supplied no local-provisioning seams)");
|
|
899
|
+
deps.log(local
|
|
900
|
+
? " Local observability: would ask a SEPARATE consent before installing the " +
|
|
901
|
+
"managed conductor Git hooks; the ledger probe is read-only."
|
|
902
|
+
: " Local observability: skipped (this caller supplied no local-provisioning seams)");
|
|
641
903
|
if (executorPlans) {
|
|
642
904
|
deps.log(` Would write executor unit: ${executorPlans.install.unitPath}`);
|
|
643
905
|
deps.log(` Would start it with: ${executorPlans.start.manualCommand}`);
|
|
@@ -668,7 +930,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
668
930
|
}
|
|
669
931
|
// ---- Phase 2: bootstrap ------------------------------------------------
|
|
670
932
|
deps.log("");
|
|
671
|
-
deps.log("Phase 2/
|
|
933
|
+
deps.log("Phase 2/8 — bootstrapping supervisor project defaults…");
|
|
672
934
|
let bootstrapResponse;
|
|
673
935
|
try {
|
|
674
936
|
bootstrapResponse = await bootstrapConductorSupervisorDefaults(access, {
|
|
@@ -692,7 +954,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
692
954
|
deps.log(` wrote supervisor setup + config (fields: ${bootstrapResponse.audited_field_names.join(", ")})`);
|
|
693
955
|
// ---- Phase 3: optional workflow scaffold -------------------------------
|
|
694
956
|
deps.log("");
|
|
695
|
-
deps.log("Phase 3/
|
|
957
|
+
deps.log("Phase 3/8 — claude-review workflow…");
|
|
696
958
|
if (options.skipWorkflow || options.reviewPolicy !== "verdict_protocol") {
|
|
697
959
|
deps.log(` skipped — the '${options.reviewPolicy}' review policy does not consume the ` +
|
|
698
960
|
"sticky verdict this workflow emits.");
|
|
@@ -749,12 +1011,47 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
749
1011
|
}
|
|
750
1012
|
}
|
|
751
1013
|
}
|
|
752
|
-
// ----
|
|
1014
|
+
// ---- Phases 4 + 5: local out-of-the-box provisioning (BAPI-775) --------
|
|
1015
|
+
// Placed after the workflow scaffold and BEFORE setup-epic verification,
|
|
1016
|
+
// executor provisioning, and the capability report, so a run that stops at a
|
|
1017
|
+
// later phase has still closed the local gap it consented to close.
|
|
1018
|
+
const manualActions = [];
|
|
1019
|
+
deps.log("");
|
|
1020
|
+
deps.log("Phase 4/8 — conductor tool visibility…");
|
|
1021
|
+
if (local) {
|
|
1022
|
+
const outcome = await runConductorToolVisibilityPhase(deps, local);
|
|
1023
|
+
manualActions.push(...outcome.manualActions);
|
|
1024
|
+
if (outcome.fatal)
|
|
1025
|
+
return 1;
|
|
1026
|
+
}
|
|
1027
|
+
else {
|
|
1028
|
+
deps.log(" skipped — this caller supplied no local-provisioning seams.");
|
|
1029
|
+
manualActions.push("tool visibility: run the packaged `install conductor` command to add the " +
|
|
1030
|
+
"`conductor` profile to your host MCP configuration.");
|
|
1031
|
+
}
|
|
1032
|
+
deps.log("");
|
|
1033
|
+
deps.log("Phase 5/8 — conductor local observability…");
|
|
1034
|
+
let observabilityFatal = false;
|
|
1035
|
+
if (local) {
|
|
1036
|
+
const outcome = await runConductorLocalObservabilityPhase(deps, local);
|
|
1037
|
+
manualActions.push(...outcome.manualActions);
|
|
1038
|
+
observabilityFatal = outcome.fatal === true;
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
deps.log(" skipped — this caller supplied no local-provisioning seams.");
|
|
1042
|
+
manualActions.push("local observability: run the packaged `install conductor` command, or " +
|
|
1043
|
+
"`conductor install-git-hooks`, to install the managed conductor Git hooks.");
|
|
1044
|
+
}
|
|
1045
|
+
// The fail-loud hook-binary refusal exits 1 — but only AFTER the phase's
|
|
1046
|
+
// read-only ledger report has been printed, which the phase guarantees.
|
|
1047
|
+
if (observabilityFatal)
|
|
1048
|
+
return 1;
|
|
1049
|
+
// ---- Phase 6: setup-epic verification (conditional) --------------------
|
|
753
1050
|
// `parseSetupEpicArgs` hard-requires --epic-key AND --plan-file before any
|
|
754
1051
|
// dry-run branch, so this phase can only run when both are supplied. Fresh
|
|
755
1052
|
// repositories skip it with an explicit message rather than hard-failing.
|
|
756
1053
|
deps.log("");
|
|
757
|
-
deps.log("Phase
|
|
1054
|
+
deps.log("Phase 6/8 — setup-epic verification…");
|
|
758
1055
|
if (options.epicKey && options.planFile) {
|
|
759
1056
|
deps.log(" verifying with `setup-epic --dry-run`…");
|
|
760
1057
|
const verification = await deps.runSetupEpicDryRun([
|
|
@@ -783,7 +1080,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
783
1080
|
// started daemon behind, and BEFORE the capability report so the matrix can
|
|
784
1081
|
// use the readiness this phase actually observed.
|
|
785
1082
|
deps.log("");
|
|
786
|
-
deps.log("Phase
|
|
1083
|
+
deps.log("Phase 7/8 — executor provisioning…");
|
|
787
1084
|
let observedReadiness = null;
|
|
788
1085
|
if (executor && executorPlans) {
|
|
789
1086
|
observedReadiness = await runExecutorProvisioningPhase(deps, executor, executorPlans, access);
|
|
@@ -796,7 +1093,7 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
796
1093
|
}
|
|
797
1094
|
// ---- Phase 6: capability matrix + final doctor -------------------------
|
|
798
1095
|
deps.log("");
|
|
799
|
-
deps.log("Phase
|
|
1096
|
+
deps.log("Phase 8/8 — capability report…");
|
|
800
1097
|
// The observed payload WINS when the executor phase actually saw the server
|
|
801
1098
|
// report readiness. Otherwise the matrix re-reads readiness itself — it is
|
|
802
1099
|
// never handed a synthesized response, so `executor_ready` still originates
|
|
@@ -820,9 +1117,21 @@ export async function runInstallBridgeConductorCli(argv, deps) {
|
|
|
820
1117
|
reviewPolicySource: options.reviewPolicy,
|
|
821
1118
|
});
|
|
822
1119
|
deps.log(formatConductorInstallDoctorReport(finalReport));
|
|
1120
|
+
// Manual actions (BAPI-775): every declined, unavailable, empty, or incomplete
|
|
1121
|
+
// local phase leaves a pointer here. Collected in one place because an
|
|
1122
|
+
// operator who scrolled past a phase still needs to know what is outstanding.
|
|
1123
|
+
if (manualActions.length > 0) {
|
|
1124
|
+
deps.log("");
|
|
1125
|
+
deps.log("Manual actions");
|
|
1126
|
+
deps.log("──────────────");
|
|
1127
|
+
for (const action of manualActions)
|
|
1128
|
+
deps.log(` → ${action}`);
|
|
1129
|
+
}
|
|
823
1130
|
deps.log("");
|
|
824
1131
|
// The executor leg can only be true when the server observed a recent claim,
|
|
825
|
-
// so a bootstrap alone can never print `complete`.
|
|
1132
|
+
// so a bootstrap alone can never print `complete`. The two local phases above
|
|
1133
|
+
// deliberately do NOT feed this predicate: local provisioning cannot
|
|
1134
|
+
// manufacture `complete`, and the terminal outcome is unchanged by BAPI-775.
|
|
826
1135
|
const allReady = matrix.apiReady && matrix.configurationReady && matrix.executorReady;
|
|
827
1136
|
deps.log(allReady ? "complete" : "complete-with-manual-steps");
|
|
828
1137
|
return conductorInstallDoctorExitCode(finalReport);
|