chore: import upstream snapshot with attribution
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:02:19 +08:00
commit 23f7624596
5116 changed files with 1656296 additions and 0 deletions
@@ -0,0 +1,740 @@
/**
* GNN Bridge for Code Graph Analysis
*
* Provides graph neural network operations for code structure analysis
* using ruvector-gnn-wasm for high-performance graph algorithms.
*
* Features:
* - Code graph construction
* - Node embedding computation
* - Impact prediction using graph propagation
* - Community detection for module discovery
* - Pattern matching in code graphs
*
* Based on ADR-035: Advanced Code Intelligence Plugin
*
* @module v3/plugins/code-intelligence/bridges/gnn-bridge
*/
import type {
IGNNBridge,
DependencyGraph,
DependencyNode,
DependencyEdge,
} from '../types.js';
/**
* WASM module interface for GNN operations
*/
interface GNNWasmModule {
/** Build graph from adjacency list */
gnn_build_graph(
nodeCount: number,
edges: Uint32Array,
edgeCount: number
): number;
/** Compute GNN embeddings */
gnn_compute_embeddings(
graphPtr: number,
features: Float32Array,
featureDim: number,
outputDim: number,
layers: number
): Float32Array;
/** Propagate labels for impact analysis */
gnn_propagate(
graphPtr: number,
initialLabels: Float32Array,
iterations: number,
dampingFactor: number
): Float32Array;
/** Community detection using Louvain algorithm */
gnn_detect_communities(
graphPtr: number,
weights: Float32Array
): Uint32Array;
/** Subgraph matching */
gnn_match_subgraph(
graphPtr: number,
patternPtr: number,
threshold: number
): Float32Array;
/** Free graph */
gnn_free(graphPtr: number): void;
/** Memory management */
alloc(size: number): number;
dealloc(ptr: number, size: number): void;
memory: WebAssembly.Memory;
}
/**
* GNN Bridge Implementation
*/
export class GNNBridge implements IGNNBridge {
// WASM module for future performance optimization (currently uses JS fallback)
private wasmModule: GNNWasmModule | null = null;
private initialized = false;
private readonly embeddingDim: number;
constructor(embeddingDim = 128) {
this.embeddingDim = embeddingDim;
}
/**
* Initialize the WASM module
*/
async initialize(): Promise<void> {
if (this.initialized) return;
try {
// Dynamic import of WASM module
this.wasmModule = await this.loadWasmModule();
this.initialized = true;
} catch {
// Fallback to pure JS implementation
console.warn('WASM GNN module not available, using JS fallback');
this.wasmModule = null;
this.initialized = true;
}
}
/**
* Check if initialized
*/
isInitialized(): boolean {
return this.initialized;
}
/**
* Build code graph from files
*/
async buildCodeGraph(
files: string[],
_includeCallGraph: boolean
): Promise<DependencyGraph> {
if (!this.initialized) {
await this.initialize();
}
const nodes: DependencyNode[] = [];
const edges: DependencyEdge[] = [];
const nodeMap = new Map<string, number>();
// Create nodes for each file
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file) continue;
nodeMap.set(file, i);
nodes.push({
id: file,
label: file.split('/').pop() ?? file,
type: 'file',
language: this.detectLanguage(file),
});
}
// Build edges from imports (simplified - in production would parse AST)
for (const file of files) {
const imports = await this.extractImports(file, files);
for (const imp of imports) {
if (nodeMap.has(imp)) {
edges.push({
from: file,
to: imp,
type: 'import',
weight: 1,
});
}
}
}
// Calculate metadata
const avgDegree = edges.length > 0 ? (edges.length * 2) / nodes.length : 0;
const maxDepth = this.calculateMaxDepth(nodes, edges);
return {
nodes,
edges,
metadata: {
totalNodes: nodes.length,
totalEdges: edges.length,
avgDegree,
maxDepth,
},
};
}
/**
* Compute node embeddings using GNN
*/
async computeNodeEmbeddings(
graph: DependencyGraph,
embeddingDim: number
): Promise<Map<string, Float32Array>> {
if (!this.initialized) {
await this.initialize();
}
const embeddings = new Map<string, Float32Array>();
const nodeCount = graph.nodes.length;
if (nodeCount === 0) {
return embeddings;
}
// Create node lookup
const nodeMap = new Map<string, number>();
graph.nodes.forEach((node, index) => {
nodeMap.set(node.id, index);
});
// Build adjacency list for WASM
const edgeArray = new Uint32Array(graph.edges.length * 2);
for (let i = 0; i < graph.edges.length; i++) {
const edge = graph.edges[i];
if (!edge) continue;
const fromIdx = nodeMap.get(edge.from) ?? 0;
const toIdx = nodeMap.get(edge.to) ?? 0;
edgeArray[i * 2] = fromIdx;
edgeArray[i * 2 + 1] = toIdx;
}
// Initialize features (simple degree-based features)
const featureDim = 16;
const features = new Float32Array(nodeCount * featureDim);
for (let i = 0; i < nodeCount; i++) {
const node = graph.nodes[i];
if (!node) continue;
// In-degree
features[i * featureDim] = graph.edges.filter(e => e.to === node.id).length;
// Out-degree
features[i * featureDim + 1] = graph.edges.filter(e => e.from === node.id).length;
// Node type encoding
features[i * featureDim + 2] = this.encodeNodeType(node.type);
// Language encoding
features[i * featureDim + 3] = node.language ? this.encodeLanguage(node.language) : 0;
}
// Compute embeddings (using JS fallback)
const embeddingMatrix = this.computeEmbeddingsJS(
graph,
features,
featureDim,
embeddingDim
);
// Extract embeddings per node
for (let i = 0; i < nodeCount; i++) {
const node = graph.nodes[i];
if (!node) continue;
const nodeEmbedding = embeddingMatrix.slice(
i * embeddingDim,
(i + 1) * embeddingDim
);
embeddings.set(node.id, nodeEmbedding);
}
return embeddings;
}
/**
* Predict impact of changes using GNN
*/
async predictImpact(
graph: DependencyGraph,
changedNodes: string[],
depth: number
): Promise<Map<string, number>> {
if (!this.initialized) {
await this.initialize();
}
const impact = new Map<string, number>();
const nodeCount = graph.nodes.length;
if (nodeCount === 0) {
return impact;
}
// Create node lookup
const nodeMap = new Map<string, number>();
graph.nodes.forEach((node, index) => {
nodeMap.set(node.id, index);
});
// Build adjacency list (reverse direction for impact propagation)
const adj: number[][] = Array.from({ length: nodeCount }, () => []);
for (const edge of graph.edges) {
const fromIdx = nodeMap.get(edge.from);
const toIdx = nodeMap.get(edge.to);
if (fromIdx !== undefined && toIdx !== undefined) {
// Reverse: impact flows from dependency to dependent
adj[toIdx]?.push(fromIdx);
}
}
// Initialize impact scores
const scores = new Float32Array(nodeCount);
for (const nodeId of changedNodes) {
const idx = nodeMap.get(nodeId);
if (idx !== undefined) {
scores[idx] = 1.0;
}
}
// Propagate impact using BFS with decay
const visited = new Set<number>();
const queue: Array<{ node: number; depth: number; score: number }> = [];
// Initialize queue with changed nodes
for (const nodeId of changedNodes) {
const idx = nodeMap.get(nodeId);
if (idx !== undefined) {
queue.push({ node: idx, depth: 0, score: 1.0 });
visited.add(idx);
}
}
// BFS propagation
while (queue.length > 0) {
const current = queue.shift();
if (!current || current.depth >= depth) continue;
for (const neighbor of adj[current.node] ?? []) {
const newScore = current.score * 0.7; // Decay factor
const neighborScore = scores[neighbor];
if (neighborScore !== undefined && newScore > neighborScore) {
scores[neighbor] = newScore;
}
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push({
node: neighbor,
depth: current.depth + 1,
score: newScore,
});
}
}
}
// Convert to map
for (let i = 0; i < nodeCount; i++) {
const node = graph.nodes[i];
const score = scores[i];
if (node && score !== undefined && score > 0) {
impact.set(node.id, score);
}
}
return impact;
}
/**
* Detect communities in code graph
*/
async detectCommunities(
graph: DependencyGraph
): Promise<Map<string, number>> {
if (!this.initialized) {
await this.initialize();
}
const communities = new Map<string, number>();
const nodeCount = graph.nodes.length;
if (nodeCount === 0) {
return communities;
}
// Create node lookup
const nodeMap = new Map<string, number>();
graph.nodes.forEach((node, index) => {
nodeMap.set(node.id, index);
});
// Build adjacency list (undirected)
const adj: Set<number>[] = Array.from({ length: nodeCount }, () => new Set());
for (const edge of graph.edges) {
const fromIdx = nodeMap.get(edge.from);
const toIdx = nodeMap.get(edge.to);
if (fromIdx !== undefined && toIdx !== undefined) {
adj[fromIdx]?.add(toIdx);
adj[toIdx]?.add(fromIdx);
}
}
// Simple community detection using connected components
// In production, would use Louvain or similar
const community = new Array(nodeCount).fill(-1);
let communityId = 0;
for (let i = 0; i < nodeCount; i++) {
if (community[i] !== -1) continue;
// BFS to find connected component
const queue = [i];
community[i] = communityId;
while (queue.length > 0) {
const current = queue.shift()!;
for (const neighbor of adj[current] ?? []) {
if (community[neighbor] === -1) {
community[neighbor] = communityId;
queue.push(neighbor);
}
}
}
communityId++;
}
// Convert to map
for (let i = 0; i < nodeCount; i++) {
const node = graph.nodes[i];
const comm = community[i];
if (node && comm !== undefined) {
communities.set(node.id, comm);
}
}
return communities;
}
/**
* Find similar code patterns
*/
async findSimilarPatterns(
graph: DependencyGraph,
patternGraph: DependencyGraph,
threshold: number
): Promise<Array<{ matchId: string; score: number }>> {
if (!this.initialized) {
await this.initialize();
}
const matches: Array<{ matchId: string; score: number }> = [];
// Compute embeddings for both graphs
const graphEmbeddings = await this.computeNodeEmbeddings(graph, this.embeddingDim);
const patternEmbeddings = await this.computeNodeEmbeddings(patternGraph, this.embeddingDim);
// Average pattern embedding
const patternAvg = new Float32Array(this.embeddingDim);
let patternCount = 0;
for (const [, embedding] of patternEmbeddings) {
for (let i = 0; i < this.embeddingDim; i++) {
patternAvg[i] = (patternAvg[i] ?? 0) + (embedding[i] ?? 0);
}
patternCount++;
}
if (patternCount > 0) {
for (let i = 0; i < this.embeddingDim; i++) {
patternAvg[i] = (patternAvg[i] ?? 0) / patternCount;
}
}
// Find similar nodes in main graph
for (const [nodeId, embedding] of graphEmbeddings) {
const similarity = this.cosineSimilarity(embedding, patternAvg);
if (similarity >= threshold) {
matches.push({ matchId: nodeId, score: similarity });
}
}
// Sort by score descending
matches.sort((a, b) => b.score - a.score);
return matches;
}
// ============================================================================
// Private Helper Methods
// ============================================================================
/**
* Load WASM module dynamically
*/
private async loadWasmModule(): Promise<GNNWasmModule> {
throw new Error('WASM module loading not implemented');
}
/**
* Detect language from file extension
*/
private detectLanguage(filePath: string): DependencyNode['language'] {
const ext = filePath.split('.').pop()?.toLowerCase();
const langMap: Record<string, DependencyNode['language']> = {
ts: 'typescript',
tsx: 'typescript',
js: 'javascript',
jsx: 'javascript',
py: 'python',
java: 'java',
go: 'go',
rs: 'rust',
cpp: 'cpp',
c: 'cpp',
cs: 'csharp',
rb: 'ruby',
php: 'php',
swift: 'swift',
kt: 'kotlin',
scala: 'scala',
};
return ext ? langMap[ext] : undefined;
}
/**
* Extract imports from file. Returns the subset of `allFiles` that this
* file imports via relative specifiers — used to build edges in the
* dependency graph. #1554/#1553: previously returned `[]` which produced
* graphs with zero edges and broke architecture-analyze, refactor-impact,
* and split-suggest. Regex-based (no AST parser dep) — handles `import …
* from`, `export … from`, and `require('…')` for relative paths only.
*/
private async extractImports(file: string, allFiles: string[]): Promise<string[]> {
const fs = await import('node:fs');
const path = await import('node:path');
let content: string;
try {
content = fs.readFileSync(file, 'utf-8');
} catch {
return [];
}
const allFilesSet = new Set(allFiles.map((f) => path.resolve(f)));
const exts = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
const baseDir = path.dirname(path.resolve(file));
const out = new Set<string>();
const importRx = /^\s*(?:import\s+[^'"]+from\s+|import\s+|export\s+\*?\s*from\s+|export\s+\{[^}]*\}\s+from\s+)['"]([^'"]+)['"]|^\s*(?:const|let|var)\s+[^=]+=\s*require\(\s*['"]([^'"]+)['"]\s*\)/gm;
let m: RegExpExecArray | null;
while ((m = importRx.exec(content)) !== null) {
const spec = m[1] ?? m[2];
if (!spec) continue;
// Only resolve relative/absolute paths — node_modules imports aren't
// graph nodes here.
if (!spec.startsWith('./') && !spec.startsWith('../') && !spec.startsWith('/')) continue;
const candidates: string[] = [];
const base = spec.startsWith('/') ? spec : path.resolve(baseDir, spec);
// Try the bare path, each extension, and `index.<ext>` under a directory.
candidates.push(base);
for (const e of exts) {
candidates.push(base + e);
candidates.push(path.join(base, `index${e}`));
}
// TS module-specifier convention: imports use `.js` even when the
// actual source is `.ts`/`.tsx`. Strip a trailing `.js`/`.cjs`/`.mjs`
// and re-try with TS extensions so source-level edges resolve.
const jsLikeMatch = base.match(/^(.+)\.(js|cjs|mjs)$/);
if (jsLikeMatch && jsLikeMatch[1]) {
const stripped = jsLikeMatch[1];
for (const e of ['.ts', '.tsx', '.js', '.jsx']) {
candidates.push(stripped + e);
candidates.push(path.join(stripped, `index${e}`));
}
}
for (const c of candidates) {
const resolved = path.resolve(c);
if (allFilesSet.has(resolved)) {
out.add(resolved);
break;
}
}
}
return Array.from(out);
}
/**
* Calculate max depth of dependency graph
*/
private calculateMaxDepth(nodes: DependencyNode[], edges: DependencyEdge[]): number {
if (nodes.length === 0) return 0;
// Build adjacency list
const adj = new Map<string, string[]>();
for (const node of nodes) {
adj.set(node.id, []);
}
for (const edge of edges) {
adj.get(edge.from)?.push(edge.to);
}
// Find nodes with no incoming edges (roots)
const hasIncoming = new Set<string>();
for (const edge of edges) {
hasIncoming.add(edge.to);
}
const roots = nodes.filter(n => !hasIncoming.has(n.id)).map(n => n.id);
if (roots.length === 0) {
// Cycle - use first node
roots.push(nodes[0]!.id);
}
// BFS to find max depth
let maxDepth = 0;
const visited = new Set<string>();
const queue: Array<{ node: string; depth: number }> = [];
for (const root of roots) {
queue.push({ node: root, depth: 0 });
visited.add(root);
}
while (queue.length > 0) {
const current = queue.shift()!;
maxDepth = Math.max(maxDepth, current.depth);
for (const neighbor of adj.get(current.node) ?? []) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push({ node: neighbor, depth: current.depth + 1 });
}
}
}
return maxDepth;
}
/**
* Encode node type as number
*/
private encodeNodeType(type: DependencyNode['type']): number {
const types: Record<DependencyNode['type'], number> = {
file: 0.1,
module: 0.2,
package: 0.3,
class: 0.4,
function: 0.5,
};
return types[type];
}
/**
* Encode language as number
*/
private encodeLanguage(language: string): number {
const languages: Record<string, number> = {
typescript: 0.1,
javascript: 0.15,
python: 0.2,
java: 0.25,
go: 0.3,
rust: 0.35,
cpp: 0.4,
csharp: 0.45,
ruby: 0.5,
php: 0.55,
};
return languages[language] ?? 0;
}
/**
* Compute embeddings using JS (fallback)
*/
private computeEmbeddingsJS(
graph: DependencyGraph,
features: Float32Array,
featureDim: number,
outputDim: number
): Float32Array {
const nodeCount = graph.nodes.length;
const embeddings = new Float32Array(nodeCount * outputDim);
// Create adjacency matrix
const nodeMap = new Map<string, number>();
graph.nodes.forEach((node, index) => {
nodeMap.set(node.id, index);
});
// Simple message passing (1 layer)
for (let i = 0; i < nodeCount; i++) {
const node = graph.nodes[i];
if (!node) continue;
// Aggregate neighbor features
const neighbors = graph.edges
.filter(e => e.to === node.id)
.map(e => nodeMap.get(e.from))
.filter((idx): idx is number => idx !== undefined);
// Initialize with own features (projected to output dim)
for (let j = 0; j < outputDim; j++) {
const featureIdx = j % featureDim;
embeddings[i * outputDim + j] = features[i * featureDim + featureIdx] ?? 0;
}
// Add neighbor contributions
if (neighbors.length > 0) {
for (const neighborIdx of neighbors) {
for (let j = 0; j < outputDim; j++) {
const featureIdx = j % featureDim;
const contribution = (features[neighborIdx * featureDim + featureIdx] ?? 0) / neighbors.length;
const embIdx = i * outputDim + j;
embeddings[embIdx] = (embeddings[embIdx] ?? 0) + contribution * 0.5;
}
}
}
}
// Normalize embeddings
for (let i = 0; i < nodeCount; i++) {
let norm = 0;
for (let j = 0; j < outputDim; j++) {
const val = embeddings[i * outputDim + j] ?? 0;
norm += val * val;
}
norm = Math.sqrt(norm);
if (norm > 0) {
for (let j = 0; j < outputDim; j++) {
embeddings[i * outputDim + j] = (embeddings[i * outputDim + j] ?? 0) / norm;
}
}
}
return embeddings;
}
/**
* Compute cosine similarity
*/
private cosineSimilarity(a: Float32Array, b: Float32Array): number {
let dot = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < a.length; i++) {
const aVal = a[i] ?? 0;
const bVal = b[i] ?? 0;
dot += aVal * bVal;
normA += aVal * aVal;
normB += bVal * bVal;
}
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
return denominator > 0 ? dot / denominator : 0;
}
}
/**
* Create and export default bridge instance
*/
export function createGNNBridge(embeddingDim = 128): IGNNBridge {
return new GNNBridge(embeddingDim);
}
export default GNNBridge;
@@ -0,0 +1,15 @@
/**
* Code Intelligence Plugin - Bridges Barrel Export
*
* @module @claude-flow/plugin-code-intelligence/bridges
*/
export {
GNNBridge,
createGNNBridge,
} from './gnn-bridge.js';
export {
MinCutBridge,
createMinCutBridge,
} from './mincut-bridge.js';
@@ -0,0 +1,652 @@
/**
* MinCut Bridge for Module Splitting
*
* Provides graph min-cut operations for optimal module boundary detection
* using ruvector-mincut-wasm for high-performance graph partitioning.
*
* Features:
* - Optimal module boundary detection
* - Multi-way graph partitioning
* - Constraint-aware splitting
* - Cut weight optimization
*
* Based on ADR-035: Advanced Code Intelligence Plugin
*
* @module v3/plugins/code-intelligence/bridges/mincut-bridge
*/
import type {
IMinCutBridge,
DependencyGraph,
SplitConstraints,
} from '../types.js';
/**
* WASM module interface for MinCut operations
*/
interface MinCutWasmModule {
/** Build flow network from graph */
mincut_build_network(
nodeCount: number,
edges: Uint32Array,
capacities: Float32Array,
edgeCount: number
): number;
/** Find min s-t cut using Ford-Fulkerson */
mincut_ford_fulkerson(
networkPtr: number,
source: number,
sink: number
): Float32Array;
/** Multi-way cut using recursive bisection */
mincut_multiway(
networkPtr: number,
terminals: Uint32Array,
numTerminals: number
): Uint32Array;
/** Spectral partitioning */
mincut_spectral_partition(
networkPtr: number,
numPartitions: number,
weights: Float32Array
): Uint32Array;
/** Free network */
mincut_free(networkPtr: number): void;
/** Memory management */
alloc(size: number): number;
dealloc(ptr: number, size: number): void;
memory: WebAssembly.Memory;
}
/**
* MinCut Bridge Implementation
*/
export class MinCutBridge implements IMinCutBridge {
// WASM module for future performance optimization (currently uses JS fallback)
private wasmModule: MinCutWasmModule | null = null;
private initialized = false;
/**
* Initialize the WASM module
*/
async initialize(): Promise<void> {
if (this.initialized) return;
try {
// Dynamic import of WASM module
this.wasmModule = await this.loadWasmModule();
this.initialized = true;
} catch {
// Fallback to pure JS implementation
console.warn('WASM MinCut module not available, using JS fallback');
this.wasmModule = null;
this.initialized = true;
}
}
/**
* Check if initialized
*/
isInitialized(): boolean {
return this.initialized;
}
/**
* Find optimal module boundaries using MinCut
*/
async findOptimalCuts(
graph: DependencyGraph,
numModules: number,
constraints: SplitConstraints
): Promise<Map<string, number>> {
if (!this.initialized) {
await this.initialize();
}
const partition = new Map<string, number>();
const nodeCount = graph.nodes.length;
if (nodeCount === 0 || numModules < 2) {
// All nodes in single partition
for (const node of graph.nodes) {
partition.set(node.id, 0);
}
return partition;
}
// Create node lookup
const nodeMap = new Map<string, number>();
const indexMap = new Map<number, string>();
graph.nodes.forEach((node, index) => {
nodeMap.set(node.id, index);
indexMap.set(index, node.id);
});
// Apply constraints for preserved boundaries
const fixed = new Map<number, number>();
if (constraints.preserveBoundaries) {
for (let i = 0; i < constraints.preserveBoundaries.length && i < numModules; i++) {
const boundary = constraints.preserveBoundaries[i];
if (boundary) {
const nodeIdx = nodeMap.get(boundary);
if (nodeIdx !== undefined) {
fixed.set(nodeIdx, i);
}
}
}
}
// Build adjacency matrix with weights
const weights: number[][] = Array.from({ length: nodeCount }, () =>
Array(nodeCount).fill(0)
);
for (const edge of graph.edges) {
const fromIdx = nodeMap.get(edge.from);
const toIdx = nodeMap.get(edge.to);
if (fromIdx !== undefined && toIdx !== undefined) {
weights[fromIdx]![toIdx] = edge.weight;
weights[toIdx]![fromIdx] = edge.weight; // Symmetric for partitioning
}
}
// Use spectral partitioning (JS fallback)
const partitionArray = this.spectralPartition(
weights,
numModules,
fixed,
constraints
);
// Convert to map
for (let i = 0; i < nodeCount; i++) {
const nodeId = indexMap.get(i);
const part = partitionArray[i];
if (nodeId && part !== undefined) {
partition.set(nodeId, part);
}
}
// Apply keepTogether constraints
if (constraints.keepTogether) {
for (const group of constraints.keepTogether) {
if (group.length > 0) {
const firstIdx = nodeMap.get(group[0] ?? '');
const firstPart = firstIdx !== undefined ? partitionArray[firstIdx] : undefined;
if (firstPart !== undefined) {
for (const nodeId of group) {
partition.set(nodeId, firstPart);
}
}
}
}
}
return partition;
}
/**
* Calculate cut weight for a given partition
*/
async calculateCutWeight(
graph: DependencyGraph,
partition: Map<string, number>
): Promise<number> {
if (!this.initialized) {
await this.initialize();
}
let cutWeight = 0;
for (const edge of graph.edges) {
const fromPart = partition.get(edge.from);
const toPart = partition.get(edge.to);
if (fromPart !== undefined && toPart !== undefined && fromPart !== toPart) {
cutWeight += edge.weight;
}
}
return cutWeight;
}
/**
* Find minimum s-t cut
*/
async minSTCut(
graph: DependencyGraph,
source: string,
sink: string
): Promise<{
cutValue: number;
cutEdges: Array<{ from: string; to: string }>;
sourceSet: string[];
sinkSet: string[];
}> {
if (!this.initialized) {
await this.initialize();
}
const nodeCount = graph.nodes.length;
// Create node lookup
const nodeMap = new Map<string, number>();
const indexMap = new Map<number, string>();
graph.nodes.forEach((node, index) => {
nodeMap.set(node.id, index);
indexMap.set(index, node.id);
});
const sourceIdx = nodeMap.get(source);
const sinkIdx = nodeMap.get(sink);
if (sourceIdx === undefined || sinkIdx === undefined) {
return {
cutValue: 0,
cutEdges: [],
sourceSet: [],
sinkSet: graph.nodes.map(n => n.id),
};
}
// Build capacity matrix
const capacity: number[][] = Array.from({ length: nodeCount }, () =>
Array(nodeCount).fill(0)
);
for (const edge of graph.edges) {
const fromIdx = nodeMap.get(edge.from);
const toIdx = nodeMap.get(edge.to);
if (fromIdx !== undefined && toIdx !== undefined) {
capacity[fromIdx]![toIdx] = edge.weight;
}
}
// Ford-Fulkerson with BFS (Edmonds-Karp)
const { maxFlow, residual } = this.edmondsKarp(capacity, sourceIdx, sinkIdx);
// Find source set using BFS on residual graph
const sourceSet = new Set<number>();
const queue = [sourceIdx];
sourceSet.add(sourceIdx);
while (queue.length > 0) {
const current = queue.shift()!;
for (let next = 0; next < nodeCount; next++) {
if (!sourceSet.has(next) && (residual[current]?.[next] ?? 0) > 0) {
sourceSet.add(next);
queue.push(next);
}
}
}
// Find cut edges
const cutEdges: Array<{ from: string; to: string }> = [];
for (const edge of graph.edges) {
const fromIdx = nodeMap.get(edge.from);
const toIdx = nodeMap.get(edge.to);
if (fromIdx !== undefined && toIdx !== undefined) {
if (sourceSet.has(fromIdx) && !sourceSet.has(toIdx)) {
cutEdges.push({ from: edge.from, to: edge.to });
}
}
}
// Convert sets to arrays
const sourceSetNodes: string[] = [];
const sinkSetNodes: string[] = [];
for (let i = 0; i < nodeCount; i++) {
const nodeId = indexMap.get(i);
if (nodeId) {
if (sourceSet.has(i)) {
sourceSetNodes.push(nodeId);
} else {
sinkSetNodes.push(nodeId);
}
}
}
return {
cutValue: maxFlow,
cutEdges,
sourceSet: sourceSetNodes,
sinkSet: sinkSetNodes,
};
}
/**
* Multi-way cut for module splitting
*/
async multiWayCut(
graph: DependencyGraph,
terminals: string[],
_weights: Map<string, number>
): Promise<{
cutValue: number;
partitions: Map<string, number>;
}> {
if (!this.initialized) {
await this.initialize();
}
const numTerminals = terminals.length;
if (numTerminals < 2) {
const partitions = new Map<string, number>();
for (const node of graph.nodes) {
partitions.set(node.id, 0);
}
return { cutValue: 0, partitions };
}
// Create node lookup
const nodeMap = new Map<string, number>();
graph.nodes.forEach((node, index) => {
nodeMap.set(node.id, index);
});
// Get terminal indices
const terminalIndices = terminals
.map(t => nodeMap.get(t))
.filter((idx): idx is number => idx !== undefined);
if (terminalIndices.length < 2) {
const partitions = new Map<string, number>();
for (const node of graph.nodes) {
partitions.set(node.id, 0);
}
return { cutValue: 0, partitions };
}
// Use isolating cuts algorithm
// Assign each node to the nearest terminal
const partitions = new Map<string, number>();
const distances = this.computeDistances(graph, terminalIndices, nodeMap);
for (const node of graph.nodes) {
const nodeIdx = nodeMap.get(node.id);
if (nodeIdx === undefined) continue;
let minDist = Infinity;
let minTerminal = 0;
for (let t = 0; t < terminalIndices.length; t++) {
const dist = distances.get(`${nodeIdx}-${t}`) ?? Infinity;
if (dist < minDist) {
minDist = dist;
minTerminal = t;
}
}
partitions.set(node.id, minTerminal);
}
// Calculate cut value
const cutValue = await this.calculateCutWeight(graph, partitions);
return { cutValue, partitions };
}
// ============================================================================
// Private Helper Methods
// ============================================================================
/**
* Load WASM module dynamically
*/
private async loadWasmModule(): Promise<MinCutWasmModule> {
throw new Error('WASM module loading not implemented');
}
/**
* Spectral partitioning using Fiedler vector
*/
private spectralPartition(
weights: number[][],
numPartitions: number,
fixed: Map<number, number>,
_constraints: SplitConstraints
): number[] {
const n = weights.length;
const partition = new Array(n).fill(0);
if (n === 0) return partition;
// Apply fixed partitions
for (const [node, part] of fixed) {
partition[node] = part;
}
// If all fixed, return
if (fixed.size >= n) return partition;
// Compute Laplacian
const laplacian: number[][] = Array.from({ length: n }, () =>
Array(n).fill(0)
);
for (let i = 0; i < n; i++) {
let degree = 0;
for (let j = 0; j < n; j++) {
if (i !== j) {
const w = (weights[i]?.[j] ?? 0) + (weights[j]?.[i] ?? 0);
laplacian[i]![j] = -w;
degree += w;
}
}
laplacian[i]![i] = degree;
}
// Power iteration to find Fiedler vector (second smallest eigenvector)
// Simplified: use random initialization and iterate
const fiedler = new Array(n).fill(0).map(() => Math.random() - 0.5);
// Normalize
let norm = Math.sqrt(fiedler.reduce((sum, v) => sum + v * v, 0));
for (let i = 0; i < n; i++) {
fiedler[i] = (fiedler[i] ?? 0) / norm;
}
// Iterate
for (let iter = 0; iter < 50; iter++) {
// Multiply by Laplacian
const newFiedler = new Array(n).fill(0);
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
newFiedler[i] += (laplacian[i]?.[j] ?? 0) * (fiedler[j] ?? 0);
}
}
// Orthogonalize against constant vector
const mean = newFiedler.reduce((a, b) => a + b, 0) / n;
for (let i = 0; i < n; i++) {
newFiedler[i] = (newFiedler[i] ?? 0) - mean;
}
// Normalize
norm = Math.sqrt(newFiedler.reduce((sum, v) => sum + v * v, 0));
if (norm > 0) {
for (let i = 0; i < n; i++) {
fiedler[i] = (newFiedler[i] ?? 0) / norm;
}
}
}
// Partition based on Fiedler vector
if (numPartitions === 2) {
// Simple bisection
for (let i = 0; i < n; i++) {
if (!fixed.has(i)) {
partition[i] = (fiedler[i] ?? 0) >= 0 ? 0 : 1;
}
}
} else {
// K-means clustering on Fiedler values
const sorted = fiedler
.map((v, i) => ({ value: v, index: i }))
.filter(item => !fixed.has(item.index))
.sort((a, b) => (a.value ?? 0) - (b.value ?? 0));
const binSize = Math.ceil(sorted.length / numPartitions);
for (let i = 0; i < sorted.length; i++) {
const item = sorted[i];
if (item) {
partition[item.index] = Math.min(Math.floor(i / binSize), numPartitions - 1);
}
}
}
return partition;
}
/**
* Edmonds-Karp algorithm (Ford-Fulkerson with BFS)
*/
private edmondsKarp(
capacity: number[][],
source: number,
sink: number
): { maxFlow: number; residual: number[][] } {
const n = capacity.length;
const residual: number[][] = capacity.map(row => [...row]);
let maxFlow = 0;
// BFS to find augmenting path
const bfs = (): number[] | null => {
const parent = new Array(n).fill(-1);
const visited = new Array(n).fill(false);
const queue = [source];
visited[source] = true;
while (queue.length > 0) {
const current = queue.shift()!;
if (current === sink) {
// Reconstruct path
const path: number[] = [];
let node = sink;
while (node !== source) {
path.unshift(node);
node = parent[node] ?? source;
}
path.unshift(source);
return path;
}
for (let next = 0; next < n; next++) {
if (!visited[next] && (residual[current]?.[next] ?? 0) > 0) {
visited[next] = true;
parent[next] = current;
queue.push(next);
}
}
}
return null;
};
// Find augmenting paths
let path = bfs();
while (path !== null) {
// Find minimum capacity along path
let minCap = Infinity;
for (let i = 0; i < path.length - 1; i++) {
const from = path[i];
const to = path[i + 1];
if (from !== undefined && to !== undefined) {
minCap = Math.min(minCap, residual[from]?.[to] ?? 0);
}
}
// Update residual capacities
for (let i = 0; i < path.length - 1; i++) {
const from = path[i];
const to = path[i + 1];
if (from !== undefined && to !== undefined) {
residual[from]![to] = (residual[from]?.[to] ?? 0) - minCap;
residual[to]![from] = (residual[to]?.[from] ?? 0) + minCap;
}
}
maxFlow += minCap;
path = bfs();
}
return { maxFlow, residual };
}
/**
* Compute distances from terminals to all nodes
*/
private computeDistances(
graph: DependencyGraph,
terminalIndices: number[],
nodeMap: Map<string, number>
): Map<string, number> {
const distances = new Map<string, number>();
const nodeCount = graph.nodes.length;
// Build adjacency list with weights
const adj: Map<number, Array<{ to: number; weight: number }>> = new Map();
for (let i = 0; i < nodeCount; i++) {
adj.set(i, []);
}
for (const edge of graph.edges) {
const fromIdx = nodeMap.get(edge.from);
const toIdx = nodeMap.get(edge.to);
if (fromIdx !== undefined && toIdx !== undefined) {
adj.get(fromIdx)?.push({ to: toIdx, weight: edge.weight });
adj.get(toIdx)?.push({ to: fromIdx, weight: edge.weight });
}
}
// Dijkstra from each terminal
for (let t = 0; t < terminalIndices.length; t++) {
const terminal = terminalIndices[t];
if (terminal === undefined) continue;
const dist = new Array(nodeCount).fill(Infinity);
dist[terminal] = 0;
const pq: Array<{ node: number; dist: number }> = [{ node: terminal, dist: 0 }];
while (pq.length > 0) {
pq.sort((a, b) => a.dist - b.dist);
const current = pq.shift()!;
if (current.dist > (dist[current.node] ?? Infinity)) continue;
for (const neighbor of adj.get(current.node) ?? []) {
const newDist = current.dist + (1 / Math.max(neighbor.weight, 0.1)); // Inverse weight as distance
if (newDist < (dist[neighbor.to] ?? Infinity)) {
dist[neighbor.to] = newDist;
pq.push({ node: neighbor.to, dist: newDist });
}
}
}
// Store distances
for (let i = 0; i < nodeCount; i++) {
distances.set(`${i}-${t}`, dist[i] ?? Infinity);
}
}
return distances;
}
}
/**
* Create and export default bridge instance
*/
export function createMinCutBridge(): IMinCutBridge {
return new MinCutBridge();
}
export default MinCutBridge;
+199
View File
@@ -0,0 +1,199 @@
/**
* Code Intelligence Plugin for Claude Flow V3
*
* A comprehensive code intelligence plugin combining graph neural networks
* for code structure analysis with ultra-fast vector search for semantic
* code similarity.
*
* Features:
* - Semantic code search
* - Architecture analysis and drift detection
* - Refactoring impact prediction using GNN
* - Module splitting suggestions using MinCut
* - Pattern learning from code history
*
* Based on ADR-035: Advanced Code Intelligence Plugin
*
* @module @claude-flow/plugin-code-intelligence
*/
// Export types
export * from './types.js';
// Export bridges
export { GNNBridge, createGNNBridge } from './bridges/gnn-bridge.js';
export { MinCutBridge, createMinCutBridge } from './bridges/mincut-bridge.js';
// Export MCP tools
export {
semanticSearchTool,
architectureAnalyzeTool,
refactorImpactTool,
splitSuggestTool,
learnPatternsTool,
codeIntelligenceTools,
toolHandlers,
createToolContext,
} from './mcp-tools.js';
export type { MCPTool, ToolContext, MCPToolResult } from './mcp-tools.js';
// Import for plugin creation
import { codeIntelligenceTools } from './mcp-tools.js';
import { createGNNBridge } from './bridges/gnn-bridge.js';
import { createMinCutBridge } from './bridges/mincut-bridge.js';
import type {
CodeIntelligenceConfig,
IGNNBridge,
IMinCutBridge,
} from './types.js';
import { DEFAULT_CONFIG } from './types.js';
/**
* Plugin metadata
*/
export const pluginMetadata = {
name: '@claude-flow/plugin-code-intelligence',
version: '3.0.0-alpha.1',
description: 'Advanced code intelligence plugin for semantic search, architecture analysis, and refactoring',
author: 'Claude Flow Team',
category: 'code-intelligence',
keywords: ['code', 'intelligence', 'semantic-search', 'architecture', 'refactoring'],
homepage: 'https://github.com/ruvnet/claude-flow',
repository: 'https://github.com/ruvnet/claude-flow.git',
};
/**
* Plugin state
*/
export type PluginState = 'uninitialized' | 'initializing' | 'ready' | 'error' | 'shutdown';
/**
* Code Intelligence Plugin Class
*/
export class CodeIntelligencePlugin {
private state: PluginState = 'uninitialized';
private config: CodeIntelligenceConfig;
private gnnBridge: IGNNBridge | null = null;
private mincutBridge: IMinCutBridge | null = null;
constructor(config: Partial<CodeIntelligenceConfig> = {}) {
this.config = { ...DEFAULT_CONFIG, ...config };
}
/**
* Get plugin metadata
*/
getMetadata() {
return pluginMetadata;
}
/**
* Get current state
*/
getState(): PluginState {
return this.state;
}
/**
* Initialize the plugin
*/
async initialize(): Promise<void> {
if (this.state === 'ready') return;
this.state = 'initializing';
try {
// Initialize WASM bridges
this.gnnBridge = createGNNBridge(this.config.search.embeddingDimension);
this.mincutBridge = createMinCutBridge();
await Promise.all([
this.gnnBridge.initialize(),
this.mincutBridge.initialize(),
]);
this.state = 'ready';
} catch (error) {
this.state = 'error';
throw error;
}
}
/**
* Shutdown the plugin
*/
async shutdown(): Promise<void> {
this.state = 'shutdown';
this.gnnBridge = null;
this.mincutBridge = null;
}
/**
* Get MCP tools provided by this plugin
*/
getMCPTools() {
return codeIntelligenceTools;
}
/**
* Get tool context for execution
*/
getToolContext() {
if (!this.gnnBridge || !this.mincutBridge) {
throw new Error('Plugin not initialized');
}
const store = new Map<string, unknown>();
const blockedPatterns = this.config.security.blockedPatterns.map(
p => new RegExp(p)
);
return {
get: <T>(key: string) => store.get(key) as T | undefined,
set: <T>(key: string, value: T) => { store.set(key, value); },
bridges: {
gnn: this.gnnBridge,
mincut: this.mincutBridge,
},
config: {
allowedRoots: this.config.security.allowedRoots,
blockedPatterns,
maskSecrets: this.config.security.maskSecrets,
},
};
}
/**
* Get configuration
*/
getConfig(): CodeIntelligenceConfig {
return this.config;
}
/**
* Update configuration
*/
updateConfig(config: Partial<CodeIntelligenceConfig>): void {
this.config = {
...this.config,
...config,
search: { ...this.config.search, ...config.search },
architecture: { ...this.config.architecture, ...config.architecture },
refactoring: { ...this.config.refactoring, ...config.refactoring },
security: { ...this.config.security, ...config.security },
};
}
}
/**
* Create plugin instance
*/
export function createPlugin(config?: Partial<CodeIntelligenceConfig>): CodeIntelligencePlugin {
return new CodeIntelligencePlugin(config);
}
/**
* Default export
*/
export default CodeIntelligencePlugin;
File diff suppressed because it is too large Load Diff
+966
View File
@@ -0,0 +1,966 @@
/**
* Code Intelligence Plugin - Type Definitions
*
* Core types for advanced code analysis including semantic search,
* architecture analysis, refactoring impact prediction, module splitting,
* and pattern learning.
*
* Based on ADR-035: Advanced Code Intelligence Plugin
*
* @module v3/plugins/code-intelligence/types
*/
import { z } from 'zod';
// ============================================================================
// Language & Search Types
// ============================================================================
/**
* Supported programming languages
*/
export const Language = z.enum([
'typescript',
'javascript',
'python',
'java',
'go',
'rust',
'cpp',
'csharp',
'ruby',
'php',
'swift',
'kotlin',
'scala',
]);
export type Language = z.infer<typeof Language>;
/**
* Language tiers for support level
*/
export const LanguageTier: Record<Language, 'tier1' | 'tier2' | 'tier3'> = {
typescript: 'tier1',
javascript: 'tier1',
python: 'tier2',
java: 'tier2',
go: 'tier3',
rust: 'tier3',
cpp: 'tier3',
csharp: 'tier2',
ruby: 'tier2',
php: 'tier2',
swift: 'tier3',
kotlin: 'tier3',
scala: 'tier2',
};
/**
* Search type for semantic code search
*/
export const SearchType = z.enum([
'semantic', // Meaning-based search
'structural', // AST pattern matching
'clone', // Code clone detection
'api_usage', // API usage pattern search
]);
export type SearchType = z.infer<typeof SearchType>;
/**
* Code search result
*/
export interface CodeSearchResult {
/** File path */
readonly filePath: string;
/** Line number */
readonly lineNumber: number;
/** Code snippet */
readonly snippet: string;
/** Match type */
readonly matchType: SearchType;
/** Relevance score (0-1) */
readonly score: number;
/** Context (surrounding lines) */
readonly context: string;
/** Symbol name if applicable */
readonly symbol?: string;
/** Language of the file */
readonly language: Language;
/** Explanation of why this matched */
readonly explanation: string;
}
/**
* Semantic search result
*/
export interface SemanticSearchResult {
/** Success status */
readonly success: boolean;
/** Search query */
readonly query: string;
/** Search type used */
readonly searchType: SearchType;
/** Results */
readonly results: CodeSearchResult[];
/** Total matches (before limit) */
readonly totalMatches: number;
/** Search scope used */
readonly scope: SearchScope;
/** Execution time in ms */
readonly durationMs: number;
}
/**
* Search scope configuration
*/
export interface SearchScope {
/** Paths to include */
readonly paths?: string[];
/** Languages to search */
readonly languages?: Language[];
/** Exclude test files */
readonly excludeTests?: boolean;
/** Exclude node_modules and similar */
readonly excludeVendor?: boolean;
/** File patterns to exclude */
readonly excludePatterns?: string[];
}
// ============================================================================
// Architecture Analysis Types
// ============================================================================
/**
* Analysis types for architecture
*/
export const AnalysisType = z.enum([
'dependency_graph',
'layer_violations',
'circular_deps',
'component_coupling',
'module_cohesion',
'dead_code',
'api_surface',
'architectural_drift',
]);
export type AnalysisType = z.infer<typeof AnalysisType>;
/**
* Output format for architecture analysis
*/
export const OutputFormat = z.enum([
'json',
'graphviz',
'mermaid',
]);
export type OutputFormat = z.infer<typeof OutputFormat>;
/**
* Dependency node
*/
export interface DependencyNode {
/** Node ID (file path or module name) */
readonly id: string;
/** Node label */
readonly label: string;
/** Node type */
readonly type: 'file' | 'module' | 'package' | 'class' | 'function';
/** Language */
readonly language?: Language;
/** Lines of code */
readonly loc?: number;
/** Complexity score */
readonly complexity?: number;
/** Layer (if applicable) */
readonly layer?: string;
}
/**
* Dependency edge
*/
export interface DependencyEdge {
/** Source node ID */
readonly from: string;
/** Target node ID */
readonly to: string;
/** Edge type */
readonly type: 'import' | 'extends' | 'implements' | 'uses' | 'calls';
/** Weight (import count or call frequency) */
readonly weight: number;
/** Is dynamic import */
readonly dynamic?: boolean;
}
/**
* Dependency graph
*/
export interface DependencyGraph {
/** All nodes */
readonly nodes: DependencyNode[];
/** All edges */
readonly edges: DependencyEdge[];
/** Graph metadata */
readonly metadata: {
totalNodes: number;
totalEdges: number;
avgDegree: number;
maxDepth: number;
};
}
/**
* Layer violation
*/
export interface LayerViolation {
/** Source module */
readonly source: string;
/** Target module */
readonly target: string;
/** Source layer */
readonly sourceLayer: string;
/** Target layer */
readonly targetLayer: string;
/** Violation type */
readonly violationType: 'upward' | 'skip' | 'cross';
/** Severity */
readonly severity: 'low' | 'medium' | 'high';
/** Suggested fix */
readonly suggestedFix: string;
}
/**
* Circular dependency
*/
export interface CircularDependency {
/** Cycle path (node IDs) */
readonly cycle: string[];
/** Cycle length */
readonly length: number;
/** Severity */
readonly severity: 'low' | 'medium' | 'high';
/** Suggested break point */
readonly suggestedBreakPoint: string;
}
/**
* Component coupling metrics
*/
export interface CouplingMetrics {
/** Component ID */
readonly componentId: string;
/** Afferent coupling (incoming dependencies) */
readonly afferentCoupling: number;
/** Efferent coupling (outgoing dependencies) */
readonly efferentCoupling: number;
/** Instability (Ce / (Ca + Ce)) */
readonly instability: number;
/** Abstractness */
readonly abstractness: number;
/** Distance from main sequence */
readonly distanceFromMain: number;
/** Is in zone of pain (high stability, low abstractness) */
readonly inZoneOfPain: boolean;
/** Is in zone of uselessness (low stability, high abstractness) */
readonly inZoneOfUselessness: boolean;
}
/**
* Module cohesion metrics
*/
export interface CohesionMetrics {
/** Module ID */
readonly moduleId: string;
/** Lack of Cohesion in Methods (LCOM) */
readonly lcom: number;
/** Tight Class Cohesion (TCC) */
readonly tcc: number;
/** Loose Class Cohesion (LCC) */
readonly lcc: number;
/** Cohesion level */
readonly level: 'high' | 'medium' | 'low';
/** Suggestions for improvement */
readonly suggestions: string[];
}
/**
* Dead code finding
*/
export interface DeadCodeFinding {
/** File path */
readonly filePath: string;
/** Symbol name */
readonly symbol: string;
/** Symbol type */
readonly symbolType: 'function' | 'class' | 'variable' | 'import' | 'export';
/** Line number */
readonly lineNumber: number;
/** Confidence (0-1) */
readonly confidence: number;
/** Reason */
readonly reason: string;
/** Is exported */
readonly isExported: boolean;
}
/**
* API surface element
*/
export interface APISurfaceElement {
/** Symbol name */
readonly name: string;
/** Symbol type */
readonly type: 'function' | 'class' | 'interface' | 'type' | 'constant';
/** File path */
readonly filePath: string;
/** Export type */
readonly exportType: 'named' | 'default' | 're-export';
/** Usage count */
readonly usageCount: number;
/** Is deprecated */
readonly deprecated: boolean;
/** Documentation coverage */
readonly documented: boolean;
}
/**
* Architectural drift
*/
export interface ArchitecturalDrift {
/** Component */
readonly component: string;
/** Baseline hash */
readonly baselineRef: string;
/** Current hash */
readonly currentRef: string;
/** Drift type */
readonly driftType: 'dependency_added' | 'dependency_removed' | 'layer_change' | 'coupling_increase';
/** Description */
readonly description: string;
/** Severity */
readonly severity: 'low' | 'medium' | 'high';
}
/**
* Architecture analysis result
*/
export interface ArchitectureAnalysisResult {
/** Success status */
readonly success: boolean;
/** Root path analyzed */
readonly rootPath: string;
/** Analyses performed */
readonly analyses: AnalysisType[];
/** Dependency graph */
readonly dependencyGraph?: DependencyGraph;
/** Layer violations */
readonly layerViolations?: LayerViolation[];
/** Circular dependencies */
readonly circularDeps?: CircularDependency[];
/** Coupling metrics */
readonly couplingMetrics?: CouplingMetrics[];
/** Cohesion metrics */
readonly cohesionMetrics?: CohesionMetrics[];
/** Dead code findings */
readonly deadCode?: DeadCodeFinding[];
/** API surface */
readonly apiSurface?: APISurfaceElement[];
/** Architectural drift */
readonly drift?: ArchitecturalDrift[];
/** Summary */
readonly summary: {
totalFiles: number;
totalModules: number;
healthScore: number;
issues: number;
warnings: number;
};
/** Execution time in ms */
readonly durationMs: number;
}
// ============================================================================
// Refactoring Impact Types
// ============================================================================
/**
* Change type for refactoring
*/
export const ChangeType = z.enum([
'rename',
'move',
'delete',
'extract',
'inline',
]);
export type ChangeType = z.infer<typeof ChangeType>;
/**
* Proposed change
*/
export interface ProposedChange {
/** File to change */
readonly file: string;
/** Change type */
readonly type: ChangeType;
/** Change details */
readonly details: {
/** Original name (for rename) */
oldName?: string;
/** New name (for rename) */
newName?: string;
/** New location (for move) */
newPath?: string;
/** Symbol to extract (for extract) */
symbol?: string;
/** Target file (for extract) */
targetFile?: string;
};
}
/**
* Impact on a file
*/
export interface FileImpact {
/** File path */
readonly filePath: string;
/** Impact type */
readonly impactType: 'direct' | 'indirect' | 'transitive';
/** Requires modification */
readonly requiresChange: boolean;
/** Changes needed */
readonly changesNeeded: string[];
/** Risk level */
readonly risk: 'low' | 'medium' | 'high';
/** Tests affected */
readonly testsAffected: string[];
}
/**
* Refactoring impact result
*/
export interface RefactoringImpactResult {
/** Success status */
readonly success: boolean;
/** Proposed changes */
readonly changes: ProposedChange[];
/** Impacted files */
readonly impactedFiles: FileImpact[];
/** Impact summary */
readonly summary: {
directlyAffected: number;
indirectlyAffected: number;
testsAffected: number;
totalRisk: 'low' | 'medium' | 'high';
};
/** Suggested order of changes */
readonly suggestedOrder: string[];
/** Potential breaking changes */
readonly breakingChanges: string[];
/** Execution time in ms */
readonly durationMs: number;
}
// ============================================================================
// Module Splitting Types
// ============================================================================
/**
* Splitting strategy
*/
export const SplitStrategy = z.enum([
'minimize_coupling',
'balance_size',
'feature_isolation',
]);
export type SplitStrategy = z.infer<typeof SplitStrategy>;
/**
* Split constraints
*/
export interface SplitConstraints {
/** Maximum module size (lines) */
readonly maxModuleSize?: number;
/** Minimum module size (lines) */
readonly minModuleSize?: number;
/** Boundaries to preserve */
readonly preserveBoundaries?: string[];
/** Files that must stay together */
readonly keepTogether?: string[][];
}
/**
* Suggested module
*/
export interface SuggestedModule {
/** Module name */
readonly name: string;
/** Files included */
readonly files: string[];
/** Total lines of code */
readonly loc: number;
/** Internal cohesion score */
readonly cohesion: number;
/** External coupling score */
readonly coupling: number;
/** Public API */
readonly publicApi: string[];
/** Dependencies on other suggested modules */
readonly dependencies: string[];
}
/**
* Module split suggestion result
*/
export interface ModuleSplitResult {
/** Success status */
readonly success: boolean;
/** Target path analyzed */
readonly targetPath: string;
/** Strategy used */
readonly strategy: SplitStrategy;
/** Suggested modules */
readonly modules: SuggestedModule[];
/** Cut edges (dependencies that cross module boundaries) */
readonly cutEdges: Array<{
from: string;
to: string;
weight: number;
}>;
/** Quality metrics */
readonly quality: {
totalCutWeight: number;
avgCohesion: number;
avgCoupling: number;
balanceScore: number;
};
/** Migration steps */
readonly migrationSteps: string[];
/** Execution time in ms */
readonly durationMs: number;
}
// ============================================================================
// Pattern Learning Types
// ============================================================================
/**
* Pattern types to learn
*/
export const PatternType = z.enum([
'bug_patterns',
'refactor_patterns',
'api_patterns',
'test_patterns',
]);
export type PatternType = z.infer<typeof PatternType>;
/**
* Learned pattern
*/
export interface LearnedPattern {
/** Pattern ID */
readonly id: string;
/** Pattern type */
readonly type: PatternType;
/** Pattern description */
readonly description: string;
/** Code before (for refactoring patterns) */
readonly codeBefore?: string;
/** Code after (for refactoring patterns) */
readonly codeAfter?: string;
/** Occurrence count */
readonly occurrences: number;
/** Authors who used this pattern */
readonly authors: string[];
/** Files where pattern appears */
readonly files: string[];
/** Confidence score */
readonly confidence: number;
/** Impact (positive/negative/neutral) */
readonly impact: 'positive' | 'negative' | 'neutral';
/** Suggested action */
readonly suggestedAction?: string;
}
/**
* Pattern learning scope
*/
export interface LearningScope {
/** Git range to analyze */
readonly gitRange?: string;
/** Authors to filter */
readonly authors?: string[];
/** Paths to include */
readonly paths?: string[];
/** Since date */
readonly since?: Date;
/** Until date */
readonly until?: Date;
}
/**
* Pattern learning result
*/
export interface PatternLearningResult {
/** Success status */
readonly success: boolean;
/** Scope used */
readonly scope: LearningScope;
/** Pattern types analyzed */
readonly patternTypes: PatternType[];
/** Learned patterns */
readonly patterns: LearnedPattern[];
/** Summary */
readonly summary: {
commitsAnalyzed: number;
filesAnalyzed: number;
patternsFound: number;
byType: Record<PatternType, number>;
};
/** Recommendations based on patterns */
readonly recommendations: string[];
/** Execution time in ms */
readonly durationMs: number;
}
// ============================================================================
// MCP Tool Input Schemas
// ============================================================================
/**
* Input schema for code/semantic-search
*/
export const SemanticSearchInputSchema = z.object({
query: z.string().min(1).max(5000),
scope: z.object({
paths: z.array(z.string().max(500)).max(100).optional(),
languages: z.array(Language).max(20).optional(),
excludeTests: z.boolean().default(false),
}).optional(),
searchType: SearchType.default('semantic'),
topK: z.number().int().min(1).max(1000).default(10),
});
export type SemanticSearchInput = z.infer<typeof SemanticSearchInputSchema>;
/**
* Input schema for code/architecture-analyze
*/
export const ArchitectureAnalyzeInputSchema = z.object({
rootPath: z.string().max(500).default('.'),
analysis: z.array(AnalysisType).optional(),
baseline: z.string().max(100).optional(),
outputFormat: OutputFormat.optional(),
layers: z.record(z.string(), z.array(z.string())).optional(),
});
export type ArchitectureAnalyzeInput = z.infer<typeof ArchitectureAnalyzeInputSchema>;
/**
* Input schema for code/refactor-impact
*/
export const RefactorImpactInputSchema = z.object({
changes: z.array(z.object({
file: z.string().max(500),
type: ChangeType,
details: z.record(z.string(), z.unknown()).optional(),
})).min(1).max(100),
depth: z.number().int().min(1).max(10).default(3),
includeTests: z.boolean().default(true),
});
export type RefactorImpactInput = z.infer<typeof RefactorImpactInputSchema>;
/**
* Input schema for code/split-suggest
*/
export const SplitSuggestInputSchema = z.object({
targetPath: z.string().max(500),
strategy: SplitStrategy.default('minimize_coupling'),
constraints: z.object({
maxModuleSize: z.number().optional(),
minModuleSize: z.number().optional(),
preserveBoundaries: z.array(z.string()).optional(),
}).optional(),
targetModules: z.number().int().min(2).max(50).optional(),
});
export type SplitSuggestInput = z.infer<typeof SplitSuggestInputSchema>;
/**
* Input schema for code/learn-patterns
*/
export const LearnPatternsInputSchema = z.object({
scope: z.object({
gitRange: z.string().default('HEAD~100..HEAD'),
authors: z.array(z.string()).optional(),
paths: z.array(z.string()).optional(),
}).optional(),
patternTypes: z.array(PatternType).optional(),
minOccurrences: z.number().int().min(1).max(100).default(3),
});
export type LearnPatternsInput = z.infer<typeof LearnPatternsInputSchema>;
// ============================================================================
// Bridge Interfaces
// ============================================================================
/**
* GNN Bridge for code graph analysis
*/
export interface IGNNBridge {
/**
* Build code graph from files
*/
buildCodeGraph(
files: string[],
includeCallGraph: boolean
): Promise<DependencyGraph>;
/**
* Compute node embeddings using GNN
*/
computeNodeEmbeddings(
graph: DependencyGraph,
embeddingDim: number
): Promise<Map<string, Float32Array>>;
/**
* Predict impact of changes using GNN
*/
predictImpact(
graph: DependencyGraph,
changedNodes: string[],
depth: number
): Promise<Map<string, number>>;
/**
* Detect communities in code graph
*/
detectCommunities(
graph: DependencyGraph
): Promise<Map<string, number>>;
/**
* Find similar code patterns
*/
findSimilarPatterns(
graph: DependencyGraph,
patternGraph: DependencyGraph,
threshold: number
): Promise<Array<{ matchId: string; score: number }>>;
/**
* Initialize the WASM module
*/
initialize(): Promise<void>;
/**
* Check if initialized
*/
isInitialized(): boolean;
}
/**
* MinCut Bridge for module splitting
*/
export interface IMinCutBridge {
/**
* Find optimal module boundaries using MinCut
*/
findOptimalCuts(
graph: DependencyGraph,
numModules: number,
constraints: SplitConstraints
): Promise<Map<string, number>>;
/**
* Calculate cut weight for a given partition
*/
calculateCutWeight(
graph: DependencyGraph,
partition: Map<string, number>
): Promise<number>;
/**
* Find minimum s-t cut
*/
minSTCut(
graph: DependencyGraph,
source: string,
sink: string
): Promise<{
cutValue: number;
cutEdges: Array<{ from: string; to: string }>;
sourceSet: string[];
sinkSet: string[];
}>;
/**
* Multi-way cut for module splitting
*/
multiWayCut(
graph: DependencyGraph,
terminals: string[],
weights: Map<string, number>
): Promise<{
cutValue: number;
partitions: Map<string, number>;
}>;
/**
* Initialize the WASM module
*/
initialize(): Promise<void>;
/**
* Check if initialized
*/
isInitialized(): boolean;
}
// ============================================================================
// Configuration Types
// ============================================================================
/**
* Plugin configuration
*/
export interface CodeIntelligenceConfig {
/** Semantic search settings */
search: {
/** Embedding dimension */
embeddingDimension: number;
/** Default top-K results */
defaultTopK: number;
/** Similarity threshold */
similarityThreshold: number;
};
/** Architecture analysis settings */
architecture: {
/** Layer definitions */
layers?: Record<string, string[]>;
/** Maximum graph depth */
maxGraphDepth: number;
/** Include vendor/node_modules */
includeVendor: boolean;
};
/** Refactoring settings */
refactoring: {
/** Default impact depth */
defaultDepth: number;
/** Include test files */
includeTests: boolean;
};
/** Security settings */
security: {
/** Allowed root paths */
allowedRoots: string[];
/** Block sensitive file patterns */
blockedPatterns: string[];
/** Mask secrets in output */
maskSecrets: boolean;
};
}
/**
* Default configuration
*/
export const DEFAULT_CONFIG: CodeIntelligenceConfig = {
search: {
embeddingDimension: 384,
defaultTopK: 10,
similarityThreshold: 0.7,
},
architecture: {
maxGraphDepth: 10,
includeVendor: false,
},
refactoring: {
defaultDepth: 3,
includeTests: true,
},
security: {
allowedRoots: ['.'],
blockedPatterns: [
'\\.env$',
'\\.git/config$',
'credentials',
'secrets?\\.',
'\\.pem$',
'\\.key$',
'id_rsa',
],
maskSecrets: true,
},
};
// ============================================================================
// Error Types
// ============================================================================
/**
* Code intelligence plugin error codes
*/
export const CodeIntelligenceErrorCodes = {
PATH_TRAVERSAL: 'CODE_PATH_TRAVERSAL',
SENSITIVE_FILE: 'CODE_SENSITIVE_FILE',
GRAPH_TOO_LARGE: 'CODE_GRAPH_TOO_LARGE',
ANALYSIS_FAILED: 'CODE_ANALYSIS_FAILED',
PARSER_ERROR: 'CODE_PARSER_ERROR',
WASM_NOT_INITIALIZED: 'CODE_WASM_NOT_INITIALIZED',
LANGUAGE_NOT_SUPPORTED: 'CODE_LANGUAGE_NOT_SUPPORTED',
GIT_ERROR: 'CODE_GIT_ERROR',
} as const;
export type CodeIntelligenceErrorCode = (typeof CodeIntelligenceErrorCodes)[keyof typeof CodeIntelligenceErrorCodes];
/**
* Code intelligence plugin error
*/
export class CodeIntelligenceError extends Error {
public readonly code: CodeIntelligenceErrorCode;
public readonly details?: Record<string, unknown>;
constructor(code: CodeIntelligenceErrorCode, message: string, details?: Record<string, unknown>) {
super(message);
this.name = 'CodeIntelligenceError';
this.code = code;
this.details = details;
}
}
// ============================================================================
// Security Utilities
// ============================================================================
/**
* Secret patterns for masking
*/
export const SECRET_PATTERNS = [
/(['"])(?:api[_-]?key|apikey|secret|password|token|auth)['"]\s*[:=]\s*['"][^'"]+['"]/gi,
/(?:sk|pk)[-_](?:live|test)[-_][a-zA-Z0-9]{24,}/g,
/ghp_[a-zA-Z0-9]{36}/g,
/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/g,
/xox[baprs]-[a-zA-Z0-9-]+/g,
/AKIA[0-9A-Z]{16}/g,
];
/**
* Mask secrets in code snippet
*/
export function maskSecrets(code: string): string {
let masked = code;
for (const pattern of SECRET_PATTERNS) {
masked = masked.replace(pattern, '[REDACTED]');
}
return masked;
}