@orangepro/orangepro-mcp 0.2.15 → 0.2.17
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 +1 -0
- package/dist/local/viz/behaviorReportHtml.js +96 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://www.npmjs.com/package/@orangepro/mcp-server )
|
|
6
6
|
[](https://glama.ai/mcp/servers/OrangeproAI/orangepro-mcp )
|
|
7
|
+
[](https://registry.modelcontextprotocol.io/?q=orangepro )
|
|
7
8
|
|
|
8
9
|
**Find the behaviors your tests miss. Generate grounded tests that actually run.**
|
|
9
10
|
|
|
@@ -243,6 +243,31 @@ nav.tabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--bd)}
|
|
|
243
243
|
.paywall-btn{display:inline-flex;align-items:center;gap:6px;background:var(--orange);color:#1a0a02;font-size:12px;font-weight:700;padding:7px 14px;border-radius:6px;text-decoration:none;transition:opacity .12s}
|
|
244
244
|
.paywall-btn:hover{opacity:.85}
|
|
245
245
|
|
|
246
|
+
|
|
247
|
+
/* === OPTION A: MODE TOGGLE (SEGMENTED PILL) === */
|
|
248
|
+
.mode-switch{display:inline-flex;align-items:center;background:var(--s2);border-radius:7px;padding:3px;gap:2px;border:1px solid var(--bd)}
|
|
249
|
+
.mode-btn{padding:5px 14px;font:650 11px var(--sans);border-radius:5px;cursor:pointer;border:none;background:transparent;color:var(--muted);transition:all .15s;letter-spacing:.01em}
|
|
250
|
+
.mode-btn:hover{color:var(--ink)}
|
|
251
|
+
.mode-btn.active{background:var(--orange);color:#1a0a02;box-shadow:0 1px 3px rgba(240,136,62,.25)}
|
|
252
|
+
body[data-mode="simple"] .expert-only{display:none!important}
|
|
253
|
+
body[data-mode="simple"] .kpis{grid-template-columns:repeat(3,1fr)}
|
|
254
|
+
body[data-mode="simple"] .gen-test .gen-test-body{display:block}
|
|
255
|
+
body[data-mode="simple"] .gen-test .gen-test-toggle{display:none}
|
|
256
|
+
body[data-mode="simple"] .risk-card{border-left:3px solid var(--orange)}
|
|
257
|
+
body[data-mode="simple"] .risk-ctx{color:var(--ink2);font-size:13px}
|
|
258
|
+
body[data-mode="simple"] .todo{font-size:12.5px;font-weight:600;color:var(--ink);background:var(--obg);border-color:var(--obd)}
|
|
259
|
+
body[data-mode="simple"] .risk-tags{display:none}
|
|
260
|
+
body[data-mode="simple"] .cat-strip{display:none}
|
|
261
|
+
body[data-mode="simple"] .risk-tools{display:none}
|
|
262
|
+
body[data-mode="simple"] .metric-scope{display:none}
|
|
263
|
+
.copy-btn{appearance:none;background:var(--s3);border:1px solid var(--bd2);color:var(--muted);font:600 10px var(--mono);padding:3px 8px;border-radius:4px;cursor:pointer;flex-shrink:0;margin-left:auto}
|
|
264
|
+
.copy-btn:hover{color:var(--green);border-color:var(--gbd)}
|
|
265
|
+
.copy-btn.copied{color:var(--green);border-color:var(--gbd)}
|
|
266
|
+
body[data-mode="expert"] .simple-only{display:none!important}
|
|
267
|
+
.simple-intro{background:linear-gradient(135deg,rgba(240,136,62,.06),rgba(240,136,62,.02));border:1px solid var(--obd);border-radius:9px;padding:14px 16px;margin-bottom:14px}
|
|
268
|
+
.simple-intro h2{font-size:15px;font-weight:700;color:var(--ink);margin:0 0 4px}
|
|
269
|
+
.simple-intro p{font-size:12.5px;color:var(--muted);margin:0;line-height:1.5}
|
|
270
|
+
|
|
246
271
|
@media(max-width:800px){
|
|
247
272
|
.kpis{grid-template-columns:repeat(3,1fr)}
|
|
248
273
|
.cols2,.beh-layout{grid-template-columns:1fr}
|
|
@@ -261,6 +286,10 @@ nav.tabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--bd)}
|
|
|
261
286
|
<span class="hdr-name" id="repo-name">—</span>
|
|
262
287
|
</div>
|
|
263
288
|
<div class="hdr-right">
|
|
289
|
+
<div class="mode-switch" id="mode-switch">
|
|
290
|
+
<button class="mode-btn active" data-mode="simple">Simple</button>
|
|
291
|
+
<button class="mode-btn" data-mode="expert">Expert</button>
|
|
292
|
+
</div>
|
|
264
293
|
<span class="hdr-tag" id="framework">—</span>
|
|
265
294
|
<span id="scan-date">—</span>
|
|
266
295
|
</div>
|
|
@@ -270,7 +299,12 @@ nav.tabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--bd)}
|
|
|
270
299
|
<section class="kpis" id="kpis"></section>
|
|
271
300
|
<p class="metric-scope" id="metric-scope"></p>
|
|
272
301
|
|
|
273
|
-
<nav class="tabs" role="tablist">
|
|
302
|
+
<nav class="tabs simple-only" role="tablist">
|
|
303
|
+
<button class="tab" role="tab" aria-selected="true" data-tab="risks">Fix These <span class="tc" id="t-risk-s"></span></button>
|
|
304
|
+
<button class="tab" role="tab" aria-selected="false" data-tab="codebase">Your Code</button>
|
|
305
|
+
<button class="tab" role="tab" aria-selected="false" data-tab="flows">Deep Dive</button>
|
|
306
|
+
</nav>
|
|
307
|
+
<nav class="tabs expert-only" role="tablist">
|
|
274
308
|
<button class="tab" role="tab" aria-selected="true" data-tab="codebase">Your Code</button>
|
|
275
309
|
<button class="tab" role="tab" aria-selected="false" data-tab="risks">Priority gaps <span class="tc" id="t-risk">—</span></button>
|
|
276
310
|
<button class="tab" role="tab" aria-selected="false" data-tab="flows">Flows <span class="tc" id="t-flow">—</span></button>
|
|
@@ -331,7 +365,11 @@ nav.tabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--bd)}
|
|
|
331
365
|
|
|
332
366
|
<!-- TAB 4: RISKS — actionable -->
|
|
333
367
|
<section class="panel" id="panel-risks" role="tabpanel">
|
|
334
|
-
<
|
|
368
|
+
<div class="simple-intro simple-only">
|
|
369
|
+
<h2>Your highest-risk blind spots</h2>
|
|
370
|
+
<p>These methods have the most structural exposure (high fan-in, recent churn, entry points) and no test proves they work. Each card includes a generated test you can copy into your repo.</p>
|
|
371
|
+
</div>
|
|
372
|
+
<p class="bridge expert-only">This is the <b>priority-gap worklist</b>, ranked by blast radius and test weakness. It is separate from the coverage-status cards above: <b>Reachable · no test signal</b> is one strict coverage bucket, not the number of priority gaps.</p>
|
|
335
373
|
<p class="bridge" id="risk-cap-note" style="font-size:12px;opacity:.75"></p>
|
|
336
374
|
<div class="risk-tools" id="risk-tools"></div>
|
|
337
375
|
<div id="risk-list"></div>
|
|
@@ -472,6 +510,25 @@ const D=window.DATA,$=(s)=>document.querySelector(s);
|
|
|
472
510
|
const el=(t,c,h)=>{const e=document.createElement(t);if(c)e.className=c;if(h!=null)e.innerHTML=h;return e};
|
|
473
511
|
const esc=s=>String(s).replace(/[&<>]/g,m=>({"&":"&","<":"<",">":">"}[m]));
|
|
474
512
|
const S=D.summary;
|
|
513
|
+
// === OPTION A: VIEW MODE ===
|
|
514
|
+
const params=new URLSearchParams(location.search);
|
|
515
|
+
let viewMode=params.get("mode")||localStorage.getItem("opro-view")||"simple";
|
|
516
|
+
document.body.dataset.mode=viewMode;
|
|
517
|
+
function activateTab(tabId){
|
|
518
|
+
document.querySelectorAll(".tab").forEach(t=>t.setAttribute("aria-selected",String(t.dataset.tab===tabId)));
|
|
519
|
+
document.querySelectorAll(".panel").forEach(p=>p.classList.toggle("active",p.id==="panel-"+tabId));
|
|
520
|
+
}
|
|
521
|
+
function setMode(m){
|
|
522
|
+
viewMode=m;localStorage.setItem("opro-view",m);document.body.dataset.mode=m;
|
|
523
|
+
document.querySelectorAll(".mode-btn").forEach(b=>{b.classList.toggle("active",b.dataset.mode===m)});
|
|
524
|
+
renderKpisForMode();
|
|
525
|
+
activateTab(m==="simple"?"risks":"codebase");
|
|
526
|
+
if(typeof renderRisks==="function")renderRisks();
|
|
527
|
+
}
|
|
528
|
+
document.querySelectorAll(".mode-btn").forEach(b=>{b.onclick=()=>setMode(b.dataset.mode)});
|
|
529
|
+
document.querySelectorAll(".mode-btn").forEach(b=>{b.classList.toggle("active",b.dataset.mode===viewMode)});
|
|
530
|
+
|
|
531
|
+
|
|
475
532
|
|
|
476
533
|
// header
|
|
477
534
|
$("#repo-name").textContent=D.repo;
|
|
@@ -493,22 +550,41 @@ $("#br-tests").textContent=D.scan.tests.total;
|
|
|
493
550
|
$("#t-beh").textContent=D.behaviors.length;
|
|
494
551
|
$("#t-flow").textContent=D.flows.length;
|
|
495
552
|
$("#t-risk").textContent=D.risks.length;
|
|
553
|
+
if($("#t-risk-s"))$("#t-risk-s").textContent=D.risks.length;
|
|
496
554
|
|
|
497
|
-
// KPIs
|
|
555
|
+
// KPIs — mode-aware
|
|
498
556
|
const priorityGapCount=D.risks.length;
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
557
|
+
function renderExpertKpis(){
|
|
558
|
+
[
|
|
559
|
+
{lbl:"Methods found",num:S.total,sub:"public, with observable outcome",t:"total"},
|
|
560
|
+
{lbl:"Dynamically Proven",num:S.proven,sub:"test breaks if you change it",t:"proven"},
|
|
561
|
+
{lbl:"Test signal",num:S.associated,sub:"hard static test link, no proof",t:"signal"},
|
|
562
|
+
{lbl:"Candidate",num:S.candidate??0,sub:"lexical match only — unconfirmed",t:"cand"},
|
|
563
|
+
{lbl:"Reachable · no test signal",num:S.reachableUntested,sub:"coverage bucket — not the risk count",t:"reach"},
|
|
564
|
+
{lbl:"Unreached · no test signal",num:S.noSignal,sub:"no detected flow or test signal",t:"nosig"},
|
|
565
|
+
{lbl:"Priority gaps",num:priorityGapCount,sub:"top-ranked worklist across unproven tiers",t:"priority"},
|
|
566
|
+
].forEach(k=>{
|
|
567
|
+
const d=el("div","kpi",\`<div class="kpi-lbl">\${k.lbl}</div><div class="kpi-num">\${k.num}</div><div class="kpi-sub">\${k.sub}</div>\`);
|
|
568
|
+
d.dataset.t=k.t;
|
|
569
|
+
$("#kpis").append(d);
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
function renderSimpleKpis(){
|
|
573
|
+
const pct=S.total>0?Math.round(((S.proven+S.associated)/S.total)*100):0;
|
|
574
|
+
[{lbl:"Blind spots found",num:priorityGapCount,sub:"highest-risk untested methods",t:"priority"},
|
|
575
|
+
{lbl:"Methods mapped",num:S.total.toLocaleString(),sub:"public methods in your codebase",t:"total"},
|
|
576
|
+
{lbl:"Have test evidence",num:pct+"%",sub:(S.proven+S.associated)+" of "+S.total+" verified or linked",t:"proven"}
|
|
577
|
+
].forEach(k=>{
|
|
578
|
+
const d=el("div","kpi",\`<div class="kpi-lbl">\${k.lbl}</div><div class="kpi-num">\${k.num}</div><div class="kpi-sub">\${k.sub}</div>\`);
|
|
579
|
+
d.dataset.t=k.t;
|
|
580
|
+
$("#kpis").append(d);
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
function renderKpisForMode(){
|
|
584
|
+
$("#kpis").innerHTML="";
|
|
585
|
+
if(viewMode==="simple") renderSimpleKpis(); else renderExpertKpis();
|
|
586
|
+
}
|
|
587
|
+
renderKpisForMode();
|
|
512
588
|
const outsideProofs=S.provenOutsideDenominator??0;
|
|
513
589
|
const coverageAccounted=(S.proven-outsideProofs)+S.associated+(S.candidate??0)+S.reachableUntested+S.noSignal;
|
|
514
590
|
$("#metric-scope").textContent="Coverage status classifies "+coverageAccounted.toLocaleString()+" of "+S.total.toLocaleString()+" mapped behaviors. Priority gaps is a separate ranked worklist of "+priorityGapCount.toLocaleString()+" unproven behaviors; it does not equal the Reachable count."+(outsideProofs?" "+outsideProofs.toLocaleString()+" additional dynamically proven behavior"+(outsideProofs===1?" is":"s are")+" shown outside the static denominator.":"");
|
|
@@ -738,7 +814,7 @@ function riskCardHtml(r){
|
|
|
738
814
|
const cBadge=t.concern?\`<span class="badge b-info" style="margin-left:6px;font-size:9px">\${esc(t.concern.replace('_',' '))}</span>\`:'';
|
|
739
815
|
const iBadge=t.runnable===false?\`<span class="badge b-cand" style="margin-left:6px;font-size:9px">Manual test</span>\`:'';
|
|
740
816
|
const bBadge=t.bucket?\`<span class="badge b-info" style="margin-left:6px;font-size:9px">\${esc(String(t.bucket).replace(/_/g,' '))}</span>\`:'';
|
|
741
|
-
testsHtml+=\`<div class="gen-test"><div class="gen-test-head"><span class="gen-test-name">\${esc(t.name)}\${cBadge}\${bBadge}\${iBadge}</span><span class="gen-test-assert">\${esc(t.assertion)}</span><span class="gen-test-toggle">▼</span></div><div class="gen-test-body">\${esc(t.code)}</div></div>\`;
|
|
817
|
+
testsHtml+=\`<div class="gen-test"><div class="gen-test-head"><span class="gen-test-name">\${esc(t.name)}\${cBadge}\${bBadge}\${iBadge}</span><span class="gen-test-assert">\${esc(t.assertion)}</span><button class="copy-btn" onclick="event.stopPropagation();navigator.clipboard.writeText(this.closest('.gen-test').querySelector('.gen-test-body').textContent).then(()=>{this.textContent='Copied!';this.classList.add('copied');setTimeout(()=>{this.textContent='Copy';this.classList.remove('copied')},1500)})">Copy</button><span class="gen-test-toggle">▼</span></div><div class="gen-test-body">\${esc(t.code)}</div></div>\`;
|
|
742
818
|
});
|
|
743
819
|
testsHtml+=\`</div>\`;
|
|
744
820
|
}
|
|
@@ -784,10 +860,9 @@ document.addEventListener('click',e=>{
|
|
|
784
860
|
|
|
785
861
|
// tabs
|
|
786
862
|
const tabs=[...document.querySelectorAll(".tab")];
|
|
787
|
-
tabs.forEach(t=>t.onclick=()=>
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
});
|
|
863
|
+
tabs.forEach(t=>t.onclick=()=>activateTab(t.dataset.tab));
|
|
864
|
+
// Set initial tab based on mode
|
|
865
|
+
activateTab(viewMode==="simple"?"risks":"codebase");
|
|
791
866
|
})();
|
|
792
867
|
</script>
|
|
793
868
|
</body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangepro/orangepro-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OrangePro (`opro`) — a local-first, BYOK CLI + MCP server that builds an evidence graph from a local checkout, ingests runtime coverage, and generates grounded tests. Metadata-only exports; no source upload; generated tests stay local.",
|
|
6
6
|
"license": "MIT",
|