code-review-ai-cli 2.1.0__tar.gz → 2.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. code_review_ai_cli-2.2.1/PKG-INFO +146 -0
  2. code_review_ai_cli-2.2.1/README.md +106 -0
  3. code_review_ai_cli-2.2.1/code_review_ai_cli.egg-info/PKG-INFO +146 -0
  4. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/pyproject.toml +1 -1
  5. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/ai_review.py +3 -12
  6. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/config.py +3 -1
  7. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/git_utils.py +0 -38
  8. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/prompts/config.yaml.template +8 -2
  9. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/tfs_client.py +59 -10
  10. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_config.py +386 -360
  11. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_git_utils.py +156 -162
  12. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_tfs_client.py +197 -2
  13. code_review_ai_cli-2.1.0/PKG-INFO +0 -433
  14. code_review_ai_cli-2.1.0/README.md +0 -393
  15. code_review_ai_cli-2.1.0/code_review_ai_cli.egg-info/PKG-INFO +0 -433
  16. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/code_review_ai_cli.egg-info/SOURCES.txt +0 -0
  17. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/code_review_ai_cli.egg-info/dependency_links.txt +0 -0
  18. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/code_review_ai_cli.egg-info/entry_points.txt +0 -0
  19. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/code_review_ai_cli.egg-info/requires.txt +0 -0
  20. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/code_review_ai_cli.egg-info/top_level.txt +0 -0
  21. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/setup.cfg +0 -0
  22. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/__init__.py +0 -0
  23. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/context_extractor.py +0 -0
  24. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/formatter.py +0 -0
  25. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/llm_client.py +0 -0
  26. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/prompt_utils.py +0 -0
  27. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/src/prompts/review_prompt.md.template +0 -0
  28. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_ai_review.py +0 -0
  29. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_context_extractor.py +0 -0
  30. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_formatter.py +0 -0
  31. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_llm_client.py +0 -0
  32. {code_review_ai_cli-2.1.0 → code_review_ai_cli-2.2.1}/tests/test_prompt_utils.py +0 -0
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: code-review-ai-cli
3
+ Version: 2.2.1
4
+ Summary: Automated AI-powered code review CLI for Azure DevOps / TFS Pull Requests
5
+ License: MIT
6
+ Keywords: code-review,ai,azure-devops,tfs,pull-request,llm
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: requests>=2.28.0
19
+ Requires-Dist: PyYAML>=6.0
20
+ Provides-Extra: bedrock
21
+ Requires-Dist: boto3>=1.34.0; extra == "bedrock"
22
+ Provides-Extra: openai
23
+ Requires-Dist: openai>=1.0.0; extra == "openai"
24
+ Provides-Extra: gemini
25
+ Requires-Dist: google-generativeai>=0.3.0; extra == "gemini"
26
+ Provides-Extra: claude
27
+ Requires-Dist: anthropic>=0.18.0; extra == "claude"
28
+ Provides-Extra: tree-sitter
29
+ Requires-Dist: tree-sitter-languages>=1.10.2; extra == "tree-sitter"
30
+ Provides-Extra: all
31
+ Requires-Dist: boto3>=1.34.0; extra == "all"
32
+ Requires-Dist: openai>=1.0.0; extra == "all"
33
+ Requires-Dist: google-generativeai>=0.3.0; extra == "all"
34
+ Requires-Dist: anthropic>=0.18.0; extra == "all"
35
+ Requires-Dist: tree-sitter-languages>=1.10.2; extra == "all"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=8.3.0; extra == "dev"
38
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
39
+ Requires-Dist: pytest-mock>=3.14.0; extra == "dev"
40
+
41
+ # AI Code Review
42
+
43
+ AI Code Review is an automated AI-powered code review CLI, compatible with various LLM providers, designed for integration with Azure DevOps, whether in the cloud or on-premises. It allows you to run a review on demand or as part of a pipeline triggered before a commit or when a pull request is created.
44
+
45
+ ## Features
46
+
47
+ - AI Pull Request review (`pr-review`)
48
+ - Structured PR comments (inline + general summary)
49
+ - `dry-run` mode to validate without posting
50
+ - PR listing with filters (`list-prs`)
51
+ - Configuration exclusively via `config.yaml`
52
+ - Providers LLM: OpenAI, Azure OpenAI, Gemini, Claude, Ollama, GitHub Copilot, AWS Bedrock
53
+ - Diff filtering by **excluded path prefixes** (`excluded_paths`) and/or **file extensions** (`file_extensions_filter`)
54
+
55
+ ## Documentation
56
+
57
+ - [Configuration Guide](docs/configuration.md) - Details on YAML parameters and settings.
58
+ - [Commands Reference](docs/commands.md) - Complete list of CLI commands and arguments.
59
+ - [Execution Flow](docs/execution_flow.md) - Mermaid diagram of the application's lifecycle.
60
+ - [Troubleshooting](docs/troubleshooting.md) - Common issues and solutions.
61
+
62
+ ## Output Review Format
63
+
64
+ ![Review Output Format Example](/imgs/review_output.png)
65
+
66
+ ## Installation
67
+
68
+ Install from PyPI:
69
+ s
70
+ ```bash
71
+ pip install code-review-ai-cli
72
+ ```
73
+
74
+ After installing the package, generate ready-to-edit configuration files in your working directory:
75
+
76
+ ```bash
77
+ ai-review init
78
+ ```
79
+
80
+ This copies two bundled templates:
81
+
82
+ - **`config.yaml`** — all available options with inline documentation
83
+ - **`review_prompt.md`** — default review style rules, injected into every LLM prompt
84
+
85
+ The tool looks for `config.yaml` in the **current working directory** at runtime. You can also pass a different path with `--config`:
86
+
87
+ ```bash
88
+ ai-review pr-review --config ~/configs/ai-review.yaml
89
+ ```
90
+
91
+ If you plan to run the test suite locally, also install development dependencies:
92
+
93
+ ```bash
94
+ pip install "code-review-ai-cli[dev]"
95
+ ```
96
+
97
+ ## Minimal Example
98
+
99
+ Example `config.yaml`:
100
+
101
+ ```yaml
102
+ llm:
103
+ provider: openai
104
+ model: gpt-4o
105
+
106
+ openai:
107
+ api_key: sk-xxxx
108
+
109
+ tfs:
110
+ base_url: https://dev.azure.com/your-organization
111
+ project: ProjectName
112
+ pat: xxxxxxxxx
113
+ verify_ssl: true
114
+ # ca_bundle: C:/certs/corporate-root-ca.pem
115
+
116
+ review:
117
+ verbosity: detailed
118
+ scope: diff_only
119
+ custom_prompt_file: review_prompt.md
120
+ # file limit sent to the LLM
121
+ max_diff_files: 50
122
+ # per-file limit
123
+ max_diff_lines: 2000
124
+ # extension allowlist (empty list = all files)
125
+ file_extensions_filter: [".cs", ".ts", ".py"]
126
+ # exclude path prefixes from the diff (applied before file_extensions_filter)
127
+ # leading slash is optional; add trailing slash for exact folder boundary
128
+ excluded_paths: ["libs/generated"]
129
+
130
+ pr:
131
+ auto_post_comments: false
132
+ dry_run: false
133
+ comment_mode: structured
134
+
135
+ output:
136
+ format: terminal
137
+ file: ""
138
+ color: true
139
+ ```
140
+
141
+ ## Available VS Code Tasks
142
+
143
+ - `AI Review: Pull Request (Interactive)`
144
+ - `AI Review: PR (Dry-Run)`
145
+ - `AI Review: List Active PRs`
146
+ - `AI Review: Interactive Mode`
@@ -0,0 +1,106 @@
1
+ # AI Code Review
2
+
3
+ AI Code Review is an automated AI-powered code review CLI, compatible with various LLM providers, designed for integration with Azure DevOps, whether in the cloud or on-premises. It allows you to run a review on demand or as part of a pipeline triggered before a commit or when a pull request is created.
4
+
5
+ ## Features
6
+
7
+ - AI Pull Request review (`pr-review`)
8
+ - Structured PR comments (inline + general summary)
9
+ - `dry-run` mode to validate without posting
10
+ - PR listing with filters (`list-prs`)
11
+ - Configuration exclusively via `config.yaml`
12
+ - Providers LLM: OpenAI, Azure OpenAI, Gemini, Claude, Ollama, GitHub Copilot, AWS Bedrock
13
+ - Diff filtering by **excluded path prefixes** (`excluded_paths`) and/or **file extensions** (`file_extensions_filter`)
14
+
15
+ ## Documentation
16
+
17
+ - [Configuration Guide](docs/configuration.md) - Details on YAML parameters and settings.
18
+ - [Commands Reference](docs/commands.md) - Complete list of CLI commands and arguments.
19
+ - [Execution Flow](docs/execution_flow.md) - Mermaid diagram of the application's lifecycle.
20
+ - [Troubleshooting](docs/troubleshooting.md) - Common issues and solutions.
21
+
22
+ ## Output Review Format
23
+
24
+ ![Review Output Format Example](/imgs/review_output.png)
25
+
26
+ ## Installation
27
+
28
+ Install from PyPI:
29
+ s
30
+ ```bash
31
+ pip install code-review-ai-cli
32
+ ```
33
+
34
+ After installing the package, generate ready-to-edit configuration files in your working directory:
35
+
36
+ ```bash
37
+ ai-review init
38
+ ```
39
+
40
+ This copies two bundled templates:
41
+
42
+ - **`config.yaml`** — all available options with inline documentation
43
+ - **`review_prompt.md`** — default review style rules, injected into every LLM prompt
44
+
45
+ The tool looks for `config.yaml` in the **current working directory** at runtime. You can also pass a different path with `--config`:
46
+
47
+ ```bash
48
+ ai-review pr-review --config ~/configs/ai-review.yaml
49
+ ```
50
+
51
+ If you plan to run the test suite locally, also install development dependencies:
52
+
53
+ ```bash
54
+ pip install "code-review-ai-cli[dev]"
55
+ ```
56
+
57
+ ## Minimal Example
58
+
59
+ Example `config.yaml`:
60
+
61
+ ```yaml
62
+ llm:
63
+ provider: openai
64
+ model: gpt-4o
65
+
66
+ openai:
67
+ api_key: sk-xxxx
68
+
69
+ tfs:
70
+ base_url: https://dev.azure.com/your-organization
71
+ project: ProjectName
72
+ pat: xxxxxxxxx
73
+ verify_ssl: true
74
+ # ca_bundle: C:/certs/corporate-root-ca.pem
75
+
76
+ review:
77
+ verbosity: detailed
78
+ scope: diff_only
79
+ custom_prompt_file: review_prompt.md
80
+ # file limit sent to the LLM
81
+ max_diff_files: 50
82
+ # per-file limit
83
+ max_diff_lines: 2000
84
+ # extension allowlist (empty list = all files)
85
+ file_extensions_filter: [".cs", ".ts", ".py"]
86
+ # exclude path prefixes from the diff (applied before file_extensions_filter)
87
+ # leading slash is optional; add trailing slash for exact folder boundary
88
+ excluded_paths: ["libs/generated"]
89
+
90
+ pr:
91
+ auto_post_comments: false
92
+ dry_run: false
93
+ comment_mode: structured
94
+
95
+ output:
96
+ format: terminal
97
+ file: ""
98
+ color: true
99
+ ```
100
+
101
+ ## Available VS Code Tasks
102
+
103
+ - `AI Review: Pull Request (Interactive)`
104
+ - `AI Review: PR (Dry-Run)`
105
+ - `AI Review: List Active PRs`
106
+ - `AI Review: Interactive Mode`
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: code-review-ai-cli
3
+ Version: 2.2.1
4
+ Summary: Automated AI-powered code review CLI for Azure DevOps / TFS Pull Requests
5
+ License: MIT
6
+ Keywords: code-review,ai,azure-devops,tfs,pull-request,llm
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: requests>=2.28.0
19
+ Requires-Dist: PyYAML>=6.0
20
+ Provides-Extra: bedrock
21
+ Requires-Dist: boto3>=1.34.0; extra == "bedrock"
22
+ Provides-Extra: openai
23
+ Requires-Dist: openai>=1.0.0; extra == "openai"
24
+ Provides-Extra: gemini
25
+ Requires-Dist: google-generativeai>=0.3.0; extra == "gemini"
26
+ Provides-Extra: claude
27
+ Requires-Dist: anthropic>=0.18.0; extra == "claude"
28
+ Provides-Extra: tree-sitter
29
+ Requires-Dist: tree-sitter-languages>=1.10.2; extra == "tree-sitter"
30
+ Provides-Extra: all
31
+ Requires-Dist: boto3>=1.34.0; extra == "all"
32
+ Requires-Dist: openai>=1.0.0; extra == "all"
33
+ Requires-Dist: google-generativeai>=0.3.0; extra == "all"
34
+ Requires-Dist: anthropic>=0.18.0; extra == "all"
35
+ Requires-Dist: tree-sitter-languages>=1.10.2; extra == "all"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=8.3.0; extra == "dev"
38
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
39
+ Requires-Dist: pytest-mock>=3.14.0; extra == "dev"
40
+
41
+ # AI Code Review
42
+
43
+ AI Code Review is an automated AI-powered code review CLI, compatible with various LLM providers, designed for integration with Azure DevOps, whether in the cloud or on-premises. It allows you to run a review on demand or as part of a pipeline triggered before a commit or when a pull request is created.
44
+
45
+ ## Features
46
+
47
+ - AI Pull Request review (`pr-review`)
48
+ - Structured PR comments (inline + general summary)
49
+ - `dry-run` mode to validate without posting
50
+ - PR listing with filters (`list-prs`)
51
+ - Configuration exclusively via `config.yaml`
52
+ - Providers LLM: OpenAI, Azure OpenAI, Gemini, Claude, Ollama, GitHub Copilot, AWS Bedrock
53
+ - Diff filtering by **excluded path prefixes** (`excluded_paths`) and/or **file extensions** (`file_extensions_filter`)
54
+
55
+ ## Documentation
56
+
57
+ - [Configuration Guide](docs/configuration.md) - Details on YAML parameters and settings.
58
+ - [Commands Reference](docs/commands.md) - Complete list of CLI commands and arguments.
59
+ - [Execution Flow](docs/execution_flow.md) - Mermaid diagram of the application's lifecycle.
60
+ - [Troubleshooting](docs/troubleshooting.md) - Common issues and solutions.
61
+
62
+ ## Output Review Format
63
+
64
+ ![Review Output Format Example](/imgs/review_output.png)
65
+
66
+ ## Installation
67
+
68
+ Install from PyPI:
69
+ s
70
+ ```bash
71
+ pip install code-review-ai-cli
72
+ ```
73
+
74
+ After installing the package, generate ready-to-edit configuration files in your working directory:
75
+
76
+ ```bash
77
+ ai-review init
78
+ ```
79
+
80
+ This copies two bundled templates:
81
+
82
+ - **`config.yaml`** — all available options with inline documentation
83
+ - **`review_prompt.md`** — default review style rules, injected into every LLM prompt
84
+
85
+ The tool looks for `config.yaml` in the **current working directory** at runtime. You can also pass a different path with `--config`:
86
+
87
+ ```bash
88
+ ai-review pr-review --config ~/configs/ai-review.yaml
89
+ ```
90
+
91
+ If you plan to run the test suite locally, also install development dependencies:
92
+
93
+ ```bash
94
+ pip install "code-review-ai-cli[dev]"
95
+ ```
96
+
97
+ ## Minimal Example
98
+
99
+ Example `config.yaml`:
100
+
101
+ ```yaml
102
+ llm:
103
+ provider: openai
104
+ model: gpt-4o
105
+
106
+ openai:
107
+ api_key: sk-xxxx
108
+
109
+ tfs:
110
+ base_url: https://dev.azure.com/your-organization
111
+ project: ProjectName
112
+ pat: xxxxxxxxx
113
+ verify_ssl: true
114
+ # ca_bundle: C:/certs/corporate-root-ca.pem
115
+
116
+ review:
117
+ verbosity: detailed
118
+ scope: diff_only
119
+ custom_prompt_file: review_prompt.md
120
+ # file limit sent to the LLM
121
+ max_diff_files: 50
122
+ # per-file limit
123
+ max_diff_lines: 2000
124
+ # extension allowlist (empty list = all files)
125
+ file_extensions_filter: [".cs", ".ts", ".py"]
126
+ # exclude path prefixes from the diff (applied before file_extensions_filter)
127
+ # leading slash is optional; add trailing slash for exact folder boundary
128
+ excluded_paths: ["libs/generated"]
129
+
130
+ pr:
131
+ auto_post_comments: false
132
+ dry_run: false
133
+ comment_mode: structured
134
+
135
+ output:
136
+ format: terminal
137
+ file: ""
138
+ color: true
139
+ ```
140
+
141
+ ## Available VS Code Tasks
142
+
143
+ - `AI Review: Pull Request (Interactive)`
144
+ - `AI Review: PR (Dry-Run)`
145
+ - `AI Review: List Active PRs`
146
+ - `AI Review: Interactive Mode`
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-review-ai-cli"
7
- version = "2.1.0"
7
+ version = "2.2.1"
8
8
  description = "Automated AI-powered code review CLI for Azure DevOps / TFS Pull Requests"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -79,7 +79,7 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
79
79
  PROJECT_ROOT = _ensure_project_root_on_path(__file__)
80
80
 
81
81
  from src.config import ReviewConfig, VALID_PROVIDERS # noqa: E402
82
- from src.git_utils import GitUtils, GitError # noqa: E402
82
+ from src.git_utils import GitUtils # noqa: E402
83
83
  from src.llm_client import LLMClient, LLMError # noqa: E402
84
84
  from src.formatter import ReviewFormatter, Colors, save_output # noqa: E402
85
85
  from src import __version__ as VERSION # noqa: E402
@@ -426,6 +426,8 @@ def run_pr_review_workflow(args: argparse.Namespace, config: ReviewConfig,
426
426
  repo_name,
427
427
  pr_id,
428
428
  review_scope=config.review_scope,
429
+ excluded_paths=config.excluded_paths or None,
430
+ file_extensions_filter=config.file_extensions_filter or None,
429
431
  )
430
432
  except TFSError as exc:
431
433
  print(formatter.format_error(str(exc)))
@@ -451,17 +453,6 @@ def run_pr_review_workflow(args: argparse.Namespace, config: ReviewConfig,
451
453
  git_utils = GitUtils.__new__(GitUtils)
452
454
  git_utils.repo_path = os.getcwd()
453
455
 
454
- # Filter extensions before limiting/truncating the diff sent to the LLM
455
- if config.file_extensions_filter:
456
- try:
457
- diff = git_utils.filter_diff_by_extensions(
458
- diff,
459
- config.file_extensions_filter,
460
- )
461
- except GitError as exc:
462
- print(formatter.format_warning(str(exc)))
463
- return 0
464
-
465
456
  # Keep only added lines (+): ignore context and removed lines
466
457
  diff = git_utils.filter_diff_additions_only(diff)
467
458
  if not diff.strip():
@@ -93,7 +93,8 @@ class ReviewConfig:
93
93
  max_scope_lines: int = 250 # Max lines in a single scope block before fallback to file_skeleton
94
94
  adaptive_context_lines: int = 10 # Context lines for Adaptive Diff fallback
95
95
  custom_prompt_file: str = "review_prompt.md" # Markdown file with extra rules/context
96
- file_extensions_filter: list = field(default_factory=list)
96
+ file_extensions_filter: list = field(default_factory=list) # File extensions to include (empty = all)
97
+ excluded_paths: list = field(default_factory=list) # Folder path segments to exclude from diff
97
98
 
98
99
  # --- PR Review ----------------------------------------------------
99
100
  auto_post_comments: bool = False # Post comments automatically
@@ -222,6 +223,7 @@ class ReviewConfig:
222
223
  "adaptive_context_lines": ("review", "adaptive_context_lines"),
223
224
  "custom_prompt_file": ("review", "custom_prompt_file"),
224
225
  "file_extensions_filter": ("review", "file_extensions_filter"),
226
+ "excluded_paths": ("review", "excluded_paths"),
225
227
  # PR
226
228
  "auto_post_comments": ("pr", "auto_post_comments"),
227
229
  "dry_run": ("pr", "dry_run"),
@@ -189,44 +189,6 @@ class GitUtils:
189
189
  )
190
190
  return limited, True, omitted_files
191
191
 
192
- def filter_diff_by_extensions(self, diff: str, extensions: list[str]) -> str:
193
- """
194
- Filters the diff to include only files with specific extensions.
195
-
196
- Args:
197
- diff: The full diff.
198
- extensions: List of extensions (e.g., ['.py', '.js', '.cs']).
199
- """
200
- if not extensions:
201
- return diff
202
-
203
- filtered_sections = []
204
- current_section: list[str] = []
205
- include_section = False
206
-
207
- for line in diff.split("\n"):
208
- if line.startswith("diff --git"):
209
- # Save previous section if applicable
210
- if include_section and current_section:
211
- filtered_sections.append("\n".join(current_section))
212
- current_section = [line]
213
- # Check if file has an allowed extension
214
- file_path = line.split(" b/")[-1] if " b/" in line else ""
215
- include_section = any(file_path.endswith(ext) for ext in extensions)
216
- else:
217
- current_section.append(line)
218
-
219
- # Last section
220
- if include_section and current_section:
221
- filtered_sections.append("\n".join(current_section))
222
-
223
- result = "\n".join(filtered_sections)
224
- if not result.strip():
225
- raise GitError(
226
- f"After filtering by extensions {extensions}, no changes remain."
227
- )
228
- return result
229
-
230
192
  def truncate_diff(self, diff: str, max_lines: int = 2000) -> tuple[str, bool]:
231
193
  """
232
194
  Kept for compatibility: applies per-file truncation when
@@ -13,7 +13,7 @@
13
13
  llm:
14
14
  provider: openai
15
15
 
16
- # Model to use (empty = provider default)
16
+ # Model to use (empty = provider default)
17
17
  # Examples:
18
18
  # openai: gpt-4o, gpt-4-turbo, gpt-4
19
19
  # gemini: gemini-1.5-pro, gemini-pro, gemini-2.0-flash
@@ -91,7 +91,7 @@ review:
91
91
  max_diff_lines: 4500
92
92
 
93
93
  # Max changed logical blocks for Enclosing Scopes strategy (diff_only mode)
94
- max_scope_blocks: 3
94
+ max_scope_blocks: 10
95
95
 
96
96
  # Max lines in a single enclosing scope before falling back to file_skeleton
97
97
  # (prevents sending entire large classes to the LLM)
@@ -100,6 +100,12 @@ review:
100
100
  # Surrounding lines to keep for Adaptive Diff fallback (diff_only mode)
101
101
  adaptive_context_lines: 10
102
102
 
103
+ # Exclude paths from the diff by prefix (empty list = no exclusions).
104
+ # Files whose path starts with a listed prefix are excluded.
105
+ # Matching is case-insensitive.
106
+ # Example: ["IoT/_input/packages", "libs/generated"]
107
+ excluded_paths: []
108
+
103
109
  # Filter by file extensions (empty list = all files)
104
110
  # Example: [".cs", ".ts"]
105
111
  file_extensions_filter: []
@@ -290,18 +290,23 @@ class TFSClient:
290
290
 
291
291
  files = []
292
292
  for change in changes.get("changeEntries", []):
293
- item = change.get("item", {})
293
+ # `item` can be null for deleted files; use `or {}` as a safe fallback.
294
+ item = change.get("item") or {}
294
295
  if item.get("isFolder"):
295
296
  continue
297
+ # `path` can be null for deleted files; fall back to `originalPath`.
298
+ path = item.get("path") or change.get("originalPath") or ""
296
299
  files.append({
297
- "path": item.get("path", ""),
300
+ "path": path,
298
301
  "change_type": change.get("changeType", "unknown"),
299
- "original_path": change.get("originalPath", ""),
302
+ "original_path": change.get("originalPath") or path,
300
303
  })
301
304
  return files
302
305
 
303
306
  def get_pull_request_diff(self, repository: str, pr_id: int,
304
- review_scope: str = "diff_only") -> str:
307
+ review_scope: str = "diff_only",
308
+ excluded_paths: list[str] | None = None,
309
+ file_extensions_filter: list[str] | None = None) -> str:
305
310
  """
306
311
  Gets the diff of a specific Pull Request.
307
312
 
@@ -317,16 +322,28 @@ class TFSClient:
317
322
  with ``+``, producing a pseudo-diff that represents the complete file
318
323
  as added content.
319
324
 
325
+ Files are filtered **before** any HTTP content request is made, so
326
+ excluded or extension-mismatched files incur no network cost.
327
+
320
328
  Args:
321
329
  repository: Repository name.
322
330
  pr_id: Pull Request ID.
323
331
  review_scope: ``"diff_only"`` (default) or ``"full_code"``.
332
+ excluded_paths: List of path prefixes to skip (case-insensitive,
333
+ leading slash optional). Files whose path starts with any
334
+ prefix are excluded before content is fetched.
335
+ Example: ``["/Libs/", "/IoT/_input/packages"]``.
336
+ file_extensions_filter: Allowlist of file extensions
337
+ (case-insensitive). When non-empty, only files whose path ends
338
+ with one of the listed extensions are processed.
339
+ Example: ``[".cs", ".ts"]``.
324
340
 
325
341
  Returns:
326
342
  Diff as text, ready to be passed to the LLM client.
327
343
 
328
344
  Raises:
329
- TFSError: If the PR has no iterations or contains no file changes.
345
+ TFSError: If the PR has no iterations or contains no file changes
346
+ after filtering.
330
347
  """
331
348
  # Get PR details
332
349
  path = f"git/repositories/{repository}/pullrequests/{pr_id}"
@@ -352,17 +369,48 @@ class TFSClient:
352
369
 
353
370
  review_scope = (review_scope or "diff_only").lower()
354
371
 
372
+ # Normalise filters once before the loop (forward slashes, lower-case).
373
+ # excluded_paths: strip optional leading slash so "Libs/" == "/Libs/".
374
+ _excl_prefixes = [
375
+ p.replace("\\", "/").lstrip("/").lower()
376
+ for p in (excluded_paths or [])
377
+ ]
378
+ _ext_filter = [e.lower() for e in (file_extensions_filter or [])]
379
+
355
380
  # Build diff from the changes
356
381
  diff_parts = []
357
382
  for change in changes.get("changeEntries", []):
358
- item = change.get("item", {})
383
+ # `item` can be null for deleted files; use `or {}` as a safe fallback.
384
+ item = change.get("item") or {}
359
385
  change_type = change.get("changeType", "unknown")
360
- file_path = item.get("path", "unknown")
361
- original_path = change.get("originalPath") or file_path
362
386
 
363
387
  if item.get("isFolder"):
364
388
  continue
365
389
 
390
+ # Deleted files are skipped from the LLM diff.
391
+ # They are still reported in the terminal summary via _get_pr_changed_files.
392
+ if change_type == "delete":
393
+ continue
394
+
395
+ # `path` can be null; fall back to `originalPath` (safe for add/edit/rename).
396
+ file_path = item.get("path") or change.get("originalPath") or "unknown"
397
+ original_path = change.get("originalPath") or file_path
398
+
399
+ # --- Early-exit filters (no HTTP request made for skipped files) ---
400
+ # Normalise once: strip leading slash, lower-case, forward slashes.
401
+ norm_path = file_path.replace("\\", "/").lstrip("/").lower()
402
+
403
+ if _excl_prefixes and any(
404
+ norm_path.startswith(excl) for excl in _excl_prefixes
405
+ ):
406
+ continue
407
+
408
+ if _ext_filter and not any(
409
+ norm_path.endswith(ext) for ext in _ext_filter
410
+ ):
411
+ continue
412
+ # ------------------------------------------------------------------
413
+
366
414
  if review_scope == "full_code":
367
415
  diff_parts.extend(
368
416
  self._build_full_code_diff_part(
@@ -388,7 +436,7 @@ class TFSClient:
388
436
  diff_parts.append("")
389
437
 
390
438
  if not diff_parts:
391
- raise TFSError(f"PR #{pr_id} contains no file changes.")
439
+ raise TFSError(f"PR #{pr_id} contains no file changes after filtering.")
392
440
 
393
441
  return "\n".join(diff_parts)
394
442
 
@@ -463,7 +511,8 @@ class TFSClient:
463
511
  file_path: Server path of the new version of the file.
464
512
  original_path: Server path of the old version (differs on renames).
465
513
  change_type: TFS change type string (``"edit"``, ``"add"``,
466
- ``"rename"``, ``"delete"``, …).
514
+ ``"rename"``). ``"delete"`` entries are filtered out
515
+ by the caller before this method is invoked.
467
516
  source_branch: Source branch ref (feature branch — new version).
468
517
  target_branch: Target branch ref (base branch — old version).
469
518