driftdetect-mcp 0.4.4 → 0.5.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/LICENSE +21 -0
- package/dist/bin/server.js +0 -0
- package/dist/enterprise-server.d.ts +7 -0
- package/dist/enterprise-server.d.ts.map +1 -1
- package/dist/enterprise-server.js +77 -7
- package/dist/enterprise-server.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -0
- package/dist/infrastructure/index.d.ts.map +1 -1
- package/dist/infrastructure/index.js +3 -0
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/infrastructure/project-resolver.d.ts +43 -0
- package/dist/infrastructure/project-resolver.d.ts.map +1 -0
- package/dist/infrastructure/project-resolver.js +108 -0
- package/dist/infrastructure/project-resolver.js.map +1 -0
- package/dist/tools/analysis/coupling.d.ts +56 -0
- package/dist/tools/analysis/coupling.d.ts.map +1 -0
- package/dist/tools/analysis/coupling.js +248 -0
- package/dist/tools/analysis/coupling.js.map +1 -0
- package/dist/tools/analysis/error-handling.d.ts +45 -0
- package/dist/tools/analysis/error-handling.d.ts.map +1 -0
- package/dist/tools/analysis/error-handling.js +220 -0
- package/dist/tools/analysis/error-handling.js.map +1 -0
- package/dist/tools/analysis/index.d.ts +11 -0
- package/dist/tools/analysis/index.d.ts.map +1 -0
- package/dist/tools/analysis/index.js +111 -0
- package/dist/tools/analysis/index.js.map +1 -0
- package/dist/tools/analysis/test-topology.d.ts +43 -0
- package/dist/tools/analysis/test-topology.d.ts.map +1 -0
- package/dist/tools/analysis/test-topology.js +294 -0
- package/dist/tools/analysis/test-topology.js.map +1 -0
- package/dist/tools/detail/code-examples.d.ts +20 -3
- package/dist/tools/detail/code-examples.d.ts.map +1 -1
- package/dist/tools/detail/code-examples.js +104 -0
- package/dist/tools/detail/code-examples.js.map +1 -1
- package/dist/tools/detail/index.d.ts +6 -4
- package/dist/tools/detail/index.d.ts.map +1 -1
- package/dist/tools/detail/index.js +44 -6
- package/dist/tools/detail/index.js.map +1 -1
- package/dist/tools/detail/pattern-get.d.ts +20 -3
- package/dist/tools/detail/pattern-get.d.ts.map +1 -1
- package/dist/tools/detail/pattern-get.js +87 -0
- package/dist/tools/detail/pattern-get.js.map +1 -1
- package/dist/tools/detail/wrappers.d.ts +97 -0
- package/dist/tools/detail/wrappers.d.ts.map +1 -0
- package/dist/tools/detail/wrappers.js +124 -0
- package/dist/tools/detail/wrappers.js.map +1 -0
- package/dist/tools/discovery/index.d.ts +3 -1
- package/dist/tools/discovery/index.d.ts.map +1 -1
- package/dist/tools/discovery/index.js +36 -3
- package/dist/tools/discovery/index.js.map +1 -1
- package/dist/tools/discovery/projects.d.ts +7 -1
- package/dist/tools/discovery/projects.d.ts.map +1 -1
- package/dist/tools/discovery/projects.js +55 -1
- package/dist/tools/discovery/projects.js.map +1 -1
- package/dist/tools/discovery/status.d.ts +16 -3
- package/dist/tools/discovery/status.d.ts.map +1 -1
- package/dist/tools/discovery/status.js +83 -1
- package/dist/tools/discovery/status.js.map +1 -1
- package/dist/tools/exploration/index.d.ts +2 -2
- package/dist/tools/exploration/index.d.ts.map +1 -1
- package/dist/tools/exploration/index.js +1 -5
- package/dist/tools/exploration/index.js.map +1 -1
- package/dist/tools/exploration/patterns-list.d.ts +21 -4
- package/dist/tools/exploration/patterns-list.d.ts.map +1 -1
- package/dist/tools/exploration/patterns-list.js +70 -0
- package/dist/tools/exploration/patterns-list.js.map +1 -1
- package/dist/tools/generation/__tests__/generation-tools.test.d.ts +6 -0
- package/dist/tools/generation/__tests__/generation-tools.test.d.ts.map +1 -0
- package/dist/tools/generation/__tests__/generation-tools.test.js +119 -0
- package/dist/tools/generation/__tests__/generation-tools.test.js.map +1 -0
- package/dist/tools/generation/explain.d.ts +75 -0
- package/dist/tools/generation/explain.d.ts.map +1 -0
- package/dist/tools/generation/explain.js +238 -0
- package/dist/tools/generation/explain.js.map +1 -0
- package/dist/tools/generation/index.d.ts +12 -0
- package/dist/tools/generation/index.d.ts.map +1 -0
- package/dist/tools/generation/index.js +90 -0
- package/dist/tools/generation/index.js.map +1 -0
- package/dist/tools/generation/suggest-changes.d.ts +64 -0
- package/dist/tools/generation/suggest-changes.d.ts.map +1 -0
- package/dist/tools/generation/suggest-changes.js +342 -0
- package/dist/tools/generation/suggest-changes.js.map +1 -0
- package/dist/tools/generation/validate-change.d.ts +76 -0
- package/dist/tools/generation/validate-change.d.ts.map +1 -0
- package/dist/tools/generation/validate-change.js +415 -0
- package/dist/tools/generation/validate-change.js.map +1 -0
- package/dist/tools/orchestration/context.d.ts +41 -0
- package/dist/tools/orchestration/context.d.ts.map +1 -1
- package/dist/tools/orchestration/context.js +215 -14
- package/dist/tools/orchestration/context.js.map +1 -1
- package/dist/tools/orchestration/index.d.ts.map +1 -1
- package/dist/tools/orchestration/index.js +13 -1
- package/dist/tools/orchestration/index.js.map +1 -1
- package/dist/tools/registry.d.ts +5 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +12 -0
- package/dist/tools/registry.js.map +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analysis Tools Index
|
|
3
|
+
*
|
|
4
|
+
* Tools for deeper analysis of codebase patterns and quality.
|
|
5
|
+
*/
|
|
6
|
+
const TEST_TOPOLOGY_ACTIONS = ['status', 'coverage', 'uncovered', 'mocks', 'affected', 'quality'];
|
|
7
|
+
const COUPLING_ACTIONS = ['status', 'cycles', 'hotspots', 'analyze', 'refactor-impact', 'unused-exports'];
|
|
8
|
+
const ERROR_HANDLING_ACTIONS = ['status', 'gaps', 'boundaries', 'unhandled', 'analyze'];
|
|
9
|
+
export const ANALYSIS_TOOLS = [
|
|
10
|
+
{
|
|
11
|
+
name: 'drift_test_topology',
|
|
12
|
+
description: 'Analyze test-to-code mappings, mock patterns, and test quality. Actions: status (overview), coverage (file coverage), uncovered (find gaps), mocks (mock analysis), affected (minimum test set), quality (test quality metrics).',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
action: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
enum: TEST_TOPOLOGY_ACTIONS,
|
|
19
|
+
description: 'Action to perform: status, coverage, uncovered, mocks, affected, quality',
|
|
20
|
+
},
|
|
21
|
+
file: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'File path for coverage/quality actions',
|
|
24
|
+
},
|
|
25
|
+
files: {
|
|
26
|
+
type: 'array',
|
|
27
|
+
items: { type: 'string' },
|
|
28
|
+
description: 'Changed files for affected action',
|
|
29
|
+
},
|
|
30
|
+
limit: {
|
|
31
|
+
type: 'number',
|
|
32
|
+
description: 'Max results for uncovered action (default: 20)',
|
|
33
|
+
},
|
|
34
|
+
minRisk: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
enum: ['low', 'medium', 'high'],
|
|
37
|
+
description: 'Minimum risk level for uncovered action (default: medium)',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['action'],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'drift_coupling',
|
|
45
|
+
description: 'Analyze module dependencies, detect cycles, and calculate coupling metrics (Robert C. Martin metrics). Actions: status (overview), cycles (dependency cycles), hotspots (highly coupled modules), analyze (specific module), refactor-impact (change impact), unused-exports (dead exports).',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
action: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
enum: COUPLING_ACTIONS,
|
|
52
|
+
description: 'Action to perform: status, cycles, hotspots, analyze, refactor-impact, unused-exports',
|
|
53
|
+
},
|
|
54
|
+
module: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Module path for analyze/refactor-impact actions',
|
|
57
|
+
},
|
|
58
|
+
limit: {
|
|
59
|
+
type: 'number',
|
|
60
|
+
description: 'Max results for hotspots/unused-exports (default: 15/20)',
|
|
61
|
+
},
|
|
62
|
+
minCoupling: {
|
|
63
|
+
type: 'number',
|
|
64
|
+
description: 'Minimum coupling threshold for hotspots (default: 3)',
|
|
65
|
+
},
|
|
66
|
+
maxCycleLength: {
|
|
67
|
+
type: 'number',
|
|
68
|
+
description: 'Maximum cycle length to report (default: 10)',
|
|
69
|
+
},
|
|
70
|
+
minSeverity: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
enum: ['info', 'warning', 'critical'],
|
|
73
|
+
description: 'Minimum severity for cycles (default: info)',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
required: ['action'],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'drift_error_handling',
|
|
81
|
+
description: 'Analyze error handling patterns, boundaries, and gaps. Detects unhandled error paths and swallowed exceptions. Actions: status (overview), gaps (find issues), boundaries (error boundaries), unhandled (unhandled paths), analyze (specific function).',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
action: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
enum: ERROR_HANDLING_ACTIONS,
|
|
88
|
+
description: 'Action to perform: status, gaps, boundaries, unhandled, analyze',
|
|
89
|
+
},
|
|
90
|
+
function: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'Function path for analyze action',
|
|
93
|
+
},
|
|
94
|
+
limit: {
|
|
95
|
+
type: 'number',
|
|
96
|
+
description: 'Max results for gaps action (default: 20)',
|
|
97
|
+
},
|
|
98
|
+
minSeverity: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
101
|
+
description: 'Minimum severity for gaps/unhandled (default: medium)',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
required: ['action'],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
export { handleTestTopology } from './test-topology.js';
|
|
109
|
+
export { handleCoupling } from './coupling.js';
|
|
110
|
+
export { handleErrorHandling } from './error-handling.js';
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/analysis/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* drift_test_topology - Test Topology Analysis
|
|
3
|
+
*
|
|
4
|
+
* Analysis tool for test-to-code mappings, mock patterns, and test quality.
|
|
5
|
+
* Answers: "Which tests cover this code?" and "What's the minimum test set?"
|
|
6
|
+
*/
|
|
7
|
+
import { type TestTopologySummary, type MockAnalysis, type MinimumTestSet, type UncoveredFunction, type TestCoverage } from 'driftdetect-core';
|
|
8
|
+
export type TestTopologyAction = 'status' | 'coverage' | 'uncovered' | 'mocks' | 'affected' | 'quality';
|
|
9
|
+
export interface TestTopologyArgs {
|
|
10
|
+
action: TestTopologyAction;
|
|
11
|
+
file?: string;
|
|
12
|
+
files?: string[];
|
|
13
|
+
limit?: number;
|
|
14
|
+
minRisk?: 'low' | 'medium' | 'high';
|
|
15
|
+
}
|
|
16
|
+
export interface TestTopologyStatusData {
|
|
17
|
+
summary: TestTopologySummary;
|
|
18
|
+
mockAnalysis: MockAnalysis;
|
|
19
|
+
generatedAt?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface TestTopologyCoverageData {
|
|
22
|
+
file: string;
|
|
23
|
+
coverage: TestCoverage;
|
|
24
|
+
}
|
|
25
|
+
export interface TestTopologyUncoveredData {
|
|
26
|
+
uncovered: UncoveredFunction[];
|
|
27
|
+
total: number;
|
|
28
|
+
}
|
|
29
|
+
export interface TestTopologyMocksData {
|
|
30
|
+
analysis: MockAnalysis;
|
|
31
|
+
warnings: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface TestTopologyAffectedData {
|
|
34
|
+
changedFiles: string[];
|
|
35
|
+
result: MinimumTestSet;
|
|
36
|
+
}
|
|
37
|
+
export declare function handleTestTopology(projectRoot: string, args: TestTopologyArgs): Promise<{
|
|
38
|
+
content: Array<{
|
|
39
|
+
type: string;
|
|
40
|
+
text: string;
|
|
41
|
+
}>;
|
|
42
|
+
}>;
|
|
43
|
+
//# sourceMappingURL=test-topology.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-topology.d.ts","sourceRoot":"","sources":["../../../src/tools/analysis/test-topology.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAO1B,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,UAAU,GACV,WAAW,GACX,OAAO,GACP,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;CACxB;AAaD,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAmB7D"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* drift_test_topology - Test Topology Analysis
|
|
3
|
+
*
|
|
4
|
+
* Analysis tool for test-to-code mappings, mock patterns, and test quality.
|
|
5
|
+
* Answers: "Which tests cover this code?" and "What's the minimum test set?"
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs/promises';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { createTestTopologyAnalyzer, createCallGraphAnalyzer, } from 'driftdetect-core';
|
|
10
|
+
import { createResponseBuilder, Errors } from '../../infrastructure/index.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// ============================================================================
|
|
14
|
+
const DRIFT_DIR = '.drift';
|
|
15
|
+
const TEST_TOPOLOGY_DIR = 'test-topology';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Handler
|
|
18
|
+
// ============================================================================
|
|
19
|
+
export async function handleTestTopology(projectRoot, args) {
|
|
20
|
+
const { action } = args;
|
|
21
|
+
switch (action) {
|
|
22
|
+
case 'status':
|
|
23
|
+
return handleStatus(projectRoot);
|
|
24
|
+
case 'coverage':
|
|
25
|
+
return handleCoverage(projectRoot, args.file);
|
|
26
|
+
case 'uncovered':
|
|
27
|
+
return handleUncovered(projectRoot, args.limit, args.minRisk);
|
|
28
|
+
case 'mocks':
|
|
29
|
+
return handleMocks(projectRoot);
|
|
30
|
+
case 'affected':
|
|
31
|
+
return handleAffected(projectRoot, args.files ?? []);
|
|
32
|
+
case 'quality':
|
|
33
|
+
return handleQuality(projectRoot, args.file);
|
|
34
|
+
default:
|
|
35
|
+
throw Errors.invalidArgument('action', `Invalid action: ${action}. Valid actions: status, coverage, uncovered, mocks, affected, quality`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Action Handlers
|
|
40
|
+
// ============================================================================
|
|
41
|
+
async function handleStatus(projectRoot) {
|
|
42
|
+
const builder = createResponseBuilder();
|
|
43
|
+
// Try to load cached data first
|
|
44
|
+
const summaryPath = path.join(projectRoot, DRIFT_DIR, TEST_TOPOLOGY_DIR, 'summary.json');
|
|
45
|
+
try {
|
|
46
|
+
const data = JSON.parse(await fs.readFile(summaryPath, 'utf-8'));
|
|
47
|
+
const { summary, mockAnalysis } = data;
|
|
48
|
+
let summaryText = `🧪 ${summary.testCases} tests in ${summary.testFiles} files. `;
|
|
49
|
+
summaryText += `Coverage: ${summary.functionCoveragePercent}% functions. `;
|
|
50
|
+
summaryText += `Quality: ${summary.avgQualityScore}/100.`;
|
|
51
|
+
const hints = {
|
|
52
|
+
nextActions: [
|
|
53
|
+
summary.functionCoveragePercent < 50
|
|
54
|
+
? 'Run drift_test_topology action="uncovered" to find gaps'
|
|
55
|
+
: 'Good coverage - consider drift_test_topology action="mocks" for quality',
|
|
56
|
+
],
|
|
57
|
+
relatedTools: ['drift_test_topology action="uncovered"', 'drift_test_topology action="mocks"'],
|
|
58
|
+
};
|
|
59
|
+
return builder
|
|
60
|
+
.withSummary(summaryText)
|
|
61
|
+
.withData({ summary, mockAnalysis, generatedAt: data.generatedAt })
|
|
62
|
+
.withHints(hints)
|
|
63
|
+
.buildContent();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw Errors.custom('NO_TEST_TOPOLOGY', 'No test topology found. Build it first using the CLI: drift test-topology build', ['drift test-topology build']);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function handleCoverage(projectRoot, file) {
|
|
70
|
+
const builder = createResponseBuilder();
|
|
71
|
+
if (!file) {
|
|
72
|
+
throw Errors.missingParameter('file');
|
|
73
|
+
}
|
|
74
|
+
// Build analyzer on-demand
|
|
75
|
+
const analyzer = await buildAnalyzer(projectRoot);
|
|
76
|
+
const coverage = analyzer.getCoverage(file);
|
|
77
|
+
if (!coverage) {
|
|
78
|
+
throw Errors.custom('NO_COVERAGE_DATA', `No coverage data for ${file}. Ensure call graph is built.`, ['drift callgraph build']);
|
|
79
|
+
}
|
|
80
|
+
const coveredCount = coverage.functions.filter((f) => f.isCovered).length;
|
|
81
|
+
let summaryText = `📊 ${file}: ${coverage.coveragePercent}% coverage. `;
|
|
82
|
+
summaryText += `${coveredCount}/${coverage.functions.length} functions tested.`;
|
|
83
|
+
const uncoveredFunctions = coverage.functions
|
|
84
|
+
.filter((f) => !f.isCovered)
|
|
85
|
+
.map((f) => f.name);
|
|
86
|
+
const hints = {
|
|
87
|
+
nextActions: uncoveredFunctions.length > 0
|
|
88
|
+
? [`Add tests for: ${uncoveredFunctions.slice(0, 3).join(', ')}`]
|
|
89
|
+
: ['Full coverage achieved!'],
|
|
90
|
+
relatedTools: ['drift_test_topology action="uncovered"'],
|
|
91
|
+
};
|
|
92
|
+
return builder
|
|
93
|
+
.withSummary(summaryText)
|
|
94
|
+
.withData({ file, coverage })
|
|
95
|
+
.withHints(hints)
|
|
96
|
+
.buildContent();
|
|
97
|
+
}
|
|
98
|
+
async function handleUncovered(projectRoot, limit, minRisk) {
|
|
99
|
+
const builder = createResponseBuilder();
|
|
100
|
+
const analyzer = await buildAnalyzer(projectRoot);
|
|
101
|
+
const uncovered = analyzer.getUncoveredFunctions({
|
|
102
|
+
limit: limit ?? 20,
|
|
103
|
+
minRisk: minRisk ?? 'medium',
|
|
104
|
+
includeReasons: true,
|
|
105
|
+
});
|
|
106
|
+
const highRisk = uncovered.filter((f) => f.riskScore >= 60);
|
|
107
|
+
const mediumRisk = uncovered.filter((f) => f.riskScore >= 30 && f.riskScore < 60);
|
|
108
|
+
let summaryText = `🔍 ${uncovered.length} uncovered functions. `;
|
|
109
|
+
if (highRisk.length > 0) {
|
|
110
|
+
summaryText += `🔴 ${highRisk.length} high risk. `;
|
|
111
|
+
}
|
|
112
|
+
if (mediumRisk.length > 0) {
|
|
113
|
+
summaryText += `🟡 ${mediumRisk.length} medium risk.`;
|
|
114
|
+
}
|
|
115
|
+
const hints = {
|
|
116
|
+
nextActions: highRisk.length > 0
|
|
117
|
+
? [`Priority: Add tests for ${highRisk[0]?.qualifiedName ?? 'high-risk functions'}`]
|
|
118
|
+
: ['Consider adding tests for medium-risk functions'],
|
|
119
|
+
warnings: highRisk.filter((f) => f.accessesSensitiveData).length > 0
|
|
120
|
+
? ['Some uncovered functions access sensitive data']
|
|
121
|
+
: undefined,
|
|
122
|
+
relatedTools: ['drift_impact_analysis', 'drift_reachability'],
|
|
123
|
+
};
|
|
124
|
+
return builder
|
|
125
|
+
.withSummary(summaryText)
|
|
126
|
+
.withData({ uncovered, total: uncovered.length })
|
|
127
|
+
.withHints(hints)
|
|
128
|
+
.buildContent();
|
|
129
|
+
}
|
|
130
|
+
async function handleMocks(projectRoot) {
|
|
131
|
+
const builder = createResponseBuilder();
|
|
132
|
+
// Load cached data
|
|
133
|
+
const summaryPath = path.join(projectRoot, DRIFT_DIR, TEST_TOPOLOGY_DIR, 'summary.json');
|
|
134
|
+
try {
|
|
135
|
+
const data = JSON.parse(await fs.readFile(summaryPath, 'utf-8'));
|
|
136
|
+
const { mockAnalysis } = data;
|
|
137
|
+
const warnings = [];
|
|
138
|
+
if (mockAnalysis.internalPercent > 50) {
|
|
139
|
+
warnings.push('High internal mocking (>50%) may indicate tight coupling');
|
|
140
|
+
}
|
|
141
|
+
if (mockAnalysis.avgMockRatio > 0.7) {
|
|
142
|
+
warnings.push('High average mock ratio - tests may be brittle');
|
|
143
|
+
}
|
|
144
|
+
if (mockAnalysis.highMockRatioTests.length > 5) {
|
|
145
|
+
warnings.push(`${mockAnalysis.highMockRatioTests.length} tests have >70% mock ratio`);
|
|
146
|
+
}
|
|
147
|
+
let summaryText = `🎭 ${mockAnalysis.totalMocks} mocks. `;
|
|
148
|
+
summaryText += `${mockAnalysis.externalPercent}% external, ${mockAnalysis.internalPercent}% internal. `;
|
|
149
|
+
summaryText += `Avg ratio: ${Math.round(mockAnalysis.avgMockRatio * 100)}%.`;
|
|
150
|
+
const hints = {
|
|
151
|
+
nextActions: warnings.length > 0
|
|
152
|
+
? ['Review high-mock tests for potential refactoring']
|
|
153
|
+
: ['Mock patterns look healthy'],
|
|
154
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
155
|
+
relatedTools: ['drift_test_topology action="quality"'],
|
|
156
|
+
};
|
|
157
|
+
return builder
|
|
158
|
+
.withSummary(summaryText)
|
|
159
|
+
.withData({ analysis: mockAnalysis, warnings })
|
|
160
|
+
.withHints(hints)
|
|
161
|
+
.buildContent();
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
throw Errors.custom('NO_TEST_TOPOLOGY', 'No test topology found. Build it first.', ['drift test-topology build']);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function handleAffected(projectRoot, files) {
|
|
168
|
+
const builder = createResponseBuilder();
|
|
169
|
+
if (files.length === 0) {
|
|
170
|
+
throw Errors.missingParameter('files');
|
|
171
|
+
}
|
|
172
|
+
const analyzer = await buildAnalyzer(projectRoot);
|
|
173
|
+
const result = analyzer.getMinimumTestSet(files);
|
|
174
|
+
let summaryText = `🎯 ${result.selectedTests}/${result.totalTests} tests needed. `;
|
|
175
|
+
summaryText += `Coverage: ${result.changedCodeCoverage}%. `;
|
|
176
|
+
summaryText += `Time saved: ~${result.timeSaved}.`;
|
|
177
|
+
const hints = {
|
|
178
|
+
nextActions: result.tests.length > 0
|
|
179
|
+
? [`Run: ${result.tests.slice(0, 3).map((t) => t.name).join(', ')}`]
|
|
180
|
+
: ['No tests found for changed files - consider adding coverage'],
|
|
181
|
+
relatedTools: ['drift_test_topology action="uncovered"'],
|
|
182
|
+
};
|
|
183
|
+
return builder
|
|
184
|
+
.withSummary(summaryText)
|
|
185
|
+
.withData({ changedFiles: files, result })
|
|
186
|
+
.withHints(hints)
|
|
187
|
+
.buildContent();
|
|
188
|
+
}
|
|
189
|
+
async function handleQuality(projectRoot, file) {
|
|
190
|
+
const builder = createResponseBuilder();
|
|
191
|
+
// Load cached data
|
|
192
|
+
const summaryPath = path.join(projectRoot, DRIFT_DIR, TEST_TOPOLOGY_DIR, 'summary.json');
|
|
193
|
+
try {
|
|
194
|
+
const data = JSON.parse(await fs.readFile(summaryPath, 'utf-8'));
|
|
195
|
+
const { summary } = data;
|
|
196
|
+
let summaryText = `📈 Test Quality: ${summary.avgQualityScore}/100. `;
|
|
197
|
+
summaryText += `Mock ratio: ${Math.round(summary.avgMockRatio * 100)}%.`;
|
|
198
|
+
const hints = {
|
|
199
|
+
nextActions: summary.avgQualityScore < 50
|
|
200
|
+
? ['Improve test quality by adding assertions and error cases']
|
|
201
|
+
: ['Test quality is good'],
|
|
202
|
+
relatedTools: ['drift_test_topology action="mocks"'],
|
|
203
|
+
};
|
|
204
|
+
return builder
|
|
205
|
+
.withSummary(summaryText)
|
|
206
|
+
.withData({ file, summary })
|
|
207
|
+
.withHints(hints)
|
|
208
|
+
.buildContent();
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
throw Errors.custom('NO_TEST_TOPOLOGY', 'No test topology found. Build it first.', ['drift test-topology build']);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ============================================================================
|
|
215
|
+
// Helpers
|
|
216
|
+
// ============================================================================
|
|
217
|
+
async function buildAnalyzer(projectRoot) {
|
|
218
|
+
const analyzer = createTestTopologyAnalyzer({});
|
|
219
|
+
// Load call graph
|
|
220
|
+
try {
|
|
221
|
+
const callGraphAnalyzer = createCallGraphAnalyzer({ rootDir: projectRoot });
|
|
222
|
+
await callGraphAnalyzer.initialize();
|
|
223
|
+
const graph = callGraphAnalyzer.getGraph();
|
|
224
|
+
if (graph) {
|
|
225
|
+
analyzer.setCallGraph(graph);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// Continue without call graph
|
|
230
|
+
}
|
|
231
|
+
// Extract tests by walking directory
|
|
232
|
+
const testFiles = await findTestFiles(projectRoot);
|
|
233
|
+
for (const testFile of testFiles) {
|
|
234
|
+
try {
|
|
235
|
+
const content = await fs.readFile(path.join(projectRoot, testFile), 'utf-8');
|
|
236
|
+
analyzer.extractFromFile(content, testFile);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// Skip files that can't be read
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
analyzer.buildMappings();
|
|
243
|
+
return analyzer;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Find test files by walking the directory tree
|
|
247
|
+
*/
|
|
248
|
+
async function findTestFiles(rootDir, subDir = '') {
|
|
249
|
+
const testFiles = [];
|
|
250
|
+
const currentDir = path.join(rootDir, subDir);
|
|
251
|
+
try {
|
|
252
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
253
|
+
for (const entry of entries) {
|
|
254
|
+
const relativePath = path.join(subDir, entry.name);
|
|
255
|
+
// Skip common non-source directories
|
|
256
|
+
if (entry.isDirectory()) {
|
|
257
|
+
if (['node_modules', 'vendor', 'dist', 'build', '.git', '.drift'].includes(entry.name)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
// Recurse into subdirectories
|
|
261
|
+
const subFiles = await findTestFiles(rootDir, relativePath);
|
|
262
|
+
testFiles.push(...subFiles);
|
|
263
|
+
}
|
|
264
|
+
else if (entry.isFile()) {
|
|
265
|
+
// Check if it's a test file
|
|
266
|
+
if (isTestFile(entry.name)) {
|
|
267
|
+
testFiles.push(relativePath);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// Directory not readable, skip
|
|
274
|
+
}
|
|
275
|
+
return testFiles;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Check if a filename matches test file patterns
|
|
279
|
+
*/
|
|
280
|
+
function isTestFile(filename) {
|
|
281
|
+
const testPatterns = [
|
|
282
|
+
/\.test\.[jt]sx?$/,
|
|
283
|
+
/\.spec\.[jt]sx?$/,
|
|
284
|
+
/_test\.py$/,
|
|
285
|
+
/test_.*\.py$/,
|
|
286
|
+
/Test\.java$/,
|
|
287
|
+
/Tests\.java$/,
|
|
288
|
+
/Test\.cs$/,
|
|
289
|
+
/Tests\.cs$/,
|
|
290
|
+
/Test\.php$/,
|
|
291
|
+
];
|
|
292
|
+
return testPatterns.some(pattern => pattern.test(filename));
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=test-topology.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-topology.js","sourceRoot":"","sources":["../../../src/tools/analysis/test-topology.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Detail tool that returns actual code snippets demonstrating patterns.
|
|
5
5
|
* Essential before generating new code to match codebase conventions.
|
|
6
|
+
*
|
|
7
|
+
* MIGRATION: Now supports both legacy PatternStore and new IPatternService.
|
|
8
|
+
* The service-based approach is preferred for new code.
|
|
6
9
|
*/
|
|
7
|
-
import type { PatternStore } from 'driftdetect-core';
|
|
10
|
+
import type { PatternStore, IPatternService } from 'driftdetect-core';
|
|
8
11
|
export interface CodeExample {
|
|
9
12
|
patternId: string;
|
|
10
13
|
patternName: string;
|
|
@@ -19,12 +22,26 @@ export interface CodeExamplesData {
|
|
|
19
22
|
patternsFound: number;
|
|
20
23
|
examplesReturned: number;
|
|
21
24
|
}
|
|
22
|
-
export
|
|
25
|
+
export interface CodeExamplesArgs {
|
|
23
26
|
categories?: string[];
|
|
24
27
|
pattern?: string;
|
|
25
28
|
maxExamples?: number;
|
|
26
29
|
contextLines?: number;
|
|
27
|
-
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Handle code examples using IPatternService (preferred)
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleCodeExamplesWithService(service: IPatternService, projectRoot: string, args: CodeExamplesArgs): Promise<{
|
|
35
|
+
content: Array<{
|
|
36
|
+
type: string;
|
|
37
|
+
text: string;
|
|
38
|
+
}>;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Handle code examples using legacy PatternStore (backward compatibility)
|
|
42
|
+
* @deprecated Use handleCodeExamplesWithService instead
|
|
43
|
+
*/
|
|
44
|
+
export declare function handleCodeExamples(store: PatternStore, projectRoot: string, args: CodeExamplesArgs): Promise<{
|
|
28
45
|
content: Array<{
|
|
29
46
|
type: string;
|
|
30
47
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-examples.d.ts","sourceRoot":"","sources":["../../../src/tools/detail/code-examples.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"code-examples.d.ts","sourceRoot":"","sources":["../../../src/tools/detail/code-examples.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAmB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAKvF,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAYD;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CA4G7D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CA0G7D"}
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Detail tool that returns actual code snippets demonstrating patterns.
|
|
5
5
|
* Essential before generating new code to match codebase conventions.
|
|
6
|
+
*
|
|
7
|
+
* MIGRATION: Now supports both legacy PatternStore and new IPatternService.
|
|
8
|
+
* The service-based approach is preferred for new code.
|
|
6
9
|
*/
|
|
7
10
|
import { createResponseBuilder, Errors } from '../../infrastructure/index.js';
|
|
8
11
|
import * as fs from 'fs/promises';
|
|
@@ -15,6 +18,107 @@ const VALID_CATEGORIES = [
|
|
|
15
18
|
];
|
|
16
19
|
const DEFAULT_MAX_EXAMPLES = 3;
|
|
17
20
|
const DEFAULT_CONTEXT_LINES = 10;
|
|
21
|
+
/**
|
|
22
|
+
* Handle code examples using IPatternService (preferred)
|
|
23
|
+
*/
|
|
24
|
+
export async function handleCodeExamplesWithService(service, projectRoot, args) {
|
|
25
|
+
const builder = createResponseBuilder();
|
|
26
|
+
// Validate categories
|
|
27
|
+
if (args.categories) {
|
|
28
|
+
for (const cat of args.categories) {
|
|
29
|
+
if (!VALID_CATEGORIES.includes(cat)) {
|
|
30
|
+
throw Errors.invalidCategory(cat, VALID_CATEGORIES);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const maxExamples = args.maxExamples ?? DEFAULT_MAX_EXAMPLES;
|
|
35
|
+
const contextLines = args.contextLines ?? DEFAULT_CONTEXT_LINES;
|
|
36
|
+
// Get patterns to show examples for
|
|
37
|
+
const result = await service.query({
|
|
38
|
+
filter: {
|
|
39
|
+
categories: args.categories,
|
|
40
|
+
search: args.pattern,
|
|
41
|
+
},
|
|
42
|
+
sort: {
|
|
43
|
+
field: 'confidence',
|
|
44
|
+
direction: 'desc',
|
|
45
|
+
},
|
|
46
|
+
pagination: {
|
|
47
|
+
offset: 0,
|
|
48
|
+
limit: 10, // Limit patterns to process
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
let patterns = result.patterns;
|
|
52
|
+
// If searching by specific pattern ID, also check exact match
|
|
53
|
+
if (args.pattern && !patterns.some(p => p.id === args.pattern)) {
|
|
54
|
+
const exactMatch = await service.getPattern(args.pattern);
|
|
55
|
+
if (exactMatch) {
|
|
56
|
+
patterns = [exactMatch, ...patterns];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const examples = [];
|
|
60
|
+
for (const pattern of patterns) {
|
|
61
|
+
// Get best locations (highest confidence, not outliers)
|
|
62
|
+
const outlierFiles = new Set(pattern.outliers.map(o => `${o.file}:${o.line}`));
|
|
63
|
+
const goodLocations = pattern.locations
|
|
64
|
+
.filter(loc => !outlierFiles.has(`${loc.file}:${loc.line}`))
|
|
65
|
+
.slice(0, maxExamples);
|
|
66
|
+
for (const loc of goodLocations) {
|
|
67
|
+
try {
|
|
68
|
+
const code = await extractCodeSnippet(path.join(projectRoot, loc.file), loc.line, contextLines);
|
|
69
|
+
if (code) {
|
|
70
|
+
examples.push({
|
|
71
|
+
patternId: pattern.id,
|
|
72
|
+
patternName: pattern.name,
|
|
73
|
+
category: pattern.category,
|
|
74
|
+
file: loc.file,
|
|
75
|
+
line: loc.line,
|
|
76
|
+
code,
|
|
77
|
+
explanation: pattern.description,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Skip files that can't be read
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
// Limit total examples
|
|
86
|
+
if (examples.length >= maxExamples * 5)
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
if (examples.length >= maxExamples * 5)
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
const data = {
|
|
93
|
+
examples,
|
|
94
|
+
patternsFound: patterns.length,
|
|
95
|
+
examplesReturned: examples.length,
|
|
96
|
+
};
|
|
97
|
+
// Build summary
|
|
98
|
+
let summary = `${examples.length} code examples from ${patterns.length} patterns.`;
|
|
99
|
+
if (args.categories?.length) {
|
|
100
|
+
summary = `${args.categories.join(', ')}: ${summary}`;
|
|
101
|
+
}
|
|
102
|
+
return builder
|
|
103
|
+
.withSummary(summary)
|
|
104
|
+
.withData(data)
|
|
105
|
+
.withHints({
|
|
106
|
+
nextActions: examples.length > 0
|
|
107
|
+
? [
|
|
108
|
+
'Use these examples as templates for new code',
|
|
109
|
+
'Use drift_pattern_get for more details on specific patterns',
|
|
110
|
+
]
|
|
111
|
+
: [
|
|
112
|
+
'Try different categories or run drift scan',
|
|
113
|
+
],
|
|
114
|
+
relatedTools: ['drift_pattern_get', 'drift_patterns_list'],
|
|
115
|
+
})
|
|
116
|
+
.buildContent();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Handle code examples using legacy PatternStore (backward compatibility)
|
|
120
|
+
* @deprecated Use handleCodeExamplesWithService instead
|
|
121
|
+
*/
|
|
18
122
|
export async function handleCodeExamples(store, projectRoot, args) {
|
|
19
123
|
const builder = createResponseBuilder();
|
|
20
124
|
// Validate categories
|