-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathdetect-circular-dependencies.ts
More file actions
124 lines (117 loc) · 3.49 KB
/
detect-circular-dependencies.ts
File metadata and controls
124 lines (117 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
import { promises as fs } from 'fs';
import type { ToolContext, ToolResponse } from './types.js';
import { InternalFileGraph } from '../utils/usage-tracker.js';
export const definition: Tool = {
name: 'detect_circular_dependencies',
description:
'Analyze the import graph to detect circular dependencies between files. ' +
'Circular dependencies can cause initialization issues, tight coupling, and maintenance problems. ' +
'Returns all detected cycles sorted by length (shorter cycles are often more problematic).',
inputSchema: {
type: 'object',
properties: {
scope: {
type: 'string',
description:
"Optional path prefix to limit analysis (e.g., 'src/features', 'libs/shared')"
}
}
}
};
export async function handle(
args: Record<string, unknown>,
ctx: ToolContext
): Promise<ToolResponse> {
const { scope } = args as { scope?: string };
try {
const intelligencePath = ctx.paths.intelligence;
const content = await fs.readFile(intelligencePath, 'utf-8');
const intelligence = JSON.parse(content);
if (!intelligence.internalFileGraph) {
return {
content: [
{
type: 'text',
text: JSON.stringify(
{
status: 'error',
message:
'Internal file graph not found. Please run refresh_index to rebuild the index with cycle detection support.'
},
null,
2
)
}
]
};
}
// Reconstruct the graph from stored data
const graph = InternalFileGraph.fromJSON(intelligence.internalFileGraph, ctx.rootPath);
const cycles = graph.findCycles(scope);
const graphStats = intelligence.internalFileGraph.stats || graph.getStats();
if (cycles.length === 0) {
return {
content: [
{
type: 'text',
text: JSON.stringify(
{
status: 'success',
message: scope
? `No circular dependencies detected in scope: ${scope}`
: 'No circular dependencies detected in the codebase.',
scope,
graphStats
},
null,
2
)
}
]
};
}
return {
content: [
{
type: 'text',
text: JSON.stringify(
{
status: 'warning',
message: `Found ${cycles.length} circular dependency cycle(s).`,
scope,
cycles: cycles.map((c) => ({
files: c.files,
length: c.length,
severity: c.length === 2 ? 'high' : c.length <= 3 ? 'medium' : 'low'
})),
count: cycles.length,
graphStats,
advice:
'Shorter cycles (length 2-3) are typically more problematic. Consider breaking the cycle by extracting shared dependencies.'
},
null,
2
)
}
]
};
} catch (error) {
return {
content: [
{
type: 'text',
text: JSON.stringify(
{
status: 'error',
message: 'Failed to detect circular dependencies. Run indexing first.',
error: error instanceof Error ? error.message : String(error)
},
null,
2
)
}
]
};
}
}