Spaces:
Sleeping
Sleeping
Daksh C Jain commited on
Commit Β·
da2bf0a
1
Parent(s): 0e050fa
fix: complete raw ASGI router implementation for SSE
Browse files- dashboard/src/app/dashboard/[id]/mr/[mriid]/page.tsx +173 -146
- dashboard/src/lib/utils.ts +9 -16
- src/context_brain_mcp.py +24 -21
dashboard/src/app/dashboard/[id]/mr/[mriid]/page.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import Navbar from "@/components/Navbar";
|
| 2 |
import { getMRDiscussions, getMRChanges, fetchGitLab } from "@/lib/gitlab";
|
| 3 |
-
import { parseAuditReport, getRiskStatus, splitReportIntoSections } from "@/lib/utils";
|
| 4 |
import Link from "next/link";
|
| 5 |
|
| 6 |
export default async function MRAnalysis({ params }: { params: Promise<{ id: string, mriid: string }> }) {
|
|
@@ -18,9 +18,8 @@ export default async function MRAnalysis({ params }: { params: Promise<{ id: str
|
|
| 18 |
console.error("Failed to fetch MR analysis data:", error);
|
| 19 |
}
|
| 20 |
|
| 21 |
-
if (!mr) return <div className="min-h-screen bg-[#020617] text-white flex items-center justify-center">Loading
|
| 22 |
|
| 23 |
-
// Filter for Duo Guardian / Context Brain comments
|
| 24 |
const agentDiscussions = discussions.filter(d =>
|
| 25 |
d.notes.some((n: any) =>
|
| 26 |
n.body.includes("Context Brain") ||
|
|
@@ -40,173 +39,201 @@ export default async function MRAnalysis({ params }: { params: Promise<{ id: str
|
|
| 40 |
const risk = getRiskStatus(mr.labels);
|
| 41 |
|
| 42 |
return (
|
| 43 |
-
<main className="min-h-screen bg-[#020617] text-slate-200">
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
<Navbar />
|
| 46 |
|
| 47 |
-
<div className="pt-32 pb-20 px-6 max-w-
|
| 48 |
-
<
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
-
<div className="grid lg:grid-cols-
|
| 53 |
-
{/* Left Column:
|
| 54 |
-
<div className="lg:col-span-
|
| 55 |
-
<div className="p-8 rounded-[
|
| 56 |
-
<div className=
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
|
|
|
| 77 |
</div>
|
| 78 |
</div>
|
| 79 |
|
| 80 |
-
<section className="p-8 rounded-[
|
| 81 |
-
<h3 className="text-sm font-
|
| 82 |
-
<div className="space-y-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
| 95 |
</div>
|
| 96 |
</section>
|
| 97 |
</div>
|
| 98 |
|
| 99 |
-
{/* Center Column: Intelligence
|
| 100 |
-
<div className="lg:col-span-
|
| 101 |
-
<
|
| 102 |
-
<div className="
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
</div>
|
| 108 |
-
<h1 className="text-5xl font-black text-white leading-[1.1] mb-6 tracking-tight">
|
| 109 |
-
{mr.title}
|
| 110 |
-
</h1>
|
| 111 |
-
</header>
|
| 112 |
-
|
| 113 |
-
{/* Mission Summary */}
|
| 114 |
-
<section className="p-10 rounded-[2.5rem] bg-slate-900/40 border border-white/5 relative group overflow-hidden">
|
| 115 |
-
<div className="relative z-10 font-outfit text-slate-200">
|
| 116 |
-
<h2 className="text-xl font-black text-slate-400 mb-6 flex items-center gap-2 uppercase tracking-widest">
|
| 117 |
-
<span>π</span> Mission Summary
|
| 118 |
</h2>
|
| 119 |
{sections.summary ? (
|
| 120 |
-
<
|
|
|
|
|
|
|
| 121 |
) : (
|
| 122 |
-
<
|
| 123 |
)}
|
| 124 |
</div>
|
| 125 |
</section>
|
| 126 |
|
| 127 |
-
{/* Multi-Agent Results Grid */}
|
| 128 |
<div className="grid md:grid-cols-2 gap-6">
|
| 129 |
-
{
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
{
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
</section>
|
| 150 |
-
|
| 151 |
-
{/* Testing Agent */}
|
| 152 |
-
<section className="p-8 rounded-[2rem] bg-gradient-to-br from-purple-900/40 to-slate-900/60 border border-purple-500/20 shadow-2xl relative group h-full">
|
| 153 |
-
<div className="absolute top-0 right-0 p-6 opacity-10 text-4xl">π§ͺ</div>
|
| 154 |
-
<h3 className="text-lg font-black text-purple-400 mb-4 flex items-center gap-2 uppercase tracking-wider text-slate-200">
|
| 155 |
-
Testing Agent
|
| 156 |
-
</h3>
|
| 157 |
-
<div className="text-sm text-slate-300 leading-relaxed whitespace-pre-wrap">
|
| 158 |
-
{sections.testing || "No SDET validation report found."}
|
| 159 |
-
</div>
|
| 160 |
-
</section>
|
| 161 |
-
|
| 162 |
-
{/* Code Quality/Security Agent */}
|
| 163 |
-
<section className="p-8 rounded-[2rem] bg-gradient-to-br from-blue-900/40 to-slate-900/60 border border-blue-500/20 shadow-2xl relative group h-full">
|
| 164 |
-
<div className="absolute top-0 right-0 p-6 opacity-10 text-4xl">π΅οΈ</div>
|
| 165 |
-
<h3 className="text-lg font-black text-blue-400 mb-4 flex items-center gap-2 uppercase tracking-wider">
|
| 166 |
-
Code Analyst
|
| 167 |
-
</h3>
|
| 168 |
-
<div className="text-sm text-slate-300 leading-relaxed whitespace-pre-wrap">
|
| 169 |
-
{sections.code || "No major code quality issues detected."}
|
| 170 |
-
</div>
|
| 171 |
-
</section>
|
| 172 |
</div>
|
|
|
|
| 173 |
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
<div key={note.id} className="group first:mt-0 mt-8 first:pt-0 pt-8 border-t first:border-0 border-white/5">
|
| 185 |
-
<div className="flex items-center justify-between mb-4">
|
| 186 |
-
<div className="flex items-center gap-4">
|
| 187 |
-
{note.author.avatar_url && <img src={note.author.avatar_url} className="w-10 h-10 rounded-2xl border border-white/10" alt="" />}
|
| 188 |
-
<div>
|
| 189 |
-
<div className="font-black text-white">{note.author.name}</div>
|
| 190 |
-
<div className="text-[10px] text-slate-500 font-bold uppercase tracking-wider">{new Date(note.created_at).toLocaleDateString()}</div>
|
| 191 |
-
</div>
|
| 192 |
-
</div>
|
| 193 |
-
<div className="text-[10px] px-2 py-1 bg-white/5 rounded-lg border border-white/5 text-slate-500 font-bold opacity-0 group-hover:opacity-100 transition-opacity uppercase tracking-tighter">Verified Agent</div>
|
| 194 |
-
</div>
|
| 195 |
-
<div className="text-slate-400 leading-relaxed text-sm whitespace-pre-wrap pl-14">
|
| 196 |
-
{note.body}
|
| 197 |
-
</div>
|
| 198 |
</div>
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
</div>
|
| 208 |
</div>
|
| 209 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
</main>
|
| 211 |
);
|
| 212 |
}
|
|
|
|
| 1 |
import Navbar from "@/components/Navbar";
|
| 2 |
import { getMRDiscussions, getMRChanges, fetchGitLab } from "@/lib/gitlab";
|
| 3 |
+
import { parseAuditReport, getRiskStatus, splitReportIntoSections, getFileImpact } from "@/lib/utils";
|
| 4 |
import Link from "next/link";
|
| 5 |
|
| 6 |
export default async function MRAnalysis({ params }: { params: Promise<{ id: string, mriid: string }> }) {
|
|
|
|
| 18 |
console.error("Failed to fetch MR analysis data:", error);
|
| 19 |
}
|
| 20 |
|
| 21 |
+
if (!mr) return <div className="min-h-screen bg-[#020617] text-white flex items-center justify-center">Loading Mission Control...</div>;
|
| 22 |
|
|
|
|
| 23 |
const agentDiscussions = discussions.filter(d =>
|
| 24 |
d.notes.some((n: any) =>
|
| 25 |
n.body.includes("Context Brain") ||
|
|
|
|
| 39 |
const risk = getRiskStatus(mr.labels);
|
| 40 |
|
| 41 |
return (
|
| 42 |
+
<main className="min-h-screen bg-[#020617] text-slate-200 selection:bg-blue-500/30">
|
| 43 |
+
{/* HUD Background Effects */}
|
| 44 |
+
<div className="fixed inset-0 pointer-events-none">
|
| 45 |
+
<div className="absolute top-0 left-0 w-full h-[800px] bg-[radial-gradient(circle_at_50%_0%,rgba(37,99,235,0.08),transparent_70%)]" />
|
| 46 |
+
<div className="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/carbon-fibre.png')] opacity-[0.03]" />
|
| 47 |
+
</div>
|
| 48 |
+
|
| 49 |
<Navbar />
|
| 50 |
|
| 51 |
+
<div className="pt-32 pb-20 px-6 max-w-[1400px] mx-auto relative z-10 animate-in fade-in slide-in-from-bottom-4 duration-1000">
|
| 52 |
+
<div className="flex flex-wrap items-center justify-between mb-12 gap-6">
|
| 53 |
+
<div>
|
| 54 |
+
<Link href={`/dashboard/${id}`} className="group inline-flex items-center gap-2 text-xs font-bold text-slate-500 uppercase tracking-widest hover:text-blue-400 transition-colors mb-6">
|
| 55 |
+
<span className="w-5 h-5 flex items-center justify-center bg-slate-900 rounded-md group-hover:-translate-x-1 transition-transform">β</span>
|
| 56 |
+
Back to Project Intelligence
|
| 57 |
+
</Link>
|
| 58 |
+
<div className="flex items-center gap-4 mb-4">
|
| 59 |
+
<span className="px-3 py-1 bg-slate-900 border border-white/10 rounded-lg text-xs font-black text-slate-400 font-mono tracking-tighter">
|
| 60 |
+
MISSION REQUEST: !{mr.iid}
|
| 61 |
+
</span>
|
| 62 |
+
<span className="flex items-center gap-2 text-xs font-bold text-slate-500 italic">
|
| 63 |
+
<img src={mr.author.avatar_url} className="w-5 h-5 rounded-full border border-white/10" alt="" />
|
| 64 |
+
{mr.author.name}
|
| 65 |
+
</span>
|
| 66 |
+
</div>
|
| 67 |
+
<h1 className="text-6xl font-black text-white leading-tight tracking-tighter">
|
| 68 |
+
{mr.title}
|
| 69 |
+
</h1>
|
| 70 |
+
</div>
|
| 71 |
+
|
| 72 |
+
<div className="flex items-center gap-6">
|
| 73 |
+
<div className="p-1 px-1 bg-slate-900/50 border border-white/5 rounded-2xl flex backdrop-blur-md">
|
| 74 |
+
<div className="px-6 py-4">
|
| 75 |
+
<div className="text-[10px] text-slate-500 font-black uppercase tracking-widest mb-1">Impact Score</div>
|
| 76 |
+
<div className="text-3xl font-black text-white">{changes?.changes?.length || 0}<span className="text-xs text-slate-500 ml-1">FILES</span></div>
|
| 77 |
+
</div>
|
| 78 |
+
<div className="w-px bg-white/5 my-4" />
|
| 79 |
+
<div className="px-6 py-4">
|
| 80 |
+
<div className="text-[10px] text-slate-500 font-black uppercase tracking-widest mb-1">Collaboration</div>
|
| 81 |
+
<div className="text-3xl font-black text-white">{mr.user_notes_count}<span className="text-xs text-slate-500 ml-1">COMM</span></div>
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
</div>
|
| 85 |
+
</div>
|
| 86 |
|
| 87 |
+
<div className="grid lg:grid-cols-12 gap-8">
|
| 88 |
+
{/* Left Column: Security Gauge & Impact List */}
|
| 89 |
+
<div className="lg:col-span-3 space-y-8">
|
| 90 |
+
<div className="p-8 rounded-[2.5rem] bg-slate-900/40 border border-white/5 backdrop-blur-xl relative overflow-hidden group">
|
| 91 |
+
<div className="absolute -right-4 -top-4 w-24 h-24 bg-blue-600/10 rounded-full blur-3xl" />
|
| 92 |
+
<div className="relative z-10">
|
| 93 |
+
<div className="flex justify-between items-start mb-8">
|
| 94 |
+
<div className="text-[10px] font-black uppercase tracking-widest text-slate-500">Security Pulse</div>
|
| 95 |
+
<div className={`w-3 h-3 rounded-full animate-pulse ${
|
| 96 |
+
risk === 'CRITICAL' ? 'bg-rose-500 shadow-[0_0_12px_rgba(244,63,94,0.5)]' : 'bg-emerald-500'
|
| 97 |
+
}`} />
|
| 98 |
+
</div>
|
| 99 |
+
|
| 100 |
+
<div className="flex flex-col items-center text-center py-4">
|
| 101 |
+
<div className={`text-7xl mb-4 transform transition-transform group-hover:scale-110 duration-500 ${
|
| 102 |
+
risk === 'CRITICAL' ? 'drop-shadow-[0_0_20px_rgba(244,63,94,0.3)]' : ''
|
| 103 |
+
}`}>
|
| 104 |
+
{risk === 'CRITICAL' ? 'π¨' : risk === 'CAUTION' ? 'β οΈ' : 'π‘οΈ'}
|
| 105 |
+
</div>
|
| 106 |
+
<div className={`text-3xl font-black tracking-tighter mb-2 ${
|
| 107 |
+
risk === 'CRITICAL' ? 'text-rose-400' : risk === 'CAUTION' ? 'text-amber-400' : 'text-emerald-400'
|
| 108 |
+
}`}>
|
| 109 |
+
{risk} RISK
|
| 110 |
+
</div>
|
| 111 |
+
<p className="text-[10px] text-slate-500 font-bold uppercase tracking-widest">Live Mission Status</p>
|
| 112 |
+
</div>
|
| 113 |
</div>
|
| 114 |
</div>
|
| 115 |
|
| 116 |
+
<section className="p-8 rounded-[2.5rem] bg-slate-900/40 border border-white/5 backdrop-blur-xl">
|
| 117 |
+
<h3 className="text-sm font-black uppercase tracking-[0.2em] text-slate-500 mb-8 font-outfit">Audited Artifacts</h3>
|
| 118 |
+
<div className="space-y-3 max-h-[400px] overflow-y-auto pr-2 custom-scrollbar">
|
| 119 |
+
{changes?.changes?.map((change: any, i: number) => {
|
| 120 |
+
const impact = getFileImpact(change.new_path, mainReport || "");
|
| 121 |
+
return (
|
| 122 |
+
<div key={i} className="group p-4 rounded-2xl bg-white/5 border border-white/5 hover:bg-white/10 transition-all flex items-center justify-between">
|
| 123 |
+
<div className="flex-1 min-w-0">
|
| 124 |
+
<div className="text-xs font-bold text-white truncate">{change.new_path.split('/').pop()}</div>
|
| 125 |
+
<div className="text-[10px] text-slate-600 truncate">{change.new_path}</div>
|
| 126 |
+
</div>
|
| 127 |
+
<div className={`w-2 h-8 rounded-full ml-4 ${
|
| 128 |
+
impact === 'HIGH' ? 'bg-rose-500 shadow-[0_0_8px_rgba(244,63,94,0.4)]' :
|
| 129 |
+
impact === 'MEDIUM' ? 'bg-amber-500' : 'bg-slate-700'
|
| 130 |
+
}`} />
|
| 131 |
+
</div>
|
| 132 |
+
);
|
| 133 |
+
})}
|
| 134 |
</div>
|
| 135 |
</section>
|
| 136 |
</div>
|
| 137 |
|
| 138 |
+
{/* Center Column: Intelligence Dashboard */}
|
| 139 |
+
<div className="lg:col-span-6 space-y-8">
|
| 140 |
+
<section className="p-10 rounded-[3rem] bg-gradient-to-br from-slate-900/80 to-blue-900/20 border border-blue-500/20 shadow-[0_32px_64px_-12px_rgba(0,0,0,0.5)] relative group">
|
| 141 |
+
<div className="absolute top-0 right-0 p-12 opacity-[0.03] text-[12rem] group-hover:rotate-12 transition-transform duration-1000 pointer-events-none font-black italic">INTELLIGENCE</div>
|
| 142 |
+
<div className="relative z-10 font-outfit">
|
| 143 |
+
<h2 className="text-2xl font-black text-white mb-8 flex items-center gap-4">
|
| 144 |
+
<span className="w-14 h-14 bg-blue-600/20 rounded-[1.25rem] flex items-center justify-center text-3xl shadow-lg shadow-blue-500/10">π§ </span>
|
| 145 |
+
MISSION SUMMARY
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
</h2>
|
| 147 |
{sections.summary ? (
|
| 148 |
+
<div className="text-2xl text-slate-300 leading-[1.6] font-medium tracking-tight whitespace-pre-wrap">
|
| 149 |
+
{sections.summary}
|
| 150 |
+
</div>
|
| 151 |
) : (
|
| 152 |
+
<div className="py-12 text-center text-slate-600 italic">Intercepting agent frequencies...</div>
|
| 153 |
)}
|
| 154 |
</div>
|
| 155 |
</section>
|
| 156 |
|
|
|
|
| 157 |
<div className="grid md:grid-cols-2 gap-6">
|
| 158 |
+
{[
|
| 159 |
+
{ id: 'finops', title: 'FinOps Logic', icon: 'πΈ', color: 'emerald', content: sections.finops, label: 'Cost Auditor' },
|
| 160 |
+
{ id: 'devops', title: 'Infrastructure', icon: 'βοΈ', color: 'indigo', content: sections.devops, label: 'Reliability Agent' },
|
| 161 |
+
{ id: 'testing', title: 'Quality SDET', icon: 'π§ͺ', color: 'purple', content: sections.testing, label: 'Automation Bot' },
|
| 162 |
+
{ id: 'code', title: 'Logic Audit', icon: 'π΅οΈ', color: 'blue', content: sections.code, label: 'Security Researcher' }
|
| 163 |
+
].map((agent) => (
|
| 164 |
+
<section key={agent.id} className={`p-8 rounded-[2.5rem] bg-slate-900/30 border border-white/5 hover:border-${agent.color}-500/30 transition-all duration-500 group relative overflow-hidden h-full`}>
|
| 165 |
+
<div className={`absolute -right-8 -bottom-8 w-32 h-32 bg-${agent.color}-500/5 rounded-full blur-3xl group-hover:scale-150 transition-transform duration-700`} />
|
| 166 |
+
<div className="relative z-10 flex flex-col h-full">
|
| 167 |
+
<div className="flex justify-between items-start mb-6">
|
| 168 |
+
<div className={`w-12 h-12 bg-${agent.color}-500/10 rounded-2xl flex items-center justify-center text-2xl shadow-inner`}>{agent.icon}</div>
|
| 169 |
+
<div className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-600 group-hover:text-slate-400 transition-colors">{agent.label}</div>
|
| 170 |
+
</div>
|
| 171 |
+
<h3 className="text-lg font-black text-white mb-4 tracking-tight">{agent.title}</h3>
|
| 172 |
+
<div className="text-sm text-slate-400 leading-relaxed font-medium">
|
| 173 |
+
{agent.content || `Awaiting ${agent.title} deployment signal...`}
|
| 174 |
+
</div>
|
| 175 |
+
</div>
|
| 176 |
+
</section>
|
| 177 |
+
))}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
</div>
|
| 179 |
+
</div>
|
| 180 |
|
| 181 |
+
{/* Right Column: Timeline & Streams */}
|
| 182 |
+
<div className="lg:col-span-3 space-y-8">
|
| 183 |
+
<section className="p-8 rounded-[2.5rem] bg-slate-900/40 border border-white/5 backdrop-blur-xl">
|
| 184 |
+
<h3 className="text-sm font-black uppercase tracking-widest text-slate-500 mb-8">Agent Chronology</h3>
|
| 185 |
+
<div className="space-y-8 relative before:absolute before:left-[19px] before:top-2 before:bottom-2 before:w-px before:bg-white/5">
|
| 186 |
+
<div className="relative flex gap-6">
|
| 187 |
+
<div className="w-10 h-10 rounded-xl bg-orange-500/20 border border-orange-500/30 flex items-center justify-center text-sm z-10">π</div>
|
| 188 |
+
<div>
|
| 189 |
+
<div className="text-xs font-black text-white">MR Deployment</div>
|
| 190 |
+
<div className="text-[10px] text-slate-500 uppercase font-bold">Initial Request</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
</div>
|
| 192 |
+
</div>
|
| 193 |
+
<div className="relative flex gap-6">
|
| 194 |
+
<div className="w-10 h-10 rounded-xl bg-blue-500/20 border border-blue-500/30 flex items-center justify-center text-sm z-10">π‘</div>
|
| 195 |
+
<div>
|
| 196 |
+
<div className="text-xs font-black text-white">Signals Detected</div>
|
| 197 |
+
<div className="text-[10px] text-slate-500 uppercase font-bold">GitLab Hook Active</div>
|
| 198 |
+
</div>
|
| 199 |
+
</div>
|
| 200 |
+
<div className="relative flex gap-6">
|
| 201 |
+
<div className="w-10 h-10 rounded-xl bg-emerald-500/20 border border-emerald-500/30 flex items-center justify-center text-sm z-10">π</div>
|
| 202 |
+
<div>
|
| 203 |
+
<div className="text-xs font-black text-white">Audits Complete</div>
|
| 204 |
+
<div className="text-[10px] text-slate-500 uppercase font-bold">4/4 Agents Reported</div>
|
| 205 |
+
</div>
|
| 206 |
+
</div>
|
| 207 |
+
</div>
|
| 208 |
+
</section>
|
| 209 |
+
|
| 210 |
+
<section className="p-8 rounded-[2.5rem] bg-blue-600/10 border border-blue-500/20 overflow-hidden relative group">
|
| 211 |
+
<div className="absolute -left-12 -bottom-12 w-48 h-48 bg-blue-500/20 rounded-full blur-[80px]" />
|
| 212 |
+
<h3 className="text-[10px] font-black uppercase tracking-[0.3em] text-blue-400 mb-6 relative z-10">Final Verdict</h3>
|
| 213 |
+
<div className="text-2xl font-black text-white mb-4 relative z-10 leading-tight">
|
| 214 |
+
{risk === 'CRITICAL' ? 'Mission Blocked' : 'Permission to Merge'}
|
| 215 |
+
</div>
|
| 216 |
+
<p className="text-xs text-blue-300 font-medium opacity-80 relative z-10">
|
| 217 |
+
{risk === 'CRITICAL'
|
| 218 |
+
? 'Resolve the detected intelligence risks before proceeding with integration.'
|
| 219 |
+
: 'System signals are green. High confidence in current logic state.'}
|
| 220 |
+
</p>
|
| 221 |
+
<div className="mt-8 pt-6 border-t border-white/5 relative z-10 flex justify-between items-center">
|
| 222 |
+
<span className="text-[10px] font-bold text-blue-500 uppercase tracking-widest">Confidence</span>
|
| 223 |
+
<span className="text-lg font-black text-white tracking-widest">{risk === 'CRITICAL' ? '20%' : '98%'}</span>
|
| 224 |
+
</div>
|
| 225 |
+
</section>
|
| 226 |
</div>
|
| 227 |
</div>
|
| 228 |
</div>
|
| 229 |
+
|
| 230 |
+
<style jsx global>{`
|
| 231 |
+
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@900&display=swap');
|
| 232 |
+
.font-outfit { font-family: 'Outfit', sans-serif; }
|
| 233 |
+
.custom-scrollbar::-webkit-scrollbar { width: 4px; }
|
| 234 |
+
.custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
|
| 235 |
+
.custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 10px; }
|
| 236 |
+
`}</style>
|
| 237 |
</main>
|
| 238 |
);
|
| 239 |
}
|
dashboard/src/lib/utils.ts
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
|
|
|
|
|
| 1 |
export const parseAuditReport = (text: string) => {
|
| 2 |
if (!text) return null;
|
| 3 |
-
|
| 4 |
-
// Look for the report starting with the brain emoji or the markdown header
|
| 5 |
const reportRegex = /### π§ Context Brain: Intelligence Report([\s\S]*?)(?=---|$|<!--)/i;
|
| 6 |
const match = text.match(reportRegex);
|
| 7 |
-
|
| 8 |
if (match) return match[1].trim();
|
| 9 |
-
|
| 10 |
-
// Fallback for discussions format
|
| 11 |
if (text.includes("## π§© Context Brain: Full Platform Audit")) {
|
| 12 |
return text.split("## π§© Context Brain: Full Platform Audit")[1].split("---")[0].trim();
|
| 13 |
}
|
| 14 |
-
|
| 15 |
return null;
|
| 16 |
};
|
| 17 |
|
| 18 |
export const splitReportIntoSections = (report: string) => {
|
| 19 |
if (!report) return {};
|
| 20 |
-
|
| 21 |
const sections: Record<string, string> = {
|
| 22 |
summary: "",
|
| 23 |
finops: "",
|
|
@@ -25,31 +20,22 @@ export const splitReportIntoSections = (report: string) => {
|
|
| 25 |
testing: "",
|
| 26 |
code: ""
|
| 27 |
};
|
| 28 |
-
|
| 29 |
-
// 1. Try to extract from bullet points if present (common in summaries)
|
| 30 |
const finopsBullet = report.match(/- \*\*FinOps\*\*:([\s\S]*?)(?=- \*\*|$)/i);
|
| 31 |
const devopsBullet = report.match(/- \*\*DevOps\*\*:([\s\S]*?)(?=- \*\*|$)/i);
|
| 32 |
const testingBullet = report.match(/- \*\*Tests\*\*:([\s\S]*?)(?=- \*\*|$)/i);
|
| 33 |
-
|
| 34 |
if (finopsBullet) sections.finops = finopsBullet[1].trim();
|
| 35 |
if (devopsBullet) sections.devops = devopsBullet[1].trim();
|
| 36 |
if (testingBullet) sections.testing = testingBullet[1].trim();
|
| 37 |
-
|
| 38 |
-
// 2. Try to extract from Headers (the more detailed report format)
|
| 39 |
const finopsHeader = report.match(/#### (?:πΈ|π°) Cloud FinOps & Cost Logic([\s\S]*?)(?=####|---|$)/i);
|
| 40 |
const devopsHeader = report.match(/#### (?:βοΈ|π) DevOps & SRE Review([\s\S]*?)(?=####|---|$)/i);
|
| 41 |
const testingHeader = report.match(/#### (?:π§ͺ|β
) Quality SDET Validation([\s\S]*?)(?=####|---|$)/i);
|
| 42 |
const codeHeader = report.match(/#### (?:π΅οΈ|π) Code Analyst Audit([\s\S]*?)(?=####|---|$)/i);
|
| 43 |
-
|
| 44 |
if (finopsHeader) sections.finops = finopsHeader[1].trim();
|
| 45 |
if (devopsHeader) sections.devops = devopsHeader[1].trim();
|
| 46 |
if (testingHeader) sections.testing = testingHeader[1].trim();
|
| 47 |
if (codeHeader) sections.code = codeHeader[1].trim();
|
| 48 |
-
|
| 49 |
-
// 3. Extract the Mission Summary (Analysis intro before bullet points or headers)
|
| 50 |
const summaryPart = report.split(/####|- \*\*/)[0];
|
| 51 |
sections.summary = summaryPart.replace(/\*\*Analysis\*\*:/i, '').trim();
|
| 52 |
-
|
| 53 |
return sections;
|
| 54 |
};
|
| 55 |
|
|
@@ -58,3 +44,10 @@ export const getRiskStatus = (labels: string[]) => {
|
|
| 58 |
if (labels.some(l => l.includes("risk-medium") || l.includes("warning"))) return "CAUTION";
|
| 59 |
return "STABLE";
|
| 60 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { fetchGitLab } from "./gitlab";
|
| 2 |
+
|
| 3 |
export const parseAuditReport = (text: string) => {
|
| 4 |
if (!text) return null;
|
|
|
|
|
|
|
| 5 |
const reportRegex = /### π§ Context Brain: Intelligence Report([\s\S]*?)(?=---|$|<!--)/i;
|
| 6 |
const match = text.match(reportRegex);
|
|
|
|
| 7 |
if (match) return match[1].trim();
|
|
|
|
|
|
|
| 8 |
if (text.includes("## π§© Context Brain: Full Platform Audit")) {
|
| 9 |
return text.split("## π§© Context Brain: Full Platform Audit")[1].split("---")[0].trim();
|
| 10 |
}
|
|
|
|
| 11 |
return null;
|
| 12 |
};
|
| 13 |
|
| 14 |
export const splitReportIntoSections = (report: string) => {
|
| 15 |
if (!report) return {};
|
|
|
|
| 16 |
const sections: Record<string, string> = {
|
| 17 |
summary: "",
|
| 18 |
finops: "",
|
|
|
|
| 20 |
testing: "",
|
| 21 |
code: ""
|
| 22 |
};
|
|
|
|
|
|
|
| 23 |
const finopsBullet = report.match(/- \*\*FinOps\*\*:([\s\S]*?)(?=- \*\*|$)/i);
|
| 24 |
const devopsBullet = report.match(/- \*\*DevOps\*\*:([\s\S]*?)(?=- \*\*|$)/i);
|
| 25 |
const testingBullet = report.match(/- \*\*Tests\*\*:([\s\S]*?)(?=- \*\*|$)/i);
|
|
|
|
| 26 |
if (finopsBullet) sections.finops = finopsBullet[1].trim();
|
| 27 |
if (devopsBullet) sections.devops = devopsBullet[1].trim();
|
| 28 |
if (testingBullet) sections.testing = testingBullet[1].trim();
|
|
|
|
|
|
|
| 29 |
const finopsHeader = report.match(/#### (?:πΈ|π°) Cloud FinOps & Cost Logic([\s\S]*?)(?=####|---|$)/i);
|
| 30 |
const devopsHeader = report.match(/#### (?:βοΈ|π) DevOps & SRE Review([\s\S]*?)(?=####|---|$)/i);
|
| 31 |
const testingHeader = report.match(/#### (?:π§ͺ|β
) Quality SDET Validation([\s\S]*?)(?=####|---|$)/i);
|
| 32 |
const codeHeader = report.match(/#### (?:π΅οΈ|π) Code Analyst Audit([\s\S]*?)(?=####|---|$)/i);
|
|
|
|
| 33 |
if (finopsHeader) sections.finops = finopsHeader[1].trim();
|
| 34 |
if (devopsHeader) sections.devops = devopsHeader[1].trim();
|
| 35 |
if (testingHeader) sections.testing = testingHeader[1].trim();
|
| 36 |
if (codeHeader) sections.code = codeHeader[1].trim();
|
|
|
|
|
|
|
| 37 |
const summaryPart = report.split(/####|- \*\*/)[0];
|
| 38 |
sections.summary = summaryPart.replace(/\*\*Analysis\*\*:/i, '').trim();
|
|
|
|
| 39 |
return sections;
|
| 40 |
};
|
| 41 |
|
|
|
|
| 44 |
if (labels.some(l => l.includes("risk-medium") || l.includes("warning"))) return "CAUTION";
|
| 45 |
return "STABLE";
|
| 46 |
};
|
| 47 |
+
|
| 48 |
+
export const getFileImpact = (filename: string, report: string) => {
|
| 49 |
+
const lowercase = filename.toLowerCase();
|
| 50 |
+
if (lowercase.includes("secret") || lowercase.includes(".env") || report.toLowerCase().includes(lowercase + " introduces security")) return "HIGH";
|
| 51 |
+
if (lowercase.includes("config") || lowercase.includes("yaml") || lowercase.includes("json")) return "MEDIUM";
|
| 52 |
+
return "LOW";
|
| 53 |
+
};
|
src/context_brain_mcp.py
CHANGED
|
@@ -231,12 +231,9 @@ if __name__ == "__main__":
|
|
| 231 |
transport = os.getenv("MCP_TRANSPORT", "stdio").lower()
|
| 232 |
|
| 233 |
if transport == "sse":
|
| 234 |
-
from starlette.applications import Starlette
|
| 235 |
-
from starlette.routing import Route, Mount
|
| 236 |
from mcp.server.sse import SseServerTransport
|
| 237 |
import uvicorn
|
| 238 |
-
|
| 239 |
-
|
| 240 |
port = int(os.getenv("PORT", "7860"))
|
| 241 |
host = "0.0.0.0"
|
| 242 |
|
|
@@ -245,23 +242,29 @@ if __name__ == "__main__":
|
|
| 245 |
|
| 246 |
sse = SseServerTransport("/messages")
|
| 247 |
|
| 248 |
-
async def
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
read_stream,
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
else:
|
| 266 |
print("π ContextBrain MR-Guardian MCP Server starting on stdio...", file=sys.stderr)
|
| 267 |
mcp.run(transport="stdio")
|
|
|
|
| 231 |
transport = os.getenv("MCP_TRANSPORT", "stdio").lower()
|
| 232 |
|
| 233 |
if transport == "sse":
|
|
|
|
|
|
|
| 234 |
from mcp.server.sse import SseServerTransport
|
| 235 |
import uvicorn
|
| 236 |
+
|
|
|
|
| 237 |
port = int(os.getenv("PORT", "7860"))
|
| 238 |
host = "0.0.0.0"
|
| 239 |
|
|
|
|
| 242 |
|
| 243 |
sse = SseServerTransport("/messages")
|
| 244 |
|
| 245 |
+
async def app(scope, receive, send):
|
| 246 |
+
if scope["type"] == "http":
|
| 247 |
+
if scope["path"] == "/sse":
|
| 248 |
+
async with sse.connect_sse(scope, receive, send) as (read_stream, write_stream):
|
| 249 |
+
await mcp.server.run(
|
| 250 |
+
read_stream,
|
| 251 |
+
write_stream,
|
| 252 |
+
mcp.server.create_initialization_options()
|
| 253 |
+
)
|
| 254 |
+
elif scope["path"].startswith("/messages"):
|
| 255 |
+
await sse.handle_post_message(scope, receive, send)
|
| 256 |
+
else:
|
| 257 |
+
await send({
|
| 258 |
+
"type": "http.response.start",
|
| 259 |
+
"status": 404,
|
| 260 |
+
"headers": [(b"content-type", b"text/plain")],
|
| 261 |
+
})
|
| 262 |
+
await send({
|
| 263 |
+
"type": "http.response.body",
|
| 264 |
+
"body": b"Not Found",
|
| 265 |
+
})
|
| 266 |
+
|
| 267 |
+
uvicorn.run(app, host=host, port=port)
|
| 268 |
else:
|
| 269 |
print("π ContextBrain MR-Guardian MCP Server starting on stdio...", file=sys.stderr)
|
| 270 |
mcp.run(transport="stdio")
|