@bridge_gpt/mcp-server 0.2.49 → 0.2.51
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 +25 -8
- package/build/base-ref.js +28 -3
- package/build/claude-review-workflow-drift-probe.js +130 -0
- package/build/claude-review-workflow-drift.js +173 -0
- package/build/claude-review-workflow.js +81 -16
- package/build/commands.generated.js +5 -5
- package/build/conduct-epic/bridge-client.js +115 -1
- package/build/conduct-epic/cli.js +351 -33
- package/build/conduct-epic/cut-protocol.js +51 -0
- package/build/conductor/done-gate.js +25 -3
- package/build/conductor/install-doctor.js +65 -5
- package/build/conductor/latest-check-selector.js +170 -0
- package/build/conductor/local-merge.js +8 -6
- package/build/conductor-bin.js +1 -1
- package/build/{brainstorm-files.js → council-files.js} +15 -15
- package/build/decision-page-schema.js +1 -1
- package/build/docs.generated.js +1 -1
- package/build/doctor.js +352 -4
- package/build/epic-integration-pr.js +280 -0
- package/build/executor/job-runner.js +7 -1
- package/build/executor/merge-job.js +46 -1
- package/build/executor/worktree.js +46 -1
- package/build/index.js +153 -65
- package/build/init.js +9 -2
- package/build/install-bridge.js +60 -2
- package/build/install-reexec.js +47 -9
- package/build/pipelines.generated.js +8 -2
- package/build/plan-epic-conductor-eligibility.js +183 -0
- package/build/plane/cli.js +12 -2
- package/build/plane/manifest.js +25 -1
- package/build/plane/member-roster.js +61 -7
- package/build/plane/preflight.js +24 -9
- package/build/plane/supervisor.js +77 -5
- package/build/plane/types.js +23 -3
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +2 -1
- package/build/setup-epic.js +32 -0
- package/build/sfcc/reads-custom-object-def.js +10 -13
- package/build/sfcc/reads-site-preference.js +5 -5
- package/build/sfcc/reads-system-object.js +4 -4
- package/build/sfcc/writes-custom-object-def.js +7 -7
- package/build/sfcc/writes-site-preference.js +4 -3
- package/build/sfcc/writes-system-object.js +7 -6
- package/build/stale-worktree-doctor.js +120 -0
- package/build/start-tickets-prereqs.js +70 -0
- package/build/start-tickets.js +91 -3
- package/build/version.generated.js +3 -2
- package/package.json +6 -3
- package/pipelines/plan-epic.json +5 -0
- package/build/chain-orchestrator.js +0 -1457
- package/build/chain-utils.js +0 -68
- package/build/command-catalog.js +0 -376
- package/build/schedule-run.js +0 -1300
- package/build/schedule-store.js +0 -172
- package/build/scheduled-prompt.js +0 -115
- package/build/scheduler-backends/at-fallback.js +0 -139
- package/build/scheduler-backends/escaping.js +0 -143
- package/build/scheduler-backends/index.js +0 -72
- package/build/scheduler-backends/launchd.js +0 -225
- package/build/scheduler-backends/systemd-user.js +0 -250
- package/build/scheduler-backends/task-scheduler.js +0 -214
- package/build/scheduler-backends/types.js +0 -23
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* No MCP tool is registered from this module — the `tools/list` token budget is
|
|
29
29
|
* effectively frozen.
|
|
30
30
|
*/
|
|
31
|
-
import { buildConductorJiraUrl, fetchConductorJsonPostWithTimeout, fetchConductorJsonPutWithTimeout, fetchConductorJsonWithTimeout, fetchConductorReadiness, fetchEffectiveSupervisorConfig, fetchEffectiveSupervisorSetup, fetchEpicRunState, fetchParseStatus, fetchPrReviewStatus, pollCiChecksForCommit, ConductorBridgeApiError, CONDUCTOR_FETCH_TIMEOUT_MS, } from "../conductor/bridge-api-client.js";
|
|
31
|
+
import { buildConductorJiraUrl, buildConductorVcsUrl, fetchConductorJsonPostWithTimeout, fetchConductorJsonPutWithTimeout, fetchConductorJsonWithTimeout, fetchConductorReadiness, fetchEffectiveSupervisorConfig, fetchEffectiveSupervisorSetup, fetchEpicRunState, fetchParseStatus, fetchPrReviewStatus, pollCiChecksForCommit, ConductorBridgeApiError, CONDUCTOR_FETCH_TIMEOUT_MS, } from "../conductor/bridge-api-client.js";
|
|
32
32
|
/**
|
|
33
33
|
* The one message returned for any failure whose cause cannot be described
|
|
34
34
|
* without risking the credential. Stable so callers can match on it.
|
|
@@ -657,3 +657,117 @@ export async function reclaimIndexScope(access, request, fetchImpl = globalThis.
|
|
|
657
657
|
},
|
|
658
658
|
};
|
|
659
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Epic-run statuses that mean a v2 conductor may still be driving the run.
|
|
662
|
+
*
|
|
663
|
+
* These are the NON-TERMINAL members of the server's `EpicRunStatus` vocabulary
|
|
664
|
+
* (`api/models/epic_run.py`: `planning | pending_approval | active | blocked |
|
|
665
|
+
* abandoned | done`); only `abandoned` and `done` are terminal. `active` is the
|
|
666
|
+
* state a run spends nearly all of its life in, so omitting it would make this
|
|
667
|
+
* whole advisory silent for exactly the overlap it exists to surface.
|
|
668
|
+
*
|
|
669
|
+
* The vocabulary matters and is easy to get wrong: `running`/`queued` belong to
|
|
670
|
+
* `ExecutorJobStatusValue` and `paused` to `RunControlState`, and none of the
|
|
671
|
+
* three is ever an `epic_runs.status`. Status alone is not liveness either — the
|
|
672
|
+
* caller additionally requires an unexpired lease, so a `blocked` run whose lease
|
|
673
|
+
* has lapsed is correctly read as wreckage rather than as a live conductor.
|
|
674
|
+
*/
|
|
675
|
+
const ACTIVE_EPIC_RUN_STATUSES = [
|
|
676
|
+
"planning",
|
|
677
|
+
"pending_approval",
|
|
678
|
+
"active",
|
|
679
|
+
"blocked",
|
|
680
|
+
];
|
|
681
|
+
/**
|
|
682
|
+
* `GET /automation/health?repo_name=` — read-only parse-dispatcher liveness.
|
|
683
|
+
*
|
|
684
|
+
* Root-mounted, NOT under `/jira`. The response is narrowed to two safe fields;
|
|
685
|
+
* signals, stale-ticket samples, queue contents, and every timestamp are
|
|
686
|
+
* deliberately dropped — an advisory line needs a verdict, not a report.
|
|
687
|
+
*
|
|
688
|
+
* `absent` is claimed ONLY on positive evidence: a `stale` or `never_seen`
|
|
689
|
+
* heartbeat together with a responding process that is not itself sweeping.
|
|
690
|
+
* Every other shape — `unknown`, a missing field, a malformed body, any read
|
|
691
|
+
* failure — is `unavailable`.
|
|
692
|
+
*/
|
|
693
|
+
export async function getParseDispatcherHealth(access, fetchImpl = globalThis.fetch) {
|
|
694
|
+
const result = await wrap(access, () => {
|
|
695
|
+
const url = new URL(buildConductorVcsUrl(access.baseUrl, "/automation/health"));
|
|
696
|
+
url.searchParams.set("repo_name", access.repoName);
|
|
697
|
+
return fetchConductorJsonWithTimeout(url.toString(), getHeaders(access), CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
698
|
+
});
|
|
699
|
+
if (!result.ok)
|
|
700
|
+
return result;
|
|
701
|
+
if (!isRecord(result.value)) {
|
|
702
|
+
return {
|
|
703
|
+
ok: true,
|
|
704
|
+
value: { observation: "unavailable", heartbeatState: null, respondingSchedulerRunning: false },
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const reconciler = isRecord(result.value["reconciler"]) ? result.value["reconciler"] : null;
|
|
708
|
+
const scheduler = isRecord(result.value["scheduler"]) ? result.value["scheduler"] : null;
|
|
709
|
+
const rawState = reconciler === null ? undefined : reconciler["state"];
|
|
710
|
+
const heartbeatState = typeof rawState === "string" ? rawState : null;
|
|
711
|
+
const respondingSchedulerRunning = scheduler !== null &&
|
|
712
|
+
scheduler["running"] === true &&
|
|
713
|
+
typeof scheduler["job_count"] === "number" &&
|
|
714
|
+
scheduler["job_count"] > 0;
|
|
715
|
+
let observation;
|
|
716
|
+
if (heartbeatState === "fresh" || respondingSchedulerRunning) {
|
|
717
|
+
observation = "observed";
|
|
718
|
+
}
|
|
719
|
+
else if (heartbeatState === "stale" || heartbeatState === "never_seen") {
|
|
720
|
+
observation = "absent";
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
observation = "unavailable";
|
|
724
|
+
}
|
|
725
|
+
return { ok: true, value: { observation, heartbeatState, respondingSchedulerRunning } };
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* `GET /jira/epic-runs/runs?repo_name=` — live v2 conductor runs on THIS repo.
|
|
729
|
+
*
|
|
730
|
+
* Scoped by the authenticated repository identity carried on `access`; no caller
|
|
731
|
+
* ever names a repository here. Liveness is judged on the LEASE, not on the
|
|
732
|
+
* status alone: an `active` row whose lease expired is a crashed run, and calling
|
|
733
|
+
* that a concurrent conductor would make the advisory cry wolf on exactly the
|
|
734
|
+
* wreckage `recover` exists to clean up.
|
|
735
|
+
*
|
|
736
|
+
* Only `epic_key` and a controlled state survive into the result. Lease owners,
|
|
737
|
+
* run ids, policies, and timestamps are dropped.
|
|
738
|
+
*/
|
|
739
|
+
export async function getLiveRepositoryConductors(access, now, fetchImpl = globalThis.fetch) {
|
|
740
|
+
const result = await wrap(access, () => {
|
|
741
|
+
const url = buildConductorJiraUrl(access.baseUrl, "/epic-runs/runs", {
|
|
742
|
+
repo_name: access.repoName,
|
|
743
|
+
});
|
|
744
|
+
return fetchConductorJsonWithTimeout(url, getHeaders(access), CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
745
|
+
});
|
|
746
|
+
if (!result.ok)
|
|
747
|
+
return result;
|
|
748
|
+
if (!isRecord(result.value) || !Array.isArray(result.value["runs"])) {
|
|
749
|
+
return { ok: true, value: { observation: "unavailable", conductors: [] } };
|
|
750
|
+
}
|
|
751
|
+
const nowMs = now.getTime();
|
|
752
|
+
const conductors = [];
|
|
753
|
+
for (const entry of result.value["runs"]) {
|
|
754
|
+
if (!isRecord(entry))
|
|
755
|
+
continue;
|
|
756
|
+
const status = entry["status"];
|
|
757
|
+
if (typeof status !== "string" || !ACTIVE_EPIC_RUN_STATUSES.includes(status))
|
|
758
|
+
continue;
|
|
759
|
+
const leaseRaw = nullableString(entry["lease_expires_at"]);
|
|
760
|
+
if (typeof leaseRaw !== "string")
|
|
761
|
+
continue;
|
|
762
|
+
const leaseMs = Date.parse(leaseRaw);
|
|
763
|
+
if (!Number.isFinite(leaseMs) || leaseMs <= nowMs)
|
|
764
|
+
continue;
|
|
765
|
+
conductors.push({
|
|
766
|
+
kind: "v2",
|
|
767
|
+
epicKey: nullableString(entry["epic_key"]) ?? null,
|
|
768
|
+
state: status,
|
|
769
|
+
livenessSource: "lease",
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
return { ok: true, value: { observation: conductors.length > 0 ? "observed" : "absent", conductors } };
|
|
773
|
+
}
|