@orangepro/orangepro-mcp 0.2.31 → 0.2.32
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/generate/draftGuidance.js +1 -1
- package/dist/local/generate/generator.js +17 -4
- package/dist/local/operations.js +2 -2
- package/dist/local/score/risk.js +8 -0
- package/dist/local/viz/behaviorReportData.js +5 -2
- package/dist/local/viz/behaviorReportHtml.js +11 -7
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ export function classifyGeneratedDraftBlocker(reason) {
|
|
|
10
10
|
if (/no required module provides package|cannot find module|module not found|unresolved import|imports module-path package/i.test(text)) {
|
|
11
11
|
return "unresolved_import";
|
|
12
12
|
}
|
|
13
|
-
if (/syntax check failed|undefined:|unknown field|redeclared in this block|expected (?:declaration|operand|'[^']+'|"[^"]+"|[^ ]+),? found|literal not terminated|cannot assign/i.test(text))
|
|
13
|
+
if (/syntax check failed|undefined:|unknown field|redeclared in this block|expected (?:declaration|operand|'[^']+'|"[^"]+"|[^ ]+),? found|literal not terminated|cannot assign|cannot use .+ as |missing ['",].+argument list/i.test(text))
|
|
14
14
|
return "generated_code";
|
|
15
15
|
return "unknown";
|
|
16
16
|
}
|
|
@@ -1515,7 +1515,13 @@ function commandAvailable(command) {
|
|
|
1515
1515
|
}
|
|
1516
1516
|
}
|
|
1517
1517
|
function shortStaticDiag(message) {
|
|
1518
|
-
|
|
1518
|
+
const compact = message.replace(/\s+/g, " ").trim();
|
|
1519
|
+
if (compact.length <= 240)
|
|
1520
|
+
return compact;
|
|
1521
|
+
// Compiler output starts with package/build boilerplate and puts the useful
|
|
1522
|
+
// file:line diagnostic at the end. Preserve both ends so classification and
|
|
1523
|
+
// remediation do not degrade to "unknown" on long package names.
|
|
1524
|
+
return `${compact.slice(0, 72)} … ${compact.slice(-165)}`;
|
|
1519
1525
|
}
|
|
1520
1526
|
function escapeRegExp(value) {
|
|
1521
1527
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -2158,6 +2164,7 @@ export async function generateTests(graph, opts, provider, fileReader, clock = s
|
|
|
2158
2164
|
const framework = pickFramework(graph, opts, targets, fileReader);
|
|
2159
2165
|
const runSelection = targetsForFramework(graph, targets, framework);
|
|
2160
2166
|
let runTargets = runSelection.targets;
|
|
2167
|
+
const explicitMulti = Boolean(opts.target_ids && opts.target_ids.length > 1);
|
|
2161
2168
|
warnings.push(...runSelection.warnings);
|
|
2162
2169
|
const systemPrompt = opts.systemPrompt ?? buildSystemPrompt();
|
|
2163
2170
|
const promptVersion = inputMode === "graph_grounded" && opts.prompt_version === "v5" ? PROMPT_VERSION_V5 : PROMPT_VERSION;
|
|
@@ -2275,7 +2282,8 @@ export async function generateTests(graph, opts, provider, fileReader, clock = s
|
|
|
2275
2282
|
}
|
|
2276
2283
|
else if (opts.prompt_version === "v5") {
|
|
2277
2284
|
const declaredDeps = readDeclaredDeps(graph.workspace.root);
|
|
2278
|
-
for (
|
|
2285
|
+
for (let targetIndex = 0; targetIndex < runTargets.length; targetIndex++) {
|
|
2286
|
+
const behavior = runTargets[targetIndex];
|
|
2279
2287
|
if (generated.length >= limit)
|
|
2280
2288
|
break;
|
|
2281
2289
|
const gc = gatherContext(graph, behavior, framework, fileReader);
|
|
@@ -2378,7 +2386,13 @@ export async function generateTests(graph, opts, provider, fileReader, clock = s
|
|
|
2378
2386
|
});
|
|
2379
2387
|
continue;
|
|
2380
2388
|
}
|
|
2381
|
-
const
|
|
2389
|
+
const remainingSlots = Math.max(1, limit - generated.length);
|
|
2390
|
+
const remainingTargets = Math.max(1, runTargets.length - targetIndex);
|
|
2391
|
+
// In explicit multi-target mode, reserve a fair share for every remaining
|
|
2392
|
+
// target. Previously the first behavior could consume the entire batch
|
|
2393
|
+
// with several scenarios, leaving later high-risk behaviors untouched.
|
|
2394
|
+
const targetLimit = explicitMulti ? Math.max(1, Math.floor(remainingSlots / remainingTargets)) : remainingSlots;
|
|
2395
|
+
const selected = scenarios.slice(0, targetLimit);
|
|
2382
2396
|
const completions = [];
|
|
2383
2397
|
try {
|
|
2384
2398
|
reportProgress(`Generating "${gc.ctx.behavior_title}" [v5 batch: ${selected.length}]…`);
|
|
@@ -2591,7 +2605,6 @@ export async function generateTests(graph, opts, provider, fileReader, clock = s
|
|
|
2591
2605
|
}
|
|
2592
2606
|
else {
|
|
2593
2607
|
// Default: target-focused, bucket-diverse generation (one test per local bucket).
|
|
2594
|
-
const explicitMulti = Boolean(opts.target_ids && opts.target_ids.length > 1);
|
|
2595
2608
|
const plan = planGroundedBuckets(graph, runTargets, framework, fileReader, limit, explicitMulti, missing, warnings);
|
|
2596
2609
|
// Repo dependency names (read once) — used by the runnable check to tell a missing
|
|
2597
2610
|
// baseUrl-local import from a genuine external package the agent has installed.
|
package/dist/local/operations.js
CHANGED
|
@@ -19,7 +19,7 @@ import { enrichFromContent } from "./enrich/index.js";
|
|
|
19
19
|
import { scoreGraph } from "./score/score.js";
|
|
20
20
|
import { doctorGraph } from "./score/doctor.js";
|
|
21
21
|
import { findGaps } from "./gaps/gaps.js";
|
|
22
|
-
import { rankRiskGaps } from "./score/risk.js";
|
|
22
|
+
import { rankPriorityGaps, rankRiskGaps } from "./score/risk.js";
|
|
23
23
|
import { generateTests } from "./generate/generator.js";
|
|
24
24
|
import { classifyGeneratedDraftBlocker } from "./generate/draftGuidance.js";
|
|
25
25
|
import { autoProve, NO_KEY_MESSAGE, isEligibleProvableTarget } from "./autoProve.js";
|
|
@@ -1631,7 +1631,7 @@ export async function opStart(root, opts = {}, deps = defaultDeps()) {
|
|
|
1631
1631
|
try {
|
|
1632
1632
|
const graphForGeneration = loadGraph(workspacePaths(root).graphPath);
|
|
1633
1633
|
const generatedTargets = new Set((graphForGeneration.generated_tests ?? []).map((t) => t.target_symbol_external_id).filter((id) => Boolean(id)));
|
|
1634
|
-
const targetIds =
|
|
1634
|
+
const targetIds = rankPriorityGaps(graphForGeneration, {
|
|
1635
1635
|
repoRoot: root,
|
|
1636
1636
|
limit: generationLimit,
|
|
1637
1637
|
provenIds: provenSymbolIds(graphForGeneration, loadLedger(root))
|
package/dist/local/score/risk.js
CHANGED
|
@@ -580,3 +580,11 @@ export function rankRiskGaps(graph, opts = {}) {
|
|
|
580
580
|
.sort((a, b) => b.risk_score - a.risk_score || b.incoming_refs - a.incoming_refs || b.git_churn - a.git_churn || a.id.localeCompare(b.id))
|
|
581
581
|
.slice(0, limit);
|
|
582
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* Canonical priority-gap portfolio shown to a local user and used for automatic
|
|
585
|
+
* generation. Keeping this policy in one function prevents `opro start` from
|
|
586
|
+
* generating for a different "top N" than behavior-coverage.html displays.
|
|
587
|
+
*/
|
|
588
|
+
export function rankPriorityGaps(graph, opts = {}) {
|
|
589
|
+
return rankRiskGaps(graph, { ...opts, maxPerFile: 3, maxPerTitle: 1 });
|
|
590
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { buildRtm } from "../rtm.js";
|
|
4
|
-
import { inspectRiskInputHealth, isEntryPoint,
|
|
4
|
+
import { inspectRiskInputHealth, isEntryPoint, rankPriorityGaps } from "../score/risk.js";
|
|
5
5
|
import { ORANGEPRO_VERSION } from "../version.js";
|
|
6
6
|
import { PROOF_BLOCKER_GUIDE } from "../proofDoctor.js";
|
|
7
7
|
import { classifyGeneratedDraftBlocker } from "../generate/draftGuidance.js";
|
|
@@ -892,7 +892,10 @@ export function buildBehaviorReportData(graph, ledger, opts = {}) {
|
|
|
892
892
|
const flowIds = flowSymbolIds(graph);
|
|
893
893
|
const summary = summaryFromRows(rows, flowIds);
|
|
894
894
|
const repoRoot = opts.repoRoot ?? graph.workspace.root;
|
|
895
|
-
const
|
|
895
|
+
const provenIds = new Set(rows
|
|
896
|
+
.filter((row) => row.evidence_tier === "proven" && Boolean(row.code_symbol))
|
|
897
|
+
.map((row) => row.code_symbol));
|
|
898
|
+
const riskGaps = rankPriorityGaps(graph, { repoRoot, limit: opts.riskLimit ?? 20, provenIds });
|
|
896
899
|
const riskHealth = inspectRiskInputHealth(repoRoot);
|
|
897
900
|
const churnAvailable = riskHealth.churnAvailable && riskGaps.every((risk) => risk.churn_available !== false);
|
|
898
901
|
const provenance = {
|
|
@@ -911,16 +911,20 @@ if(D.viewMeta){
|
|
|
911
911
|
if(rm&&rm.scored>rm.shown)$("#risk-cap-note").textContent="Showing the top "+rm.shown+" of "+rm.scored.toLocaleString()+" scored behaviors — every behavior is scored; only the highest-risk are surfaced here. Full ranking: opro gaps --limit N, or .orangepro/graph.json.";
|
|
912
912
|
if(fm&&fm.shown>0&&fm.prunedByCaps>0)$("#flow-cap-note").textContent="Showing "+fm.shown.toLocaleString()+" flows, endpoint-anchored first. "+fm.prunedByCaps.toLocaleString()+" additional branch expansions were pruned by depth/branch/global rendering caps — pruning affects display only, not scoring.";
|
|
913
913
|
}
|
|
914
|
-
// Platform CTA: top banner in risk panel
|
|
915
|
-
const riskTopBanner=el("div","platform-top-banner",
|
|
916
|
-
\`<span class="platform-top-banner-text">Local scan shows <b>\${D.risks.length}</b> priority gaps. Full ranked list, incident correlation, and CI merge gate on Platform.</span>
|
|
917
|
-
<a class="platform-footer-btn" href="https://orangepro.ai/get-started" target="_blank">Unlock Full Analysis →</a>\`);
|
|
918
|
-
riskList.before(riskTopBanner);
|
|
919
914
|
const generatedRiskCount=D.risks.filter(r=>r.generatedTests&&r.generatedTests.length).length;
|
|
920
915
|
const generatedOutputCopy=[
|
|
921
916
|
D.generatedRunnableTotal?\`\${D.generatedRunnableTotal} runnable generated test\${D.generatedRunnableTotal===1?"":"s"}\`:'',
|
|
922
917
|
D.generatedDraftTotal?\`\${D.generatedDraftTotal} grounded draft\${D.generatedDraftTotal===1?"":"s"} with code withheld\`:'',
|
|
923
918
|
].filter(Boolean).join(' and ');
|
|
919
|
+
const generationSummary=D.generatedTotal
|
|
920
|
+
?\` Generated output: <b>\${generatedOutputCopy}</b>; <b>\${D.shownCount}</b> shown inline across <b>\${generatedRiskCount} of \${D.risks.length}</b> priority flows.\`
|
|
921
|
+
:'';
|
|
922
|
+
// Platform CTA: top banner in risk panel. Keep generated-output totals beside
|
|
923
|
+
// the flow count so a user cannot mistake "5 flows" for "5 generated tests".
|
|
924
|
+
const riskTopBanner=el("div","platform-top-banner",
|
|
925
|
+
\`<span class="platform-top-banner-text">Local scan shows <b>\${D.risks.length}</b> priority gaps.\${generationSummary} Full ranked list, incident correlation, and CI merge gate on Platform.</span>
|
|
926
|
+
<a class="platform-footer-btn" href="https://orangepro.ai/get-started" target="_blank">Unlock Full Analysis →</a>\`);
|
|
927
|
+
riskList.before(riskTopBanner);
|
|
924
928
|
let activeRiskFilter=generatedRiskCount?"generated":"all";
|
|
925
929
|
function riskMatchesFilter(r){
|
|
926
930
|
const hasGenerated=Boolean(r.generatedTests&&r.generatedTests.length);
|
|
@@ -931,8 +935,8 @@ function riskMatchesFilter(r){
|
|
|
931
935
|
function renderRiskFilters(){
|
|
932
936
|
const options=[
|
|
933
937
|
["all","All",D.risks.length],
|
|
934
|
-
["generated","Flows with generated output",generatedRiskCount],
|
|
935
|
-
["missing","
|
|
938
|
+
["generated","Flows with generated output",generatedRiskCount+"/"+D.risks.length],
|
|
939
|
+
["missing","Flows without generated output",D.risks.filter(r=>!(r.generatedTests&&r.generatedTests.length)).length+"/"+D.risks.length]
|
|
936
940
|
];
|
|
937
941
|
riskTools.innerHTML=options.map(([key,label,count])=>\`<button class="risk-filter" type="button" data-risk-filter="\${key}" aria-pressed="\${key===activeRiskFilter}">\${label} <span class="gc">\${count}</span></button>\`).join("");
|
|
938
942
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangepro/orangepro-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.32",
|
|
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",
|