feat(diff): add filtering capabilities to get_pull_request_diff
- Add include_patterns, exclude_patterns, and file_path parameters to get_pull_request_diff - Implement a diff parser to filter the diff based on the provided patterns - Add minimatch dependency for glob pattern matching - Update documentation and changelog
This commit is contained in:
parent
793355bdaa
commit
d649a116fe
9 changed files with 446 additions and 18 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.7.0] - 2025-01-26
|
||||
|
||||
### Added
|
||||
- **Enhanced `get_pull_request_diff` with filtering capabilities**:
|
||||
- Added `include_patterns` parameter to filter diff by file patterns (whitelist)
|
||||
- Added `exclude_patterns` parameter to exclude files from diff (blacklist)
|
||||
- Added `file_path` parameter to get diff for a specific file only
|
||||
- Patterns support standard glob syntax (e.g., `*.js`, `src/**/*.res`, `node_modules/**`)
|
||||
- Response includes filtering metadata showing total files, included/excluded counts, and excluded file list
|
||||
- Added `minimatch` dependency for glob pattern matching
|
||||
- Created `DiffParser` utility class for parsing and filtering unified diff format
|
||||
|
||||
### Changed
|
||||
- Modified `get_pull_request_diff` tool to support optional filtering without breaking existing usage
|
||||
- Updated tool definition and type guards to include new optional parameters
|
||||
- Enhanced documentation with comprehensive examples of filtering usage
|
||||
|
||||
## [0.6.1] - 2025-01-26
|
||||
|
||||
### Added
|
||||
|
|
72
README.md
72
README.md
|
@ -421,7 +421,10 @@ This tool is particularly useful for:
|
|||
|
||||
### Get Pull Request Diff
|
||||
|
||||
Get the diff/changes for a pull request with optional filtering capabilities:
|
||||
|
||||
```typescript
|
||||
// Get full diff (default behavior)
|
||||
{
|
||||
"tool": "get_pull_request_diff",
|
||||
"arguments": {
|
||||
|
@ -431,6 +434,75 @@ This tool is particularly useful for:
|
|||
"context_lines": 5 // Optional (default: 3)
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude specific file types
|
||||
{
|
||||
"tool": "get_pull_request_diff",
|
||||
"arguments": {
|
||||
"workspace": "PROJ",
|
||||
"repository": "my-repo",
|
||||
"pull_request_id": 123,
|
||||
"exclude_patterns": ["*.lock", "*.svg", "node_modules/**", "*.min.js"]
|
||||
}
|
||||
}
|
||||
|
||||
// Include only specific file types
|
||||
{
|
||||
"tool": "get_pull_request_diff",
|
||||
"arguments": {
|
||||
"workspace": "PROJ",
|
||||
"repository": "my-repo",
|
||||
"pull_request_id": 123,
|
||||
"include_patterns": ["*.res", "*.resi", "src/**/*.js"]
|
||||
}
|
||||
}
|
||||
|
||||
// Get diff for a specific file only
|
||||
{
|
||||
"tool": "get_pull_request_diff",
|
||||
"arguments": {
|
||||
"workspace": "PROJ",
|
||||
"repository": "my-repo",
|
||||
"pull_request_id": 123,
|
||||
"file_path": "src/components/Button.res"
|
||||
}
|
||||
}
|
||||
|
||||
// Combine filters
|
||||
{
|
||||
"tool": "get_pull_request_diff",
|
||||
"arguments": {
|
||||
"workspace": "PROJ",
|
||||
"repository": "my-repo",
|
||||
"pull_request_id": 123,
|
||||
"include_patterns": ["src/**/*"],
|
||||
"exclude_patterns": ["*.test.js", "*.spec.js"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Filtering Options:**
|
||||
- `include_patterns`: Array of glob patterns to include (whitelist)
|
||||
- `exclude_patterns`: Array of glob patterns to exclude (blacklist)
|
||||
- `file_path`: Get diff for a specific file only
|
||||
- Patterns support standard glob syntax (e.g., `*.js`, `src/**/*.res`, `!test/**`)
|
||||
|
||||
**Response includes filtering metadata:**
|
||||
```json
|
||||
{
|
||||
"message": "Pull request diff retrieved successfully",
|
||||
"pull_request_id": 123,
|
||||
"diff": "..filtered diff content..",
|
||||
"filter_metadata": {
|
||||
"total_files": 15,
|
||||
"included_files": 12,
|
||||
"excluded_files": 3,
|
||||
"excluded_file_list": ["package-lock.json", "logo.svg", "yarn.lock"],
|
||||
"filters_applied": {
|
||||
"exclude_patterns": ["*.lock", "*.svg"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Approve Pull Request
|
||||
|
|
53
package-lock.json
generated
53
package-lock.json
generated
|
@ -1,20 +1,28 @@
|
|||
{
|
||||
"name": "bitbucket-mcp-server",
|
||||
"version": "1.0.0",
|
||||
"name": "@nexus2520/bitbucket-mcp-server",
|
||||
"version": "0.7.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bitbucket-mcp-server",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"name": "@nexus2520/bitbucket-mcp-server",
|
||||
"version": "0.7.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.12.1",
|
||||
"axios": "^1.9.0"
|
||||
"axios": "^1.9.0",
|
||||
"minimatch": "^9.0.3"
|
||||
},
|
||||
"bin": {
|
||||
"bitbucket-mcp-server": "build/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/node": "^22.15.29",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
|
@ -38,6 +46,12 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.15.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
|
||||
|
@ -89,6 +103,11 @@
|
|||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||
|
@ -108,6 +127,14 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
@ -684,6 +711,20 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@nexus2520/bitbucket-mcp-server",
|
||||
"version": "0.6.1",
|
||||
"version": "0.7.0",
|
||||
"description": "MCP server for Bitbucket API integration - supports both Cloud and Server",
|
||||
"type": "module",
|
||||
"main": "./build/index.js",
|
||||
|
@ -44,9 +44,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.12.1",
|
||||
"axios": "^1.9.0"
|
||||
"axios": "^1.9.0",
|
||||
"minimatch": "^9.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/node": "^22.15.29",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
isApprovePullRequestArgs,
|
||||
isRequestChangesArgs
|
||||
} from '../types/guards.js';
|
||||
import { DiffParser } from '../utils/diff-parser.js';
|
||||
|
||||
export class ReviewHandlers {
|
||||
constructor(
|
||||
|
@ -20,7 +21,15 @@ export class ReviewHandlers {
|
|||
);
|
||||
}
|
||||
|
||||
const { workspace, repository, pull_request_id, context_lines = 3 } = args;
|
||||
const {
|
||||
workspace,
|
||||
repository,
|
||||
pull_request_id,
|
||||
context_lines = 3,
|
||||
include_patterns,
|
||||
exclude_patterns,
|
||||
file_path
|
||||
} = args;
|
||||
|
||||
try {
|
||||
let apiPath: string;
|
||||
|
@ -39,8 +48,13 @@ export class ReviewHandlers {
|
|||
// For diff, we want the raw text response
|
||||
config.headers = { 'Accept': 'text/plain' };
|
||||
|
||||
const diff = await this.apiClient.makeRequest<string>('get', apiPath, undefined, config);
|
||||
const rawDiff = await this.apiClient.makeRequest<string>('get', apiPath, undefined, config);
|
||||
|
||||
// Check if filtering is needed
|
||||
const needsFiltering = file_path || include_patterns || exclude_patterns;
|
||||
|
||||
if (!needsFiltering) {
|
||||
// Return raw diff without filtering
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
|
@ -48,11 +62,65 @@ export class ReviewHandlers {
|
|||
text: JSON.stringify({
|
||||
message: 'Pull request diff retrieved successfully',
|
||||
pull_request_id,
|
||||
diff: diff
|
||||
diff: rawDiff
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Apply filtering
|
||||
const diffParser = new DiffParser();
|
||||
const sections = diffParser.parseDiffIntoSections(rawDiff);
|
||||
|
||||
const filterOptions = {
|
||||
includePatterns: include_patterns,
|
||||
excludePatterns: exclude_patterns,
|
||||
filePath: file_path
|
||||
};
|
||||
|
||||
const filteredResult = diffParser.filterSections(sections, filterOptions);
|
||||
const filteredDiff = diffParser.reconstructDiff(filteredResult.sections);
|
||||
|
||||
// Build response with filtering metadata
|
||||
const response: any = {
|
||||
message: 'Pull request diff retrieved successfully',
|
||||
pull_request_id,
|
||||
diff: filteredDiff
|
||||
};
|
||||
|
||||
// Add filter metadata
|
||||
if (filteredResult.metadata.excludedFiles > 0 || file_path || include_patterns || exclude_patterns) {
|
||||
response.filter_metadata = {
|
||||
total_files: filteredResult.metadata.totalFiles,
|
||||
included_files: filteredResult.metadata.includedFiles,
|
||||
excluded_files: filteredResult.metadata.excludedFiles
|
||||
};
|
||||
|
||||
if (filteredResult.metadata.excludedFileList.length > 0) {
|
||||
response.filter_metadata.excluded_file_list = filteredResult.metadata.excludedFileList;
|
||||
}
|
||||
|
||||
response.filter_metadata.filters_applied = {};
|
||||
if (file_path) {
|
||||
response.filter_metadata.filters_applied.file_path = file_path;
|
||||
}
|
||||
if (include_patterns) {
|
||||
response.filter_metadata.filters_applied.include_patterns = include_patterns;
|
||||
}
|
||||
if (exclude_patterns) {
|
||||
response.filter_metadata.filters_applied.exclude_patterns = exclude_patterns;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(response, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return this.apiClient.handleApiError(error, `getting diff for pull request ${pull_request_id} in ${workspace}/${repository}`);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class BitbucketMCPServer {
|
|||
this.server = new Server(
|
||||
{
|
||||
name: 'bitbucket-mcp-server',
|
||||
version: '0.6.1',
|
||||
version: '0.7.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
|
|
|
@ -274,7 +274,7 @@ export const toolDefinitions = [
|
|||
},
|
||||
{
|
||||
name: 'get_pull_request_diff',
|
||||
description: 'Get the diff/changes for a pull request',
|
||||
description: 'Get the diff/changes for a pull request with optional filtering',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
@ -294,6 +294,20 @@ export const toolDefinitions = [
|
|||
type: 'number',
|
||||
description: 'Number of context lines around changes (optional, default: 3)',
|
||||
},
|
||||
include_patterns: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Array of glob patterns to include (e.g., ["*.res", "src/**/*.js"]) (optional)',
|
||||
},
|
||||
exclude_patterns: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Array of glob patterns to exclude (e.g., ["*.lock", "*.svg"]) (optional)',
|
||||
},
|
||||
file_path: {
|
||||
type: 'string',
|
||||
description: 'Specific file path to get diff for (e.g., "src/index.ts") (optional)',
|
||||
},
|
||||
},
|
||||
required: ['workspace', 'repository', 'pull_request_id'],
|
||||
},
|
||||
|
|
|
@ -152,13 +152,19 @@ export const isGetPullRequestDiffArgs = (
|
|||
repository: string;
|
||||
pull_request_id: number;
|
||||
context_lines?: number;
|
||||
include_patterns?: string[];
|
||||
exclude_patterns?: string[];
|
||||
file_path?: string;
|
||||
} =>
|
||||
typeof args === 'object' &&
|
||||
args !== null &&
|
||||
typeof args.workspace === 'string' &&
|
||||
typeof args.repository === 'string' &&
|
||||
typeof args.pull_request_id === 'number' &&
|
||||
(args.context_lines === undefined || typeof args.context_lines === 'number');
|
||||
(args.context_lines === undefined || typeof args.context_lines === 'number') &&
|
||||
(args.include_patterns === undefined || (Array.isArray(args.include_patterns) && args.include_patterns.every((p: any) => typeof p === 'string'))) &&
|
||||
(args.exclude_patterns === undefined || (Array.isArray(args.exclude_patterns) && args.exclude_patterns.every((p: any) => typeof p === 'string'))) &&
|
||||
(args.file_path === undefined || typeof args.file_path === 'string');
|
||||
|
||||
export const isApprovePullRequestArgs = (
|
||||
args: any
|
||||
|
|
208
src/utils/diff-parser.ts
Normal file
208
src/utils/diff-parser.ts
Normal file
|
@ -0,0 +1,208 @@
|
|||
import { minimatch } from 'minimatch';
|
||||
|
||||
export interface DiffSection {
|
||||
filePath: string;
|
||||
oldPath?: string; // For renamed files
|
||||
content: string;
|
||||
isNew: boolean;
|
||||
isDeleted: boolean;
|
||||
isRenamed: boolean;
|
||||
isBinary: boolean;
|
||||
}
|
||||
|
||||
export interface FilterOptions {
|
||||
includePatterns?: string[];
|
||||
excludePatterns?: string[];
|
||||
filePath?: string;
|
||||
}
|
||||
|
||||
export interface FilteredResult {
|
||||
sections: DiffSection[];
|
||||
metadata: {
|
||||
totalFiles: number;
|
||||
includedFiles: number;
|
||||
excludedFiles: number;
|
||||
excludedFileList: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export class DiffParser {
|
||||
/**
|
||||
* Parse a unified diff into file sections
|
||||
*/
|
||||
parseDiffIntoSections(diff: string): DiffSection[] {
|
||||
const sections: DiffSection[] = [];
|
||||
|
||||
// Split by file boundaries - handle both formats
|
||||
const fileChunks = diff.split(/(?=^diff --git)/gm).filter(chunk => chunk.trim());
|
||||
|
||||
for (const chunk of fileChunks) {
|
||||
const section = this.parseFileSection(chunk);
|
||||
if (section) {
|
||||
sections.push(section);
|
||||
}
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single file section from the diff
|
||||
*/
|
||||
private parseFileSection(chunk: string): DiffSection | null {
|
||||
const lines = chunk.split('\n');
|
||||
if (lines.length === 0) return null;
|
||||
|
||||
// Extract file paths from the diff header
|
||||
let filePath = '';
|
||||
let oldPath: string | undefined;
|
||||
let isNew = false;
|
||||
let isDeleted = false;
|
||||
let isRenamed = false;
|
||||
let isBinary = false;
|
||||
|
||||
// Look for diff --git line - handle both standard and Bitbucket Server formats
|
||||
const gitDiffMatch = lines[0].match(/^diff --git (?:a\/|src:\/\/)(.+?) (?:b\/|dst:\/\/)(.+?)$/);
|
||||
if (gitDiffMatch) {
|
||||
const [, aPath, bPath] = gitDiffMatch;
|
||||
filePath = bPath;
|
||||
|
||||
// Check subsequent lines for file status
|
||||
for (let i = 1; i < Math.min(lines.length, 10); i++) {
|
||||
const line = lines[i];
|
||||
|
||||
if (line.startsWith('new file mode')) {
|
||||
isNew = true;
|
||||
} else if (line.startsWith('deleted file mode')) {
|
||||
isDeleted = true;
|
||||
filePath = aPath; // Use the original path for deleted files
|
||||
} else if (line.startsWith('rename from')) {
|
||||
isRenamed = true;
|
||||
oldPath = line.replace('rename from ', '');
|
||||
} else if (line.includes('Binary files') && line.includes('differ')) {
|
||||
isBinary = true;
|
||||
} else if (line.startsWith('--- ')) {
|
||||
// Alternative way to detect new/deleted
|
||||
if (line.includes('/dev/null')) {
|
||||
isNew = true;
|
||||
}
|
||||
} else if (line.startsWith('+++ ')) {
|
||||
if (line.includes('/dev/null')) {
|
||||
isDeleted = true;
|
||||
}
|
||||
// Extract path from +++ line if needed - handle both formats
|
||||
const match = line.match(/^\+\+\+ (?:b\/|dst:\/\/)(.+)$/);
|
||||
if (match && !filePath) {
|
||||
filePath = match[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try to extract from --- and +++ lines
|
||||
if (!filePath) {
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('+++ ')) {
|
||||
const match = line.match(/^\+\+\+ (?:b\/|dst:\/\/)(.+)$/);
|
||||
if (match) {
|
||||
filePath = match[1];
|
||||
break;
|
||||
}
|
||||
} else if (line.startsWith('--- ')) {
|
||||
const match = line.match(/^--- (?:a\/|src:\/\/)(.+)$/);
|
||||
if (match) {
|
||||
filePath = match[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filePath) return null;
|
||||
|
||||
return {
|
||||
filePath,
|
||||
oldPath,
|
||||
content: chunk,
|
||||
isNew,
|
||||
isDeleted,
|
||||
isRenamed,
|
||||
isBinary
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filters to diff sections
|
||||
*/
|
||||
filterSections(sections: DiffSection[], options: FilterOptions): FilteredResult {
|
||||
const excludedFileList: string[] = [];
|
||||
let filteredSections = sections;
|
||||
|
||||
// If specific file path is requested, only keep that file
|
||||
if (options.filePath) {
|
||||
filteredSections = sections.filter(section =>
|
||||
section.filePath === options.filePath ||
|
||||
section.oldPath === options.filePath
|
||||
);
|
||||
|
||||
// Track excluded files
|
||||
sections.forEach(section => {
|
||||
if (section.filePath !== options.filePath &&
|
||||
section.oldPath !== options.filePath) {
|
||||
excludedFileList.push(section.filePath);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Apply exclude patterns first (blacklist)
|
||||
if (options.excludePatterns && options.excludePatterns.length > 0) {
|
||||
filteredSections = filteredSections.filter(section => {
|
||||
const shouldExclude = options.excludePatterns!.some(pattern =>
|
||||
minimatch(section.filePath, pattern, { matchBase: true })
|
||||
);
|
||||
|
||||
if (shouldExclude) {
|
||||
excludedFileList.push(section.filePath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Apply include patterns if specified (whitelist)
|
||||
if (options.includePatterns && options.includePatterns.length > 0) {
|
||||
filteredSections = filteredSections.filter(section => {
|
||||
const shouldInclude = options.includePatterns!.some(pattern =>
|
||||
minimatch(section.filePath, pattern, { matchBase: true })
|
||||
);
|
||||
|
||||
if (!shouldInclude) {
|
||||
excludedFileList.push(section.filePath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sections: filteredSections,
|
||||
metadata: {
|
||||
totalFiles: sections.length,
|
||||
includedFiles: filteredSections.length,
|
||||
excludedFiles: sections.length - filteredSections.length,
|
||||
excludedFileList
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstruct a unified diff from filtered sections
|
||||
*/
|
||||
reconstructDiff(sections: DiffSection[]): string {
|
||||
if (sections.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Join all sections with proper spacing
|
||||
return sections.map(section => section.content).join('\n');
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue