@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 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,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
- <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>
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=>({"&":"&amp;","<":"&lt;",">":"&gt;"}[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
- {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
- });
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">&#9660;</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">&#9660;</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
- 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
- });
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.15",
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",