@orangepro/orangepro-mcp 0.2.15 → 0.2.16

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 CHANGED
@@ -4,6 +4,7 @@
4
4
  [![MIT License](https://img.shields.io/badge/license-MIT-green.svg )](LICENSE)
5
5
  [![npm downloads](https://img.shields.io/npm/dw/@orangepro/mcp-server.svg )](https://www.npmjs.com/package/@orangepro/mcp-server )
6
6
  [![orangepro-mcp MCP server](https://glama.ai/mcp/servers/OrangeproAI/orangepro-mcp/badges/score.svg )](https://glama.ai/mcp/servers/OrangeproAI/orangepro-mcp )
7
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-ai.orangepro%2Fmcp-orange.svg )](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,29 @@ 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 === */
248
+ .mode-toggle{appearance:none;background:var(--s2);border:1px solid var(--bd);color:var(--muted);font:600 11px var(--sans);padding:5px 10px;border-radius:5px;cursor:pointer;transition:color .12s,border-color .12s}
249
+ .mode-toggle:hover{color:var(--ink);border-color:var(--bd2)}
250
+ body[data-mode="simple"] .expert-only{display:none!important}
251
+ body[data-mode="simple"] .kpis{grid-template-columns:repeat(3,1fr)}
252
+ body[data-mode="simple"] .gen-test .gen-test-body{display:block}
253
+ body[data-mode="simple"] .gen-test .gen-test-toggle{display:none}
254
+ body[data-mode="simple"] .risk-card{border-left:3px solid var(--orange)}
255
+ body[data-mode="simple"] .risk-ctx{color:var(--ink2);font-size:13px}
256
+ body[data-mode="simple"] .todo{font-size:12.5px;font-weight:600;color:var(--ink);background:var(--obg);border-color:var(--obd)}
257
+ body[data-mode="simple"] .risk-tags{display:none}
258
+ body[data-mode="simple"] .cat-strip{display:none}
259
+ body[data-mode="simple"] .risk-tools{display:none}
260
+ body[data-mode="simple"] .metric-scope{display:none}
261
+ .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}
262
+ .copy-btn:hover{color:var(--green);border-color:var(--gbd)}
263
+ .copy-btn.copied{color:var(--green);border-color:var(--gbd)}
264
+ body[data-mode="expert"] .simple-only{display:none!important}
265
+ .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}
266
+ .simple-intro h2{font-size:15px;font-weight:700;color:var(--ink);margin:0 0 4px}
267
+ .simple-intro p{font-size:12.5px;color:var(--muted);margin:0;line-height:1.5}
268
+
246
269
  @media(max-width:800px){
247
270
  .kpis{grid-template-columns:repeat(3,1fr)}
248
271
  .cols2,.beh-layout{grid-template-columns:1fr}
@@ -261,6 +284,7 @@ nav.tabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--bd)}
261
284
  <span class="hdr-name" id="repo-name">—</span>
262
285
  </div>
263
286
  <div class="hdr-right">
287
+ <button class="mode-toggle" id="mode-toggle" type="button">Expert view →</button>
264
288
  <span class="hdr-tag" id="framework">—</span>
265
289
  <span id="scan-date">—</span>
266
290
  </div>
@@ -270,7 +294,12 @@ nav.tabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--bd)}
270
294
  <section class="kpis" id="kpis"></section>
271
295
  <p class="metric-scope" id="metric-scope"></p>
272
296
 
273
- <nav class="tabs" role="tablist">
297
+ <nav class="tabs simple-only" role="tablist">
298
+ <button class="tab" role="tab" aria-selected="true" data-tab="risks">Fix These <span class="tc" id="t-risk-s"></span></button>
299
+ <button class="tab" role="tab" aria-selected="false" data-tab="codebase">Your Code</button>
300
+ <button class="tab" role="tab" aria-selected="false" data-tab="flows">Deep Dive</button>
301
+ </nav>
302
+ <nav class="tabs expert-only" role="tablist">
274
303
  <button class="tab" role="tab" aria-selected="true" data-tab="codebase">Your Code</button>
275
304
  <button class="tab" role="tab" aria-selected="false" data-tab="risks">Priority gaps <span class="tc" id="t-risk">—</span></button>
276
305
  <button class="tab" role="tab" aria-selected="false" data-tab="flows">Flows <span class="tc" id="t-flow">—</span></button>
@@ -331,7 +360,11 @@ nav.tabs{display:flex;gap:2px;margin:18px 0 0;border-bottom:1px solid var(--bd)}
331
360
 
332
361
  <!-- TAB 4: RISKS — actionable -->
333
362
  <section class="panel" id="panel-risks" role="tabpanel">
334
- <p class="bridge">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>
363
+ <div class="simple-intro simple-only">
364
+ <h2>Your highest-risk blind spots</h2>
365
+ <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>
366
+ </div>
367
+ <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
368
  <p class="bridge" id="risk-cap-note" style="font-size:12px;opacity:.75"></p>
336
369
  <div class="risk-tools" id="risk-tools"></div>
337
370
  <div id="risk-list"></div>
@@ -472,6 +505,25 @@ const D=window.DATA,$=(s)=>document.querySelector(s);
472
505
  const el=(t,c,h)=>{const e=document.createElement(t);if(c)e.className=c;if(h!=null)e.innerHTML=h;return e};
473
506
  const esc=s=>String(s).replace(/[&<>]/g,m=>({"&":"&amp;","<":"&lt;",">":"&gt;"}[m]));
474
507
  const S=D.summary;
508
+ // === OPTION A: VIEW MODE ===
509
+ const params=new URLSearchParams(location.search);
510
+ let viewMode=params.get("mode")||localStorage.getItem("opro-view")||"simple";
511
+ document.body.dataset.mode=viewMode;
512
+ function activateTab(tabId){
513
+ document.querySelectorAll(".tab").forEach(t=>t.setAttribute("aria-selected",String(t.dataset.tab===tabId)));
514
+ document.querySelectorAll(".panel").forEach(p=>p.classList.toggle("active",p.id==="panel-"+tabId));
515
+ }
516
+ function setMode(m){
517
+ viewMode=m;localStorage.setItem("opro-view",m);document.body.dataset.mode=m;
518
+ document.getElementById("mode-toggle").textContent=m==="simple"?"Expert view \\u2192":"\\u2190 Simple view";
519
+ renderKpisForMode();
520
+ activateTab(m==="simple"?"risks":"codebase");
521
+ if(typeof renderRisks==="function")renderRisks();
522
+ }
523
+ document.getElementById("mode-toggle").onclick=()=>setMode(viewMode==="simple"?"expert":"simple");
524
+ document.getElementById("mode-toggle").textContent=viewMode==="simple"?"Expert view \\u2192":"\\u2190 Simple view";
525
+
526
+
475
527
 
476
528
  // header
477
529
  $("#repo-name").textContent=D.repo;
@@ -493,22 +545,41 @@ $("#br-tests").textContent=D.scan.tests.total;
493
545
  $("#t-beh").textContent=D.behaviors.length;
494
546
  $("#t-flow").textContent=D.flows.length;
495
547
  $("#t-risk").textContent=D.risks.length;
548
+ if($("#t-risk-s"))$("#t-risk-s").textContent=D.risks.length;
496
549
 
497
- // KPIs
550
+ // KPIs — mode-aware
498
551
  const priorityGapCount=D.risks.length;
499
- [
500
- {lbl:"Methods found",num:S.total,sub:"public, with observable outcome",t:"total"},
501
- {lbl:"Dynamically Proven",num:S.proven,sub:"test breaks if you change it",t:"proven"},
502
- {lbl:"Test signal",num:S.associated,sub:"hard static test link, no proof",t:"signal"},
503
- {lbl:"Candidate",num:S.candidate??0,sub:"lexical match only unconfirmed",t:"cand"},
504
- {lbl:"Reachable · no test signal",num:S.reachableUntested,sub:"coverage bucketnot the risk count",t:"reach"},
505
- {lbl:"Unreached · no test signal",num:S.noSignal,sub:"no detected flow or test signal",t:"nosig"},
506
- {lbl:"Priority gaps",num:priorityGapCount,sub:"top-ranked worklist across unproven tiers",t:"priority"},
507
- ].forEach(k=>{
508
- 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>\`);
509
- d.dataset.t=k.t;
510
- $("#kpis").append(d);
511
- });
552
+ function renderExpertKpis(){
553
+ [
554
+ {lbl:"Methods found",num:S.total,sub:"public, with observable outcome",t:"total"},
555
+ {lbl:"Dynamically Proven",num:S.proven,sub:"test breaks if you change it",t:"proven"},
556
+ {lbl:"Test signal",num:S.associated,sub:"hard static test link, no proof",t:"signal"},
557
+ {lbl:"Candidate",num:S.candidate??0,sub:"lexical match only unconfirmed",t:"cand"},
558
+ {lbl:"Reachable · no test signal",num:S.reachableUntested,sub:"coverage bucket not the risk count",t:"reach"},
559
+ {lbl:"Unreached · no test signal",num:S.noSignal,sub:"no detected flow or test signal",t:"nosig"},
560
+ {lbl:"Priority gaps",num:priorityGapCount,sub:"top-ranked worklist across unproven tiers",t:"priority"},
561
+ ].forEach(k=>{
562
+ 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>\`);
563
+ d.dataset.t=k.t;
564
+ $("#kpis").append(d);
565
+ });
566
+ }
567
+ function renderSimpleKpis(){
568
+ const pct=S.total>0?Math.round(((S.proven+S.associated)/S.total)*100):0;
569
+ [{lbl:"Blind spots found",num:priorityGapCount,sub:"highest-risk untested methods",t:"priority"},
570
+ {lbl:"Methods mapped",num:S.total.toLocaleString(),sub:"public methods in your codebase",t:"total"},
571
+ {lbl:"Have test evidence",num:pct+"%",sub:(S.proven+S.associated)+" of "+S.total+" verified or linked",t:"proven"}
572
+ ].forEach(k=>{
573
+ 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>\`);
574
+ d.dataset.t=k.t;
575
+ $("#kpis").append(d);
576
+ });
577
+ }
578
+ function renderKpisForMode(){
579
+ $("#kpis").innerHTML="";
580
+ if(viewMode==="simple") renderSimpleKpis(); else renderExpertKpis();
581
+ }
582
+ renderKpisForMode();
512
583
  const outsideProofs=S.provenOutsideDenominator??0;
513
584
  const coverageAccounted=(S.proven-outsideProofs)+S.associated+(S.candidate??0)+S.reachableUntested+S.noSignal;
514
585
  $("#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 +809,7 @@ function riskCardHtml(r){
738
809
  const cBadge=t.concern?\`<span class="badge b-info" style="margin-left:6px;font-size:9px">\${esc(t.concern.replace('_',' '))}</span>\`:'';
739
810
  const iBadge=t.runnable===false?\`<span class="badge b-cand" style="margin-left:6px;font-size:9px">Manual test</span>\`:'';
740
811
  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">&#9660;</span></div><div class="gen-test-body">\${esc(t.code)}</div></div>\`;
812
+ 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">&#9660;</span></div><div class="gen-test-body">\${esc(t.code)}</div></div>\`;
742
813
  });
743
814
  testsHtml+=\`</div>\`;
744
815
  }
@@ -784,10 +855,9 @@ document.addEventListener('click',e=>{
784
855
 
785
856
  // tabs
786
857
  const tabs=[...document.querySelectorAll(".tab")];
787
- tabs.forEach(t=>t.onclick=()=>{
788
- tabs.forEach(x=>x.setAttribute("aria-selected",String(x===t)));
789
- document.querySelectorAll(".panel").forEach(p=>p.classList.toggle("active",p.id==="panel-"+t.dataset.tab));
790
- });
858
+ tabs.forEach(t=>t.onclick=()=>activateTab(t.dataset.tab));
859
+ // Set initial tab based on mode
860
+ activateTab(viewMode==="simple"?"risks":"codebase");
791
861
  })();
792
862
  </script>
793
863
  </body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangepro/orangepro-mcp",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
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",