From 754ac015c917dc3d29f14293db3c34c813241590 Mon Sep 17 00:00:00 2001 From: Abdul Shaik Date: Wed, 11 Mar 2026 12:38:10 +0530 Subject: [PATCH 1/5] feat: add excludePatterns parameter to violation reporting tools Add optional excludePatterns parameter to report-violations and report-all-violations tools, enabling users to exclude files/directories from violation scans using glob patterns. Features: - Supports single string or array of glob patterns - Standard glob syntax: *, **, ?, character classes - Custom glob-to-regex converter (no external dependencies) - Post-processing filter approach (filters after plugin execution) - Cross-platform path normalization - Fail-fast validation with clear error messages Changes: - Add excludePatterns to schema definitions (schema-helpers.ts) - Update type interfaces across all layers - Implement inline pattern matching in coverage-analyzer.ts - Pass excludePatterns through base-analyzer.ts - Update both tool handlers to accept and forward parameter Examples: - Single: excludePatterns: 'node_modules/**' - Multiple: excludePatterns: ['node_modules/**', '**/dist/**', '**/*.spec.ts'] Fixes path handling bug: Use issue.source.file directly (already relative) instead of calling path.relative() Build: Passes successfully without errors Backward Compatible: Optional parameter, no breaking changes --- .../ds/report-violations/models/types.ts | 2 + .../report-all-violations.tool.ts | 1 + .../report-violations.tool.ts | 8 +- .../tools/ds/shared/models/schema-helpers.ts | 15 ++ .../violation-analysis/base-analyzer.ts | 2 + .../violation-analysis/coverage-analyzer.ts | 143 +++++++++++++++++- .../ds/shared/violation-analysis/types.ts | 2 + 7 files changed, 167 insertions(+), 6 deletions(-) diff --git a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/models/types.ts b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/models/types.ts index d20bf21..12893cd 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/models/types.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/models/types.ts @@ -9,6 +9,7 @@ export interface ReportViolationsOptions extends BaseHandlerOptions { componentName: string; groupBy?: 'file' | 'folder'; saveAsFile?: boolean; + excludePatterns?: string | string[]; } export interface ViolationEntry { @@ -45,6 +46,7 @@ export interface ReportAllViolationsOptions extends BaseHandlerOptions { directory: string; groupBy?: 'component' | 'file'; saveAsFile?: boolean; + excludePatterns?: string | string[]; } export interface AllViolationsEntry { diff --git a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts index ce168ca..c977cd6 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts @@ -91,6 +91,7 @@ export const reportAllViolationsHandler = createHandler< returnRawData: true, directory: params.directory, dsComponents, + excludePatterns: params.excludePatterns, }); const raw = coverageResult.rawData?.rawPluginResult; diff --git a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts index bc456eb..01093c8 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts @@ -30,7 +30,13 @@ export const reportViolationsHandler = createHandler< >( reportViolationsSchema.name, async (params, { cwd, workspaceRoot }) => { - const result = await analyzeViolationsBase(params); + const result = await analyzeViolationsBase({ + cwd, + directory: params.directory, + componentName: params.componentName, + deprecatedCssClassesPath: params.deprecatedCssClassesPath, + excludePatterns: params.excludePatterns, + }); const failedAudits = filterFailedAudits(result); if (failedAudits.length === 0) { diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts index 774b4e6..2306332 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts @@ -24,6 +24,19 @@ export const COMMON_SCHEMA_PROPERTIES = { 'How to group the violation results in the output. "file" groups violations by individual file paths, "folder" groups by directory structure.', default: 'file' as const, }, + + excludePatterns: { + anyOf: [ + { type: 'string' as const }, + { type: 'array' as const, items: { type: 'string' as const } }, + ], + description: + 'Glob pattern(s) to exclude files/directories from scanning. ' + + 'Supports standard glob syntax: * (any chars except /), ** (any chars including /), ' + + '? (single char), [...] (character class). ' + + 'Examples: "node_modules/**", "**/dist/**", "**/*.spec.ts". ' + + 'Can be a single string or array of strings.', + }, } as const; /** @@ -71,6 +84,7 @@ export const createProjectAnalysisSchema = ( type: 'object', properties: { directory: COMMON_SCHEMA_PROPERTIES.directory, + excludePatterns: COMMON_SCHEMA_PROPERTIES.excludePatterns, ...additionalProperties, }, required: ['directory'], @@ -87,6 +101,7 @@ export const createViolationReportingSchema = ( directory: COMMON_SCHEMA_PROPERTIES.directory, componentName: COMMON_SCHEMA_PROPERTIES.componentName, groupBy: COMMON_SCHEMA_PROPERTIES.groupBy, + excludePatterns: COMMON_SCHEMA_PROPERTIES.excludePatterns, ...additionalProperties, }, required: ['directory', 'componentName'], diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts index 08d17cb..cf6b33e 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts @@ -19,6 +19,7 @@ export async function analyzeViolationsBase( directory, componentName, deprecatedCssClassesPath, + excludePatterns, } = options; validateComponentName(componentName); @@ -53,6 +54,7 @@ export async function analyzeViolationsBase( returnRawData: true, directory, dsComponents, + excludePatterns, }; const result = await collectFilesViolations(params); diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts index 0aa9bd2..89b769f 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts @@ -25,6 +25,100 @@ export function extractComponentName(title: string): string { return componentName; } +/** + * Normalizes excludePatterns to an array format + */ +function normalizeExcludePatterns( + patterns: string | string[] | undefined, +): string[] { + if (!patterns) { + return []; + } + return Array.isArray(patterns) ? patterns : [patterns]; +} + +/** + * Converts a glob pattern to a regular expression + * Supports: *, **, ? + */ +function globToRegex(pattern: string): RegExp { + // Escape special regex characters except glob wildcards + let regexPattern = pattern + .replace(/[.+^${}()|[\]\\]/g, '\\$&') + .replace(/\?/g, '.') + .replace(/\*\*/g, '') + .replace(/\*/g, '[^/]*') + .replace(//g, '.*'); + + // Handle leading **/ to match from start or middle of path + if (regexPattern.startsWith('^.*\\/')) { + // Make the leading .*/ optional to match paths starting with the pattern + regexPattern = '^(?:.*\\/)?' + regexPattern.substring(6); + } + + // Anchor the pattern if not already anchored + if (!regexPattern.startsWith('^')) { + regexPattern = `^${regexPattern}`; + } + if (!regexPattern.endsWith('$')) { + regexPattern = `${regexPattern}$`; + } + + return new RegExp(regexPattern); +} + +/** + * Validates glob pattern syntax + * @throws Error if pattern is invalid + */ +function validateGlobPattern(pattern: string): void { + try { + // Test pattern compilation + globToRegex(pattern); + } catch (ctx) { + throw new Error( + `Invalid glob pattern "${pattern}": ${ctx instanceof Error ? ctx.message : 'Unknown error'}`, + ); + } +} + +/** + * Validates all patterns in the array + * @throws Error if any pattern is invalid + */ +function validateExcludePatterns( + patterns: string | string[] | undefined, +): void { + const normalized = normalizeExcludePatterns(patterns); + for (const pattern of normalized) { + validateGlobPattern(pattern); + } +} + +/** + * Checks if a file path matches any exclude pattern + * @param filePath - Relative file path from scan root + * @param excludePatterns - Array of glob patterns + * @returns true if file should be excluded + */ +function shouldExcludeFile( + filePath: string, + excludePatterns: string[], +): boolean { + if (excludePatterns.length === 0) { + return false; + } + + // Normalize path separators for cross-platform compatibility (backslash to forward slash) + const normalizedPath = filePath.replace(/\\/g, '/'); + + // Check if path matches any pattern + return excludePatterns.some((pattern) => { + const regex = globToRegex(pattern); + return regex.test(normalizedPath); + }); +} + /** * Main implementation function for reporting project coverage */ @@ -44,17 +138,26 @@ export async function analyzeProjectCoverage( ); } + // Validate exclude patterns early (fail-fast validation) + if (params.excludePatterns) { + validateExcludePatterns(params.excludePatterns); + } + + const normalizedPatterns = normalizeExcludePatterns(params.excludePatterns); + if (params.cwd) { process.chdir(params.cwd); } + const scanRootDirectory = resolveCrossPlatformPath( + params.cwd || process.cwd(), + params.directory, + ); + // Execute the coverage plugin const pluginConfig = await dsComponentCoveragePlugin({ dsComponents: params.dsComponents, - directory: resolveCrossPlatformPath( - params.cwd || process.cwd(), - params.directory, - ), + directory: scanRootDirectory, }); const { executePlugin } = await import('@code-pushup/core'); @@ -63,13 +166,43 @@ export async function analyzeProjectCoverage( persist: { outputDir: '' }, })) as BaseViolationResult; + // Filter violations from excluded files + const filteredResult: BaseViolationResult = { + ...result, + audits: result.audits.map((audit) => { + if (!audit.details?.issues) { + return audit; + } + + const filteredIssues = audit.details.issues.filter((issue) => { + if (!issue.source?.file) { + return true; // Keep issues without file information + } + + // The file path is already relative to the scan root directory + const relativePath = issue.source.file; + + // Check if file should be excluded + return !shouldExcludeFile(relativePath, normalizedPatterns); + }); + + return { + ...audit, + details: { + ...audit.details, + issues: filteredIssues, + }, + }; + }), + }; + const formattedResult: FormattedCoverageResult = { textOutput: '', // No longer used, kept for backwards compatibility }; if (params.returnRawData) { formattedResult.rawData = { - rawPluginResult: result, + rawPluginResult: filteredResult, pluginOptions: { directory: params.directory, dsComponents: params.dsComponents, diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/types.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/types.ts index c8d1fe5..f564ef6 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/types.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/types.ts @@ -7,6 +7,7 @@ export interface BaseViolationOptions { directory: string; componentName: string; deprecatedCssClassesPath?: string; + excludePatterns?: string | string[]; } export interface BaseViolationIssue { @@ -39,6 +40,7 @@ export interface ReportCoverageParams { outputFormat?: 'text'; directory: string; dsComponents: DsComponent[]; + excludePatterns?: string | string[]; } export interface DsComponent { From e02e3ca8fe8a095f6ef80c222d23e947f32dae27 Mon Sep 17 00:00:00 2001 From: Abdul Shaik Date: Sat, 14 Mar 2026 12:22:34 +0530 Subject: [PATCH 2/5] fix: apply code formatting to coverage-analyzer.ts --- .../lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts index 89b769f..b3c16d9 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts @@ -53,7 +53,7 @@ function globToRegex(pattern: string): RegExp { // Handle leading **/ to match from start or middle of path if (regexPattern.startsWith('^.*\\/')) { // Make the leading .*/ optional to match paths starting with the pattern - regexPattern = '^(?:.*\\/)?' + regexPattern.substring(6); + regexPattern = '^(?:.*\\/)?' + regexPattern.substring(6); } // Anchor the pattern if not already anchored From 2b1278739422c1c9f134df13f02f8216394e7096 Mon Sep 17 00:00:00 2001 From: Abdul Shaik Date: Sat, 14 Mar 2026 14:50:31 +0530 Subject: [PATCH 3/5] fix: glob pattern matching and path handling for excludePatterns --- .../report-violations.tool.ts | 1 + .../violation-analysis/coverage-analyzer.ts | 51 ++++++------------- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts index 01093c8..1f11a48 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-violations.tool.ts @@ -37,6 +37,7 @@ export const reportViolationsHandler = createHandler< deprecatedCssClassesPath: params.deprecatedCssClassesPath, excludePatterns: params.excludePatterns, }); + const failedAudits = filterFailedAudits(result); if (failedAudits.length === 0) { diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts index b3c16d9..815014e 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts @@ -38,11 +38,10 @@ function normalizeExcludePatterns( } /** - * Converts a glob pattern to a regular expression + * Converts a glob pattern to a regular expression. * Supports: *, **, ? */ function globToRegex(pattern: string): RegExp { - // Escape special regex characters except glob wildcards let regexPattern = pattern .replace(/[.+^${}()|[\]\\]/g, '\\$&') .replace(/\?/g, '.') @@ -50,16 +49,13 @@ function globToRegex(pattern: string): RegExp { .replace(/\*/g, '[^/]*') .replace(//g, '.*'); - // Handle leading **/ to match from start or middle of path - if (regexPattern.startsWith('^.*\\/')) { - // Make the leading .*/ optional to match paths starting with the pattern - regexPattern = '^(?:.*\\/)?' + regexPattern.substring(6); - } - - // Anchor the pattern if not already anchored - if (!regexPattern.startsWith('^')) { + if (pattern.startsWith('**/')) { + regexPattern = regexPattern.replace(/^\.\*\//, ''); + regexPattern = `^(?:.*\\/)?${regexPattern}`; + } else { regexPattern = `^${regexPattern}`; } + if (!regexPattern.endsWith('$')) { regexPattern = `${regexPattern}$`; } @@ -67,13 +63,8 @@ function globToRegex(pattern: string): RegExp { return new RegExp(regexPattern); } -/** - * Validates glob pattern syntax - * @throws Error if pattern is invalid - */ function validateGlobPattern(pattern: string): void { try { - // Test pattern compilation globToRegex(pattern); } catch (ctx) { throw new Error( @@ -82,10 +73,6 @@ function validateGlobPattern(pattern: string): void { } } -/** - * Validates all patterns in the array - * @throws Error if any pattern is invalid - */ function validateExcludePatterns( patterns: string | string[] | undefined, ): void { @@ -95,12 +82,6 @@ function validateExcludePatterns( } } -/** - * Checks if a file path matches any exclude pattern - * @param filePath - Relative file path from scan root - * @param excludePatterns - Array of glob patterns - * @returns true if file should be excluded - */ function shouldExcludeFile( filePath: string, excludePatterns: string[], @@ -109,10 +90,8 @@ function shouldExcludeFile( return false; } - // Normalize path separators for cross-platform compatibility (backslash to forward slash) const normalizedPath = filePath.replace(/\\/g, '/'); - // Check if path matches any pattern return excludePatterns.some((pattern) => { const regex = globToRegex(pattern); return regex.test(normalizedPath); @@ -125,7 +104,6 @@ function shouldExcludeFile( export async function analyzeProjectCoverage( params: ReportCoverageParams, ): Promise { - // Validate input parameters if (!params.directory || typeof params.directory !== 'string') { throw new Error('Directory parameter is required and must be a string'); } @@ -138,7 +116,6 @@ export async function analyzeProjectCoverage( ); } - // Validate exclude patterns early (fail-fast validation) if (params.excludePatterns) { validateExcludePatterns(params.excludePatterns); } @@ -154,7 +131,6 @@ export async function analyzeProjectCoverage( params.directory, ); - // Execute the coverage plugin const pluginConfig = await dsComponentCoveragePlugin({ dsComponents: params.dsComponents, directory: scanRootDirectory, @@ -166,7 +142,6 @@ export async function analyzeProjectCoverage( persist: { outputDir: '' }, })) as BaseViolationResult; - // Filter violations from excluded files const filteredResult: BaseViolationResult = { ...result, audits: result.audits.map((audit) => { @@ -176,13 +151,17 @@ export async function analyzeProjectCoverage( const filteredIssues = audit.details.issues.filter((issue) => { if (!issue.source?.file) { - return true; // Keep issues without file information + return true; } - // The file path is already relative to the scan root directory - const relativePath = issue.source.file; + let relativePath = issue.source.file; + const dirPrefix = params.directory + .replace(/^\.\//, '') + .replace(/\\/g, '/'); + if (relativePath.startsWith(dirPrefix + '/')) { + relativePath = relativePath.substring(dirPrefix.length + 1); + } - // Check if file should be excluded return !shouldExcludeFile(relativePath, normalizedPatterns); }); @@ -197,7 +176,7 @@ export async function analyzeProjectCoverage( }; const formattedResult: FormattedCoverageResult = { - textOutput: '', // No longer used, kept for backwards compatibility + textOutput: '', }; if (params.returnRawData) { From 92aea717beacbdd32bbc7f036a6a2ada1135dcdc Mon Sep 17 00:00:00 2001 From: Abdul Shaik Date: Mon, 16 Mar 2026 17:44:47 +0530 Subject: [PATCH 4/5] fix: address PR review comments - path normalization, regex precompilation, score fix, schema cleanup --- .../tools/ds/shared/models/schema-helpers.ts | 2 +- .../violation-analysis/base-analyzer.ts | 23 ++++++---------- .../violation-analysis/coverage-analyzer.ts | 27 +++++++++---------- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts index 2306332..65d5dbe 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/models/schema-helpers.ts @@ -33,7 +33,7 @@ export const COMMON_SCHEMA_PROPERTIES = { description: 'Glob pattern(s) to exclude files/directories from scanning. ' + 'Supports standard glob syntax: * (any chars except /), ** (any chars including /), ' + - '? (single char), [...] (character class). ' + + '? (single char). ' + 'Examples: "node_modules/**", "**/dist/**", "**/*.spec.ts". ' + 'Can be a single string or array of strings.', }, diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts index cf6b33e..fb91a47 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts @@ -14,47 +14,40 @@ import { analyzeProjectCoverage as collectFilesViolations } from './coverage-ana export async function analyzeViolationsBase( options: BaseViolationOptions, ): Promise { - const { - cwd = process.cwd(), - directory, - componentName, - deprecatedCssClassesPath, - excludePatterns, - } = options; + const cwd = options.cwd || process.cwd(); - validateComponentName(componentName); + validateComponentName(options.componentName); - if (!directory || typeof directory !== 'string') { + if (!options.directory || typeof options.directory !== 'string') { throw new Error('Directory parameter is required and must be a string'); } process.chdir(cwd); - if (!deprecatedCssClassesPath) { + if (!options.deprecatedCssClassesPath) { throw new Error( 'Missing ds.deprecatedCssClassesPath. Provide --ds.deprecatedCssClassesPath in mcp.json file.', ); } const deprecatedCssClasses = await getDeprecatedCssClasses( - componentName, - deprecatedCssClassesPath, + options.componentName, + options.deprecatedCssClassesPath, cwd, ); const dsComponents = [ { - componentName, + componentName: options.componentName, deprecatedCssClasses, }, ]; const params: ReportCoverageParams = { + ...options, cwd, returnRawData: true, - directory, dsComponents, - excludePatterns, }; const result = await collectFilesViolations(params); diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts index 815014e..b1133ee 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts @@ -7,6 +7,7 @@ import { FormattedCoverageResult, } from './types.js'; import { resolveCrossPlatformPath } from '../utils/cross-platform-path.js'; +import { normalizeFilePath } from './formatters.js'; /** * Extracts component name from audit title - performance optimized with caching @@ -84,18 +85,15 @@ function validateExcludePatterns( function shouldExcludeFile( filePath: string, - excludePatterns: string[], + excludeRegexes: RegExp[], ): boolean { - if (excludePatterns.length === 0) { + if (excludeRegexes.length === 0) { return false; } const normalizedPath = filePath.replace(/\\/g, '/'); - return excludePatterns.some((pattern) => { - const regex = globToRegex(pattern); - return regex.test(normalizedPath); - }); + return excludeRegexes.some((regex) => regex.test(normalizedPath)); } /** @@ -121,6 +119,7 @@ export async function analyzeProjectCoverage( } const normalizedPatterns = normalizeExcludePatterns(params.excludePatterns); + const excludeRegexes = normalizedPatterns.map(globToRegex); if (params.cwd) { process.chdir(params.cwd); @@ -154,19 +153,19 @@ export async function analyzeProjectCoverage( return true; } - let relativePath = issue.source.file; - const dirPrefix = params.directory - .replace(/^\.\//, '') - .replace(/\\/g, '/'); - if (relativePath.startsWith(dirPrefix + '/')) { - relativePath = relativePath.substring(dirPrefix.length + 1); - } + const relativePath = normalizeFilePath( + issue.source.file, + params.directory, + ); - return !shouldExcludeFile(relativePath, normalizedPatterns); + return !shouldExcludeFile(relativePath, excludeRegexes); }); + const score = filteredIssues.length === 0 ? 1 : audit.score; + return { ...audit, + score, details: { ...audit.details, issues: filteredIssues, From 505a7a41447ee319185e4e4ef902d04513213f57 Mon Sep 17 00:00:00 2001 From: Abdul Shaik Date: Mon, 16 Mar 2026 18:14:21 +0530 Subject: [PATCH 5/5] fix: glob ? should not match directory separator --- .../lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts index b1133ee..150146b 100644 --- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts +++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/coverage-analyzer.ts @@ -45,7 +45,7 @@ function normalizeExcludePatterns( function globToRegex(pattern: string): RegExp { let regexPattern = pattern .replace(/[.+^${}()|[\]\\]/g, '\\$&') - .replace(/\?/g, '.') + .replace(/\?/g, '[^/]') .replace(/\*\*/g, '') .replace(/\*/g, '[^/]*') .replace(//g, '.*');