@orangepro/orangepro-mcp 0.2.39 → 0.2.41

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/dist/local/cli.js CHANGED
@@ -692,6 +692,8 @@ async function main() {
692
692
  printJson(res);
693
693
  else {
694
694
  out(`Test gaps (${res.gaps.length} of ${res.total_behaviors} behaviors):`);
695
+ for (const w of res.warnings ?? [])
696
+ out(` warning: ${w}`);
695
697
  if (res.guidance)
696
698
  out(` ${res.guidance}`);
697
699
  for (const g of res.gaps) {
@@ -656,7 +656,9 @@ export function opAnalyze(root, opts = {}, deps = defaultDeps()) {
656
656
  const coverageSuccess = runtimeCoveragePrepare?.generated
657
657
  .filter((g) => g.ok && g.artifact_path)
658
658
  .map((g) => `coverage artifact generated for ${g.language} module ${g.module_dir}: ${g.artifact_path}`) ?? [];
659
- const warnings = [...coverageSuccess, ...coverageWarnings, ...(runtimeCoveragePrepare?.warnings ?? []), ...analyzeFragment.warnings, ...enrichFragments.flatMap((f) => f.warnings)];
659
+ // Risk-config problems must surface on EVERY entry point that ranks, not just `start`.
660
+ const riskConfigWarnings = loadRiskConfig(scanRoot).warnings;
661
+ const warnings = [...coverageSuccess, ...coverageWarnings, ...(runtimeCoveragePrepare?.warnings ?? []), ...analyzeFragment.warnings, ...enrichFragments.flatMap((f) => f.warnings), ...riskConfigWarnings];
660
662
  return {
661
663
  graph_path: paths.graphPath,
662
664
  sources_count: graph.sources.length,
@@ -876,8 +878,11 @@ export function opGaps(root, opts = {}) {
876
878
  is_new_code: gap.is_new_code,
877
879
  integration_signal: gap.integration_signal
878
880
  }));
881
+ // Same root the ranking itself uses (the analyzed source), not the workspace cwd.
882
+ const riskConfigWarnings = loadRiskConfig(graph.workspace.root).warnings;
879
883
  return {
880
884
  ...gaps,
885
+ ...(riskConfigWarnings.length ? { warnings: riskConfigWarnings } : {}),
881
886
  top_risk_gaps: topRiskGaps,
882
887
  risk_model: {
883
888
  formula: "OrangePro Risk Score = Probability(1-10) × Impact(1-10) × DetectionDifficulty(1|5|10); P = normalize(git_churn*0.35 + fan_out*0.30 + new_code*15 + complexity*0.20); I = normalize(fan_in*0.30 + route_weight*0.30 + flow_position*0.20 + data_sensitivity*0.20); D = {proven:1, associated:5, none:10}",
@@ -301,8 +301,6 @@ body{padding-bottom:48px}
301
301
  .mode-btn.active{background:var(--orange);color:#1a0a02;box-shadow:0 1px 3px rgba(240,136,62,.25)}
302
302
  body[data-mode="simple"] .expert-only{display:none!important}
303
303
  body[data-mode="simple"] .kpis{grid-template-columns:repeat(3,1fr)}
304
- body[data-mode="simple"] .gen-test .gen-test-body{display:block}
305
- body[data-mode="simple"] .gen-test .gen-test-toggle{display:none}
306
304
  body[data-mode="simple"] .risk-card{border-left:3px solid var(--orange)}
307
305
  body[data-mode="simple"] .risk-ctx{color:var(--ink2);font-size:13px}
308
306
  body[data-mode="simple"] .todo{font-size:12.5px;font-weight:600;color:var(--ink);background:var(--obg);border-color:var(--obd)}
@@ -990,7 +988,7 @@ function riskCardHtml(r){
990
988
  const cBadge=t.concern?\`<span class="badge b-info" style="margin-left:6px;font-size:9px">\${esc(t.concern.replace('_',' '))}</span>\`:'';
991
989
  const iBadge=t.runnable===false?\`<span class="badge b-cand" style="margin-left:6px;font-size:9px">Manual test</span>\`:'';
992
990
  const bBadge=t.bucket?\`<span class="badge b-info" style="margin-left:6px;font-size:9px">\${esc(String(t.bucket).replace(/_/g,' '))}</span>\`:'';
993
- 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>\`;
991
+ testsHtml+=\`<div class="gen-test"><div class="gen-test-head" role="button" tabindex="0" aria-expanded="false"><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" aria-hidden="true">&#9660;</span></div><div class="gen-test-body">\${esc(t.code)}</div></div>\`;
994
992
  });
995
993
  testsHtml+=\`</div>\`;
996
994
  }
@@ -1067,10 +1065,24 @@ renderRisks();
1067
1065
  </div>\`;
1068
1066
  host.addEventListener("click",e=>{const r=e.target.closest("[data-path]");if(r&&topPaths.has(r.getAttribute("data-path")))scrollToRisk(r.getAttribute("data-path"));});
1069
1067
  })();
1070
- // toggle test expand
1068
+ // Generated-test drafts are collapsed by default in every view. Keep the
1069
+ // control operable by pointer and keyboard; Simple mode must not override it.
1070
+ function toggleGeneratedTest(h){
1071
+ const item=h.closest('.gen-test');
1072
+ if(!item)return;
1073
+ const open=item.classList.toggle('open');
1074
+ h.setAttribute('aria-expanded',String(open));
1075
+ }
1071
1076
  document.addEventListener('click',e=>{
1072
1077
  const h=e.target.closest('.gen-test-head');
1073
- if(h)h.parentElement.classList.toggle('open');
1078
+ if(h)toggleGeneratedTest(h);
1079
+ });
1080
+ document.addEventListener('keydown',e=>{
1081
+ const h=e.target.closest('.gen-test-head');
1082
+ if(h&&(e.key==='Enter'||e.key===' ')){
1083
+ e.preventDefault();
1084
+ toggleGeneratedTest(h);
1085
+ }
1074
1086
  });
1075
1087
 
1076
1088
  // tabs
@@ -92,7 +92,16 @@ export function loadConfig(paths) {
92
92
  privacy: defaultPrivacySettings()
93
93
  };
94
94
  }
95
- return JSON.parse(readFileSync(paths.configPath, "utf8"));
95
+ const raw = readFileSync(paths.configPath, "utf8");
96
+ try {
97
+ return JSON.parse(raw);
98
+ }
99
+ catch (err) {
100
+ // Fail loud AND clear: this file also carries the per-repo risk config, so a
101
+ // silent default here would be exactly the failure a verification layer must not have.
102
+ const hint = /\/\//.test(raw) ? " JSON does not allow // comments." : "";
103
+ throw new Error(`Unreadable ${paths.configPath}: ${err.message}.${hint} Fix the JSON, or delete the file to regenerate defaults.`);
104
+ }
96
105
  }
97
106
  export function saveConfig(paths, config) {
98
107
  writeJson(paths.configPath, config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangepro/orangepro-mcp",
3
- "version": "0.2.39",
3
+ "version": "0.2.41",
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",