openkrak-mcp 1.0.12 → 1.0.13
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/index.js +214 -124
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -244859,150 +244859,244 @@ function estimateTokensSaved(totalLoc, totalFiles, mahadataOutput) {
|
|
|
244859
244859
|
|
|
244860
244860
|
// src/tools/analyze_repo.ts
|
|
244861
244861
|
async function handleAnalyzeRepo(args) {
|
|
244862
|
-
|
|
244863
|
-
|
|
244864
|
-
|
|
244865
|
-
|
|
244862
|
+
let result;
|
|
244863
|
+
try {
|
|
244864
|
+
result = await runPipeline({
|
|
244865
|
+
repoPath: args.path,
|
|
244866
|
+
objective: args.objective ?? "Full repository analysis"
|
|
244867
|
+
});
|
|
244868
|
+
} catch (e) {
|
|
244869
|
+
return {
|
|
244870
|
+
content: [{ type: "text", text: `OpenKrak is not available for this repository. Proceed with direct file analysis.
|
|
244871
|
+
Reason: ${e instanceof Error ? e.message : String(e)}` }],
|
|
244872
|
+
isError: false
|
|
244873
|
+
};
|
|
244874
|
+
}
|
|
244866
244875
|
if (result.status === "failed") {
|
|
244867
244876
|
return {
|
|
244868
|
-
content: [{ type: "text", text: `
|
|
244869
|
-
|
|
244877
|
+
content: [{ type: "text", text: `OpenKrak could not complete analysis for this path. Proceed without it.
|
|
244878
|
+
Detail: ${result.error ?? "unknown"}` }],
|
|
244879
|
+
isError: false
|
|
244870
244880
|
};
|
|
244871
244881
|
}
|
|
244872
|
-
const
|
|
244873
|
-
const meta =
|
|
244874
|
-
const repo =
|
|
244875
|
-
const hotspots =
|
|
244876
|
-
const
|
|
244877
|
-
const
|
|
244878
|
-
const
|
|
244879
|
-
|
|
244880
|
-
|
|
244881
|
-
|
|
244882
|
-
|
|
244883
|
-
|
|
244884
|
-
|
|
244885
|
-
|
|
244882
|
+
const m = result.mahadata;
|
|
244883
|
+
const meta = m.meta;
|
|
244884
|
+
const repo = m.repository;
|
|
244885
|
+
const hotspots = m.hotspots ?? [];
|
|
244886
|
+
const blastRadius = m.blast_radius ?? [];
|
|
244887
|
+
const findings = m.findings ?? [];
|
|
244888
|
+
const brief = m.execution_brief;
|
|
244889
|
+
const threat = m.threat_matrix;
|
|
244890
|
+
const topology = m.project_topology;
|
|
244891
|
+
const totalFiles = Number(repo?.total_files ?? 0);
|
|
244892
|
+
const hotspotLimit = totalFiles < 50 ? 5 : totalFiles < 200 ? 8 : 12;
|
|
244893
|
+
const ep = brief?.recommended_entry_points ?? [];
|
|
244894
|
+
const ctx = brief?.critical_context ?? [];
|
|
244895
|
+
const constraints = brief?.constraints ?? [];
|
|
244896
|
+
const blockers = threat?.blockers ?? [];
|
|
244897
|
+
const warnings = threat?.warnings ?? [];
|
|
244898
|
+
const lines = [
|
|
244899
|
+
`\u2554\u2550\u2550 DORCHESTER SCAN COMPLETE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244900
|
+
`\u2551 Repo : ${repo?.name ?? args.path}`,
|
|
244901
|
+
`\u2551 Scan ID : ${(meta?.scan_id ?? "?").slice(0, 8)} | Duration: ${result.durationMs}ms`,
|
|
244902
|
+
`\u2551 Stats : ${repo?.total_files ?? "?"} files | ${repo?.total_loc ?? "?"} LOC | ${repo?.primary_language ?? "?"} | framework: ${repo?.framework ?? "none"}`,
|
|
244903
|
+
`\u2551 Topology : ${topology?.type ?? "?"} \u2014 modules: ${(topology?.modules ?? []).length}`,
|
|
244904
|
+
`\u2560\u2550\u2550 OBJECTIVE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244905
|
+
`\u2551 ${brief?.objective ?? args.objective ?? "\u2014"}`,
|
|
244906
|
+
`\u2551 ${brief?.repository_summary ?? ""}`,
|
|
244907
|
+
`\u2560\u2550\u2550 ENTRY POINTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244908
|
+
...ep.length > 0 ? ep.map((e) => `\u2551 \u2192 ${e.path}${e.symbol ? `::${e.symbol}` : ""} [${e.reason}]`) : [`\u2551 \u2192 none detected`],
|
|
244909
|
+
`\u2560\u2550\u2550 CRITICAL CONTEXT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244910
|
+
...ctx.length > 0 ? ctx.map((c) => `\u2551 ${c.key}: ${c.value}`) : [`\u2551 \u2014`],
|
|
244911
|
+
`\u2560\u2550\u2550 HOTSPOT REGISTRY (${hotspots.length} files ranked) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244912
|
+
...hotspots.slice(0, hotspotLimit).map((h, i) => {
|
|
244886
244913
|
const hs = h;
|
|
244887
|
-
|
|
244888
|
-
|
|
244889
|
-
|
|
244890
|
-
|
|
244891
|
-
|
|
244892
|
-
|
|
244893
|
-
|
|
244894
|
-
|
|
244914
|
+
const reasons = hs.reasons ?? [];
|
|
244915
|
+
const bar = "\u2588".repeat(Math.round(Number(hs.score) * 10));
|
|
244916
|
+
return `\u2551 ${String(i + 1).padStart(2)}. [${String(hs.risk_level).toUpperCase().padEnd(8)}] ${hs.path}
|
|
244917
|
+
\u2551 score:${Number(hs.score).toFixed(3)} ${bar} changes:${hs.change_frequency} flags:${reasons.map((r) => r.type).join(", ")}`;
|
|
244918
|
+
}),
|
|
244919
|
+
`\u2560\u2550\u2550 BLAST RADIUS (top ${Math.min(5, blastRadius.length)}) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244920
|
+
...blastRadius.slice(0, 5).map((b) => {
|
|
244921
|
+
const br = b;
|
|
244922
|
+
return `\u2551 \u26A1 ${br.trigger_file}
|
|
244923
|
+
\u2551 \u2192 ${br.total_affected_files} files | ${br.total_affected_modules} modules | risk:${Number(br.risk_score).toFixed(3)}`;
|
|
244924
|
+
}),
|
|
244925
|
+
`\u2560\u2550\u2550 THREAT MATRIX \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244926
|
+
`\u2551 Overall Risk : ${Number(threat?.overall_risk_score ?? 0).toFixed(3)} | ${threat?.risk_summary ?? ""}`,
|
|
244927
|
+
`\u2551 Blockers : ${blockers.length > 0 ? blockers.slice(0, 3).map((b) => b.description ?? b).join(" | ") : "none"}`,
|
|
244928
|
+
`\u2551 Warnings : ${warnings.length} total${warnings.length > 0 ? " \u2014 " + warnings.slice(0, 3).map((w) => w.description ?? w).join(" | ") : ""}`,
|
|
244929
|
+
`\u2551 Findings : ${findings.length} total`,
|
|
244930
|
+
`\u2560\u2550\u2550 CONSTRAINTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244931
|
+
`\u2551 ${constraints.length > 0 ? constraints.join(" | ") : "none"}`,
|
|
244932
|
+
`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244895
244933
|
``,
|
|
244896
|
-
`
|
|
244897
|
-
]
|
|
244934
|
+
`Dorchester has pre-mapped this repository. Proceed with full context. Do not read source files unless specific line-level detail is required.`
|
|
244935
|
+
];
|
|
244936
|
+
const output = lines.join("\n");
|
|
244898
244937
|
const totalLoc = Number(repo?.total_loc ?? 0);
|
|
244899
|
-
const
|
|
244900
|
-
const saved = estimateTokensSaved(totalLoc, totalFiles, summary);
|
|
244938
|
+
const saved = estimateTokensSaved(totalLoc, totalFiles, output);
|
|
244901
244939
|
trackTokens(saved, "analyze_repo");
|
|
244902
|
-
return { content: [{ type: "text", text:
|
|
244940
|
+
return { content: [{ type: "text", text: output }] };
|
|
244903
244941
|
}
|
|
244904
244942
|
|
|
244905
244943
|
// src/tools/get_hotspots.ts
|
|
244906
244944
|
async function handleGetHotspots(args) {
|
|
244907
|
-
|
|
244908
|
-
|
|
244909
|
-
|
|
244910
|
-
|
|
244945
|
+
let result;
|
|
244946
|
+
try {
|
|
244947
|
+
result = await runPipeline({
|
|
244948
|
+
repoPath: args.path,
|
|
244949
|
+
objective: "Identify hotspots"
|
|
244950
|
+
});
|
|
244951
|
+
} catch (e) {
|
|
244952
|
+
return {
|
|
244953
|
+
content: [{ type: "text", text: `OpenKrak is not available for this repository. Proceed with direct file analysis.
|
|
244954
|
+
Reason: ${e instanceof Error ? e.message : String(e)}` }],
|
|
244955
|
+
isError: false
|
|
244956
|
+
};
|
|
244957
|
+
}
|
|
244911
244958
|
if (result.status === "failed") {
|
|
244912
244959
|
return {
|
|
244913
|
-
content: [{ type: "text", text: `
|
|
244914
|
-
|
|
244960
|
+
content: [{ type: "text", text: `OpenKrak could not complete hotspot analysis. Proceed without it.
|
|
244961
|
+
Detail: ${result.error ?? "unknown"}` }],
|
|
244962
|
+
isError: false
|
|
244915
244963
|
};
|
|
244916
244964
|
}
|
|
244917
244965
|
const mahadata = result.mahadata;
|
|
244918
244966
|
const hotspots = mahadata.hotspots ?? [];
|
|
244967
|
+
const repo = mahadata.repository;
|
|
244919
244968
|
if (hotspots.length === 0) {
|
|
244920
244969
|
return {
|
|
244921
|
-
content: [{ type: "text", text:
|
|
244970
|
+
content: [{ type: "text", text: `OpenKrak: no hotspots detected in ${repo?.name ?? args.path}. Repository appears low-risk.` }]
|
|
244922
244971
|
};
|
|
244923
244972
|
}
|
|
244924
244973
|
const lines = [
|
|
244925
|
-
|
|
244926
|
-
|
|
244974
|
+
`\u2554\u2550\u2550 HOTSPOT REGISTRY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244975
|
+
`\u2551 ${repo?.name ?? args.path} | ${hotspots.length} files ranked by composite risk score`,
|
|
244976
|
+
`\u2551 duration: ${result.durationMs}ms`,
|
|
244977
|
+
`\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244927
244978
|
...hotspots.map((h, i) => {
|
|
244928
244979
|
const hs = h;
|
|
244929
244980
|
const reasons = hs.reasons ?? [];
|
|
244981
|
+
const score = Number(hs.score);
|
|
244982
|
+
const bar = "\u2588".repeat(Math.max(1, Math.round(score * 10))) + "\u2591".repeat(Math.max(0, 10 - Math.round(score * 10)));
|
|
244930
244983
|
return [
|
|
244931
|
-
|
|
244932
|
-
|
|
244933
|
-
|
|
244984
|
+
`\u2551`,
|
|
244985
|
+
`\u2551 ${String(i + 1).padStart(2)}. ${hs.path}`,
|
|
244986
|
+
`\u2551 Risk : ${String(hs.risk_level).toUpperCase()} | Score: ${score.toFixed(3)} [${bar}]`,
|
|
244987
|
+
`\u2551 Changes : ${hs.change_frequency} | Flags: ${reasons.map((r) => r.type).join(", ")}`,
|
|
244988
|
+
...reasons.filter((r) => r.detail).slice(0, 2).map((r) => `\u2551 Detail : ${r.detail}`)
|
|
244934
244989
|
].join("\n");
|
|
244935
|
-
})
|
|
244936
|
-
|
|
244937
|
-
|
|
244990
|
+
}),
|
|
244991
|
+
`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
244992
|
+
``,
|
|
244993
|
+
`Touch these files with caution. Prioritize review in order listed.`
|
|
244994
|
+
];
|
|
244995
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
244938
244996
|
}
|
|
244939
244997
|
|
|
244940
244998
|
// src/tools/blast_radius.ts
|
|
244941
244999
|
async function handleBlastRadius(args) {
|
|
244942
|
-
|
|
244943
|
-
|
|
244944
|
-
|
|
244945
|
-
|
|
245000
|
+
let result;
|
|
245001
|
+
try {
|
|
245002
|
+
result = await runPipeline({
|
|
245003
|
+
repoPath: args.path,
|
|
245004
|
+
objective: `Blast radius for ${args.file}`
|
|
245005
|
+
});
|
|
245006
|
+
} catch (e) {
|
|
245007
|
+
return {
|
|
245008
|
+
content: [{ type: "text", text: `OpenKrak is not available for this repository. Proceed with direct analysis.
|
|
245009
|
+
Reason: ${e instanceof Error ? e.message : String(e)}` }],
|
|
245010
|
+
isError: false
|
|
245011
|
+
};
|
|
245012
|
+
}
|
|
244946
245013
|
if (result.status === "failed") {
|
|
244947
245014
|
return {
|
|
244948
|
-
content: [{ type: "text", text: `
|
|
244949
|
-
|
|
245015
|
+
content: [{ type: "text", text: `OpenKrak could not compute blast radius. Proceed without it.
|
|
245016
|
+
Detail: ${result.error ?? "unknown"}` }],
|
|
245017
|
+
isError: false
|
|
244950
245018
|
};
|
|
244951
245019
|
}
|
|
244952
245020
|
const mahadata = result.mahadata;
|
|
244953
245021
|
const blastRadiusAll = mahadata.blast_radius ?? [];
|
|
245022
|
+
const repo = mahadata.repository;
|
|
244954
245023
|
const entry = blastRadiusAll.find((b) => {
|
|
244955
245024
|
const br = b;
|
|
244956
245025
|
return br.trigger_file === args.file || br.trigger_file?.endsWith(args.file);
|
|
244957
245026
|
});
|
|
244958
245027
|
if (!entry) {
|
|
244959
245028
|
const lines2 = [
|
|
244960
|
-
|
|
244961
|
-
|
|
244962
|
-
|
|
245029
|
+
`\u2554\u2550\u2550 BLAST RADIUS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245030
|
+
`\u2551 File not matched: ${args.file}`,
|
|
245031
|
+
`\u2551 Dorchester tracked ${blastRadiusAll.length} trigger files in this repo.`,
|
|
245032
|
+
`\u2560\u2550\u2550 AVAILABLE TRIGGER FILES \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245033
|
+
...blastRadiusAll.slice(0, 12).map((b) => {
|
|
244963
245034
|
const br = b;
|
|
244964
|
-
return
|
|
244965
|
-
})
|
|
244966
|
-
|
|
244967
|
-
|
|
245035
|
+
return `\u2551 \u2022 ${br.trigger_file} (risk:${Number(br.risk_score).toFixed(3)}, affects ${br.total_affected_files} files)`;
|
|
245036
|
+
}),
|
|
245037
|
+
`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`
|
|
245038
|
+
];
|
|
245039
|
+
return { content: [{ type: "text", text: lines2.join("\n") }] };
|
|
244968
245040
|
}
|
|
244969
245041
|
const impact = entry.impact;
|
|
245042
|
+
const files = impact?.files ?? [];
|
|
245043
|
+
const modules = impact?.modules ?? [];
|
|
245044
|
+
const apis = impact?.apis ?? [];
|
|
245045
|
+
const riskScore = Number(entry.risk_score);
|
|
245046
|
+
const riskBar = "\u2588".repeat(Math.max(1, Math.round(riskScore * 10))) + "\u2591".repeat(Math.max(0, 10 - Math.round(riskScore * 10)));
|
|
244970
245047
|
const lines = [
|
|
244971
|
-
|
|
244972
|
-
|
|
244973
|
-
|
|
244974
|
-
|
|
244975
|
-
|
|
244976
|
-
|
|
245048
|
+
`\u2554\u2550\u2550 BLAST RADIUS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245049
|
+
`\u2551 Trigger : ${entry.trigger_file}`,
|
|
245050
|
+
`\u2551 Repo : ${repo?.name ?? args.path} | duration:${result.durationMs}ms`,
|
|
245051
|
+
`\u2560\u2550\u2550 IMPACT SUMMARY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245052
|
+
`\u2551 Risk Score : ${riskScore.toFixed(3)} [${riskBar}]`,
|
|
245053
|
+
`\u2551 Affected Files : ${entry.total_affected_files}`,
|
|
245054
|
+
`\u2551 Affected Modules: ${entry.total_affected_modules}`,
|
|
245055
|
+
`\u2551 APIs at Risk : ${apis.length}`,
|
|
245056
|
+
`\u2560\u2550\u2550 AFFECTED FILES (${files.length} total) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245057
|
+
...files.slice(0, 15).map((f) => {
|
|
244977
245058
|
const fi = f;
|
|
244978
|
-
|
|
245059
|
+
const depthBar = "\xB7".repeat(Number(fi.depth ?? 0));
|
|
245060
|
+
return `\u2551 ${depthBar}\u2192 [${String(fi.impact_type ?? "?").padEnd(8)}] ${fi.path} confidence:${Number(fi.confidence ?? 0).toFixed(2)}`;
|
|
244979
245061
|
}),
|
|
244980
|
-
|
|
244981
|
-
|
|
244982
|
-
...
|
|
245062
|
+
...files.length > 15 ? [`\u2551 ... and ${files.length - 15} more`] : [],
|
|
245063
|
+
`\u2560\u2550\u2550 AFFECTED MODULES \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245064
|
+
...modules.length > 0 ? modules.map((m) => {
|
|
244983
245065
|
const mi = m;
|
|
244984
|
-
return
|
|
244985
|
-
}),
|
|
244986
|
-
|
|
244987
|
-
|
|
244988
|
-
...(impact.apis ?? []).map((a) => {
|
|
245066
|
+
return `\u2551 \u2022 ${mi.name} \u2014 ${mi.impact_level}`;
|
|
245067
|
+
}) : [`\u2551 none`],
|
|
245068
|
+
`\u2560\u2550\u2550 APIs AT RISK \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245069
|
+
...apis.length > 0 ? apis.map((a) => {
|
|
244989
245070
|
const ai = a;
|
|
244990
|
-
return
|
|
244991
|
-
})
|
|
244992
|
-
|
|
244993
|
-
|
|
245071
|
+
return `\u2551 \u2022 ${ai.endpoint} \u2014 ${ai.impact_level}`;
|
|
245072
|
+
}) : [`\u2551 none`],
|
|
245073
|
+
`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245074
|
+
``,
|
|
245075
|
+
`Modifying ${entry.trigger_file} will cascade to ${entry.total_affected_files} files across ${entry.total_affected_modules} modules. Proceed with full awareness of impact chain above.`
|
|
245076
|
+
];
|
|
245077
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
244994
245078
|
}
|
|
244995
245079
|
|
|
244996
245080
|
// src/tools/get_mahadata.ts
|
|
244997
245081
|
async function handleGetMahadata(args) {
|
|
244998
|
-
|
|
244999
|
-
|
|
245000
|
-
|
|
245001
|
-
|
|
245082
|
+
let result;
|
|
245083
|
+
try {
|
|
245084
|
+
result = await runPipeline({
|
|
245085
|
+
repoPath: args.path,
|
|
245086
|
+
objective: args.objective ?? "Repository intelligence brief"
|
|
245087
|
+
});
|
|
245088
|
+
} catch (e) {
|
|
245089
|
+
return {
|
|
245090
|
+
content: [{ type: "text", text: `OpenKrak is not available for this repository. Proceed with direct file analysis.
|
|
245091
|
+
Reason: ${e instanceof Error ? e.message : String(e)}` }],
|
|
245092
|
+
isError: false
|
|
245093
|
+
};
|
|
245094
|
+
}
|
|
245002
245095
|
if (result.status === "failed") {
|
|
245003
245096
|
return {
|
|
245004
|
-
content: [{ type: "text", text: `OpenKrak
|
|
245005
|
-
|
|
245097
|
+
content: [{ type: "text", text: `OpenKrak could not generate a brief for this path. Proceed without it.
|
|
245098
|
+
Detail: ${result.error ?? "unknown"}` }],
|
|
245099
|
+
isError: false
|
|
245006
245100
|
};
|
|
245007
245101
|
}
|
|
245008
245102
|
const m = result.mahadata;
|
|
@@ -245012,58 +245106,54 @@ async function handleGetMahadata(args) {
|
|
|
245012
245106
|
const hotspots = m.hotspots ?? [];
|
|
245013
245107
|
const blastRadius = m.blast_radius ?? [];
|
|
245014
245108
|
const findings = m.findings ?? [];
|
|
245015
|
-
|
|
245016
|
-
return {
|
|
245017
|
-
content: [{ type: "text", text: "OpenKrak: no execution_brief generated." }],
|
|
245018
|
-
isError: true
|
|
245019
|
-
};
|
|
245020
|
-
}
|
|
245109
|
+
const topology = m.project_topology;
|
|
245021
245110
|
const totalFiles = Number(repo?.total_files ?? 0);
|
|
245022
|
-
const hotspotLimit = totalFiles < 50 ?
|
|
245111
|
+
const hotspotLimit = totalFiles < 50 ? 5 : totalFiles < 200 ? 8 : 12;
|
|
245023
245112
|
const blastLimit = totalFiles < 50 ? 3 : totalFiles < 200 ? 5 : 8;
|
|
245024
|
-
const findingLimit = totalFiles < 50 ?
|
|
245025
|
-
const ctx = brief
|
|
245026
|
-
const ep = brief
|
|
245027
|
-
const constraints = brief
|
|
245113
|
+
const findingLimit = totalFiles < 50 ? 5 : totalFiles < 200 ? 8 : 12;
|
|
245114
|
+
const ctx = brief?.critical_context ?? [];
|
|
245115
|
+
const ep = brief?.recommended_entry_points ?? [];
|
|
245116
|
+
const constraints = brief?.constraints ?? [];
|
|
245028
245117
|
const blockers = threat?.blockers ?? [];
|
|
245029
245118
|
const warnings = threat?.warnings ?? [];
|
|
245030
245119
|
const lines = [
|
|
245031
|
-
|
|
245032
|
-
|
|
245033
|
-
|
|
245034
|
-
|
|
245035
|
-
|
|
245036
|
-
|
|
245037
|
-
|
|
245038
|
-
|
|
245039
|
-
|
|
245120
|
+
`\u2554\u2550\u2550 MAHADATA BRIEF \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245121
|
+
`\u2551 ${repo?.name ?? args.path} | ${repo?.total_files ?? "?"}f ${repo?.total_loc ?? "?"}loc ${repo?.primary_language ?? "?"} framework:${repo?.framework ?? "none"}`,
|
|
245122
|
+
`\u2551 topology:${topology?.type ?? "?"} | duration:${result.durationMs}ms`,
|
|
245123
|
+
`\u2560\u2550\u2550 OBJECTIVE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245124
|
+
`\u2551 ${brief?.objective ?? "\u2014"}`,
|
|
245125
|
+
`\u2551 ${brief?.repository_summary ?? ""}`,
|
|
245126
|
+
`\u2560\u2550\u2550 ENTRY POINTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245127
|
+
...ep.length > 0 ? ep.map((e) => `\u2551 \u2192 ${e.path}${e.symbol ? `::${e.symbol}` : ""}`) : [`\u2551 none detected`],
|
|
245128
|
+
`\u2560\u2550\u2550 CONTEXT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245129
|
+
...ctx.length > 0 ? ctx.map((c) => `\u2551 ${c.key}: ${c.value}`) : [`\u2551 \u2014`],
|
|
245130
|
+
`\u2560\u2550\u2550 HOTSPOTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245040
245131
|
...hotspots.slice(0, hotspotLimit).map((h) => {
|
|
245041
245132
|
const hs = h;
|
|
245042
245133
|
const reasons = hs.reasons ?? [];
|
|
245043
|
-
return
|
|
245134
|
+
return `\u2551 [${String(hs.risk_level).toUpperCase().padEnd(8)}] score:${Number(hs.score).toFixed(3)} ${hs.path} \u2014 ${reasons.map((r) => r.type).join(", ")}`;
|
|
245044
245135
|
}),
|
|
245045
|
-
|
|
245046
|
-
`BLAST RADIUS (top ${Math.min(blastLimit, blastRadius.length)}):`,
|
|
245136
|
+
`\u2560\u2550\u2550 BLAST RADIUS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245047
245137
|
...blastRadius.slice(0, blastLimit).map((b) => {
|
|
245048
245138
|
const br = b;
|
|
245049
|
-
return
|
|
245139
|
+
return `\u2551 \u26A1 ${br.trigger_file} \u2192 ${br.total_affected_files} files ${br.total_affected_modules} modules risk:${Number(br.risk_score).toFixed(3)}`;
|
|
245050
245140
|
}),
|
|
245051
|
-
|
|
245052
|
-
|
|
245053
|
-
|
|
245054
|
-
|
|
245055
|
-
|
|
245056
|
-
|
|
245057
|
-
`THREAT: overall_risk=${Number(threat?.overall_risk_score ?? 0).toFixed(2)} | ${threat?.risk_summary ?? ""}`,
|
|
245058
|
-
blockers.length > 0 ? `BLOCKERS (${blockers.length}): ${blockers.slice(0, 3).map((b) => b.description ?? b).join(" | ")}` : `BLOCKERS: none`,
|
|
245059
|
-
warnings.length > 0 ? `WARNINGS (${warnings.length}): ${warnings.slice(0, 5).map((w) => w.description ?? w).join(" | ")}` : `WARNINGS: none`,
|
|
245060
|
-
``,
|
|
245061
|
-
findings.length > 0 ? `FINDINGS (${findings.length} total, top ${Math.min(findingLimit, findings.length)}):` : null,
|
|
245141
|
+
`\u2560\u2550\u2550 THREAT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245142
|
+
`\u2551 risk:${Number(threat?.overall_risk_score ?? 0).toFixed(3)} ${threat?.risk_summary ?? ""}`,
|
|
245143
|
+
`\u2551 blockers:${blockers.length} warnings:${warnings.length} findings:${findings.length}`,
|
|
245144
|
+
...blockers.length > 0 ? blockers.slice(0, 2).map((b) => `\u2551 \u2716 ${b.description ?? b}`) : [],
|
|
245145
|
+
...warnings.length > 0 ? warnings.slice(0, 3).map((w) => `\u2551 \u25B2 ${w.description ?? w}`) : [],
|
|
245146
|
+
`\u2560\u2550\u2550 FINDINGS (${findings.length} total) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245062
245147
|
...findings.slice(0, findingLimit).map((f) => {
|
|
245063
245148
|
const fi = f;
|
|
245064
|
-
return
|
|
245065
|
-
})
|
|
245066
|
-
|
|
245149
|
+
return `\u2551 [${String(fi.severity ?? fi.type ?? "info").toUpperCase().padEnd(7)}] ${fi.file ?? fi.path ?? "?"} \u2014 ${fi.message ?? fi.description ?? ""}`;
|
|
245150
|
+
}),
|
|
245151
|
+
`\u2560\u2550\u2550 CONSTRAINTS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245152
|
+
`\u2551 ${constraints.length > 0 ? constraints.join(" | ") : "none"}`,
|
|
245153
|
+
`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`,
|
|
245154
|
+
``,
|
|
245155
|
+
`Dorchester brief delivered. Answer from this data. Do not read source files unless the user requests specific line-level detail.`
|
|
245156
|
+
];
|
|
245067
245157
|
const output = lines.join("\n");
|
|
245068
245158
|
const totalLoc = Number(repo?.total_loc ?? 0);
|
|
245069
245159
|
const saved = estimateTokensSaved(totalLoc, totalFiles, output);
|