@bridge_gpt/mcp-server 0.2.23 → 0.2.25
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 +96 -24
- package/build/commands.generated.js +4 -4
- package/build/conductor/epic-reconcile.js +7 -1
- package/build/conductor/epic-runtime.js +5 -0
- package/build/connect-github-api.js +365 -0
- package/build/connect-github.js +415 -0
- package/build/decision-page-schema.js +34 -5
- package/build/decision-page-template.js +126 -39
- package/build/docs.generated.js +5 -0
- package/build/index.js +1872 -633
- package/build/init.js +29 -0
- package/build/install-bridge.js +739 -91
- package/build/install-doctor.js +64 -0
- package/build/pipelines.generated.js +122 -128
- package/build/readme.generated.js +1 -1
- package/build/sfcc/log-gate.js +85 -0
- package/build/sfcc/log-query.js +170 -0
- package/build/sfcc/register.js +10 -0
- package/build/sfcc/setup-status.js +33 -3
- package/build/start-tickets.js +48 -13
- package/build/version.generated.js +1 -1
- package/{CONDUCTOR.md → docs/CONDUCTOR.md} +2 -2
- package/docs/install/github-app.md +252 -0
- package/docs/install/mcp-tool-integrations.md +305 -0
- package/docs/install/sfcc-integration.md +140 -0
- package/package.json +5 -5
- package/pipelines/learn-repository.json +111 -119
- package/public/css/main.min.css +258 -65
- package/public/css/main.min.css.map +1 -1
- package/public/js/main.min.js +233 -74
- package/public/js/main.min.js.map +1 -1
- package/smoke-test/SMOKE-TEST.md +3 -2
package/public/js/main.min.js
CHANGED
|
@@ -1842,6 +1842,10 @@ const GITHUB_CONNECT_MESSAGES = {
|
|
|
1842
1842
|
'already-consumed': 'This GitHub connection link has already been used. Please start the connection again.',
|
|
1843
1843
|
'unresolved': 'GitHub returned without completing the connection. Please try again.',
|
|
1844
1844
|
'no-repositories': 'The GitHub App installation did not include any repositories Bridge can access.',
|
|
1845
|
+
// Honest about the dead end (BAPI-631): GitHub sends the org owner an approval
|
|
1846
|
+
// request out-of-band, and their approval does not redirect back here. Waiting on
|
|
1847
|
+
// this page accomplishes nothing, so say so and point at the fallback that works.
|
|
1848
|
+
'awaiting-organization-approval': 'Your request to install the GitHub App was sent to an organization owner for approval. Approval happens on GitHub and will not return you to this page, so waiting here will not finish the connection. Once an owner approves the install, use the manual fallback below to finish connecting.',
|
|
1845
1849
|
'already-linked': 'This GitHub installation is already connected to a different Bridge account. If this is unexpected, contact support.',
|
|
1846
1850
|
'binding-failed': 'GitHub was verified, but Bridge could not finish linking the repository. Use the manual fallback below.',
|
|
1847
1851
|
pickerLoading: 'Loading the repositories from your GitHub installation…',
|
|
@@ -1850,10 +1854,33 @@ const GITHUB_CONNECT_MESSAGES = {
|
|
|
1850
1854
|
connectedTitle: 'GitHub connected',
|
|
1851
1855
|
};
|
|
1852
1856
|
|
|
1857
|
+
// User-facing copy for the optional editor/MCP section and the install-key
|
|
1858
|
+
// outcomes (BAPI-630). GitHub-specific feedback stays in the sanitized
|
|
1859
|
+
// GITHUB_CONNECT_MESSAGES map above — these two surfaces are deliberately
|
|
1860
|
+
// separate so MCP failure copy can never claim the GitHub task failed.
|
|
1861
|
+
const UI_STRINGS = {
|
|
1862
|
+
// Scopes an install-instructions failure to the optional section. Prefixed
|
|
1863
|
+
// onto the detail resolved by getApiErrorMessage() so the user learns which
|
|
1864
|
+
// area failed and that the primary GitHub task is unaffected.
|
|
1865
|
+
mcpErrorScope: 'Editor and MCP setup instructions could not be loaded. Connecting GitHub above is unaffected and still available.',
|
|
1866
|
+
mcpMissingProject: 'Missing projectId — please navigate here from your project dashboard.',
|
|
1867
|
+
installKeyInserted: 'Admin install key generated and inserted into the snippets.',
|
|
1868
|
+
// Zero snippet slots: report the limitation truthfully rather than claiming
|
|
1869
|
+
// an insertion that did not happen (BAPI-630 / CI-2).
|
|
1870
|
+
installKeyNoSlots: 'Admin install key generated, but no editor snippets were available for automatic insertion. Copy the key below, or retry loading the optional editor and MCP instructions.',
|
|
1871
|
+
};
|
|
1872
|
+
|
|
1853
1873
|
// Holds the plaintext minted key for the current page lifetime ONLY. Never
|
|
1854
1874
|
// persisted to storage, cookies, URLs, datasets, or sent back to the server.
|
|
1855
1875
|
let generatedInstallKey = null;
|
|
1856
1876
|
|
|
1877
|
+
// The project's repository name, initialized once from the server-rendered
|
|
1878
|
+
// container dataset and thereafter kept in module state. Both the GitHub
|
|
1879
|
+
// connect and install-key actions read it from here, so neither re-reads the
|
|
1880
|
+
// dataset per action and the install-instructions response never has to write
|
|
1881
|
+
// a value back into the DOM.
|
|
1882
|
+
let currentRepoName = '';
|
|
1883
|
+
|
|
1857
1884
|
// ============================================================
|
|
1858
1885
|
// Clipboard helper
|
|
1859
1886
|
// ============================================================
|
|
@@ -2303,6 +2330,26 @@ function renderInstallStatus(installStatus, projectId) {
|
|
|
2303
2330
|
// Data loading
|
|
2304
2331
|
// ============================================================
|
|
2305
2332
|
|
|
2333
|
+
// Reveal an optional-section error. The error region lives inside the MCP
|
|
2334
|
+
// drawer, which is collapsed by default — so expanding the drawer through the
|
|
2335
|
+
// shared utility is what actually makes the message visible. Without this the
|
|
2336
|
+
// failure would be silent, which is the exact mode BAPI-630 removes. Only the
|
|
2337
|
+
// MCP drawer is touched; no GitHub control or status region is involved.
|
|
2338
|
+
function showMcpError(message) {
|
|
2339
|
+
const errorEl = document.getElementById('get-started-error');
|
|
2340
|
+
const msgEl = document.getElementById('get-started-error-msg');
|
|
2341
|
+
if (msgEl) msgEl.textContent = message;
|
|
2342
|
+
if (errorEl) errorEl.classList.remove('hidden');
|
|
2343
|
+
|
|
2344
|
+
const drawer = document.querySelector('#get-started-container .get-started-mcp-drawer');
|
|
2345
|
+
if (drawer) (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.setDrawerExpanded)(drawer, true);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// Loads the OPTIONAL editor/MCP instructions. Scope discipline (BAPI-630): this
|
|
2349
|
+
// function may only touch #get-started-loading, #get-started-error, and
|
|
2350
|
+
// #get-started-content. It must never hide, reveal, or gate any GitHub control,
|
|
2351
|
+
// status, result, picker, or fallback region — a failure here is not a failure
|
|
2352
|
+
// of the page's primary task.
|
|
2306
2353
|
async function loadInstallInstructions(projectId) {
|
|
2307
2354
|
const loadingEl = document.getElementById('get-started-loading');
|
|
2308
2355
|
const errorEl = document.getElementById('get-started-error');
|
|
@@ -2323,12 +2370,12 @@ async function loadInstallInstructions(projectId) {
|
|
|
2323
2370
|
|
|
2324
2371
|
if (loadingEl) loadingEl.classList.add('hidden');
|
|
2325
2372
|
|
|
2326
|
-
//
|
|
2327
|
-
//
|
|
2328
|
-
//
|
|
2329
|
-
const
|
|
2330
|
-
if (
|
|
2331
|
-
|
|
2373
|
+
// Keep the shared repo-name state authoritative from the payload, but
|
|
2374
|
+
// never let an empty/whitespace response erase a valid server-rendered
|
|
2375
|
+
// value. Held in module state — not written back into the dataset.
|
|
2376
|
+
const responseRepoName = (data.repo_name || '').trim();
|
|
2377
|
+
if (responseRepoName) {
|
|
2378
|
+
currentRepoName = responseRepoName;
|
|
2332
2379
|
}
|
|
2333
2380
|
|
|
2334
2381
|
const repoLabel = document.getElementById('get-started-repo-label');
|
|
@@ -2341,19 +2388,15 @@ async function loadInstallInstructions(projectId) {
|
|
|
2341
2388
|
renderInstallStatus(data.install_status, projectId);
|
|
2342
2389
|
|
|
2343
2390
|
if (contentEl) contentEl.classList.remove('hidden');
|
|
2344
|
-
|
|
2345
|
-
// Render any GitHub connection return-state now that the repo dataset is
|
|
2346
|
-
// synchronized from the install-instructions payload (BAPI-506).
|
|
2347
|
-
renderGitHubConnectionReturnState();
|
|
2348
2391
|
} catch (error) {
|
|
2349
2392
|
if (loadingEl) loadingEl.classList.add('hidden');
|
|
2350
2393
|
if (contentEl) contentEl.classList.add('hidden');
|
|
2351
2394
|
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2395
|
+
// The scoped sentence is the message; the shared sanitizer's detail rides
|
|
2396
|
+
// along parenthetically as a diagnostic aside so it reads as an aside
|
|
2397
|
+
// rather than a dangling fragment of the sentence before it.
|
|
2398
|
+
const detail = (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.getApiErrorMessage)(error, 'Failed to load install instructions.');
|
|
2399
|
+
showMcpError(`${UI_STRINGS.mcpErrorScope} (${detail})`);
|
|
2357
2400
|
}
|
|
2358
2401
|
}
|
|
2359
2402
|
|
|
@@ -2379,11 +2422,18 @@ function setInstallKeyLoading(loading) {
|
|
|
2379
2422
|
const btn = document.getElementById('generate-install-key-btn');
|
|
2380
2423
|
const panel = document.getElementById('install-key-panel');
|
|
2381
2424
|
const errorEl = document.getElementById('install-key-error');
|
|
2425
|
+
const statusEl = document.getElementById('install-key-status');
|
|
2382
2426
|
if (loading) {
|
|
2383
2427
|
if (errorEl) {
|
|
2384
2428
|
errorEl.textContent = '';
|
|
2385
2429
|
errorEl.classList.add('hidden');
|
|
2386
2430
|
}
|
|
2431
|
+
// Drop any prior success/warning styling so a new attempt cannot inherit
|
|
2432
|
+
// the semantics of the last one. Never logs or exposes the key itself.
|
|
2433
|
+
if (statusEl) {
|
|
2434
|
+
statusEl.classList.remove('install-key-status--success', 'install-key-status--warning');
|
|
2435
|
+
statusEl.textContent = 'Generating the admin install key…';
|
|
2436
|
+
}
|
|
2387
2437
|
if (btn) {
|
|
2388
2438
|
btn.disabled = true;
|
|
2389
2439
|
btn.textContent = 'Generating…';
|
|
@@ -2419,7 +2469,7 @@ async function handleGenerateInstallKeyClick() {
|
|
|
2419
2469
|
// Security page, so we accept this over reusing/auto-revoking prior keys.
|
|
2420
2470
|
if (generatedInstallKey) return;
|
|
2421
2471
|
|
|
2422
|
-
const repoName =
|
|
2472
|
+
const repoName = getGitHubRepoName();
|
|
2423
2473
|
if (!repoName) {
|
|
2424
2474
|
showInstallKeyError('Could not generate the install key. Please try again.');
|
|
2425
2475
|
return;
|
|
@@ -2478,10 +2528,21 @@ async function handleGenerateInstallKeyClick() {
|
|
|
2478
2528
|
const resultEl = document.getElementById('install-key-result');
|
|
2479
2529
|
if (resultEl) resultEl.classList.remove('hidden');
|
|
2480
2530
|
|
|
2481
|
-
injectInstallKeyIntoSnippets
|
|
2531
|
+
// Report what actually happened. injectInstallKeyIntoSnippets returns the
|
|
2532
|
+
// number of slots it filled; zero means the snippets were never rendered
|
|
2533
|
+
// (e.g. the optional MCP fetch failed), so claiming insertion would be a
|
|
2534
|
+
// lie the user cannot verify (BAPI-630 / CI-2).
|
|
2535
|
+
const insertedSlotCount = injectInstallKeyIntoSnippets(apiKey);
|
|
2482
2536
|
|
|
2483
2537
|
const statusEl = document.getElementById('install-key-status');
|
|
2484
|
-
if (statusEl)
|
|
2538
|
+
if (statusEl) {
|
|
2539
|
+
const inserted = insertedSlotCount > 0;
|
|
2540
|
+
statusEl.textContent = inserted
|
|
2541
|
+
? UI_STRINGS.installKeyInserted
|
|
2542
|
+
: UI_STRINGS.installKeyNoSlots;
|
|
2543
|
+
statusEl.classList.toggle('install-key-status--success', inserted);
|
|
2544
|
+
statusEl.classList.toggle('install-key-status--warning', !inserted);
|
|
2545
|
+
}
|
|
2485
2546
|
|
|
2486
2547
|
// Permanently disable the button after success to avoid repeated minting.
|
|
2487
2548
|
const btn = document.getElementById('generate-install-key-btn');
|
|
@@ -2536,9 +2597,7 @@ function initializeInstallKeyGeneration(container) {
|
|
|
2536
2597
|
// ============================================================
|
|
2537
2598
|
|
|
2538
2599
|
function getGitHubRepoName() {
|
|
2539
|
-
|
|
2540
|
-
const repoName = container && container.dataset ? container.dataset.repoName : '';
|
|
2541
|
-
return (repoName || '').trim();
|
|
2600
|
+
return currentRepoName;
|
|
2542
2601
|
}
|
|
2543
2602
|
|
|
2544
2603
|
function setGitHubConnectLoading(loading) {
|
|
@@ -2756,6 +2815,11 @@ function init() {
|
|
|
2756
2815
|
const container = document.getElementById('get-started-container');
|
|
2757
2816
|
if (!container) return;
|
|
2758
2817
|
|
|
2818
|
+
// Seed the shared repo-name state once from the server-rendered dataset.
|
|
2819
|
+
// Every later read goes through module state, so the GitHub button works on
|
|
2820
|
+
// first paint without waiting for the optional MCP fetch (BAPI-630).
|
|
2821
|
+
currentRepoName = ((container.dataset && container.dataset.repoName) || '').trim();
|
|
2822
|
+
|
|
2759
2823
|
(0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.initModal)();
|
|
2760
2824
|
|
|
2761
2825
|
// Click-driven install-key generation; does not mint a key on page load.
|
|
@@ -2764,6 +2828,11 @@ function init() {
|
|
|
2764
2828
|
// Wire the "Connect GitHub" launch button (BAPI-506).
|
|
2765
2829
|
initializeGitHubConnect();
|
|
2766
2830
|
|
|
2831
|
+
// Render any ?githubConnection=<status> return state here — NOT from the
|
|
2832
|
+
// install-instructions success path. A user coming back from GitHub must
|
|
2833
|
+
// see the outcome even when the optional MCP fetch fails (BAPI-630 / R1).
|
|
2834
|
+
renderGitHubConnectionReturnState();
|
|
2835
|
+
|
|
2767
2836
|
// Bind keyboard navigation once — renderSnippetTabs may run multiple times (retry),
|
|
2768
2837
|
// so binding here prevents the listener from stacking on the tabList element.
|
|
2769
2838
|
const tabList = document.getElementById('editor-tabs');
|
|
@@ -2809,11 +2878,8 @@ function init() {
|
|
|
2809
2878
|
|
|
2810
2879
|
if (!projectId) {
|
|
2811
2880
|
const loadingEl = document.getElementById('get-started-loading');
|
|
2812
|
-
const errorEl = document.getElementById('get-started-error');
|
|
2813
|
-
const msgEl = document.getElementById('get-started-error-msg');
|
|
2814
2881
|
if (loadingEl) loadingEl.classList.add('hidden');
|
|
2815
|
-
|
|
2816
|
-
if (errorEl) errorEl.classList.remove('hidden');
|
|
2882
|
+
showMcpError(UI_STRINGS.mcpMissingProject);
|
|
2817
2883
|
return;
|
|
2818
2884
|
}
|
|
2819
2885
|
|
|
@@ -3496,7 +3562,9 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
3496
3562
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
3497
3563
|
/* harmony export */ hideModal: () => (/* binding */ hideModal),
|
|
3498
3564
|
/* harmony export */ initModal: () => (/* binding */ initModal),
|
|
3499
|
-
/* harmony export */
|
|
3565
|
+
/* harmony export */ replaceModalBody: () => (/* binding */ replaceModalBody),
|
|
3566
|
+
/* harmony export */ showModal: () => (/* binding */ showModal),
|
|
3567
|
+
/* harmony export */ showModalNode: () => (/* binding */ showModalNode)
|
|
3500
3568
|
/* harmony export */ });
|
|
3501
3569
|
// src/js/modal.js
|
|
3502
3570
|
|
|
@@ -3550,6 +3618,49 @@ function showModal(title, message, onConfirm = null, showCancel = false, onClose
|
|
|
3550
3618
|
}
|
|
3551
3619
|
}
|
|
3552
3620
|
|
|
3621
|
+
/**
|
|
3622
|
+
* Show the modal with a caller-built DOM node as its body.
|
|
3623
|
+
*
|
|
3624
|
+
* Additive companion to {@link showModal} for rich, structured content (e.g. the
|
|
3625
|
+
* SFCC ticket preview). The node is appended directly — never serialized through
|
|
3626
|
+
* innerHTML — so untrusted values built with textContent stay safe. The caller
|
|
3627
|
+
* supplies its own action controls inside `contentEl`, so the shared
|
|
3628
|
+
* confirm/cancel actions are hidden. `onClose` runs when the modal is dismissed,
|
|
3629
|
+
* letting the caller restore focus to the invoking element.
|
|
3630
|
+
*
|
|
3631
|
+
* @param {string} title - Modal header text.
|
|
3632
|
+
* @param {HTMLElement} contentEl - Caller-created body node.
|
|
3633
|
+
* @param {Function|null} onClose - Optional callback fired on dismiss.
|
|
3634
|
+
*/
|
|
3635
|
+
function showModalNode(title, contentEl, onClose = null) {
|
|
3636
|
+
if (pageModalTitle) pageModalTitle.textContent = title;
|
|
3637
|
+
replaceModalBody(contentEl);
|
|
3638
|
+
|
|
3639
|
+
currentConfirmCallback = null;
|
|
3640
|
+
currentCloseCallback = onClose;
|
|
3641
|
+
|
|
3642
|
+
if (pageModalActions) pageModalActions.classList.add('hidden');
|
|
3643
|
+
if (pageModal) {
|
|
3644
|
+
pageModal.style.display = 'block';
|
|
3645
|
+
} else {
|
|
3646
|
+
console.error('Modal element not found.');
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
/**
|
|
3651
|
+
* Replace the modal body with a caller-built DOM node without re-showing it.
|
|
3652
|
+
*
|
|
3653
|
+
* Used to transition the same open modal between states (processing → preview →
|
|
3654
|
+
* success/error). Clears the previous content with textContent then appends the
|
|
3655
|
+
* node — never innerHTML.
|
|
3656
|
+
* @param {HTMLElement} contentEl
|
|
3657
|
+
*/
|
|
3658
|
+
function replaceModalBody(contentEl) {
|
|
3659
|
+
if (!pageModalBody) return;
|
|
3660
|
+
pageModalBody.textContent = '';
|
|
3661
|
+
if (contentEl) pageModalBody.appendChild(contentEl);
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3553
3664
|
/**
|
|
3554
3665
|
* Hides the modal dialog.
|
|
3555
3666
|
*/
|
|
@@ -5063,10 +5174,12 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
5063
5174
|
/* harmony export */ resetProjectDetailBaseline: () => (/* binding */ resetProjectDetailBaseline),
|
|
5064
5175
|
/* harmony export */ setupConditionalVisibilityListeners: () => (/* binding */ setupConditionalVisibilityListeners),
|
|
5065
5176
|
/* harmony export */ setupValidationListeners: () => (/* binding */ setupValidationListeners),
|
|
5177
|
+
/* harmony export */ shouldWarnForBlankCriticalPriorities: () => (/* binding */ shouldWarnForBlankCriticalPriorities),
|
|
5066
5178
|
/* harmony export */ toggleCiFollowupControlsVisibility: () => (/* binding */ toggleCiFollowupControlsVisibility),
|
|
5067
5179
|
/* harmony export */ toggleDeepResearchTimeoutVisibility: () => (/* binding */ toggleDeepResearchTimeoutVisibility),
|
|
5068
5180
|
/* harmony export */ toggleStoryPointsFieldVisibility: () => (/* binding */ toggleStoryPointsFieldVisibility),
|
|
5069
5181
|
/* harmony export */ updateCiFollowupInstructionsByteCounter: () => (/* binding */ updateCiFollowupInstructionsByteCounter),
|
|
5182
|
+
/* harmony export */ updateCriticalPrioritiesWarning: () => (/* binding */ updateCriticalPrioritiesWarning),
|
|
5070
5183
|
/* harmony export */ validateInput: () => (/* binding */ validateInput),
|
|
5071
5184
|
/* harmony export */ validateReviewPolicyOverrides: () => (/* binding */ validateReviewPolicyOverrides)
|
|
5072
5185
|
/* harmony export */ });
|
|
@@ -5097,6 +5210,44 @@ const estimateScaleJsonErrorDiv = document.getElementById('estimate_scale-error'
|
|
|
5097
5210
|
// are sent to the backend.
|
|
5098
5211
|
let projectDetailBaseline = null;
|
|
5099
5212
|
|
|
5213
|
+
// --- BAPI-590: critical-priorities blank-configuration warning ---
|
|
5214
|
+
// Non-blocking signal shown when the critical-priorities check is enabled but no
|
|
5215
|
+
// priorities are configured (the check is silently skipped). Defined once at
|
|
5216
|
+
// module scope so the client- and route-side copies stay identical.
|
|
5217
|
+
const CRITICAL_PRIORITIES_BLANK_WARNING =
|
|
5218
|
+
'Critical-priorities checking is enabled, but no priorities are configured; this check will be skipped.';
|
|
5219
|
+
|
|
5220
|
+
/**
|
|
5221
|
+
* Whether the blank-priorities warning condition holds: the check is enabled and
|
|
5222
|
+
* the priorities textarea is empty after trimming whitespace. Reads the checkbox
|
|
5223
|
+
* and textarea directly from the DOM and is safe to call when either control is
|
|
5224
|
+
* absent (returns false).
|
|
5225
|
+
* @returns {boolean}
|
|
5226
|
+
*/
|
|
5227
|
+
function shouldWarnForBlankCriticalPriorities() {
|
|
5228
|
+
const toggle = document.getElementById('check_critical_priorities');
|
|
5229
|
+
const textarea = document.getElementById('critical_priorities');
|
|
5230
|
+
if (!toggle || !textarea) return false;
|
|
5231
|
+
return !!toggle.checked && (textarea.value || '').trim() === '';
|
|
5232
|
+
}
|
|
5233
|
+
|
|
5234
|
+
/**
|
|
5235
|
+
* Show or hide the dedicated critical-priorities warning container from the
|
|
5236
|
+
* current control state. Guards every DOM lookup, sets text via textContent, and
|
|
5237
|
+
* toggles only the `.hidden` class on the warning node (never inline display).
|
|
5238
|
+
*/
|
|
5239
|
+
function updateCriticalPrioritiesWarning() {
|
|
5240
|
+
const warningEl = document.getElementById('critical_priorities-warning');
|
|
5241
|
+
if (!warningEl) return;
|
|
5242
|
+
if (shouldWarnForBlankCriticalPriorities()) {
|
|
5243
|
+
warningEl.textContent = CRITICAL_PRIORITIES_BLANK_WARNING;
|
|
5244
|
+
warningEl.classList.remove('hidden');
|
|
5245
|
+
} else {
|
|
5246
|
+
warningEl.textContent = '';
|
|
5247
|
+
warningEl.classList.add('hidden');
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5250
|
+
|
|
5100
5251
|
// --- Custom DOM value readers for fields the shared diff util cannot read by id ---
|
|
5101
5252
|
|
|
5102
5253
|
/**
|
|
@@ -5907,10 +6058,22 @@ function setupConditionalVisibilityListeners() {
|
|
|
5907
6058
|
reviewPolicyResetAll.addEventListener('click', handleReviewPolicyResetAll);
|
|
5908
6059
|
}
|
|
5909
6060
|
|
|
5910
|
-
//
|
|
6061
|
+
// BAPI-590: refresh the non-blocking critical-priorities warning whenever the
|
|
6062
|
+
// check is toggled or the priorities text changes.
|
|
6063
|
+
const checkCriticalPriorities = document.getElementById('check_critical_priorities');
|
|
6064
|
+
if (checkCriticalPriorities) {
|
|
6065
|
+
checkCriticalPriorities.addEventListener('change', updateCriticalPrioritiesWarning);
|
|
6066
|
+
}
|
|
6067
|
+
const criticalPriorities = document.getElementById('critical_priorities');
|
|
6068
|
+
if (criticalPriorities) {
|
|
6069
|
+
criticalPriorities.addEventListener('input', updateCriticalPrioritiesWarning);
|
|
6070
|
+
}
|
|
6071
|
+
|
|
6072
|
+
// Apply initial state for all dependent regions before baseline capture.
|
|
5911
6073
|
toggleDeepResearchTimeoutVisibility();
|
|
5912
6074
|
toggleStoryPointsFieldVisibility();
|
|
5913
6075
|
toggleCiFollowupControlsVisibility();
|
|
6076
|
+
updateCriticalPrioritiesWarning();
|
|
5914
6077
|
}
|
|
5915
6078
|
|
|
5916
6079
|
// Canonical descriptor list driving baseline capture, sparse diffing, section
|
|
@@ -5964,21 +6127,13 @@ const PROJECT_DETAIL_FIELD_DESCRIPTORS = [
|
|
|
5964
6127
|
{ field: 'backend_correctness_standards', type: 'text', id: 'backend_correctness_standards' },
|
|
5965
6128
|
{ field: 'template_correctness_standards', type: 'text', id: 'template_correctness_standards' },
|
|
5966
6129
|
{ field: 'style_correctness_standards', type: 'text', id: 'style_correctness_standards' },
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
{ field: 'backend_styleguide', type: 'text', id: 'backend_styleguide' },
|
|
5972
|
-
{ field: 'css_styleguide', type: 'text', id: 'css_styleguide' },
|
|
5973
|
-
{ field: 'review_correctness', type: 'boolean', id: 'review_correctness' },
|
|
5974
|
-
{ field: 'review_style', type: 'boolean', id: 'review_style' },
|
|
6130
|
+
// BAPI-590: the simplified two-check reviewer surface (requirements check +
|
|
6131
|
+
// critical-priorities check/text). The legacy reviewer controls (style guides,
|
|
6132
|
+
// file types, include/exclude paths, correctness/style/architecture toggles,
|
|
6133
|
+
// test/doc generation, documentation standards, score threshold) were removed.
|
|
5975
6134
|
{ field: 'review_requirements', type: 'boolean', id: 'review_requirements' },
|
|
5976
|
-
{ field: '
|
|
5977
|
-
{ field: '
|
|
5978
|
-
{ field: 'create_documentation', type: 'boolean', id: 'create_documentation' },
|
|
5979
|
-
{ field: 'documentation_standards', type: 'text', id: 'documentation_standards' },
|
|
5980
|
-
{ field: 'score_threshold_for_review', type: 'text', id: 'score_threshold_for_review' },
|
|
5981
|
-
{ field: 'no_comment_on_passing_score', type: 'boolean', id: 'no_comment_on_passing_score' },
|
|
6135
|
+
{ field: 'check_critical_priorities', type: 'boolean', id: 'check_critical_priorities' },
|
|
6136
|
+
{ field: 'critical_priorities', type: 'text', id: 'critical_priorities' },
|
|
5982
6137
|
];
|
|
5983
6138
|
|
|
5984
6139
|
// Maps each descriptor field to exactly one backend payload section. The four
|
|
@@ -6022,21 +6177,9 @@ const FIELD_TO_SECTION = {
|
|
|
6022
6177
|
backend_correctness_standards: 'code_reviewer',
|
|
6023
6178
|
template_correctness_standards: 'code_reviewer',
|
|
6024
6179
|
style_correctness_standards: 'code_reviewer',
|
|
6025
|
-
reviewable_file_types: 'code_reviewer',
|
|
6026
|
-
include_path: 'code_reviewer',
|
|
6027
|
-
exclude_path: 'code_reviewer',
|
|
6028
|
-
frontend_styleguide: 'code_reviewer',
|
|
6029
|
-
backend_styleguide: 'code_reviewer',
|
|
6030
|
-
css_styleguide: 'code_reviewer',
|
|
6031
|
-
review_correctness: 'code_reviewer',
|
|
6032
|
-
review_style: 'code_reviewer',
|
|
6033
6180
|
review_requirements: 'code_reviewer',
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
create_documentation: 'code_reviewer',
|
|
6037
|
-
documentation_standards: 'code_reviewer',
|
|
6038
|
-
score_threshold_for_review: 'code_reviewer',
|
|
6039
|
-
no_comment_on_passing_score: 'code_reviewer',
|
|
6181
|
+
check_critical_priorities: 'code_reviewer',
|
|
6182
|
+
critical_priorities: 'code_reviewer',
|
|
6040
6183
|
};
|
|
6041
6184
|
|
|
6042
6185
|
/**
|
|
@@ -6173,7 +6316,9 @@ const EXPLANATION_TEXTS = {
|
|
|
6173
6316
|
'css_styleguide': 'Describe style conventions for CSS/SASS/LESS code. Optional. If blank, CSS style reviews are skipped.',
|
|
6174
6317
|
'review_correctness': 'Enable/disable checking code for bugs and errors. Defaults to true. Optional.',
|
|
6175
6318
|
'review_style': 'Enable/disable checking code for style guide adherence. Defaults to true. Optional.',
|
|
6176
|
-
'review_requirements': '
|
|
6319
|
+
'review_requirements': 'When enabled, the code reviewer checks whether the pull request actually covers the ticket\'s stated requirements (requires a ticket ID in the branch or commit). Best for initial commits. Defaults to enabled.',
|
|
6320
|
+
'check_critical_priorities': 'When enabled, the code reviewer additionally checks each pull request against the Critical Priorities you configure below. If this check is on but no priorities are configured, the check is skipped.',
|
|
6321
|
+
'critical_priorities': 'The critical priorities the code reviewer holds every pull request to (e.g., "Never log secrets or PII", "All DB writes must be repo-scoped"). Leave blank to skip the critical-priorities check even when it is enabled.',
|
|
6177
6322
|
'review_architecture': 'Enable/disable checking for integration errors and correctness (if correctness review is off). Defaults to true. Optional.',
|
|
6178
6323
|
'create_tests': 'Enable/disable AI generation of unit/integration tests for the PR. Defaults to false. Optional.',
|
|
6179
6324
|
'create_documentation': 'Enable/disable AI generation of documentation for the PR. Defaults to false. Optional.',
|
|
@@ -6468,24 +6613,18 @@ function populateProjectForms(data) {
|
|
|
6468
6613
|
if (el) el.value = reviewer[field] || '';
|
|
6469
6614
|
});
|
|
6470
6615
|
|
|
6471
|
-
// Code Reviewer Form (
|
|
6616
|
+
// Code Reviewer Form (BAPI-590: simplified two-check surface). The legacy
|
|
6617
|
+
// reviewer controls were removed, so only the requirements check and the
|
|
6618
|
+
// critical-priorities check/text are populated here.
|
|
6472
6619
|
if (codeReviewForm) {
|
|
6473
|
-
codeReviewForm.elements.reviewable_file_types.value = (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.arrayToString)(reviewer.reviewable_file_types || []);
|
|
6474
|
-
codeReviewForm.elements.include_path.value = (reviewer.include_path || []).join('\n');
|
|
6475
|
-
codeReviewForm.elements.exclude_path.value = (reviewer.exclude_path || []).join('\n');
|
|
6476
|
-
codeReviewForm.elements.frontend_styleguide.value = reviewer.frontend_styleguide || '';
|
|
6477
|
-
codeReviewForm.elements.backend_styleguide.value = reviewer.backend_styleguide || '';
|
|
6478
|
-
codeReviewForm.elements.css_styleguide.value = reviewer.css_styleguide || '';
|
|
6479
|
-
codeReviewForm.elements.review_correctness.checked = reviewer.review_correctness !== undefined ? reviewer.review_correctness : true;
|
|
6480
|
-
codeReviewForm.elements.review_style.checked = reviewer.review_style !== undefined ? reviewer.review_style : true;
|
|
6481
6620
|
codeReviewForm.elements.review_requirements.checked = reviewer.review_requirements !== undefined ? reviewer.review_requirements : true;
|
|
6482
|
-
codeReviewForm.elements.
|
|
6483
|
-
codeReviewForm.elements.
|
|
6484
|
-
codeReviewForm.elements.create_documentation.checked = reviewer.create_documentation || false;
|
|
6485
|
-
codeReviewForm.elements.documentation_standards.value = reviewer.documentation_standards || '';
|
|
6486
|
-
codeReviewForm.elements.score_threshold_for_review.value = reviewer.score_threshold_for_review !== undefined ? reviewer.score_threshold_for_review : 85;
|
|
6487
|
-
codeReviewForm.elements.no_comment_on_passing_score.checked = reviewer.no_comment_on_passing_score !== undefined ? reviewer.no_comment_on_passing_score : true;
|
|
6621
|
+
codeReviewForm.elements.check_critical_priorities.checked = !!reviewer.check_critical_priorities;
|
|
6622
|
+
codeReviewForm.elements.critical_priorities.value = reviewer.critical_priorities || '';
|
|
6488
6623
|
}
|
|
6624
|
+
|
|
6625
|
+
// Surface an already-invalid persisted state (check enabled, priorities blank)
|
|
6626
|
+
// immediately after applying the returned reviewer values.
|
|
6627
|
+
updateCriticalPrioritiesWarning();
|
|
6489
6628
|
}
|
|
6490
6629
|
|
|
6491
6630
|
/**
|
|
@@ -6566,9 +6705,18 @@ async function handleSaveProject() {
|
|
|
6566
6705
|
const response = await (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.apiCall)(`/setup/project-detail/${currentProjectId}`, 'PUT', projectData);
|
|
6567
6706
|
|
|
6568
6707
|
if (response) {
|
|
6569
|
-
|
|
6570
|
-
//
|
|
6571
|
-
//
|
|
6708
|
+
// BAPI-590: keep the adjacent priorities warning visible after save, and
|
|
6709
|
+
// surface the route's optional warning as a saved-with-warning result
|
|
6710
|
+
// rather than presenting the (successful) save as a failure.
|
|
6711
|
+
updateCriticalPrioritiesWarning();
|
|
6712
|
+
const savedWarning = typeof response.warning === 'string' ? response.warning.trim() : '';
|
|
6713
|
+
if (savedWarning) {
|
|
6714
|
+
(0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Saved with warning', `Project configuration saved.\n\n${savedWarning}`);
|
|
6715
|
+
} else {
|
|
6716
|
+
(0,_modal_js__WEBPACK_IMPORTED_MODULE_0__.showModal)('Success', 'Project configuration saved successfully!');
|
|
6717
|
+
}
|
|
6718
|
+
// Re-snapshot from the saved values (the route-level warning accompanies a
|
|
6719
|
+
// successful persist) so an immediate second save with no edits is clean.
|
|
6572
6720
|
resetProjectDetailBaseline();
|
|
6573
6721
|
}
|
|
6574
6722
|
// A null response is the centralized 401 redirect (handled by apiCall);
|
|
@@ -6723,6 +6871,11 @@ function validateAllForms() {
|
|
|
6723
6871
|
allValid = false;
|
|
6724
6872
|
}
|
|
6725
6873
|
|
|
6874
|
+
// BAPI-590: refresh the blank-priorities warning during pre-save validation.
|
|
6875
|
+
// This is a non-blocking signal only — it never sets custom validity, adds
|
|
6876
|
+
// `.input-error`, or flips `allValid`.
|
|
6877
|
+
updateCriticalPrioritiesWarning();
|
|
6878
|
+
|
|
6726
6879
|
return allValid;
|
|
6727
6880
|
}
|
|
6728
6881
|
|
|
@@ -6763,6 +6916,12 @@ function handleInputClearError(event) {
|
|
|
6763
6916
|
if (element.id === 'ci_followup_instructions') {
|
|
6764
6917
|
updateCiFollowupInstructionsByteCounter();
|
|
6765
6918
|
}
|
|
6919
|
+
// BAPI-590: the priorities warning lives in its own container (not the
|
|
6920
|
+
// `-error` node), so clearing a genuine field error never erases it; refresh
|
|
6921
|
+
// it here so live edits keep the warning state accurate.
|
|
6922
|
+
if (element.id === 'critical_priorities') {
|
|
6923
|
+
updateCriticalPrioritiesWarning();
|
|
6924
|
+
}
|
|
6766
6925
|
}
|
|
6767
6926
|
|
|
6768
6927
|
/**
|