code-auditor-mcp 1.20.0 → 2.0.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.
- package/dist/adapters/GoAdapter.d.ts +53 -0
- package/dist/adapters/GoAdapter.d.ts.map +1 -0
- package/dist/adapters/GoAdapter.js +602 -0
- package/dist/adapters/GoAdapter.js.map +1 -0
- package/dist/adapters/GoConfig.d.ts +35 -0
- package/dist/adapters/GoConfig.d.ts.map +1 -0
- package/dist/adapters/GoConfig.js +155 -0
- package/dist/adapters/GoConfig.js.map +1 -0
- package/dist/adapters/LanguageAdapter.d.ts +135 -0
- package/dist/adapters/LanguageAdapter.d.ts.map +1 -0
- package/dist/adapters/LanguageAdapter.js +47 -0
- package/dist/adapters/LanguageAdapter.js.map +1 -0
- package/dist/adapters/TypeScriptAdapter.d.ts +43 -0
- package/dist/adapters/TypeScriptAdapter.d.ts.map +1 -0
- package/dist/adapters/TypeScriptAdapter.js +386 -0
- package/dist/adapters/TypeScriptAdapter.js.map +1 -0
- package/dist/adapters/UniversalAnalyzer.d.ts +42 -0
- package/dist/adapters/UniversalAnalyzer.d.ts.map +1 -0
- package/dist/adapters/UniversalAnalyzer.js +103 -0
- package/dist/adapters/UniversalAnalyzer.js.map +1 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts +48 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts.map +1 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.js +178 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.js.map +1 -0
- package/dist/adapters/solidAnalyzerCompat.d.ts +21 -0
- package/dist/adapters/solidAnalyzerCompat.d.ts.map +1 -0
- package/dist/adapters/solidAnalyzerCompat.js +78 -0
- package/dist/adapters/solidAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +103 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +425 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +117 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +618 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +129 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +473 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -0
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +126 -0
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -0
- package/dist/analyzers/cross-language/SchemaValidator.js +522 -0
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.js +59 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/documentationAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/documentationAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/documentationAnalyzerCompat.js +52 -0
- package/dist/analyzers/documentationAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/dryAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/dryAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/dryAnalyzerCompat.js +44 -0
- package/dist/analyzers/dryAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/schemaAnalyzer.d.ts +21 -0
- package/dist/analyzers/schemaAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/schemaAnalyzer.js +405 -0
- package/dist/analyzers/schemaAnalyzer.js.map +1 -0
- package/dist/analyzers/schemaAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/schemaAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/schemaAnalyzerCompat.js +69 -0
- package/dist/analyzers/schemaAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/solidAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/solidAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/solidAnalyzerCompat.js +78 -0
- package/dist/analyzers/solidAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +71 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +276 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +82 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +489 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +48 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +175 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +64 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +286 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +119 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +812 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +23 -7
- package/dist/auditRunner.js.map +1 -1
- package/dist/codeIndexDB-enhanced.d.ts +80 -0
- package/dist/codeIndexDB-enhanced.d.ts.map +1 -0
- package/dist/codeIndexDB-enhanced.js +488 -0
- package/dist/codeIndexDB-enhanced.js.map +1 -0
- package/dist/codeIndexDb.d.ts +70 -1
- package/dist/codeIndexDb.d.ts.map +1 -1
- package/dist/codeIndexDb.js +199 -2
- package/dist/codeIndexDb.js.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +181 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -0
- package/dist/languages/LanguageOrchestrator.js +365 -0
- package/dist/languages/LanguageOrchestrator.js.map +1 -0
- package/dist/languages/LanguageRegistry.d.ts +43 -0
- package/dist/languages/LanguageRegistry.d.ts.map +1 -0
- package/dist/languages/LanguageRegistry.js +86 -0
- package/dist/languages/LanguageRegistry.js.map +1 -0
- package/dist/languages/RuntimeManager.d.ts +181 -0
- package/dist/languages/RuntimeManager.d.ts.map +1 -0
- package/dist/languages/RuntimeManager.js +841 -0
- package/dist/languages/RuntimeManager.js.map +1 -0
- package/dist/languages/UniversalAnalyzer.d.ts +37 -0
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -0
- package/dist/languages/UniversalAnalyzer.js +101 -0
- package/dist/languages/UniversalAnalyzer.js.map +1 -0
- package/dist/languages/go/GoAdapter.d.ts +57 -0
- package/dist/languages/go/GoAdapter.d.ts.map +1 -0
- package/dist/languages/go/GoAdapter.js +360 -0
- package/dist/languages/go/GoAdapter.js.map +1 -0
- package/dist/languages/index.d.ts +13 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +23 -0
- package/dist/languages/index.js.map +1 -0
- package/dist/languages/types.d.ts +182 -0
- package/dist/languages/types.d.ts.map +1 -0
- package/dist/languages/types.js +5 -0
- package/dist/languages/types.js.map +1 -0
- package/dist/languages/typescript/TypeScriptAdapter.d.ts +108 -0
- package/dist/languages/typescript/TypeScriptAdapter.d.ts.map +1 -0
- package/dist/languages/typescript/TypeScriptAdapter.js +567 -0
- package/dist/languages/typescript/TypeScriptAdapter.js.map +1 -0
- package/dist/languages/typescript/test-extraction.d.ts +2 -0
- package/dist/languages/typescript/test-extraction.d.ts.map +1 -0
- package/dist/languages/typescript/test-extraction.js +36 -0
- package/dist/languages/typescript/test-extraction.js.map +1 -0
- package/dist/mcp-hybrid.d.ts +10 -0
- package/dist/mcp-hybrid.d.ts.map +1 -0
- package/dist/mcp-hybrid.js +41 -0
- package/dist/mcp-hybrid.js.map +1 -0
- package/dist/mcp-index.d.ts +11 -0
- package/dist/mcp-index.d.ts.map +1 -0
- package/dist/mcp-index.js +101 -0
- package/dist/mcp-index.js.map +1 -0
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +67 -10
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts +49 -0
- package/dist/mcp-tools-shared.d.ts.map +1 -0
- package/dist/mcp-tools-shared.js +1282 -0
- package/dist/mcp-tools-shared.js.map +1 -0
- package/dist/mcp-ui-server.d.ts +27 -0
- package/dist/mcp-ui-server.d.ts.map +1 -0
- package/dist/mcp-ui-server.js +484 -0
- package/dist/mcp-ui-server.js.map +1 -0
- package/dist/mcp-ui-simple.d.ts +27 -0
- package/dist/mcp-ui-simple.d.ts.map +1 -0
- package/dist/mcp-ui-simple.js +425 -0
- package/dist/mcp-ui-simple.js.map +1 -0
- package/dist/mcp.js +7 -4
- package/dist/mcp.js.map +1 -1
- package/dist/services/SchemaParser.d.ts +89 -0
- package/dist/services/SchemaParser.d.ts.map +1 -0
- package/dist/services/SchemaParser.js +434 -0
- package/dist/services/SchemaParser.js.map +1 -0
- package/dist/types/crossLanguage.d.ts +267 -0
- package/dist/types/crossLanguage.d.ts.map +1 -0
- package/dist/types/crossLanguage.js +6 -0
- package/dist/types/crossLanguage.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +6 -0
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +12 -1
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/examples/schema-example.json +364 -0
- package/examples/schema-example.yaml +137 -0
- package/package.json +10 -3
|
@@ -0,0 +1,1282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP Tools Logic
|
|
3
|
+
*
|
|
4
|
+
* This contains all the tool definitions and handlers that are shared
|
|
5
|
+
* between stdio and HTTP/UI MCP server implementations.
|
|
6
|
+
*/
|
|
7
|
+
import { createAuditRunner } from './auditRunner.js';
|
|
8
|
+
import { LanguageOrchestrator } from './languages/LanguageOrchestrator.js';
|
|
9
|
+
import { RuntimeManager } from './languages/RuntimeManager.js';
|
|
10
|
+
import { searchFunctions, findDefinition, syncFileIndex } from './codeIndexService.js';
|
|
11
|
+
import { CodeMapGenerator } from './services/CodeMapGenerator.js';
|
|
12
|
+
import { analyzeDocumentation } from './analyzers/documentationAnalyzer.js';
|
|
13
|
+
import { CodeIndexDB } from './codeIndexDB.js';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import fs from 'node:fs/promises';
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
export const tools = [
|
|
18
|
+
// Core Audit Tools
|
|
19
|
+
{
|
|
20
|
+
name: 'audit',
|
|
21
|
+
description: 'Run a comprehensive code audit on files or directories, including React component analysis',
|
|
22
|
+
parameters: [
|
|
23
|
+
{
|
|
24
|
+
name: 'path',
|
|
25
|
+
type: 'string',
|
|
26
|
+
required: false,
|
|
27
|
+
description: 'The file or directory path to audit (defaults to current directory)',
|
|
28
|
+
default: process.cwd(),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'analyzers',
|
|
32
|
+
type: 'array',
|
|
33
|
+
required: false,
|
|
34
|
+
description: 'List of analyzers to run (solid, dry, documentation, react, data-access)',
|
|
35
|
+
default: ['solid', 'dry', 'documentation', 'react', 'data-access'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'minSeverity',
|
|
39
|
+
type: 'string',
|
|
40
|
+
required: false,
|
|
41
|
+
description: 'Minimum severity level to report',
|
|
42
|
+
default: 'warning',
|
|
43
|
+
enum: ['info', 'warning', 'critical'],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'indexFunctions',
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
required: false,
|
|
49
|
+
description: 'Automatically index functions during audit',
|
|
50
|
+
default: true,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'analyzerConfigs',
|
|
54
|
+
type: 'object',
|
|
55
|
+
required: false,
|
|
56
|
+
description: 'Analyzer-specific configuration overrides (e.g., SOLID thresholds, DRY settings)',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'generateCodeMap',
|
|
60
|
+
type: 'boolean',
|
|
61
|
+
required: false,
|
|
62
|
+
description: 'Generate and return a human-readable code map as part of the audit results',
|
|
63
|
+
default: true,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'audit_health',
|
|
69
|
+
description: 'Quick health check of a codebase with key metrics',
|
|
70
|
+
parameters: [
|
|
71
|
+
{
|
|
72
|
+
name: 'path',
|
|
73
|
+
type: 'string',
|
|
74
|
+
required: false,
|
|
75
|
+
description: 'The directory path to check',
|
|
76
|
+
default: process.cwd(),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'threshold',
|
|
80
|
+
type: 'number',
|
|
81
|
+
required: false,
|
|
82
|
+
description: 'Health score threshold (0-100) for pass/fail',
|
|
83
|
+
default: 70,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'indexFunctions',
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
required: false,
|
|
89
|
+
description: 'Automatically index functions during health check',
|
|
90
|
+
default: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'analyzerConfigs',
|
|
94
|
+
type: 'object',
|
|
95
|
+
required: false,
|
|
96
|
+
description: 'Analyzer-specific configuration overrides (e.g., SOLID thresholds, DRY settings)',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'generateCodeMap',
|
|
100
|
+
type: 'boolean',
|
|
101
|
+
required: false,
|
|
102
|
+
description: 'Generate and return a human-readable code map as part of the health check results',
|
|
103
|
+
default: true,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
// Code Index Tools
|
|
108
|
+
{
|
|
109
|
+
name: 'search_code',
|
|
110
|
+
description: 'Search indexed functions and React components with natural language queries. Supports operators: entity:component, component:functional|class|memo|forwardRef, hook:useState|useEffect|etc, prop:propName, dep:packageName, dependency:lodash, uses:express, calls:functionName, calledby:functionName, dependents-of:functionName, used-by:functionName, depends-on:module, imports-from:file, unused-imports, dead-imports, type:fileType, file:path, lang:language, complexity:1-10, jsdoc:true|false',
|
|
111
|
+
parameters: [
|
|
112
|
+
{
|
|
113
|
+
name: 'query',
|
|
114
|
+
type: 'string',
|
|
115
|
+
required: true,
|
|
116
|
+
description: 'Search query with natural language and/or operators. Examples: "Button component:functional", "entity:component hook:useState", "render prop:onClick", "dep:lodash", "calls:validateUser", "unused-imports", "dependents-of:authenticate"',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'filters',
|
|
120
|
+
type: 'object',
|
|
121
|
+
required: false,
|
|
122
|
+
description: 'Optional filters (language, filePath, dependencies, componentType, entityType, searchMode). Set searchMode to "content" to search within function bodies, "metadata" for names/signatures only, or "both" for combined search',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'limit',
|
|
126
|
+
type: 'number',
|
|
127
|
+
required: false,
|
|
128
|
+
description: 'Maximum results to return',
|
|
129
|
+
default: 50,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'offset',
|
|
133
|
+
type: 'number',
|
|
134
|
+
required: false,
|
|
135
|
+
description: 'Offset for pagination',
|
|
136
|
+
default: 0,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'find_definition',
|
|
142
|
+
description: 'Find the exact definition of a specific function or React component',
|
|
143
|
+
parameters: [
|
|
144
|
+
{
|
|
145
|
+
name: 'name',
|
|
146
|
+
type: 'string',
|
|
147
|
+
required: true,
|
|
148
|
+
description: 'Function or component name to find',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: 'filePath',
|
|
152
|
+
type: 'string',
|
|
153
|
+
required: false,
|
|
154
|
+
description: 'Optional file path to narrow search',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'sync_index',
|
|
160
|
+
description: 'Synchronize, cleanup, or reset the code index',
|
|
161
|
+
parameters: [
|
|
162
|
+
{
|
|
163
|
+
name: 'mode',
|
|
164
|
+
type: 'string',
|
|
165
|
+
required: false,
|
|
166
|
+
description: 'Operation mode: sync (update), cleanup (remove stale), or reset (clear all)',
|
|
167
|
+
default: 'sync',
|
|
168
|
+
enum: ['sync', 'cleanup', 'reset'],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'path',
|
|
172
|
+
type: 'string',
|
|
173
|
+
required: false,
|
|
174
|
+
description: 'Optional specific path to sync',
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
// AI Configuration Tool
|
|
179
|
+
{
|
|
180
|
+
name: 'generate_ai_config',
|
|
181
|
+
description: 'Generate configuration files for AI coding assistants',
|
|
182
|
+
parameters: [
|
|
183
|
+
{
|
|
184
|
+
name: 'tools',
|
|
185
|
+
type: 'array',
|
|
186
|
+
required: true,
|
|
187
|
+
description: 'AI tools to configure (cursor, continue, copilot, claude, zed, windsurf, cody, aider, cline, pearai)',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'outputDir',
|
|
191
|
+
type: 'string',
|
|
192
|
+
required: false,
|
|
193
|
+
description: 'Output directory for configuration files',
|
|
194
|
+
default: '.',
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
// Workflow Guide Tool
|
|
199
|
+
{
|
|
200
|
+
name: 'get_workflow_guide',
|
|
201
|
+
description: 'Get recommended workflows and best practices for using code auditor tools effectively',
|
|
202
|
+
parameters: [
|
|
203
|
+
{
|
|
204
|
+
name: 'scenario',
|
|
205
|
+
type: 'string',
|
|
206
|
+
required: false,
|
|
207
|
+
description: 'Specific scenario: initial-setup, react-development, code-review, find-patterns, maintenance. Leave empty to see all.',
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
// Analyzer Configuration Tools
|
|
212
|
+
{
|
|
213
|
+
name: 'set_analyzer_config',
|
|
214
|
+
description: 'Set or update analyzer configuration that persists across audit runs',
|
|
215
|
+
parameters: [
|
|
216
|
+
{
|
|
217
|
+
name: 'analyzerName',
|
|
218
|
+
type: 'string',
|
|
219
|
+
required: true,
|
|
220
|
+
description: 'The analyzer to configure (solid, dry, security, etc.)',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'config',
|
|
224
|
+
type: 'object',
|
|
225
|
+
required: true,
|
|
226
|
+
description: 'Configuration object for the analyzer (e.g., thresholds, rules)',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: 'projectPath',
|
|
230
|
+
type: 'string',
|
|
231
|
+
required: false,
|
|
232
|
+
description: 'Optional project path for project-specific config (defaults to global)',
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'get_analyzer_config',
|
|
238
|
+
description: 'Get current configuration for an analyzer',
|
|
239
|
+
parameters: [
|
|
240
|
+
{
|
|
241
|
+
name: 'analyzerName',
|
|
242
|
+
type: 'string',
|
|
243
|
+
required: false,
|
|
244
|
+
description: 'Specific analyzer name, or omit to get all configs',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: 'projectPath',
|
|
248
|
+
type: 'string',
|
|
249
|
+
required: false,
|
|
250
|
+
description: 'Optional project path to get project-specific config',
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'reset_analyzer_config',
|
|
256
|
+
description: 'Reset analyzer configuration to defaults',
|
|
257
|
+
parameters: [
|
|
258
|
+
{
|
|
259
|
+
name: 'analyzerName',
|
|
260
|
+
type: 'string',
|
|
261
|
+
required: false,
|
|
262
|
+
description: 'Specific analyzer to reset, or omit to reset all',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'projectPath',
|
|
266
|
+
type: 'string',
|
|
267
|
+
required: false,
|
|
268
|
+
description: 'Optional project path to reset only project-specific config',
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: 'get_code_map_section',
|
|
274
|
+
description: 'Retrieve a specific section of a previously generated code map',
|
|
275
|
+
parameters: [
|
|
276
|
+
{
|
|
277
|
+
name: 'mapId',
|
|
278
|
+
type: 'string',
|
|
279
|
+
required: true,
|
|
280
|
+
description: 'The map ID returned from a previous audit with code map generation',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: 'sectionType',
|
|
284
|
+
type: 'string',
|
|
285
|
+
required: true,
|
|
286
|
+
description: 'The section type to retrieve (e.g., overview, files, dependencies, documentation)',
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'list_code_map_sections',
|
|
292
|
+
description: 'List all available sections for a code map',
|
|
293
|
+
parameters: [
|
|
294
|
+
{
|
|
295
|
+
name: 'mapId',
|
|
296
|
+
type: 'string',
|
|
297
|
+
required: true,
|
|
298
|
+
description: 'The map ID returned from a previous audit',
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
// Database Schema Management Tools
|
|
303
|
+
{
|
|
304
|
+
name: 'generate_schema_discovery_sql',
|
|
305
|
+
description: 'Generate SQL queries for LLMs to extract database schema information automatically',
|
|
306
|
+
parameters: [
|
|
307
|
+
{
|
|
308
|
+
name: 'databaseType',
|
|
309
|
+
type: 'string',
|
|
310
|
+
required: true,
|
|
311
|
+
enum: ['postgresql', 'mysql', 'sqlite', 'sqlserver', 'oracle'],
|
|
312
|
+
description: 'Type of database to generate queries for',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: 'includeIndexes',
|
|
316
|
+
type: 'boolean',
|
|
317
|
+
required: false,
|
|
318
|
+
default: true,
|
|
319
|
+
description: 'Include queries to discover indexes',
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: 'includeConstraints',
|
|
323
|
+
type: 'boolean',
|
|
324
|
+
required: false,
|
|
325
|
+
default: true,
|
|
326
|
+
description: 'Include queries to discover foreign key constraints',
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: 'specificTables',
|
|
330
|
+
type: 'array',
|
|
331
|
+
required: false,
|
|
332
|
+
description: 'Limit discovery to specific table names (optional)',
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: 'get_schemas',
|
|
338
|
+
description: 'List all loaded database schemas with their metadata',
|
|
339
|
+
parameters: [],
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'search_schema',
|
|
343
|
+
description: 'Search for tables, columns, or relationships in loaded schemas',
|
|
344
|
+
parameters: [
|
|
345
|
+
{
|
|
346
|
+
name: 'query',
|
|
347
|
+
type: 'string',
|
|
348
|
+
required: true,
|
|
349
|
+
description: 'Search query for table/column names or descriptions',
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: 'schemaId',
|
|
353
|
+
type: 'string',
|
|
354
|
+
required: false,
|
|
355
|
+
description: 'Limit search to specific schema ID',
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: 'searchType',
|
|
359
|
+
type: 'string',
|
|
360
|
+
required: false,
|
|
361
|
+
enum: ['tables', 'columns', 'relationships', 'all'],
|
|
362
|
+
default: 'all',
|
|
363
|
+
description: 'Type of schema elements to search',
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: 'analyze_schema_usage',
|
|
369
|
+
description: 'Analyze how database tables are used in the codebase',
|
|
370
|
+
parameters: [
|
|
371
|
+
{
|
|
372
|
+
name: 'path',
|
|
373
|
+
type: 'string',
|
|
374
|
+
required: false,
|
|
375
|
+
default: '.',
|
|
376
|
+
description: 'Path to analyze for schema usage patterns',
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: 'schemaId',
|
|
380
|
+
type: 'string',
|
|
381
|
+
required: false,
|
|
382
|
+
description: 'Schema ID to analyze against',
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: 'includeUsagePatterns',
|
|
386
|
+
type: 'boolean',
|
|
387
|
+
required: false,
|
|
388
|
+
default: true,
|
|
389
|
+
description: 'Whether to include detailed usage patterns',
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'find_table_usage',
|
|
395
|
+
description: 'Find all functions that interact with a specific database table',
|
|
396
|
+
parameters: [
|
|
397
|
+
{
|
|
398
|
+
name: 'tableName',
|
|
399
|
+
type: 'string',
|
|
400
|
+
required: true,
|
|
401
|
+
description: 'Name of the table to find usage for',
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: 'usageType',
|
|
405
|
+
type: 'string',
|
|
406
|
+
required: false,
|
|
407
|
+
enum: ['query', 'insert', 'update', 'delete', 'reference', 'all'],
|
|
408
|
+
default: 'all',
|
|
409
|
+
description: 'Type of table usage to find',
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: 'validate_schema_consistency',
|
|
415
|
+
description: 'Validate schema consistency and find potential issues',
|
|
416
|
+
parameters: [
|
|
417
|
+
{
|
|
418
|
+
name: 'schemaId',
|
|
419
|
+
type: 'string',
|
|
420
|
+
required: false,
|
|
421
|
+
description: 'Schema ID to validate (validates all if not specified)',
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'checkCircularDeps',
|
|
425
|
+
type: 'boolean',
|
|
426
|
+
required: false,
|
|
427
|
+
default: true,
|
|
428
|
+
description: 'Check for circular dependencies',
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: 'checkNamingConventions',
|
|
432
|
+
type: 'boolean',
|
|
433
|
+
required: false,
|
|
434
|
+
default: true,
|
|
435
|
+
description: 'Check naming convention compliance',
|
|
436
|
+
},
|
|
437
|
+
],
|
|
438
|
+
},
|
|
439
|
+
];
|
|
440
|
+
// UI-specific tools (only available in UI mode)
|
|
441
|
+
export const uiTools = [
|
|
442
|
+
{
|
|
443
|
+
name: 'audit_dashboard',
|
|
444
|
+
description: 'Generates an interactive dashboard with detailed audit findings, code maps, and remediation options.',
|
|
445
|
+
parameters: [
|
|
446
|
+
{
|
|
447
|
+
name: 'path',
|
|
448
|
+
type: 'string',
|
|
449
|
+
required: false,
|
|
450
|
+
description: 'Path to audit',
|
|
451
|
+
default: '.',
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: 'analyzers',
|
|
455
|
+
type: 'array',
|
|
456
|
+
required: false,
|
|
457
|
+
description: 'Analyzers to run',
|
|
458
|
+
default: ['solid', 'dry', 'documentation', 'react', 'data-access'],
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: 'minSeverity',
|
|
462
|
+
type: 'string',
|
|
463
|
+
required: false,
|
|
464
|
+
description: 'Minimum severity level',
|
|
465
|
+
default: 'warning',
|
|
466
|
+
enum: ['info', 'warning', 'critical'],
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
name: 'code_map_viewer',
|
|
472
|
+
description: 'Generates an interactive, navigable code map with file structure, complexity analysis, and documentation coverage.',
|
|
473
|
+
parameters: [
|
|
474
|
+
{
|
|
475
|
+
name: 'path',
|
|
476
|
+
type: 'string',
|
|
477
|
+
required: false,
|
|
478
|
+
description: 'Path to analyze',
|
|
479
|
+
default: '.',
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
];
|
|
484
|
+
/**
|
|
485
|
+
* Shared tool handler implementations
|
|
486
|
+
*/
|
|
487
|
+
export class ToolHandlers {
|
|
488
|
+
static async handleAudit(args) {
|
|
489
|
+
const auditPath = path.resolve(args.path || process.cwd());
|
|
490
|
+
const indexFunctions = args.indexFunctions !== false; // Default true
|
|
491
|
+
const generateCodeMap = args.generateCodeMap !== false; // Default true
|
|
492
|
+
// Check if path is a file or directory
|
|
493
|
+
const stats = await fs.stat(auditPath).catch(() => null);
|
|
494
|
+
const isFile = stats?.isFile() || false;
|
|
495
|
+
// Get stored analyzer configs from database
|
|
496
|
+
const db = CodeIndexDB.getInstance();
|
|
497
|
+
await db.initialize();
|
|
498
|
+
const storedConfigs = await db.getAllAnalyzerConfigs(auditPath);
|
|
499
|
+
// Merge stored configs with any provided configs
|
|
500
|
+
const analyzerConfigs = {
|
|
501
|
+
...storedConfigs,
|
|
502
|
+
...(args.analyzerConfigs || {})
|
|
503
|
+
};
|
|
504
|
+
const options = {
|
|
505
|
+
projectRoot: isFile ? path.dirname(auditPath) : auditPath,
|
|
506
|
+
enabledAnalyzers: args.analyzers || ['solid', 'dry', 'documentation', 'react', 'data-access'],
|
|
507
|
+
minSeverity: (args.minSeverity || 'warning'),
|
|
508
|
+
verbose: false,
|
|
509
|
+
indexFunctions,
|
|
510
|
+
...(isFile && { includePaths: [auditPath] }),
|
|
511
|
+
...(Object.keys(analyzerConfigs).length > 0 && { analyzerConfigs }),
|
|
512
|
+
};
|
|
513
|
+
// Check if this is a multi-language project by detecting Go files
|
|
514
|
+
console.error(chalk.yellow('[DEBUG]'), `Checking for Go files in: ${auditPath}`);
|
|
515
|
+
const hasGoFiles = await ToolHandlers.hasFilesWithExtensions(auditPath, ['.go']);
|
|
516
|
+
console.error(chalk.yellow('[DEBUG]'), `Go files detected: ${hasGoFiles}`);
|
|
517
|
+
let auditResult;
|
|
518
|
+
if (hasGoFiles) {
|
|
519
|
+
// Use multi-language orchestrator for projects with Go files
|
|
520
|
+
console.error(chalk.blue('[INFO]'), 'Multi-language project detected, using LanguageOrchestrator');
|
|
521
|
+
const runtimeManager = new RuntimeManager();
|
|
522
|
+
await runtimeManager.initialize();
|
|
523
|
+
const codeIndex = CodeIndexDB.getInstance();
|
|
524
|
+
await codeIndex.initialize();
|
|
525
|
+
const orchestrator = new LanguageOrchestrator(runtimeManager, codeIndex);
|
|
526
|
+
const polyglotResult = await orchestrator.analyzePolyglotProject(auditPath, {
|
|
527
|
+
analyzers: options.enabledAnalyzers,
|
|
528
|
+
minSeverity: options.minSeverity,
|
|
529
|
+
updateIndex: indexFunctions,
|
|
530
|
+
enableCrossLanguageAnalysis: true,
|
|
531
|
+
buildCrossReferences: true,
|
|
532
|
+
});
|
|
533
|
+
// Convert polyglot result to legacy audit result format
|
|
534
|
+
auditResult = ToolHandlers.convertPolyglotToAuditResult(polyglotResult, auditPath);
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
// Use legacy audit system for TypeScript-only projects
|
|
538
|
+
const runner = createAuditRunner(options);
|
|
539
|
+
auditResult = await runner.run();
|
|
540
|
+
}
|
|
541
|
+
// Handle function indexing if enabled and functions were collected
|
|
542
|
+
let indexingResult = null;
|
|
543
|
+
if (indexFunctions && auditResult.metadata.fileToFunctionsMap) {
|
|
544
|
+
try {
|
|
545
|
+
const syncStats = { added: 0, updated: 0, removed: 0 };
|
|
546
|
+
// Sync each file's functions to handle additions, updates, and removals
|
|
547
|
+
for (const [filePath, functions] of Object.entries(auditResult.metadata.fileToFunctionsMap)) {
|
|
548
|
+
const fileStats = await syncFileIndex(filePath, functions);
|
|
549
|
+
syncStats.added += fileStats.added;
|
|
550
|
+
syncStats.updated += fileStats.updated;
|
|
551
|
+
syncStats.removed += fileStats.removed;
|
|
552
|
+
}
|
|
553
|
+
indexingResult = {
|
|
554
|
+
success: true,
|
|
555
|
+
registered: syncStats.added + syncStats.updated,
|
|
556
|
+
failed: 0,
|
|
557
|
+
syncStats
|
|
558
|
+
};
|
|
559
|
+
console.error(chalk.blue('[INFO]'), `Synced functions: ${syncStats.added} added, ${syncStats.updated} updated, ${syncStats.removed} removed`);
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
console.error(chalk.yellow('[WARN]'), 'Failed to sync functions:', error);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// Generate code map if requested and functions were indexed
|
|
566
|
+
let codeMapResult = null;
|
|
567
|
+
if (generateCodeMap && indexingResult && indexingResult.success) {
|
|
568
|
+
try {
|
|
569
|
+
const mapGenerator = new CodeMapGenerator();
|
|
570
|
+
const mapOptions = {
|
|
571
|
+
includeComplexity: true,
|
|
572
|
+
includeDocumentation: true,
|
|
573
|
+
includeDependencies: true,
|
|
574
|
+
includeUsage: false,
|
|
575
|
+
groupByDirectory: true,
|
|
576
|
+
maxDepth: 10,
|
|
577
|
+
showUnusedImports: true,
|
|
578
|
+
minComplexity: 7,
|
|
579
|
+
};
|
|
580
|
+
// Generate documentation metrics
|
|
581
|
+
let documentation = undefined;
|
|
582
|
+
try {
|
|
583
|
+
const files = Object.keys(auditResult.metadata.fileToFunctionsMap || {});
|
|
584
|
+
if (files.length > 0) {
|
|
585
|
+
const docResult = await analyzeDocumentation(files);
|
|
586
|
+
documentation = docResult.metrics;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
catch (docError) {
|
|
590
|
+
console.error(chalk.yellow('[WARN]'), 'Failed to analyze documentation:', docError);
|
|
591
|
+
}
|
|
592
|
+
// Use paginated code map generation
|
|
593
|
+
const paginatedResult = await mapGenerator.generatePaginatedCodeMap(auditPath, {
|
|
594
|
+
...mapOptions,
|
|
595
|
+
includeDocumentation: !!documentation
|
|
596
|
+
});
|
|
597
|
+
codeMapResult = {
|
|
598
|
+
success: true,
|
|
599
|
+
mapId: paginatedResult.mapId,
|
|
600
|
+
summary: paginatedResult.summary,
|
|
601
|
+
quickPreview: paginatedResult.quickPreview,
|
|
602
|
+
sections: paginatedResult.summary.sectionsAvailable,
|
|
603
|
+
documentationCoverage: documentation?.coverageScore
|
|
604
|
+
};
|
|
605
|
+
console.error(chalk.blue('[INFO]'), `Generated paginated code map: ${paginatedResult.summary.stats.totalFiles} files, ${paginatedResult.summary.totalSections} sections`);
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
console.error(chalk.yellow('[WARN]'), 'Failed to generate code map:', error);
|
|
609
|
+
codeMapResult = {
|
|
610
|
+
success: false,
|
|
611
|
+
error: error instanceof Error ? error.message : 'Failed to generate code map'
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
// Format for MCP
|
|
616
|
+
return {
|
|
617
|
+
summary: {
|
|
618
|
+
totalViolations: auditResult.summary.totalViolations,
|
|
619
|
+
criticalIssues: auditResult.summary.criticalIssues,
|
|
620
|
+
warnings: auditResult.summary.warnings,
|
|
621
|
+
suggestions: auditResult.summary.suggestions,
|
|
622
|
+
filesAnalyzed: auditResult.metadata.filesAnalyzed,
|
|
623
|
+
executionTime: auditResult.metadata.auditDuration,
|
|
624
|
+
healthScore: ToolHandlers.calculateHealthScore(auditResult),
|
|
625
|
+
},
|
|
626
|
+
violations: ToolHandlers.getAllViolations(auditResult).slice(0, 100), // Limit to first 100
|
|
627
|
+
recommendations: auditResult.recommendations,
|
|
628
|
+
...(indexingResult && { functionIndexing: indexingResult }),
|
|
629
|
+
...(codeMapResult && { codeMap: codeMapResult }),
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
static async handleAuditHealth(args) {
|
|
633
|
+
const auditPath = path.resolve(args.path || process.cwd());
|
|
634
|
+
const threshold = args.threshold || 70;
|
|
635
|
+
const indexFunctions = args.indexFunctions !== false; // Default true
|
|
636
|
+
const generateCodeMap = args.generateCodeMap !== false; // Default true
|
|
637
|
+
// Get stored analyzer configs from database
|
|
638
|
+
const db = CodeIndexDB.getInstance();
|
|
639
|
+
await db.initialize();
|
|
640
|
+
const storedConfigs = await db.getAllAnalyzerConfigs(auditPath);
|
|
641
|
+
// Merge stored configs with any provided configs
|
|
642
|
+
const analyzerConfigs = {
|
|
643
|
+
...storedConfigs,
|
|
644
|
+
...(args.analyzerConfigs || {})
|
|
645
|
+
};
|
|
646
|
+
const runner = createAuditRunner({
|
|
647
|
+
projectRoot: auditPath,
|
|
648
|
+
enabledAnalyzers: ['solid', 'dry', 'documentation', 'react', 'data-access'],
|
|
649
|
+
minSeverity: 'warning',
|
|
650
|
+
verbose: false,
|
|
651
|
+
indexFunctions,
|
|
652
|
+
...(Object.keys(analyzerConfigs).length > 0 && { analyzerConfigs }),
|
|
653
|
+
});
|
|
654
|
+
const auditResult = await runner.run();
|
|
655
|
+
const healthScore = ToolHandlers.calculateHealthScore(auditResult);
|
|
656
|
+
// Handle function indexing if enabled and functions were collected
|
|
657
|
+
let indexingResult = null;
|
|
658
|
+
if (indexFunctions && auditResult.metadata.fileToFunctionsMap) {
|
|
659
|
+
try {
|
|
660
|
+
const syncStats = { added: 0, updated: 0, removed: 0 };
|
|
661
|
+
// Sync each file's functions to handle additions, updates, and removals
|
|
662
|
+
for (const [filePath, functions] of Object.entries(auditResult.metadata.fileToFunctionsMap)) {
|
|
663
|
+
const fileStats = await syncFileIndex(filePath, functions);
|
|
664
|
+
syncStats.added += fileStats.added;
|
|
665
|
+
syncStats.updated += fileStats.updated;
|
|
666
|
+
syncStats.removed += fileStats.removed;
|
|
667
|
+
}
|
|
668
|
+
indexingResult = {
|
|
669
|
+
success: true,
|
|
670
|
+
registered: syncStats.added + syncStats.updated,
|
|
671
|
+
failed: 0,
|
|
672
|
+
syncStats
|
|
673
|
+
};
|
|
674
|
+
console.error(chalk.blue('[INFO]'), `Synced functions: ${syncStats.added} added, ${syncStats.updated} updated, ${syncStats.removed} removed`);
|
|
675
|
+
}
|
|
676
|
+
catch (error) {
|
|
677
|
+
console.error(chalk.yellow('[WARN]'), 'Failed to sync functions:', error);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// Generate code map if requested and functions were indexed
|
|
681
|
+
let codeMapResult = null;
|
|
682
|
+
if (generateCodeMap && indexingResult && indexingResult.success) {
|
|
683
|
+
try {
|
|
684
|
+
const mapGenerator = new CodeMapGenerator();
|
|
685
|
+
const mapOptions = {
|
|
686
|
+
includeComplexity: true,
|
|
687
|
+
includeDocumentation: true,
|
|
688
|
+
includeDependencies: true,
|
|
689
|
+
includeUsage: false,
|
|
690
|
+
groupByDirectory: true,
|
|
691
|
+
maxDepth: 8, // Slightly smaller for health check
|
|
692
|
+
showUnusedImports: true,
|
|
693
|
+
minComplexity: 7,
|
|
694
|
+
};
|
|
695
|
+
// Generate documentation metrics
|
|
696
|
+
let documentation = undefined;
|
|
697
|
+
try {
|
|
698
|
+
const files = Object.keys(auditResult.metadata.fileToFunctionsMap || {});
|
|
699
|
+
if (files.length > 0) {
|
|
700
|
+
const docResult = await analyzeDocumentation(files);
|
|
701
|
+
documentation = docResult.metrics;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
catch (docError) {
|
|
705
|
+
console.error(chalk.yellow('[WARN]'), 'Failed to analyze documentation:', docError);
|
|
706
|
+
}
|
|
707
|
+
// Use paginated code map generation for health check too
|
|
708
|
+
const paginatedResult = await mapGenerator.generatePaginatedCodeMap(auditPath, {
|
|
709
|
+
...mapOptions,
|
|
710
|
+
includeDocumentation: !!documentation
|
|
711
|
+
});
|
|
712
|
+
codeMapResult = {
|
|
713
|
+
success: true,
|
|
714
|
+
mapId: paginatedResult.mapId,
|
|
715
|
+
summary: paginatedResult.summary,
|
|
716
|
+
quickPreview: paginatedResult.quickPreview,
|
|
717
|
+
sections: paginatedResult.summary.sectionsAvailable,
|
|
718
|
+
documentationCoverage: documentation?.coverageScore
|
|
719
|
+
};
|
|
720
|
+
console.error(chalk.blue('[INFO]'), `Generated paginated code map: ${paginatedResult.summary.stats.totalFiles} files, ${paginatedResult.summary.totalSections} sections`);
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
console.error(chalk.yellow('[WARN]'), 'Failed to generate code map:', error);
|
|
724
|
+
codeMapResult = {
|
|
725
|
+
success: false,
|
|
726
|
+
error: error instanceof Error ? error.message : 'Failed to generate code map'
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
healthScore,
|
|
732
|
+
threshold,
|
|
733
|
+
passed: healthScore >= threshold,
|
|
734
|
+
status: healthScore >= threshold ? 'healthy' : 'needs-attention',
|
|
735
|
+
metrics: {
|
|
736
|
+
filesAnalyzed: auditResult.metadata.filesAnalyzed,
|
|
737
|
+
totalViolations: auditResult.summary.totalViolations,
|
|
738
|
+
criticalViolations: auditResult.summary.criticalIssues,
|
|
739
|
+
warningViolations: auditResult.summary.warnings,
|
|
740
|
+
},
|
|
741
|
+
recommendation: ToolHandlers.getHealthRecommendation(healthScore, auditResult),
|
|
742
|
+
...(indexingResult && { functionIndexing: indexingResult }),
|
|
743
|
+
...(codeMapResult && { codeMap: codeMapResult }),
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
// Add all other tool handlers here following the same pattern...
|
|
747
|
+
// (I'll include key ones for brevity)
|
|
748
|
+
static async handleSearchCode(args) {
|
|
749
|
+
const query = args.query;
|
|
750
|
+
const filters = args.filters;
|
|
751
|
+
const limit = args.limit || 50;
|
|
752
|
+
const offset = args.offset || 0;
|
|
753
|
+
if (query !== undefined && typeof query !== 'string') {
|
|
754
|
+
throw new Error('query must be a string');
|
|
755
|
+
}
|
|
756
|
+
return await searchFunctions({
|
|
757
|
+
query,
|
|
758
|
+
filters,
|
|
759
|
+
limit,
|
|
760
|
+
offset
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
static async handleFindDefinition(args) {
|
|
764
|
+
const name = args.name;
|
|
765
|
+
const filePath = args.filePath;
|
|
766
|
+
if (!name || typeof name !== 'string') {
|
|
767
|
+
throw new Error('name must be a non-empty string');
|
|
768
|
+
}
|
|
769
|
+
const definition = await findDefinition(name, filePath);
|
|
770
|
+
return definition || { error: 'Function not found' };
|
|
771
|
+
}
|
|
772
|
+
// Schema Management Tool Handlers
|
|
773
|
+
static async handleGenerateSchemaDiscoverySQL(args) {
|
|
774
|
+
const databaseType = args.databaseType;
|
|
775
|
+
const includeIndexes = args.includeIndexes !== false;
|
|
776
|
+
const includeConstraints = args.includeConstraints !== false;
|
|
777
|
+
const specificTables = args.specificTables;
|
|
778
|
+
if (!databaseType || typeof databaseType !== 'string') {
|
|
779
|
+
throw new Error('databaseType must be a non-empty string');
|
|
780
|
+
}
|
|
781
|
+
const sqlQueries = ToolHandlers.generateSchemaDiscoveryQueries(databaseType, includeIndexes, includeConstraints, specificTables);
|
|
782
|
+
return {
|
|
783
|
+
databaseType,
|
|
784
|
+
queries: sqlQueries,
|
|
785
|
+
instructions: [
|
|
786
|
+
"Execute these SQL queries against your database",
|
|
787
|
+
"Copy the results and use create_schema_from_sql_result to import them",
|
|
788
|
+
"Each query discovers different aspects of your database schema",
|
|
789
|
+
"Run them in order and collect all results"
|
|
790
|
+
],
|
|
791
|
+
nextStep: "Use create_schema_from_sql_result with the query results"
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
static async handleCreateSchemaFromSqlResult(args) {
|
|
795
|
+
const schemaName = args.schemaName;
|
|
796
|
+
const databaseType = args.databaseType;
|
|
797
|
+
const tablesData = args.tablesData;
|
|
798
|
+
const columnsData = args.columnsData;
|
|
799
|
+
const constraintsData = args.constraintsData;
|
|
800
|
+
if (!schemaName || !databaseType || !tablesData || !columnsData) {
|
|
801
|
+
throw new Error('schemaName, databaseType, tablesData, and columnsData are required');
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
const schema = ToolHandlers.buildSchemaFromSqlData(schemaName, databaseType, tablesData, columnsData, constraintsData);
|
|
805
|
+
const db = CodeIndexDB.getInstance();
|
|
806
|
+
await db.initialize();
|
|
807
|
+
const schemaId = await db.storeSchema(schema);
|
|
808
|
+
return {
|
|
809
|
+
success: true,
|
|
810
|
+
schemaId,
|
|
811
|
+
schemaName: schema.name,
|
|
812
|
+
stats: {
|
|
813
|
+
databaseCount: schema.databases.length,
|
|
814
|
+
tableCount: schema.databases.reduce((acc, db) => acc + db.tables.length, 0),
|
|
815
|
+
columnCount: schema.databases.reduce((acc, db) => acc + db.tables.reduce((tAcc, table) => tAcc + table.columns.length, 0), 0)
|
|
816
|
+
},
|
|
817
|
+
message: `Schema '${schema.name}' created from SQL results`
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
catch (error) {
|
|
821
|
+
return {
|
|
822
|
+
success: false,
|
|
823
|
+
error: `Failed to create schema from SQL results: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
static async handleAddTableManually(args) {
|
|
828
|
+
const schemaName = args.schemaName;
|
|
829
|
+
const tableName = args.tableName;
|
|
830
|
+
const columns = args.columns;
|
|
831
|
+
const databaseType = args.databaseType || 'postgresql';
|
|
832
|
+
if (!schemaName || !tableName || !columns || !Array.isArray(columns)) {
|
|
833
|
+
throw new Error('schemaName, tableName, and columns array are required');
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
const db = CodeIndexDB.getInstance();
|
|
837
|
+
await db.initialize();
|
|
838
|
+
// Get existing schema or create new one
|
|
839
|
+
const existingSchemas = await db.getAllSchemas();
|
|
840
|
+
let schema = existingSchemas.find(s => s.schema.name === schemaName)?.schema;
|
|
841
|
+
if (!schema) {
|
|
842
|
+
// Create new schema
|
|
843
|
+
schema = {
|
|
844
|
+
version: "1.0.0",
|
|
845
|
+
name: schemaName,
|
|
846
|
+
description: `Manually created schema: ${schemaName}`,
|
|
847
|
+
databases: [{
|
|
848
|
+
name: 'default',
|
|
849
|
+
type: databaseType,
|
|
850
|
+
tables: []
|
|
851
|
+
}]
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
// Add the table
|
|
855
|
+
const newTable = {
|
|
856
|
+
name: tableName,
|
|
857
|
+
type: 'table',
|
|
858
|
+
columns: columns.map(col => ({
|
|
859
|
+
name: col.name,
|
|
860
|
+
type: col.type,
|
|
861
|
+
nullable: col.nullable !== false,
|
|
862
|
+
primaryKey: col.primaryKey || false,
|
|
863
|
+
unique: col.unique || false,
|
|
864
|
+
indexed: col.indexed || false,
|
|
865
|
+
description: col.description
|
|
866
|
+
})),
|
|
867
|
+
references: [],
|
|
868
|
+
indexes: []
|
|
869
|
+
};
|
|
870
|
+
schema.databases[0].tables.push(newTable);
|
|
871
|
+
// Store updated schema
|
|
872
|
+
const schemaId = await db.storeSchema(schema);
|
|
873
|
+
return {
|
|
874
|
+
success: true,
|
|
875
|
+
schemaId,
|
|
876
|
+
tableName,
|
|
877
|
+
columnCount: columns.length,
|
|
878
|
+
message: `Table '${tableName}' added to schema '${schemaName}'`
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
catch (error) {
|
|
882
|
+
return {
|
|
883
|
+
success: false,
|
|
884
|
+
error: `Failed to add table: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
static async handleListSchemas(args) {
|
|
889
|
+
try {
|
|
890
|
+
const db = CodeIndexDB.getInstance();
|
|
891
|
+
await db.initialize();
|
|
892
|
+
const schemas = await db.getAllSchemas();
|
|
893
|
+
const stats = await db.getSchemaStats();
|
|
894
|
+
return {
|
|
895
|
+
schemas: schemas.map(s => ({
|
|
896
|
+
schemaId: s.schemaId,
|
|
897
|
+
name: s.schema.name,
|
|
898
|
+
description: s.schema.description,
|
|
899
|
+
databaseCount: s.schema.databases.length,
|
|
900
|
+
tableCount: s.metadata.tableCount,
|
|
901
|
+
relationshipCount: s.metadata.relationshipCount,
|
|
902
|
+
indexedAt: s.metadata.indexedAt
|
|
903
|
+
})),
|
|
904
|
+
totalStats: stats
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
catch (error) {
|
|
908
|
+
return {
|
|
909
|
+
success: false,
|
|
910
|
+
error: `Failed to list schemas: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
static async handleSearchSchemaElements(args) {
|
|
915
|
+
const query = args.query;
|
|
916
|
+
const elementType = args.elementType || 'all';
|
|
917
|
+
if (!query || typeof query !== 'string') {
|
|
918
|
+
throw new Error('query must be a non-empty string');
|
|
919
|
+
}
|
|
920
|
+
try {
|
|
921
|
+
const db = CodeIndexDB.getInstance();
|
|
922
|
+
await db.initialize();
|
|
923
|
+
const schemas = (await db.getAllSchemas()).map(s => s.schema);
|
|
924
|
+
const results = [];
|
|
925
|
+
const queryLower = query.toLowerCase();
|
|
926
|
+
for (const schema of schemas) {
|
|
927
|
+
for (const database of schema.databases) {
|
|
928
|
+
for (const table of database.tables) {
|
|
929
|
+
// Search tables
|
|
930
|
+
if ((elementType === 'all' || elementType === 'tables') &&
|
|
931
|
+
(table.name.toLowerCase().includes(queryLower) ||
|
|
932
|
+
table.description?.toLowerCase().includes(queryLower))) {
|
|
933
|
+
results.push({
|
|
934
|
+
type: 'table',
|
|
935
|
+
tableName: table.name,
|
|
936
|
+
databaseName: database.name,
|
|
937
|
+
schemaName: schema.name,
|
|
938
|
+
description: table.description,
|
|
939
|
+
columnCount: table.columns.length,
|
|
940
|
+
tags: table.tags
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
// Search columns
|
|
944
|
+
if (elementType === 'all' || elementType === 'columns') {
|
|
945
|
+
for (const column of table.columns) {
|
|
946
|
+
if (column.name.toLowerCase().includes(queryLower) ||
|
|
947
|
+
column.description?.toLowerCase().includes(queryLower) ||
|
|
948
|
+
column.type.toLowerCase().includes(queryLower)) {
|
|
949
|
+
results.push({
|
|
950
|
+
type: 'column',
|
|
951
|
+
columnName: column.name,
|
|
952
|
+
tableName: table.name,
|
|
953
|
+
databaseName: database.name,
|
|
954
|
+
schemaName: schema.name,
|
|
955
|
+
columnType: column.type,
|
|
956
|
+
description: column.description,
|
|
957
|
+
nullable: column.nullable,
|
|
958
|
+
primaryKey: column.primaryKey
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
967
|
+
query,
|
|
968
|
+
elementType,
|
|
969
|
+
resultCount: results.length,
|
|
970
|
+
results
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
catch (error) {
|
|
974
|
+
return {
|
|
975
|
+
success: false,
|
|
976
|
+
error: `Failed to search schema elements: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
// Schema Helper Functions
|
|
981
|
+
static generateSchemaDiscoveryQueries(databaseType, includeIndexes, includeConstraints, specificTables) {
|
|
982
|
+
const queries = [];
|
|
983
|
+
const tableFilter = specificTables && specificTables.length > 0
|
|
984
|
+
? `WHERE table_name IN (${specificTables.map(t => `'${t}'`).join(', ')})`
|
|
985
|
+
: '';
|
|
986
|
+
switch (databaseType.toLowerCase()) {
|
|
987
|
+
case 'postgresql':
|
|
988
|
+
queries.push({
|
|
989
|
+
name: 'tables',
|
|
990
|
+
sql: `SELECT table_name, table_type, table_schema
|
|
991
|
+
FROM information_schema.tables
|
|
992
|
+
WHERE table_schema NOT IN ('information_schema', 'pg_catalog') ${tableFilter}
|
|
993
|
+
ORDER BY table_schema, table_name;`,
|
|
994
|
+
description: 'Get all tables and views'
|
|
995
|
+
});
|
|
996
|
+
queries.push({
|
|
997
|
+
name: 'columns',
|
|
998
|
+
sql: `SELECT table_name, column_name, data_type, is_nullable, column_default,
|
|
999
|
+
character_maximum_length, numeric_precision, numeric_scale
|
|
1000
|
+
FROM information_schema.columns
|
|
1001
|
+
WHERE table_schema NOT IN ('information_schema', 'pg_catalog') ${tableFilter}
|
|
1002
|
+
ORDER BY table_name, ordinal_position;`,
|
|
1003
|
+
description: 'Get all columns with types and constraints'
|
|
1004
|
+
});
|
|
1005
|
+
if (includeConstraints) {
|
|
1006
|
+
queries.push({
|
|
1007
|
+
name: 'foreign_keys',
|
|
1008
|
+
sql: `SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table_name,
|
|
1009
|
+
ccu.column_name AS foreign_column_name, rc.delete_rule, rc.update_rule
|
|
1010
|
+
FROM information_schema.table_constraints AS tc
|
|
1011
|
+
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
|
|
1012
|
+
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
|
|
1013
|
+
JOIN information_schema.referential_constraints AS rc ON tc.constraint_name = rc.constraint_name
|
|
1014
|
+
WHERE tc.constraint_type = 'FOREIGN KEY' ${tableFilter.replace('table_name', 'tc.table_name')}
|
|
1015
|
+
ORDER BY tc.table_name, kcu.column_name;`,
|
|
1016
|
+
description: 'Get foreign key relationships'
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
if (includeIndexes) {
|
|
1020
|
+
queries.push({
|
|
1021
|
+
name: 'indexes',
|
|
1022
|
+
sql: `SELECT tablename, indexname, indexdef
|
|
1023
|
+
FROM pg_indexes
|
|
1024
|
+
WHERE schemaname NOT IN ('information_schema', 'pg_catalog') ${tableFilter.replace('table_name', 'tablename')}
|
|
1025
|
+
ORDER BY tablename, indexname;`,
|
|
1026
|
+
description: 'Get all indexes'
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
break;
|
|
1030
|
+
case 'mysql':
|
|
1031
|
+
queries.push({
|
|
1032
|
+
name: 'tables',
|
|
1033
|
+
sql: `SELECT table_name, table_type, table_schema
|
|
1034
|
+
FROM information_schema.tables
|
|
1035
|
+
WHERE table_schema = DATABASE() ${tableFilter}
|
|
1036
|
+
ORDER BY table_name;`,
|
|
1037
|
+
description: 'Get all tables and views'
|
|
1038
|
+
});
|
|
1039
|
+
queries.push({
|
|
1040
|
+
name: 'columns',
|
|
1041
|
+
sql: `SELECT table_name, column_name, data_type, is_nullable, column_default,
|
|
1042
|
+
character_maximum_length, numeric_precision, numeric_scale,
|
|
1043
|
+
column_key, extra
|
|
1044
|
+
FROM information_schema.columns
|
|
1045
|
+
WHERE table_schema = DATABASE() ${tableFilter}
|
|
1046
|
+
ORDER BY table_name, ordinal_position;`,
|
|
1047
|
+
description: 'Get all columns with types and constraints'
|
|
1048
|
+
});
|
|
1049
|
+
if (includeConstraints) {
|
|
1050
|
+
queries.push({
|
|
1051
|
+
name: 'foreign_keys',
|
|
1052
|
+
sql: `SELECT table_name, column_name, referenced_table_name, referenced_column_name,
|
|
1053
|
+
delete_rule, update_rule
|
|
1054
|
+
FROM information_schema.key_column_usage
|
|
1055
|
+
WHERE table_schema = DATABASE() AND referenced_table_name IS NOT NULL ${tableFilter}
|
|
1056
|
+
ORDER BY table_name, column_name;`,
|
|
1057
|
+
description: 'Get foreign key relationships'
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
break;
|
|
1061
|
+
case 'sqlite':
|
|
1062
|
+
queries.push({
|
|
1063
|
+
name: 'tables',
|
|
1064
|
+
sql: `SELECT name as table_name, type as table_type
|
|
1065
|
+
FROM sqlite_master
|
|
1066
|
+
WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
|
|
1067
|
+
ORDER BY name;`,
|
|
1068
|
+
description: 'Get all tables and views'
|
|
1069
|
+
});
|
|
1070
|
+
queries.push({
|
|
1071
|
+
name: 'table_info',
|
|
1072
|
+
sql: `-- Run this for each table: PRAGMA table_info(table_name);
|
|
1073
|
+
-- This will give you column information for each table`,
|
|
1074
|
+
description: 'Get column information (run PRAGMA table_info for each table)'
|
|
1075
|
+
});
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
return queries;
|
|
1079
|
+
}
|
|
1080
|
+
static buildSchemaFromSqlData(schemaName, databaseType, tablesData, columnsData, constraintsData) {
|
|
1081
|
+
// Build schema from SQL results
|
|
1082
|
+
const tables = [];
|
|
1083
|
+
const tableMap = new Map();
|
|
1084
|
+
// Process tables
|
|
1085
|
+
const tableRows = Array.isArray(tablesData) ? tablesData : tablesData.rows || [];
|
|
1086
|
+
for (const row of tableRows) {
|
|
1087
|
+
const tableName = row.table_name || row.TABLE_NAME;
|
|
1088
|
+
if (!tableMap.has(tableName)) {
|
|
1089
|
+
tableMap.set(tableName, {
|
|
1090
|
+
name: tableName,
|
|
1091
|
+
type: 'table',
|
|
1092
|
+
columns: [],
|
|
1093
|
+
references: [],
|
|
1094
|
+
indexes: []
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
// Process columns
|
|
1099
|
+
const columnRows = Array.isArray(columnsData) ? columnsData : columnsData.rows || [];
|
|
1100
|
+
for (const row of columnRows) {
|
|
1101
|
+
const tableName = row.table_name || row.TABLE_NAME;
|
|
1102
|
+
const table = tableMap.get(tableName);
|
|
1103
|
+
if (table) {
|
|
1104
|
+
table.columns.push({
|
|
1105
|
+
name: row.column_name || row.COLUMN_NAME,
|
|
1106
|
+
type: row.data_type || row.DATA_TYPE,
|
|
1107
|
+
nullable: (row.is_nullable || row.IS_NULLABLE) === 'YES',
|
|
1108
|
+
primaryKey: (row.column_key || row.COLUMN_KEY) === 'PRI',
|
|
1109
|
+
defaultValue: row.column_default || row.COLUMN_DEFAULT,
|
|
1110
|
+
length: row.character_maximum_length || row.CHARACTER_MAXIMUM_LENGTH,
|
|
1111
|
+
precision: row.numeric_precision || row.NUMERIC_PRECISION,
|
|
1112
|
+
scale: row.numeric_scale || row.NUMERIC_SCALE
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
// Process constraints if provided
|
|
1117
|
+
if (constraintsData) {
|
|
1118
|
+
const constraintRows = Array.isArray(constraintsData) ? constraintsData : constraintsData.rows || [];
|
|
1119
|
+
for (const row of constraintRows) {
|
|
1120
|
+
const tableName = row.table_name || row.TABLE_NAME;
|
|
1121
|
+
const table = tableMap.get(tableName);
|
|
1122
|
+
if (table) {
|
|
1123
|
+
table.references.push({
|
|
1124
|
+
foreignKey: row.column_name || row.COLUMN_NAME,
|
|
1125
|
+
referencedTable: row.foreign_table_name || row.REFERENCED_TABLE_NAME,
|
|
1126
|
+
referencedColumn: row.foreign_column_name || row.REFERENCED_COLUMN_NAME,
|
|
1127
|
+
onDelete: row.delete_rule || row.DELETE_RULE,
|
|
1128
|
+
onUpdate: row.update_rule || row.UPDATE_RULE
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return {
|
|
1134
|
+
version: "1.0.0",
|
|
1135
|
+
name: schemaName,
|
|
1136
|
+
description: `Schema discovered from ${databaseType} database`,
|
|
1137
|
+
databases: [{
|
|
1138
|
+
name: 'main',
|
|
1139
|
+
type: databaseType,
|
|
1140
|
+
tables: Array.from(tableMap.values())
|
|
1141
|
+
}],
|
|
1142
|
+
metadata: {
|
|
1143
|
+
createdAt: new Date().toISOString(),
|
|
1144
|
+
source: 'sql-discovery'
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
// Helper functions
|
|
1149
|
+
static getAllViolations(result) {
|
|
1150
|
+
const violations = [];
|
|
1151
|
+
for (const [analyzerName, analyzerResult] of Object.entries(result.analyzerResults)) {
|
|
1152
|
+
for (const violation of analyzerResult.violations) {
|
|
1153
|
+
violations.push({
|
|
1154
|
+
...violation,
|
|
1155
|
+
analyzer: analyzerName,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
return violations;
|
|
1160
|
+
}
|
|
1161
|
+
static calculateHealthScore(result) {
|
|
1162
|
+
const filesAnalyzed = result.metadata?.filesAnalyzed || 1;
|
|
1163
|
+
const critical = result.summary.criticalIssues || 0;
|
|
1164
|
+
const warnings = result.summary.warnings || 0;
|
|
1165
|
+
const suggestions = result.summary.suggestions || 0;
|
|
1166
|
+
const weights = {
|
|
1167
|
+
critical: 10,
|
|
1168
|
+
warning: 3,
|
|
1169
|
+
suggestion: 0.5
|
|
1170
|
+
};
|
|
1171
|
+
const weightedViolations = (critical * weights.critical) +
|
|
1172
|
+
(warnings * weights.warning) +
|
|
1173
|
+
(suggestions * weights.suggestion);
|
|
1174
|
+
const violationsPerFile = weightedViolations / filesAnalyzed;
|
|
1175
|
+
let score = 100 - (violationsPerFile * 2);
|
|
1176
|
+
return Math.max(0, Math.round(Math.min(100, score)));
|
|
1177
|
+
}
|
|
1178
|
+
static getHealthRecommendation(score, result) {
|
|
1179
|
+
if (score >= 90)
|
|
1180
|
+
return 'Excellent code health!';
|
|
1181
|
+
if (score >= 70)
|
|
1182
|
+
return 'Good code health with room for improvement';
|
|
1183
|
+
if (result.summary.criticalIssues > 0) {
|
|
1184
|
+
return `Fix ${result.summary.criticalIssues} critical violations first`;
|
|
1185
|
+
}
|
|
1186
|
+
return 'Code health needs attention - run detailed audit';
|
|
1187
|
+
}
|
|
1188
|
+
static async hasFilesWithExtensions(dirPath, extensions) {
|
|
1189
|
+
try {
|
|
1190
|
+
console.error(chalk.yellow('[DEBUG]'), `Checking path: ${dirPath} for extensions: ${extensions.join(', ')}`);
|
|
1191
|
+
const stats = await fs.stat(dirPath);
|
|
1192
|
+
if (stats.isFile()) {
|
|
1193
|
+
const ext = path.extname(dirPath).toLowerCase();
|
|
1194
|
+
console.error(chalk.yellow('[DEBUG]'), `File: ${dirPath}, ext: ${ext}, match: ${extensions.includes(ext)}`);
|
|
1195
|
+
return extensions.includes(ext);
|
|
1196
|
+
}
|
|
1197
|
+
// Use simple readdir and walk manually to avoid recursive option issues
|
|
1198
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
1199
|
+
console.error(chalk.yellow('[DEBUG]'), `Directory: ${dirPath}, entries: ${entries.length}`);
|
|
1200
|
+
for (const entry of entries) {
|
|
1201
|
+
if (entry.isFile()) {
|
|
1202
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
1203
|
+
console.error(chalk.yellow('[DEBUG]'), `File: ${entry.name}, ext: ${ext}, match: ${extensions.includes(ext)}`);
|
|
1204
|
+
if (extensions.includes(ext)) {
|
|
1205
|
+
console.error(chalk.green('[DEBUG]'), `Found matching file: ${entry.name}`);
|
|
1206
|
+
return true;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
else if (entry.isDirectory()) {
|
|
1210
|
+
// Recursively check subdirectories
|
|
1211
|
+
const subDirPath = path.join(dirPath, entry.name);
|
|
1212
|
+
console.error(chalk.yellow('[DEBUG]'), `Checking subdirectory: ${subDirPath}`);
|
|
1213
|
+
const hasInSubdir = await ToolHandlers.hasFilesWithExtensions(subDirPath, extensions);
|
|
1214
|
+
if (hasInSubdir) {
|
|
1215
|
+
console.error(chalk.green('[DEBUG]'), `Found matching files in subdirectory: ${subDirPath}`);
|
|
1216
|
+
return true;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
console.error(chalk.red('[DEBUG]'), `No matching files found in: ${dirPath}`);
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
catch (error) {
|
|
1224
|
+
console.error(chalk.red('[DEBUG]'), `Error checking path ${dirPath}:`, error);
|
|
1225
|
+
return false;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
static convertPolyglotToAuditResult(polyglotResult, auditPath) {
|
|
1229
|
+
// Convert polyglot result to legacy AuditResult format
|
|
1230
|
+
const violations = polyglotResult.violations || [];
|
|
1231
|
+
const criticalIssues = violations.filter((v) => v.severity === 'critical').length;
|
|
1232
|
+
const warnings = violations.filter((v) => v.severity === 'warning').length;
|
|
1233
|
+
const suggestions = violations.filter((v) => v.severity === 'suggestion').length;
|
|
1234
|
+
return {
|
|
1235
|
+
timestamp: new Date(),
|
|
1236
|
+
summary: {
|
|
1237
|
+
totalViolations: violations.length,
|
|
1238
|
+
criticalIssues,
|
|
1239
|
+
warnings,
|
|
1240
|
+
suggestions,
|
|
1241
|
+
totalFiles: polyglotResult.metrics?.totalFiles || 0,
|
|
1242
|
+
violationsByCategory: {},
|
|
1243
|
+
topIssues: violations.slice(0, 5)
|
|
1244
|
+
},
|
|
1245
|
+
analyzerResults: {
|
|
1246
|
+
solid: {
|
|
1247
|
+
violations: violations.filter((v) => v.analyzer === 'solid'),
|
|
1248
|
+
filesProcessed: polyglotResult.metrics?.totalFiles || 0,
|
|
1249
|
+
executionTime: polyglotResult.metrics?.executionTime || 0
|
|
1250
|
+
},
|
|
1251
|
+
dry: {
|
|
1252
|
+
violations: violations.filter((v) => v.analyzer === 'dry'),
|
|
1253
|
+
filesProcessed: polyglotResult.metrics?.totalFiles || 0,
|
|
1254
|
+
executionTime: polyglotResult.metrics?.executionTime || 0
|
|
1255
|
+
},
|
|
1256
|
+
go: {
|
|
1257
|
+
violations: violations.filter((v) => v.analyzer === 'go'),
|
|
1258
|
+
filesProcessed: polyglotResult.metrics?.totalFiles || 0,
|
|
1259
|
+
executionTime: polyglotResult.metrics?.executionTime || 0
|
|
1260
|
+
}
|
|
1261
|
+
},
|
|
1262
|
+
recommendations: [],
|
|
1263
|
+
metadata: {
|
|
1264
|
+
auditDuration: polyglotResult.metrics?.executionTime || 0,
|
|
1265
|
+
filesAnalyzed: polyglotResult.metrics?.totalFiles || 0,
|
|
1266
|
+
analyzersRun: polyglotResult.metrics?.analyzersRun || ['solid', 'dry'],
|
|
1267
|
+
fileToFunctionsMap: polyglotResult.indexEntries ? ToolHandlers.createFileToFunctionsMap(polyglotResult.indexEntries) : {}
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
static createFileToFunctionsMap(indexEntries) {
|
|
1272
|
+
const fileMap = {};
|
|
1273
|
+
for (const entry of indexEntries) {
|
|
1274
|
+
if (!fileMap[entry.file]) {
|
|
1275
|
+
fileMap[entry.file] = [];
|
|
1276
|
+
}
|
|
1277
|
+
fileMap[entry.file].push(entry);
|
|
1278
|
+
}
|
|
1279
|
+
return fileMap;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
//# sourceMappingURL=mcp-tools-shared.js.map
|