skhaall-codeguard 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/cli.d.ts +16 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +295 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/core/indexer.d.ts +21 -0
  8. package/dist/core/indexer.d.ts.map +1 -0
  9. package/dist/core/indexer.js +96 -0
  10. package/dist/core/indexer.js.map +1 -0
  11. package/dist/graph/dependency-graph.d.ts +37 -0
  12. package/dist/graph/dependency-graph.d.ts.map +1 -0
  13. package/dist/graph/dependency-graph.js +94 -0
  14. package/dist/graph/dependency-graph.js.map +1 -0
  15. package/dist/graph/impact-resolver.d.ts +40 -0
  16. package/dist/graph/impact-resolver.d.ts.map +1 -0
  17. package/dist/graph/impact-resolver.js +108 -0
  18. package/dist/graph/impact-resolver.js.map +1 -0
  19. package/dist/index.d.ts +8 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +299 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/parsers/base-parser.d.ts +111 -0
  24. package/dist/parsers/base-parser.d.ts.map +1 -0
  25. package/dist/parsers/base-parser.js +7 -0
  26. package/dist/parsers/base-parser.js.map +1 -0
  27. package/dist/parsers/detector.d.ts +10 -0
  28. package/dist/parsers/detector.d.ts.map +1 -0
  29. package/dist/parsers/detector.js +61 -0
  30. package/dist/parsers/detector.js.map +1 -0
  31. package/dist/parsers/extractors/api-calls.d.ts +10 -0
  32. package/dist/parsers/extractors/api-calls.d.ts.map +1 -0
  33. package/dist/parsers/extractors/api-calls.js +99 -0
  34. package/dist/parsers/extractors/api-calls.js.map +1 -0
  35. package/dist/parsers/extractors/auth-guards.d.ts +8 -0
  36. package/dist/parsers/extractors/auth-guards.d.ts.map +1 -0
  37. package/dist/parsers/extractors/auth-guards.js +23 -0
  38. package/dist/parsers/extractors/auth-guards.js.map +1 -0
  39. package/dist/parsers/prisma-parser.d.ts +45 -0
  40. package/dist/parsers/prisma-parser.d.ts.map +1 -0
  41. package/dist/parsers/prisma-parser.js +197 -0
  42. package/dist/parsers/prisma-parser.js.map +1 -0
  43. package/dist/parsers/typescript-parser.d.ts +28 -0
  44. package/dist/parsers/typescript-parser.d.ts.map +1 -0
  45. package/dist/parsers/typescript-parser.js +374 -0
  46. package/dist/parsers/typescript-parser.js.map +1 -0
  47. package/dist/setup.d.ts +10 -0
  48. package/dist/setup.d.ts.map +1 -0
  49. package/dist/setup.js +135 -0
  50. package/dist/setup.js.map +1 -0
  51. package/dist/storage/index-store.d.ts +36 -0
  52. package/dist/storage/index-store.d.ts.map +1 -0
  53. package/dist/storage/index-store.js +108 -0
  54. package/dist/storage/index-store.js.map +1 -0
  55. package/dist/tools/changelog.d.ts +36 -0
  56. package/dist/tools/changelog.d.ts.map +1 -0
  57. package/dist/tools/changelog.js +212 -0
  58. package/dist/tools/changelog.js.map +1 -0
  59. package/dist/tools/check.d.ts +31 -0
  60. package/dist/tools/check.d.ts.map +1 -0
  61. package/dist/tools/check.js +180 -0
  62. package/dist/tools/check.js.map +1 -0
  63. package/dist/tools/graph.d.ts +20 -0
  64. package/dist/tools/graph.d.ts.map +1 -0
  65. package/dist/tools/graph.js +192 -0
  66. package/dist/tools/graph.js.map +1 -0
  67. package/dist/tools/guard.d.ts +30 -0
  68. package/dist/tools/guard.d.ts.map +1 -0
  69. package/dist/tools/guard.js +132 -0
  70. package/dist/tools/guard.js.map +1 -0
  71. package/dist/tools/health.d.ts +33 -0
  72. package/dist/tools/health.d.ts.map +1 -0
  73. package/dist/tools/health.js +241 -0
  74. package/dist/tools/health.js.map +1 -0
  75. package/dist/tools/impact.d.ts +10 -0
  76. package/dist/tools/impact.d.ts.map +1 -0
  77. package/dist/tools/impact.js +45 -0
  78. package/dist/tools/impact.js.map +1 -0
  79. package/dist/tools/regression.d.ts +29 -0
  80. package/dist/tools/regression.d.ts.map +1 -0
  81. package/dist/tools/regression.js +151 -0
  82. package/dist/tools/regression.js.map +1 -0
  83. package/dist/tools/routes.d.ts +40 -0
  84. package/dist/tools/routes.d.ts.map +1 -0
  85. package/dist/tools/routes.js +203 -0
  86. package/dist/tools/routes.js.map +1 -0
  87. package/dist/tools/schema.d.ts +37 -0
  88. package/dist/tools/schema.d.ts.map +1 -0
  89. package/dist/tools/schema.js +214 -0
  90. package/dist/tools/schema.js.map +1 -0
  91. package/dist/tools/search.d.ts +18 -0
  92. package/dist/tools/search.d.ts.map +1 -0
  93. package/dist/tools/search.js +99 -0
  94. package/dist/tools/search.js.map +1 -0
  95. package/dist/tools/tool-definitions.d.ts +15 -0
  96. package/dist/tools/tool-definitions.d.ts.map +1 -0
  97. package/dist/tools/tool-definitions.js +157 -0
  98. package/dist/tools/tool-definitions.js.map +1 -0
  99. package/dist/utils/import-resolver.d.ts +33 -0
  100. package/dist/utils/import-resolver.d.ts.map +1 -0
  101. package/dist/utils/import-resolver.js +150 -0
  102. package/dist/utils/import-resolver.js.map +1 -0
  103. package/dist/utils/logger.d.ts +15 -0
  104. package/dist/utils/logger.d.ts.map +1 -0
  105. package/dist/utils/logger.js +49 -0
  106. package/dist/utils/logger.js.map +1 -0
  107. package/dist/utils/path.d.ts +6 -0
  108. package/dist/utils/path.d.ts.map +1 -0
  109. package/dist/utils/path.js +12 -0
  110. package/dist/utils/path.js.map +1 -0
  111. package/dist/utils/scanner.d.ts +13 -0
  112. package/dist/utils/scanner.d.ts.map +1 -0
  113. package/dist/utils/scanner.js +55 -0
  114. package/dist/utils/scanner.js.map +1 -0
  115. package/dist/utils/validators.d.ts +45 -0
  116. package/dist/utils/validators.d.ts.map +1 -0
  117. package/dist/utils/validators.js +54 -0
  118. package/dist/utils/validators.js.map +1 -0
  119. package/package.json +57 -0
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Outil MCP : graph
3
+ * Genere un diagramme Mermaid du graphe de dependances.
4
+ * Deux modes : complet (tout le projet) ou focus (centre sur un fichier).
5
+ */
6
+ import { DependencyGraph } from '../graph/dependency-graph.js';
7
+ export function generateGraph(index, focusFile) {
8
+ const graph = DependencyGraph.fromIndex(index);
9
+ const edges = graph.getEdges();
10
+ if (focusFile) {
11
+ return generateFocusGraph(graph, index, focusFile);
12
+ }
13
+ return generateFullGraph(graph, index, edges);
14
+ }
15
+ /** Graphe complet du projet */
16
+ function generateFullGraph(graph, index, edges) {
17
+ const lines = ['graph LR'];
18
+ const nodeIds = new Map();
19
+ let nodeCounter = 0;
20
+ function getNodeId(filePath) {
21
+ if (!nodeIds.has(filePath)) {
22
+ nodeIds.set(filePath, `N${nodeCounter++}`);
23
+ }
24
+ return nodeIds.get(filePath);
25
+ }
26
+ // Declarer les noeuds avec des labels courts
27
+ const allFiles = new Set();
28
+ for (const edge of edges) {
29
+ allFiles.add(edge.from);
30
+ allFiles.add(edge.to);
31
+ }
32
+ for (const filePath of allFiles) {
33
+ const id = getNodeId(filePath);
34
+ const label = shortLabel(filePath);
35
+ const shape = getNodeShape(filePath, index);
36
+ lines.push(` ${id}${shape[0]}"${label}"${shape[1]}`);
37
+ }
38
+ // Aretes
39
+ for (const edge of edges) {
40
+ const fromId = getNodeId(edge.from);
41
+ const toId = getNodeId(edge.to);
42
+ lines.push(` ${fromId} --> ${toId}`);
43
+ }
44
+ // Styles par type de fichier
45
+ const styles = generateStyles(allFiles, index, nodeIds);
46
+ lines.push(...styles);
47
+ return {
48
+ mermaid: lines.join('\n'),
49
+ nodeCount: allFiles.size,
50
+ edgeCount: edges.length,
51
+ mode: 'full',
52
+ };
53
+ }
54
+ /** Graphe centre sur un fichier (dependances + dependants a 2 niveaux) */
55
+ function generateFocusGraph(graph, index, focusFile) {
56
+ const lines = ['graph LR'];
57
+ const nodeIds = new Map();
58
+ let nodeCounter = 0;
59
+ const relevantFiles = new Set();
60
+ const relevantEdges = [];
61
+ function getNodeId(filePath) {
62
+ if (!nodeIds.has(filePath)) {
63
+ nodeIds.set(filePath, `N${nodeCounter++}`);
64
+ }
65
+ return nodeIds.get(filePath);
66
+ }
67
+ // Le fichier focus
68
+ relevantFiles.add(focusFile);
69
+ // Dependances directes (ce fichier importe)
70
+ const deps = graph.getDependencies(focusFile);
71
+ for (const dep of deps) {
72
+ relevantFiles.add(dep);
73
+ relevantEdges.push({ from: focusFile, to: dep });
74
+ }
75
+ // Dependants directs (qui importe ce fichier)
76
+ const dependents = graph.getDependents(focusFile);
77
+ for (const dep of dependents) {
78
+ relevantFiles.add(dep);
79
+ relevantEdges.push({ from: dep, to: focusFile });
80
+ // Dependants de niveau 2
81
+ const level2 = graph.getDependents(dep);
82
+ for (const dep2 of level2) {
83
+ if (!relevantFiles.has(dep2)) {
84
+ relevantFiles.add(dep2);
85
+ relevantEdges.push({ from: dep2, to: dep });
86
+ }
87
+ }
88
+ }
89
+ // Declarer les noeuds
90
+ for (const filePath of relevantFiles) {
91
+ const id = getNodeId(filePath);
92
+ const label = shortLabel(filePath);
93
+ const shape = getNodeShape(filePath, index);
94
+ lines.push(` ${id}${shape[0]}"${label}"${shape[1]}`);
95
+ }
96
+ // Aretes
97
+ for (const edge of relevantEdges) {
98
+ const fromId = getNodeId(edge.from);
99
+ const toId = getNodeId(edge.to);
100
+ lines.push(` ${fromId} --> ${toId}`);
101
+ }
102
+ // Style focus
103
+ const focusId = getNodeId(focusFile);
104
+ lines.push(` style ${focusId} fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px,color:#fff`);
105
+ // Styles par type
106
+ const styles = generateStyles(relevantFiles, index, nodeIds);
107
+ lines.push(...styles);
108
+ return {
109
+ mermaid: lines.join('\n'),
110
+ nodeCount: relevantFiles.size,
111
+ edgeCount: relevantEdges.length,
112
+ mode: 'focus',
113
+ };
114
+ }
115
+ /** Extrait un label court depuis un chemin de fichier */
116
+ function shortLabel(filePath) {
117
+ const normalized = filePath.replace(/\\/g, '/');
118
+ const srcIdx = normalized.lastIndexOf('/src/');
119
+ if (srcIdx !== -1) {
120
+ return normalized.slice(srcIdx + 5).replace(/\.(ts|tsx|js|jsx)$/, '');
121
+ }
122
+ return normalized.split('/').slice(-2).join('/').replace(/\.(ts|tsx|js|jsx)$/, '');
123
+ }
124
+ /** Forme du noeud selon le type de fichier */
125
+ function getNodeShape(filePath, index) {
126
+ const node = index.files[filePath];
127
+ const normalized = filePath.replace(/\\/g, '/');
128
+ // Routes API = hexagone
129
+ if (node?.routes && node.routes.length > 0)
130
+ return ['{{', '}}'];
131
+ if (/\/route\.(ts|js)$/.test(normalized))
132
+ return ['{{', '}}'];
133
+ // Pages = rectangle arrondi
134
+ if (/\/page\.(tsx?|jsx?)$/.test(normalized))
135
+ return ['(', ')'];
136
+ // Types/interfaces purs = losange
137
+ if (node && node.exports.length > 0 && node.exports.every((e) => e.isTypeOnly))
138
+ return ['{', '}'];
139
+ // Defaut = rectangle
140
+ return ['["', '"]'];
141
+ }
142
+ /** Genere les styles CSS par categorie de fichier */
143
+ function generateStyles(files, index, nodeIds) {
144
+ const styles = [];
145
+ const parsers = [];
146
+ const tools = [];
147
+ const utils = [];
148
+ const graph = [];
149
+ for (const filePath of files) {
150
+ const id = nodeIds.get(filePath);
151
+ if (!id)
152
+ continue;
153
+ const normalized = filePath.replace(/\\/g, '/');
154
+ if (normalized.includes('/parsers/'))
155
+ parsers.push(id);
156
+ else if (normalized.includes('/tools/'))
157
+ tools.push(id);
158
+ else if (normalized.includes('/utils/') || normalized.includes('/storage/'))
159
+ utils.push(id);
160
+ else if (normalized.includes('/graph/'))
161
+ graph.push(id);
162
+ }
163
+ if (parsers.length > 0)
164
+ styles.push(` classDef parser fill:#4dabf7,stroke:#1971c2,color:#fff`);
165
+ if (tools.length > 0)
166
+ styles.push(` classDef tool fill:#69db7c,stroke:#2b8a3e,color:#fff`);
167
+ if (utils.length > 0)
168
+ styles.push(` classDef util fill:#ffd43b,stroke:#e67700,color:#333`);
169
+ if (graph.length > 0)
170
+ styles.push(` classDef graphMod fill:#da77f2,stroke:#9c36b5,color:#fff`);
171
+ if (parsers.length > 0)
172
+ styles.push(` class ${parsers.join(',')} parser`);
173
+ if (tools.length > 0)
174
+ styles.push(` class ${tools.join(',')} tool`);
175
+ if (utils.length > 0)
176
+ styles.push(` class ${utils.join(',')} util`);
177
+ if (graph.length > 0)
178
+ styles.push(` class ${graph.join(',')} graphMod`);
179
+ return styles;
180
+ }
181
+ /** Formate le resultat pour affichage MCP */
182
+ export function formatGraphResult(result) {
183
+ const lines = [];
184
+ lines.push(`## Graphe de dependances (${result.mode === 'focus' ? 'focus' : 'complet'})`);
185
+ lines.push(`**Noeuds** : ${result.nodeCount} | **Aretes** : ${result.edgeCount}`);
186
+ lines.push('');
187
+ lines.push('```mermaid');
188
+ lines.push(result.mermaid);
189
+ lines.push('```');
190
+ return lines.join('\n');
191
+ }
192
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/tools/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Outil MCP : guard
3
+ * "Est-ce safe de modifier ce fichier ?"
4
+ * Analyse les risques AVANT modification et donne une recommandation.
5
+ */
6
+ import type { ProjectIndex } from '../storage/index-store.js';
7
+ import type { ImpactResult } from '../graph/impact-resolver.js';
8
+ export interface GuardWarning {
9
+ level: 'info' | 'warn' | 'danger';
10
+ message: string;
11
+ }
12
+ export interface GuardResult {
13
+ filePath: string;
14
+ /** Recommendation globale */
15
+ safe: boolean;
16
+ /** Risque global (herite de l'impact analysis) */
17
+ risk: ImpactResult['risk'];
18
+ /** Avertissements detailles */
19
+ warnings: GuardWarning[];
20
+ /** Fichiers a verifier apres la modification */
21
+ filesToCheck: string[];
22
+ /** Exports du fichier (ce qui peut casser les dependants) */
23
+ exports: string[];
24
+ /** Nombre total de fichiers impactes */
25
+ impactCount: number;
26
+ }
27
+ export declare function runGuard(index: ProjectIndex, filePath: string): GuardResult;
28
+ /** Formate le resultat pour affichage MCP */
29
+ export declare function formatGuardResult(result: GuardResult): string;
30
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/tools/guard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,kDAAkD;IAClD,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,+BAA+B;IAC/B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,gDAAgD;IAChD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAoG3E;AAED,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAqC7D"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Outil MCP : guard
3
+ * "Est-ce safe de modifier ce fichier ?"
4
+ * Analyse les risques AVANT modification et donne une recommandation.
5
+ */
6
+ import { ImpactResolver } from '../graph/impact-resolver.js';
7
+ import { DependencyGraph } from '../graph/dependency-graph.js';
8
+ export function runGuard(index, filePath) {
9
+ const resolver = new ImpactResolver(index);
10
+ const impact = resolver.resolve(filePath);
11
+ const graph = DependencyGraph.fromIndex(index);
12
+ const node = index.files[filePath];
13
+ const warnings = [];
14
+ // -- Analyse des risques --
15
+ // Fichier tres partage
16
+ const directDeps = impact.directDependents;
17
+ if (directDeps.length >= 10) {
18
+ warnings.push({
19
+ level: 'danger',
20
+ message: `Fichier tres partage : ${directDeps.length} fichiers l'importent directement. Toute modification peut casser en cascade.`,
21
+ });
22
+ }
23
+ else if (directDeps.length >= 5) {
24
+ warnings.push({
25
+ level: 'warn',
26
+ message: `${directDeps.length} fichiers importent ce fichier directement.`,
27
+ });
28
+ }
29
+ // Exporte beaucoup de symboles
30
+ const exports = node?.exports.map((e) => e.name) ?? [];
31
+ if (exports.length >= 10) {
32
+ warnings.push({
33
+ level: 'warn',
34
+ message: `${exports.length} exports — modifier un seul peut avoir un effet domino.`,
35
+ });
36
+ }
37
+ // Fichier dans un domaine sensible
38
+ const normalized = filePath.replace(/\\/g, '/');
39
+ if (normalized.includes('/auth') || normalized.includes('/middleware') || normalized.includes('/guard')) {
40
+ warnings.push({
41
+ level: 'danger',
42
+ message: 'Fichier dans un domaine sensible (auth/middleware/guard). Verifier la securite apres modification.',
43
+ });
44
+ }
45
+ if (normalized.includes('schema.prisma') || normalized.includes('/models/')) {
46
+ warnings.push({
47
+ level: 'danger',
48
+ message: 'Fichier lie au schema de donnees. Penser a prisma db push + verifier les DTOs et types frontend.',
49
+ });
50
+ }
51
+ if (normalized.includes('/config') || normalized.includes('.config.')) {
52
+ warnings.push({
53
+ level: 'warn',
54
+ message: 'Fichier de configuration. Impact potentiel sur tout le projet.',
55
+ });
56
+ }
57
+ // Routes API impactees
58
+ if (impact.affectedRoutes.length > 0) {
59
+ warnings.push({
60
+ level: 'warn',
61
+ message: `${impact.affectedRoutes.length} route(s) API affectee(s) : ${impact.affectedRoutes.map((r) => `${r.method} ${r.path}`).join(', ')}`,
62
+ });
63
+ }
64
+ // Cascade profonde
65
+ const indirectCount = impact.allDependents.length - directDeps.length;
66
+ if (indirectCount > 0) {
67
+ warnings.push({
68
+ level: 'info',
69
+ message: `${indirectCount} fichier(s) impacte(s) en cascade (transitif).`,
70
+ });
71
+ }
72
+ // Fichier non indexe (inconnu)
73
+ if (!node) {
74
+ warnings.push({
75
+ level: 'info',
76
+ message: 'Fichier absent de l\'index. Lancez "reindex" pour une analyse complete.',
77
+ });
78
+ }
79
+ // -- Recommandation --
80
+ const safe = impact.risk === 'low' || impact.risk === 'medium';
81
+ // Fichiers a verifier : les dependants directs en priorite
82
+ const filesToCheck = [...directDeps];
83
+ // Ajouter les fichiers de routes impactes s'ils ne sont pas deja dans la liste
84
+ for (const route of impact.affectedRoutes) {
85
+ if (!filesToCheck.includes(route.filePath)) {
86
+ filesToCheck.push(route.filePath);
87
+ }
88
+ }
89
+ return {
90
+ filePath,
91
+ safe,
92
+ risk: impact.risk,
93
+ warnings,
94
+ filesToCheck,
95
+ exports,
96
+ impactCount: impact.impactCount,
97
+ };
98
+ }
99
+ /** Formate le resultat pour affichage MCP */
100
+ export function formatGuardResult(result) {
101
+ const lines = [];
102
+ const icon = result.safe ? 'OK' : 'ATTENTION';
103
+ lines.push(`## Guard : ${icon} — ${result.filePath}`);
104
+ lines.push(`**Risque** : ${result.risk.toUpperCase()}`);
105
+ lines.push(`**Fichiers impactes** : ${result.impactCount}`);
106
+ if (result.warnings.length > 0) {
107
+ lines.push('');
108
+ lines.push('### Avertissements');
109
+ for (const w of result.warnings) {
110
+ const prefix = w.level === 'danger' ? '/!\\ ' : w.level === 'warn' ? '! ' : '';
111
+ lines.push(`- ${prefix}${w.message}`);
112
+ }
113
+ }
114
+ if (result.filesToCheck.length > 0) {
115
+ lines.push('');
116
+ lines.push('### Fichiers a verifier apres modification');
117
+ for (const f of result.filesToCheck) {
118
+ lines.push(`- ${f}`);
119
+ }
120
+ }
121
+ if (result.exports.length > 0) {
122
+ lines.push('');
123
+ lines.push(`### Exports (${result.exports.length}) — ne pas supprimer/renommer sans verifier`);
124
+ lines.push(result.exports.join(', '));
125
+ }
126
+ if (!result.safe) {
127
+ lines.push('');
128
+ lines.push('> Modification risquee. Verifier les fichiers listes ci-dessus apres le changement.');
129
+ }
130
+ return lines.join('\n');
131
+ }
132
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/tools/guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Outil MCP : health
3
+ * Score de sante global du projet.
4
+ * Scanne l'index complet et detecte les problemes structurels.
5
+ */
6
+ import type { ProjectIndex } from '../storage/index-store.js';
7
+ import { DependencyGraph } from '../graph/dependency-graph.js';
8
+ export type HealthGrade = 'A' | 'B' | 'C' | 'D' | 'F';
9
+ export interface HealthIssue {
10
+ severity: 'error' | 'warning' | 'info';
11
+ category: string;
12
+ message: string;
13
+ file?: string;
14
+ }
15
+ export interface HealthResult {
16
+ grade: HealthGrade;
17
+ score: number;
18
+ fileCount: number;
19
+ issues: HealthIssue[];
20
+ metrics: {
21
+ brokenImports: number;
22
+ orphanFiles: number;
23
+ highRiskFiles: number;
24
+ circularDeps: number;
25
+ largeFiles: number;
26
+ totalExports: number;
27
+ totalImports: number;
28
+ };
29
+ }
30
+ export declare function runHealth(index: ProjectIndex, graph?: DependencyGraph): HealthResult;
31
+ /** Formate le resultat pour affichage MCP */
32
+ export declare function formatHealthResult(result: HealthResult): string;
33
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/tools/health.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAI/D,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,YAAY,CAmJpF;AA+DD,6CAA6C;AAC7C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA8C/D"}
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Outil MCP : health
3
+ * Score de sante global du projet.
4
+ * Scanne l'index complet et detecte les problemes structurels.
5
+ */
6
+ import { DependencyGraph } from '../graph/dependency-graph.js';
7
+ import { resolveImportPath } from '../utils/import-resolver.js';
8
+ import { toShortPath } from '../utils/path.js';
9
+ export function runHealth(index, graph) {
10
+ const g = graph ?? DependencyGraph.fromIndex(index);
11
+ const issues = [];
12
+ let penalty = 0;
13
+ const files = Object.entries(index.files);
14
+ let brokenImports = 0;
15
+ let orphanFiles = 0;
16
+ let highRiskFiles = 0;
17
+ let circularDeps = 0;
18
+ let largeFiles = 0;
19
+ let totalExports = 0;
20
+ let totalImports = 0;
21
+ // -- 1. Imports casses (resolution complete) --
22
+ for (const [filePath, node] of files) {
23
+ totalImports += node.imports.length;
24
+ totalExports += node.exports.length;
25
+ for (const imp of node.imports) {
26
+ if (!imp.source.startsWith('.'))
27
+ continue;
28
+ const resolved = resolveImportPath(filePath, imp.source, index.files, index.projectRoot);
29
+ if (!resolved) {
30
+ brokenImports++;
31
+ issues.push({
32
+ severity: 'error',
33
+ category: 'Import casse',
34
+ message: `Import "${imp.name}" from "${imp.source}" — cible introuvable`,
35
+ file: filePath,
36
+ });
37
+ }
38
+ }
39
+ }
40
+ penalty += brokenImports * 5;
41
+ // -- 2. Fichiers orphelins --
42
+ for (const [filePath] of files) {
43
+ const dependents = g.getDependents(filePath);
44
+ const deps = g.getDependencies(filePath);
45
+ const normalized = filePath.replace(/\\/g, '/');
46
+ const isEntryPoint = normalized.includes('index.ts') ||
47
+ normalized.includes('index.js') ||
48
+ normalized.includes('main.ts') ||
49
+ normalized.includes('cli.ts') ||
50
+ normalized.includes('/app/') ||
51
+ normalized.includes('route.ts') ||
52
+ normalized.includes('route.js');
53
+ // Fichiers de test — pas du code mort
54
+ const isTestFile = /\.(test|spec|e2e)\.(ts|tsx|js|jsx|mjs)$/.test(normalized) ||
55
+ /test[-.].*\.(ts|tsx|js|jsx|mjs)$/.test(normalized) ||
56
+ normalized.includes('/__tests__/') ||
57
+ normalized.includes('/tests/');
58
+ // Fichiers de config — utilises par les frameworks, pas par le code
59
+ const isConfigFile = /\.(config|setup)\.(ts|js|mjs|cjs)$/.test(normalized) ||
60
+ normalized.endsWith('/middleware.ts') ||
61
+ normalized.endsWith('/middleware.js') ||
62
+ normalized.includes('/instrumentation.') ||
63
+ normalized.endsWith('postcss.config.mjs') ||
64
+ normalized.endsWith('.config.ts') ||
65
+ normalized.endsWith('.config.js') ||
66
+ normalized.endsWith('.config.mjs');
67
+ if (dependents.length === 0 && deps.length === 0 && !isEntryPoint && !isTestFile && !isConfigFile) {
68
+ orphanFiles++;
69
+ issues.push({
70
+ severity: 'warning',
71
+ category: 'Fichier orphelin',
72
+ message: 'Ni importe ni importateur — potentiellement du code mort',
73
+ file: filePath,
74
+ });
75
+ }
76
+ // Les fichiers config sont listes en info (pas de penalite, mais visibles)
77
+ if (dependents.length === 0 && deps.length === 0 && isConfigFile) {
78
+ issues.push({
79
+ severity: 'info',
80
+ category: 'Fichier config',
81
+ message: 'Fichier de configuration (utilise par le framework, pas importe par le code)',
82
+ file: filePath,
83
+ });
84
+ }
85
+ }
86
+ // Penalite attenuee sur les gros projets (1pt au lieu de 2 au-dela de 100 fichiers)
87
+ const orphanPenalty = files.length > 100 ? 1 : 2;
88
+ penalty += orphanFiles * orphanPenalty;
89
+ // -- 3. Fichiers a haut risque (seuil adaptatif : 20% du projet, minimum 10) --
90
+ const highRiskThreshold = Math.max(10, Math.round(files.length * 0.2));
91
+ for (const [filePath] of files) {
92
+ const dependents = g.getDependents(filePath);
93
+ if (dependents.length >= highRiskThreshold) {
94
+ highRiskFiles++;
95
+ issues.push({
96
+ severity: 'warning',
97
+ category: 'Fichier a haut risque',
98
+ message: `${dependents.length} fichiers en dependent — toute modification impacte en cascade`,
99
+ file: filePath,
100
+ });
101
+ }
102
+ }
103
+ penalty += highRiskFiles * 3;
104
+ // -- 4. Dependances circulaires (Tarjan — SCC) --
105
+ const sccs = findStronglyConnectedComponents(g, files.map(([f]) => f));
106
+ circularDeps = sccs.length;
107
+ for (const scc of sccs) {
108
+ issues.push({
109
+ severity: 'error',
110
+ category: 'Dependance circulaire',
111
+ message: scc.map(toShortPath).join(' <-> '),
112
+ });
113
+ }
114
+ penalty += circularDeps * 8;
115
+ // -- 5. Fichiers volumineux --
116
+ for (const [filePath, node] of files) {
117
+ if (node.exports.length >= 15) {
118
+ largeFiles++;
119
+ issues.push({
120
+ severity: 'info',
121
+ category: 'Fichier volumineux',
122
+ message: `${node.exports.length} exports — envisager de decouper`,
123
+ file: filePath,
124
+ });
125
+ }
126
+ }
127
+ penalty += largeFiles;
128
+ // -- Score final --
129
+ const score = Math.max(0, Math.min(100, 100 - penalty));
130
+ const grade = scoreToGrade(score);
131
+ if (brokenImports === 0) {
132
+ issues.push({ severity: 'info', category: 'Imports', message: 'Aucun import casse detecte' });
133
+ }
134
+ if (circularDeps === 0) {
135
+ issues.push({ severity: 'info', category: 'Dependances', message: 'Aucune dependance circulaire' });
136
+ }
137
+ return {
138
+ grade, score, fileCount: files.length, issues,
139
+ metrics: { brokenImports, orphanFiles, highRiskFiles, circularDeps, largeFiles, totalExports, totalImports },
140
+ };
141
+ }
142
+ function scoreToGrade(score) {
143
+ if (score >= 90)
144
+ return 'A';
145
+ if (score >= 75)
146
+ return 'B';
147
+ if (score >= 60)
148
+ return 'C';
149
+ if (score >= 40)
150
+ return 'D';
151
+ return 'F';
152
+ }
153
+ /**
154
+ * Tarjan — trouve les composantes fortement connexes (cycles de toute taille).
155
+ * Retourne uniquement les SCC de taille >= 2 (les vrais cycles).
156
+ */
157
+ function findStronglyConnectedComponents(graph, filePaths) {
158
+ let indexCounter = 0;
159
+ const stack = [];
160
+ const onStack = new Set();
161
+ const indices = new Map();
162
+ const lowlinks = new Map();
163
+ const result = [];
164
+ function strongconnect(v) {
165
+ indices.set(v, indexCounter);
166
+ lowlinks.set(v, indexCounter);
167
+ indexCounter++;
168
+ stack.push(v);
169
+ onStack.add(v);
170
+ for (const w of graph.getDependencies(v)) {
171
+ if (!indices.has(w)) {
172
+ strongconnect(w);
173
+ lowlinks.set(v, Math.min(lowlinks.get(v), lowlinks.get(w)));
174
+ }
175
+ else if (onStack.has(w)) {
176
+ lowlinks.set(v, Math.min(lowlinks.get(v), indices.get(w)));
177
+ }
178
+ }
179
+ if (lowlinks.get(v) === indices.get(v)) {
180
+ const scc = [];
181
+ let w;
182
+ do {
183
+ w = stack.pop();
184
+ onStack.delete(w);
185
+ scc.push(w);
186
+ } while (w !== v);
187
+ // Uniquement les cycles (SCC de taille >= 2)
188
+ if (scc.length >= 2) {
189
+ result.push(scc);
190
+ }
191
+ }
192
+ }
193
+ for (const v of filePaths) {
194
+ if (!indices.has(v)) {
195
+ strongconnect(v);
196
+ }
197
+ }
198
+ return result;
199
+ }
200
+ /** Formate le resultat pour affichage MCP */
201
+ export function formatHealthResult(result) {
202
+ const lines = [];
203
+ lines.push(`## Health : ${result.grade} (${result.score}/100)`);
204
+ lines.push(`**Fichiers indexes** : ${result.fileCount}`);
205
+ lines.push(`**Imports** : ${result.metrics.totalImports} | **Exports** : ${result.metrics.totalExports}`);
206
+ lines.push('');
207
+ lines.push('### Metriques');
208
+ lines.push(`| Metrique | Valeur |`);
209
+ lines.push(`|---|---|`);
210
+ lines.push(`| Imports casses | ${result.metrics.brokenImports} |`);
211
+ lines.push(`| Fichiers orphelins | ${result.metrics.orphanFiles} |`);
212
+ lines.push(`| Fichiers haut risque | ${result.metrics.highRiskFiles} |`);
213
+ lines.push(`| Dependances circulaires | ${result.metrics.circularDeps} |`);
214
+ lines.push(`| Fichiers volumineux | ${result.metrics.largeFiles} |`);
215
+ const errors = result.issues.filter((i) => i.severity === 'error');
216
+ const warnings = result.issues.filter((i) => i.severity === 'warning');
217
+ const infos = result.issues.filter((i) => i.severity === 'info');
218
+ if (errors.length > 0) {
219
+ lines.push('');
220
+ lines.push('### Erreurs');
221
+ for (const issue of errors) {
222
+ lines.push(`- [${issue.category}] ${issue.message}${issue.file ? ` (${toShortPath(issue.file)})` : ''}`);
223
+ }
224
+ }
225
+ if (warnings.length > 0) {
226
+ lines.push('');
227
+ lines.push('### Avertissements');
228
+ for (const issue of warnings) {
229
+ lines.push(`- [${issue.category}] ${issue.message}${issue.file ? ` (${toShortPath(issue.file)})` : ''}`);
230
+ }
231
+ }
232
+ if (infos.length > 0) {
233
+ lines.push('');
234
+ lines.push('### Info');
235
+ for (const issue of infos) {
236
+ lines.push(`- ${issue.message}`);
237
+ }
238
+ }
239
+ return lines.join('\n');
240
+ }
241
+ //# sourceMappingURL=health.js.map