gitpr-cli 1.1.0__tar.gz → 1.2.0__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 (115) hide show
  1. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/PKG-INFO +9 -5
  2. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/README.md +8 -4
  3. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/PKG-INFO +9 -5
  4. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/SOURCES.txt +15 -0
  5. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/cache.py +63 -2
  6. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/config.py +52 -0
  7. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/config_schema.py +60 -0
  8. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/core.py +49 -4
  9. gitpr_cli-1.2.0/src/diff_parser.py +321 -0
  10. gitpr_cli-1.2.0/src/fix/__init__.py +16 -0
  11. gitpr_cli-1.2.0/src/fix/apply_fix.py +479 -0
  12. gitpr_cli-1.2.0/src/fix/fix_history.py +153 -0
  13. gitpr_cli-1.2.0/src/fix/patch_applier.py +172 -0
  14. gitpr_cli-1.2.0/src/fix/patch_extractor.py +61 -0
  15. gitpr_cli-1.2.0/src/fix/patch_provenance.py +96 -0
  16. gitpr_cli-1.2.0/src/fix/patch_safety_classifier.py +101 -0
  17. gitpr_cli-1.2.0/src/fix/rollback_fix.py +69 -0
  18. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/azure_devops_provider.py +13 -0
  19. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/base.py +29 -1
  20. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/bitbucket_provider.py +9 -0
  21. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/github_provider.py +77 -4
  22. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/gitlab_provider.py +50 -3
  23. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/linter_engine.py +11 -2
  24. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/main.py +469 -56
  25. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/mcp_server.py +263 -0
  26. gitpr_cli-1.2.0/src/review/__init__.py +13 -0
  27. gitpr_cli-1.2.0/src/review/diff_normalizer.py +84 -0
  28. gitpr_cli-1.2.0/src/review/diff_source.py +69 -0
  29. gitpr_cli-1.2.0/src/review/remote_pr.py +319 -0
  30. gitpr_cli-1.2.0/src/review/render.py +75 -0
  31. gitpr_cli-1.2.0/src/reviewer_resolution.py +159 -0
  32. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/reviewer_suggestion.py +19 -3
  33. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/suggest_reviewers.py +19 -12
  34. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/chat_app.py +3 -31
  35. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/pr_publish_app.py +222 -38
  36. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/updater.py +2 -2
  37. gitpr_cli-1.2.0/tests/test_diff_parser.py +359 -0
  38. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_main_suggest_reviewers.py +40 -4
  39. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_mcp_server.py +227 -4
  40. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_pr_publish_app.py +146 -5
  41. gitpr_cli-1.2.0/tests/test_reviewer_resolution.py +255 -0
  42. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_reviewer_suggestion.py +23 -0
  43. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_suggest_reviewers.py +32 -1
  44. gitpr_cli-1.1.0/src/diff_parser.py +0 -159
  45. gitpr_cli-1.1.0/tests/test_diff_parser.py +0 -204
  46. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/LICENSE +0 -0
  47. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/dependency_links.txt +0 -0
  48. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/entry_points.txt +0 -0
  49. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/requires.txt +0 -0
  50. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/top_level.txt +0 -0
  51. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/pyproject.toml +0 -0
  52. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/setup.cfg +0 -0
  53. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/__init__.py +0 -0
  54. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ai_providers.py +0 -0
  55. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/blame_engine.py +0 -0
  56. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/changelog_builder.py +0 -0
  57. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/chat_memory.py +0 -0
  58. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/commit_classifier.py +0 -0
  59. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/doc_links.py +0 -0
  60. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/github_api.py +0 -0
  61. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/i18n.py +0 -0
  62. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/__init__.py +0 -0
  63. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/__init__.py +0 -0
  64. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/factory.py +0 -0
  65. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/issue_engine.py +0 -0
  66. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/linter_wizard.py +0 -0
  67. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/metrics.py +0 -0
  68. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/net.py +0 -0
  69. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/release_engine.py +0 -0
  70. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/security.py +0 -0
  71. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/spinner.py +0 -0
  72. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/tui_issue.py +0 -0
  73. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/__init__.py +0 -0
  74. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/config_app.py +0 -0
  75. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/help_screen.py +0 -0
  76. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/issue_app.py +0 -0
  77. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/linter_app.py +0 -0
  78. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/metrics_app.py +0 -0
  79. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/pr_publish_help.py +0 -0
  80. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/usage_log.py +0 -0
  81. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/version_bump.py +0 -0
  82. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_blame_engine_ranges.py +0 -0
  83. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_blame_metrics.py +0 -0
  84. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_changelog_builder.py +0 -0
  85. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_chat_backend.py +0 -0
  86. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_commit_classifier.py +0 -0
  87. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_app.py +0 -0
  88. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_cli.py +0 -0
  89. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_schema.py +0 -0
  90. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_store.py +0 -0
  91. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_suggest_reviewers.py +0 -0
  92. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_validation.py +0 -0
  93. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_core.py +0 -0
  94. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_external_linters.py +0 -0
  95. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_i18n.py +0 -0
  96. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_install_wizard.py +0 -0
  97. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_issue_engine.py +0 -0
  98. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_linter_metrics.py +0 -0
  99. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_linter_presets.py +0 -0
  100. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_mcp_prompts.py +0 -0
  101. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_mcp_server_e2e.py +0 -0
  102. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_metrics.py +0 -0
  103. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_net_timeouts.py +0 -0
  104. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_plugins.py +0 -0
  105. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_pr_publish_linter_modal.py +0 -0
  106. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_pre_save.py +0 -0
  107. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_release_cli.py +0 -0
  108. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_release_engine.py +0 -0
  109. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_skill_command.py +0 -0
  110. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_skill_context.py +0 -0
  111. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_smart_excludes.py +0 -0
  112. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_thinking_words.py +0 -0
  113. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_updater.py +0 -0
  114. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_usage_log.py +0 -0
  115. {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_version_bump.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpr-cli
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: AI-powered PR automation, commit messages, and code review (Gemini, DeepSeek, and Ollama)
5
5
  Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
6
6
  Requires-Python: >=3.10
@@ -148,11 +148,11 @@ You can pass the following *flags* for specific actions:
148
148
  * `--status`: Lists uncommitted file changes categorized as **new**, **modified**, and **deleted** — fast, no AI, no network. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/git-status.md)
149
149
  * `--no-unstaged-check`: Skips the unstaged files verification before AI processing for a single invocation. Equivalent to `GITPR_SKIP_UNSTAGED_CHECK=true` for one run. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/git-status.md)
150
150
  * `--linter-setup`: **Interactive external linter wizard.** Guides you through installing and configuring external linters (ESLint, PHPCS, Stylelint) as a Checkstyle XML bridge. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/linter-regras-customizadas.md)
151
- * `--mcp`: Starts GitPR as an **MCP server** (Model Context Protocol) on stdio transport. Enables integration with VS Code, Cursor, Claude Desktop, and other MCP-compatible editors — exposing all GitPR AI capabilities as 12 annotated tools, 15 resources, and 7 pre-built prompts directly inside your IDE. Also available as the standalone `gitpr-mcp` command.
151
+ * `--mcp`: Starts GitPR as an **MCP server** (Model Context Protocol) on stdio transport. Enables integration with VS Code, Cursor, Claude Desktop, and other MCP-compatible editors — exposing all GitPR AI capabilities as 14 annotated tools, 18 resources, and 7 pre-built prompts directly inside your IDE. Also available as the standalone `gitpr-mcp` command.
152
152
  * `--plugins`: Lists all **globally installed plugins** — custom linter packs from `~/.gitpr/plugins/linter/` and MCP prompt templates from `~/.gitpr/plugins/prompts/`. These plugins apply across all your projects without duplication. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/plugins-system.md)
153
153
  * `--install`: **Interactive Setup Wizard.** Runs a guided 4-step setup: downloads skill templates, installs Git hooks, configures MCP for detected editors, and checks/requests your AI provider API key. Each step asks for confirmation before proceeding.
154
154
  * `-ih` or `--installhooks`: Automatically installs **local Git Hooks** (`pre-commit` and `prepare-commit-msg`) in your repository.
155
- * `-s` or `--skill`: Creates the AI context template files (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`, `.gitpr.issue.md`, `.gitpr.blame.md`) and the Linter (`.gitpr.linter.yml`) at the project root.
155
+ * `-s` or `--skill`: Creates the AI context template files (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`, `.gitpr.issue.md`, `.gitpr.blame.md`) and the Linter (`.gitpr.linter.yml`) inside `.gitpr/skill/`.
156
156
  * `-is` or `--issue`: Automatically generates a draft of a **standardized Issue** and opens an interactive interface (TUI) for editing or direct submission via REST API. This feature has **3 context engines** depending on the command combination:
157
157
  * **New Code Issue (`gitpr -is`):** Reads the current `git diff`. **Why use:** Ideal for quickly documenting the task you just finished programming, before committing.
158
158
  * **Epic/Release Issue (`gitpr -is -ht`):** Reads the full history of the current branch (Git Log + PR Cache). **Why use:** Ideal for generating consolidated documentation of an entire release or a large *feature* that took several days/commits to complete.
@@ -229,7 +229,7 @@ You can dynamically switch models by configuring the `GEMINI_API_MODEL_PRIMARY`
229
229
 
230
230
  ## 🎯 Customizable "Skills" System (Prompt Engineering)
231
231
 
232
- Instead of hiding AI instructions in the source code, GitPR uses local Markdown files that act as *System Instructions*. When running `gitpr -s`, the following files are generated at the root of your project to customize the AI's "persona" according to your company's business rules:
232
+ Instead of hiding AI instructions in the source code, GitPR uses local Markdown files that act as *System Instructions*. When running `gitpr -s`, the following files are generated inside `.gitpr/skill/` to customize the AI's "persona" according to your company's business rules:
233
233
 
234
234
  * `.gitpr.commit.md`: Rules for generating short commit messages.
235
235
  * `.gitpr.pr.md`: Required topic structure for the Pull Request description.
@@ -335,6 +335,8 @@ Once configured, use natural language in your editor's AI chat:
335
335
  | `generate_issue` | Structured issue from diff, history, or blame |
336
336
  | `list_unstaged_files` | Uncommitted file changes categorized (new/modified/deleted) |
337
337
  | `analyze_unstaged_diff` | Unstaged diff only (working tree vs index) |
338
+ | `list_fix_candidates` | Fix candidates of the last review: patch, classification, id (read-only) |
339
+ | `review_remote_pr` | AI review of a pull request already open on the forge, fetched by number (read-only) |
338
340
 
339
341
  ### Direct CLI Invocation
340
342
 
@@ -430,12 +432,14 @@ If you want to implement GitPR as an automated quality barrier in your team, che
430
432
 
431
433
  * [**Pull Request (Default Mode)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/pr-descricao-padrao.md) — Complete flow for generating PR descriptions without flags.
432
434
  * [**Pull Request Publisher TUI**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/pull-request-publication.md) — How to review and publish Pull Requests directly to GitHub from the terminal.
433
- * [**AI Code Review**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/code-review-ia.md) — Guide to review modes (`--review`, `--fullreview`) and file auditing (`--input`).
435
+ * [**AI Code Review**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/code-review-ia.md) — Guide to review modes (`--review`, `--fullreview`), file auditing (`--input`) and remote pull request review (`gitpr review-pr`).
436
+ * [**Remote Pull Request Review (gitpr review-pr)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/review-pr.md) — How the `gitpr review-pr` subcommand reviews a pull request that is already open on the forge, fetching its diff from the API without checking out its branch.
434
437
  * [**AI Commit Messages**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/commit-message-ia.md) — How to generate messages in the Conventional Commits standard and integrate with Git Hooks.
435
438
  * [**Issue Generation and TUI Interface**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/issue-tui-help.md) — How to use the terminal graphical interface (TUI) and the 3 context engines to manage structured Issues.
436
439
  * [**Code Archaeologist (Git Blame)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/blame-arqueologo.md) — How to trace the origin of business rules with `git blame` and AI.
437
440
  * [**Skills and Templates System**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/skill-template.md) — How to customize AI behavior with `.gitpr.*.md` files.
438
441
  * [**Release Notes & Changelog (gitpr release)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/release-notes.md) — How the `gitpr release` subcommand generates the changelog / release notes of a repository, suggests the next semantic version and publishes releases on the forge.
442
+ * [**Fix Command (gitpr fix)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/fix-command.md) — How the `gitpr fix` subcommand turns the findings of the last review into patches you read before they touch your tree, classifies each one by safety, and undoes an applied patch on demand.
439
443
 
440
444
  ### Configuration & Infrastructure
441
445
 
@@ -129,11 +129,11 @@ You can pass the following *flags* for specific actions:
129
129
  * `--status`: Lists uncommitted file changes categorized as **new**, **modified**, and **deleted** — fast, no AI, no network. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/git-status.md)
130
130
  * `--no-unstaged-check`: Skips the unstaged files verification before AI processing for a single invocation. Equivalent to `GITPR_SKIP_UNSTAGED_CHECK=true` for one run. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/git-status.md)
131
131
  * `--linter-setup`: **Interactive external linter wizard.** Guides you through installing and configuring external linters (ESLint, PHPCS, Stylelint) as a Checkstyle XML bridge. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/linter-regras-customizadas.md)
132
- * `--mcp`: Starts GitPR as an **MCP server** (Model Context Protocol) on stdio transport. Enables integration with VS Code, Cursor, Claude Desktop, and other MCP-compatible editors — exposing all GitPR AI capabilities as 12 annotated tools, 15 resources, and 7 pre-built prompts directly inside your IDE. Also available as the standalone `gitpr-mcp` command.
132
+ * `--mcp`: Starts GitPR as an **MCP server** (Model Context Protocol) on stdio transport. Enables integration with VS Code, Cursor, Claude Desktop, and other MCP-compatible editors — exposing all GitPR AI capabilities as 14 annotated tools, 18 resources, and 7 pre-built prompts directly inside your IDE. Also available as the standalone `gitpr-mcp` command.
133
133
  * `--plugins`: Lists all **globally installed plugins** — custom linter packs from `~/.gitpr/plugins/linter/` and MCP prompt templates from `~/.gitpr/plugins/prompts/`. These plugins apply across all your projects without duplication. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/plugins-system.md)
134
134
  * `--install`: **Interactive Setup Wizard.** Runs a guided 4-step setup: downloads skill templates, installs Git hooks, configures MCP for detected editors, and checks/requests your AI provider API key. Each step asks for confirmation before proceeding.
135
135
  * `-ih` or `--installhooks`: Automatically installs **local Git Hooks** (`pre-commit` and `prepare-commit-msg`) in your repository.
136
- * `-s` or `--skill`: Creates the AI context template files (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`, `.gitpr.issue.md`, `.gitpr.blame.md`) and the Linter (`.gitpr.linter.yml`) at the project root.
136
+ * `-s` or `--skill`: Creates the AI context template files (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`, `.gitpr.issue.md`, `.gitpr.blame.md`) and the Linter (`.gitpr.linter.yml`) inside `.gitpr/skill/`.
137
137
  * `-is` or `--issue`: Automatically generates a draft of a **standardized Issue** and opens an interactive interface (TUI) for editing or direct submission via REST API. This feature has **3 context engines** depending on the command combination:
138
138
  * **New Code Issue (`gitpr -is`):** Reads the current `git diff`. **Why use:** Ideal for quickly documenting the task you just finished programming, before committing.
139
139
  * **Epic/Release Issue (`gitpr -is -ht`):** Reads the full history of the current branch (Git Log + PR Cache). **Why use:** Ideal for generating consolidated documentation of an entire release or a large *feature* that took several days/commits to complete.
@@ -210,7 +210,7 @@ You can dynamically switch models by configuring the `GEMINI_API_MODEL_PRIMARY`
210
210
 
211
211
  ## 🎯 Customizable "Skills" System (Prompt Engineering)
212
212
 
213
- Instead of hiding AI instructions in the source code, GitPR uses local Markdown files that act as *System Instructions*. When running `gitpr -s`, the following files are generated at the root of your project to customize the AI's "persona" according to your company's business rules:
213
+ Instead of hiding AI instructions in the source code, GitPR uses local Markdown files that act as *System Instructions*. When running `gitpr -s`, the following files are generated inside `.gitpr/skill/` to customize the AI's "persona" according to your company's business rules:
214
214
 
215
215
  * `.gitpr.commit.md`: Rules for generating short commit messages.
216
216
  * `.gitpr.pr.md`: Required topic structure for the Pull Request description.
@@ -316,6 +316,8 @@ Once configured, use natural language in your editor's AI chat:
316
316
  | `generate_issue` | Structured issue from diff, history, or blame |
317
317
  | `list_unstaged_files` | Uncommitted file changes categorized (new/modified/deleted) |
318
318
  | `analyze_unstaged_diff` | Unstaged diff only (working tree vs index) |
319
+ | `list_fix_candidates` | Fix candidates of the last review: patch, classification, id (read-only) |
320
+ | `review_remote_pr` | AI review of a pull request already open on the forge, fetched by number (read-only) |
319
321
 
320
322
  ### Direct CLI Invocation
321
323
 
@@ -411,12 +413,14 @@ If you want to implement GitPR as an automated quality barrier in your team, che
411
413
 
412
414
  * [**Pull Request (Default Mode)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/pr-descricao-padrao.md) — Complete flow for generating PR descriptions without flags.
413
415
  * [**Pull Request Publisher TUI**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/pull-request-publication.md) — How to review and publish Pull Requests directly to GitHub from the terminal.
414
- * [**AI Code Review**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/code-review-ia.md) — Guide to review modes (`--review`, `--fullreview`) and file auditing (`--input`).
416
+ * [**AI Code Review**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/code-review-ia.md) — Guide to review modes (`--review`, `--fullreview`), file auditing (`--input`) and remote pull request review (`gitpr review-pr`).
417
+ * [**Remote Pull Request Review (gitpr review-pr)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/review-pr.md) — How the `gitpr review-pr` subcommand reviews a pull request that is already open on the forge, fetching its diff from the API without checking out its branch.
415
418
  * [**AI Commit Messages**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/commit-message-ia.md) — How to generate messages in the Conventional Commits standard and integrate with Git Hooks.
416
419
  * [**Issue Generation and TUI Interface**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/issue-tui-help.md) — How to use the terminal graphical interface (TUI) and the 3 context engines to manage structured Issues.
417
420
  * [**Code Archaeologist (Git Blame)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/blame-arqueologo.md) — How to trace the origin of business rules with `git blame` and AI.
418
421
  * [**Skills and Templates System**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/skill-template.md) — How to customize AI behavior with `.gitpr.*.md` files.
419
422
  * [**Release Notes & Changelog (gitpr release)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/release-notes.md) — How the `gitpr release` subcommand generates the changelog / release notes of a repository, suggests the next semantic version and publishes releases on the forge.
423
+ * [**Fix Command (gitpr fix)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/fix-command.md) — How the `gitpr fix` subcommand turns the findings of the last review into patches you read before they touch your tree, classifies each one by safety, and undoes an applied patch on demand.
420
424
 
421
425
  ### Configuration & Infrastructure
422
426
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpr-cli
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: AI-powered PR automation, commit messages, and code review (Gemini, DeepSeek, and Ollama)
5
5
  Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
6
6
  Requires-Python: >=3.10
@@ -148,11 +148,11 @@ You can pass the following *flags* for specific actions:
148
148
  * `--status`: Lists uncommitted file changes categorized as **new**, **modified**, and **deleted** — fast, no AI, no network. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/git-status.md)
149
149
  * `--no-unstaged-check`: Skips the unstaged files verification before AI processing for a single invocation. Equivalent to `GITPR_SKIP_UNSTAGED_CHECK=true` for one run. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/git-status.md)
150
150
  * `--linter-setup`: **Interactive external linter wizard.** Guides you through installing and configuring external linters (ESLint, PHPCS, Stylelint) as a Checkstyle XML bridge. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/linter-regras-customizadas.md)
151
- * `--mcp`: Starts GitPR as an **MCP server** (Model Context Protocol) on stdio transport. Enables integration with VS Code, Cursor, Claude Desktop, and other MCP-compatible editors — exposing all GitPR AI capabilities as 12 annotated tools, 15 resources, and 7 pre-built prompts directly inside your IDE. Also available as the standalone `gitpr-mcp` command.
151
+ * `--mcp`: Starts GitPR as an **MCP server** (Model Context Protocol) on stdio transport. Enables integration with VS Code, Cursor, Claude Desktop, and other MCP-compatible editors — exposing all GitPR AI capabilities as 14 annotated tools, 18 resources, and 7 pre-built prompts directly inside your IDE. Also available as the standalone `gitpr-mcp` command.
152
152
  * `--plugins`: Lists all **globally installed plugins** — custom linter packs from `~/.gitpr/plugins/linter/` and MCP prompt templates from `~/.gitpr/plugins/prompts/`. These plugins apply across all your projects without duplication. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/plugins-system.md)
153
153
  * `--install`: **Interactive Setup Wizard.** Runs a guided 4-step setup: downloads skill templates, installs Git hooks, configures MCP for detected editors, and checks/requests your AI provider API key. Each step asks for confirmation before proceeding.
154
154
  * `-ih` or `--installhooks`: Automatically installs **local Git Hooks** (`pre-commit` and `prepare-commit-msg`) in your repository.
155
- * `-s` or `--skill`: Creates the AI context template files (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`, `.gitpr.issue.md`, `.gitpr.blame.md`) and the Linter (`.gitpr.linter.yml`) at the project root.
155
+ * `-s` or `--skill`: Creates the AI context template files (`.gitpr.commit.md`, `.gitpr.pr.md`, `.gitpr.review.md`, `.gitpr.filereview.md`, `.gitpr.issue.md`, `.gitpr.blame.md`) and the Linter (`.gitpr.linter.yml`) inside `.gitpr/skill/`.
156
156
  * `-is` or `--issue`: Automatically generates a draft of a **standardized Issue** and opens an interactive interface (TUI) for editing or direct submission via REST API. This feature has **3 context engines** depending on the command combination:
157
157
  * **New Code Issue (`gitpr -is`):** Reads the current `git diff`. **Why use:** Ideal for quickly documenting the task you just finished programming, before committing.
158
158
  * **Epic/Release Issue (`gitpr -is -ht`):** Reads the full history of the current branch (Git Log + PR Cache). **Why use:** Ideal for generating consolidated documentation of an entire release or a large *feature* that took several days/commits to complete.
@@ -229,7 +229,7 @@ You can dynamically switch models by configuring the `GEMINI_API_MODEL_PRIMARY`
229
229
 
230
230
  ## 🎯 Customizable "Skills" System (Prompt Engineering)
231
231
 
232
- Instead of hiding AI instructions in the source code, GitPR uses local Markdown files that act as *System Instructions*. When running `gitpr -s`, the following files are generated at the root of your project to customize the AI's "persona" according to your company's business rules:
232
+ Instead of hiding AI instructions in the source code, GitPR uses local Markdown files that act as *System Instructions*. When running `gitpr -s`, the following files are generated inside `.gitpr/skill/` to customize the AI's "persona" according to your company's business rules:
233
233
 
234
234
  * `.gitpr.commit.md`: Rules for generating short commit messages.
235
235
  * `.gitpr.pr.md`: Required topic structure for the Pull Request description.
@@ -335,6 +335,8 @@ Once configured, use natural language in your editor's AI chat:
335
335
  | `generate_issue` | Structured issue from diff, history, or blame |
336
336
  | `list_unstaged_files` | Uncommitted file changes categorized (new/modified/deleted) |
337
337
  | `analyze_unstaged_diff` | Unstaged diff only (working tree vs index) |
338
+ | `list_fix_candidates` | Fix candidates of the last review: patch, classification, id (read-only) |
339
+ | `review_remote_pr` | AI review of a pull request already open on the forge, fetched by number (read-only) |
338
340
 
339
341
  ### Direct CLI Invocation
340
342
 
@@ -430,12 +432,14 @@ If you want to implement GitPR as an automated quality barrier in your team, che
430
432
 
431
433
  * [**Pull Request (Default Mode)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/pr-descricao-padrao.md) — Complete flow for generating PR descriptions without flags.
432
434
  * [**Pull Request Publisher TUI**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/pull-request-publication.md) — How to review and publish Pull Requests directly to GitHub from the terminal.
433
- * [**AI Code Review**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/code-review-ia.md) — Guide to review modes (`--review`, `--fullreview`) and file auditing (`--input`).
435
+ * [**AI Code Review**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/code-review-ia.md) — Guide to review modes (`--review`, `--fullreview`), file auditing (`--input`) and remote pull request review (`gitpr review-pr`).
436
+ * [**Remote Pull Request Review (gitpr review-pr)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/review-pr.md) — How the `gitpr review-pr` subcommand reviews a pull request that is already open on the forge, fetching its diff from the API without checking out its branch.
434
437
  * [**AI Commit Messages**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/commit-message-ia.md) — How to generate messages in the Conventional Commits standard and integrate with Git Hooks.
435
438
  * [**Issue Generation and TUI Interface**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/issue-tui-help.md) — How to use the terminal graphical interface (TUI) and the 3 context engines to manage structured Issues.
436
439
  * [**Code Archaeologist (Git Blame)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/blame-arqueologo.md) — How to trace the origin of business rules with `git blame` and AI.
437
440
  * [**Skills and Templates System**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/skill-template.md) — How to customize AI behavior with `.gitpr.*.md` files.
438
441
  * [**Release Notes & Changelog (gitpr release)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/release-notes.md) — How the `gitpr release` subcommand generates the changelog / release notes of a repository, suggests the next semantic version and publishes releases on the forge.
442
+ * [**Fix Command (gitpr fix)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/fix-command.md) — How the `gitpr fix` subcommand turns the findings of the last review into patches you read before they touch your tree, classifies each one by safety, and undoes an applied patch on demand.
439
443
 
440
444
  ### Configuration & Infrastructure
441
445
 
@@ -29,6 +29,7 @@ src/mcp_server.py
29
29
  src/metrics.py
30
30
  src/net.py
31
31
  src/release_engine.py
32
+ src/reviewer_resolution.py
32
33
  src/reviewer_suggestion.py
33
34
  src/security.py
34
35
  src/spinner.py
@@ -37,6 +38,14 @@ src/tui_issue.py
37
38
  src/updater.py
38
39
  src/usage_log.py
39
40
  src/version_bump.py
41
+ src/fix/__init__.py
42
+ src/fix/apply_fix.py
43
+ src/fix/fix_history.py
44
+ src/fix/patch_applier.py
45
+ src/fix/patch_extractor.py
46
+ src/fix/patch_provenance.py
47
+ src/fix/patch_safety_classifier.py
48
+ src/fix/rollback_fix.py
40
49
  src/infrastructure/__init__.py
41
50
  src/infrastructure/scm/__init__.py
42
51
  src/infrastructure/scm/azure_devops_provider.py
@@ -45,6 +54,11 @@ src/infrastructure/scm/bitbucket_provider.py
45
54
  src/infrastructure/scm/factory.py
46
55
  src/infrastructure/scm/github_provider.py
47
56
  src/infrastructure/scm/gitlab_provider.py
57
+ src/review/__init__.py
58
+ src/review/diff_normalizer.py
59
+ src/review/diff_source.py
60
+ src/review/remote_pr.py
61
+ src/review/render.py
48
62
  src/ui/__init__.py
49
63
  src/ui/chat_app.py
50
64
  src/ui/config_app.py
@@ -85,6 +99,7 @@ tests/test_pr_publish_linter_modal.py
85
99
  tests/test_pre_save.py
86
100
  tests/test_release_cli.py
87
101
  tests/test_release_engine.py
102
+ tests/test_reviewer_resolution.py
88
103
  tests/test_reviewer_suggestion.py
89
104
  tests/test_skill_command.py
90
105
  tests/test_skill_context.py
@@ -47,9 +47,22 @@ def get_cached_response(action_folder, prompt_text):
47
47
 
48
48
 
49
49
  def save_cached_response(
50
- action_folder, action_type, prompt_text, response_dict, meta_raw=None
50
+ action_folder,
51
+ action_type,
52
+ prompt_text,
53
+ response_dict,
54
+ meta_raw=None,
55
+ reviewed_diff=None,
51
56
  ):
52
- """Saves the AI response to the local cache."""
57
+ """Saves the AI response to the local cache.
58
+
59
+ ``reviewed_diff`` is the diff the review actually ran on. When given it is
60
+ stored at the top level of the record, so ``gitpr fix`` can re-derive its
61
+ patch from the revision that was reviewed instead of whatever the working
62
+ tree happens to hold later — the two differ whenever the review came from a
63
+ pull request (src/review/remote_pr.py) or from a full-branch diff. Records
64
+ written before this field existed simply lack it, and the reader falls back.
65
+ """
53
66
  md5_hash = generate_md5(prompt_text)
54
67
  folder_path = get_cache_base_dir() / action_folder
55
68
  folder_path.mkdir(parents=True, exist_ok=True)
@@ -76,6 +89,9 @@ def save_cached_response(
76
89
  "response": response_dict,
77
90
  }
78
91
 
92
+ if reviewed_diff is not None:
93
+ cache_data["diff"] = reviewed_diff
94
+
79
95
  try:
80
96
  with open(cache_file, "w", encoding="utf-8") as f:
81
97
  json.dump(cache_data, f, indent=2, ensure_ascii=False)
@@ -83,6 +99,51 @@ def save_cached_response(
83
99
  pass # Silent cache failure to avoid crashing the tool
84
100
 
85
101
 
102
+ # Review action types whose prompt is a branch diff. `filereview` is left out
103
+ # on purpose: it audits a single file and has no diff to re-derive.
104
+ REVIEW_ACTION_TYPES = ("review", "fullreview")
105
+
106
+
107
+ def resolve_last_review(repo_name, branch_name):
108
+ """The most recent cached review for *repo_name* and *branch_name*, or None.
109
+
110
+ All three review modes write into the same ``review/`` folder (see
111
+ ``core.generate_pr_content``), so ``action_type`` is what tells them apart —
112
+ without that filter a file audit would be mistaken for a branch review.
113
+
114
+ Returns the cache record itself: the caller reads
115
+ ``record["response"]["review"]`` for the text and ``record["action_type"]``
116
+ to know which diff produced it, so the patch can be re-derived against
117
+ today's tree rather than the tree of the day the review ran.
118
+ """
119
+ review_folder = get_cache_base_dir() / "review"
120
+ if not review_folder.exists():
121
+ return None
122
+
123
+ newest = None
124
+ for cache_file in review_folder.glob("*.json"):
125
+ try:
126
+ with open(cache_file, "r", encoding="utf-8", errors="replace") as f:
127
+ data = json.load(f)
128
+ except (json.JSONDecodeError, IOError):
129
+ continue
130
+
131
+ if not isinstance(data, dict):
132
+ continue
133
+ if data.get("repo") != repo_name or data.get("branch") != branch_name:
134
+ continue
135
+ if data.get("action_type") not in REVIEW_ACTION_TYPES:
136
+ continue
137
+ if not (data.get("response") or {}).get("review"):
138
+ continue
139
+
140
+ # Timestamps are '%Y-%m-%d %H:%M:%S', so string order is date order.
141
+ if newest is None or data.get("datetime", "") > newest.get("datetime", ""):
142
+ newest = data
143
+
144
+ return newest
145
+
146
+
86
147
  def get_cached_pr_descriptions(repo_name, branch_name):
87
148
  """Searches the cache for all historically generated PRs for this repository and branch."""
88
149
  from src.i18n import __
@@ -61,6 +61,16 @@ DEFAULT_CONFIG = {
61
61
  "GITPR_RELEASE_AI_SUMMARY": "true",
62
62
  "GITPR_RELEASE_AUTO_BUMP": "true",
63
63
  "GITPR_RELEASE_PUBLISH_DRAFT_BY_DEFAULT": "true",
64
+ # Fix feature (gitpr fix). The excluded paths are glob patterns a patch may
65
+ # not touch and still count as safe; they are about *risk*, not diff noise,
66
+ # which is why they are not shared with the smart-excludes list.
67
+ "GITPR_FIX_SAFE_MAX_LINES_CHANGED": "5",
68
+ "GITPR_FIX_SAFE_EXCLUDED_PATHS": (
69
+ "database/migrations/**;**/*.ci.yml;docker/**;terraform/**;.github/workflows/**"
70
+ ),
71
+ "GITPR_FIX_REQUIRE_CONFIRMATION": "true",
72
+ "GITPR_FIX_CREATE_BRANCH_ON_ALL_SAFE": "true",
73
+ "GITPR_FIX_BRANCH_NAME_TEMPLATE": "fix/gitpr-{datetime}",
64
74
  }
65
75
 
66
76
  # Fallbacks used when the .env value is missing or not a positive number.
@@ -131,6 +141,7 @@ SKILL_FILES_BY_TYPE = {
131
141
  "issue": ".gitpr.issue.md",
132
142
  "blame": ".gitpr.blame.md",
133
143
  "release": ".gitpr.release.md",
144
+ "fix": ".gitpr.fix.md",
134
145
  }
135
146
  SKILL_TYPES = tuple(SKILL_FILES_BY_TYPE)
136
147
 
@@ -680,6 +691,47 @@ def get_release_settings():
680
691
  }
681
692
 
682
693
 
694
+ def get_fix_settings():
695
+ """Returns the gitpr fix configuration as a flat dict.
696
+
697
+ ``safe_excluded_paths`` is the ; separated list of glob patterns a patch may
698
+ not touch and still be classified safe; an empty value falls back to the
699
+ built-in list, because clearing the field by accident must not silently drop
700
+ the protection. ``branch_name_template`` names the branch of a batch with
701
+ the vocabulary the output filenames already use ({branch} and {datetime}),
702
+ so there is one placeholder syntax to learn.
703
+ """
704
+ load_dotenv(ENV_FILE)
705
+ try:
706
+ max_lines = int(
707
+ (os.getenv("GITPR_FIX_SAFE_MAX_LINES_CHANGED") or "").strip() or 5
708
+ )
709
+ if max_lines <= 0:
710
+ raise ValueError
711
+ except ValueError:
712
+ max_lines = 5
713
+
714
+ # The fallback is spelled out, like every other getter here, instead of
715
+ # looked up in DEFAULT_CONFIG.
716
+ raw_paths = os.getenv("GITPR_FIX_SAFE_EXCLUDED_PATHS") or (
717
+ "database/migrations/**;**/*.ci.yml;docker/**;terraform/**;.github/workflows/**"
718
+ )
719
+ excluded_paths = tuple(
720
+ item.strip() for item in raw_paths.split(";") if item.strip()
721
+ )
722
+
723
+ template = (os.getenv("GITPR_FIX_BRANCH_NAME_TEMPLATE") or "").strip()
724
+ return {
725
+ "safe_max_lines_changed": max_lines,
726
+ "safe_excluded_paths": excluded_paths,
727
+ "require_confirmation": _env_bool_default_true("GITPR_FIX_REQUIRE_CONFIRMATION"),
728
+ "create_branch_on_all_safe": _env_bool_default_true(
729
+ "GITPR_FIX_CREATE_BRANCH_ON_ALL_SAFE"
730
+ ),
731
+ "branch_name_template": template or "fix/gitpr-{datetime}",
732
+ }
733
+
734
+
683
735
  def validate_github_token(token):
684
736
  """
685
737
  Validates a GitHub PAT by making a lightweight API call to /user.
@@ -180,6 +180,12 @@ CATEGORIES = (
180
180
  __("Changelog generation and release publishing."),
181
181
  doc="release-notes.md",
182
182
  ),
183
+ Category(
184
+ "fix",
185
+ __("Fix"),
186
+ __("Review findings turned into patches, and the safety rules of a batch."),
187
+ doc="fix-command.md",
188
+ ),
183
189
  Category(
184
190
  "scm",
185
191
  __("SCM / Forge"),
@@ -221,6 +227,7 @@ SKILL_LABELS = {
221
227
  "issue": __("Issue"),
222
228
  "blame": __("Blame"),
223
229
  "release": __("Release"),
230
+ "fix": __("Fix"),
224
231
  }
225
232
 
226
233
  # Sub-headers shown inside a category. Generic words go through __(); forge and
@@ -614,6 +621,59 @@ FIELDS = (
614
621
  kind=KIND_TEMPLATE,
615
622
  default="{branch}_{datetime}_RELEASE.md",
616
623
  ),
624
+ # --------------------------------------------------------------------- Fix
625
+ ConfigField(
626
+ key="GITPR_FIX_SAFE_MAX_LINES_CHANGED",
627
+ label=__("Safe Patch Size"),
628
+ description=__(
629
+ "Maximum number of added plus removed lines a patch may have and still be classified as safe."
630
+ ),
631
+ category="fix",
632
+ kind=KIND_INT,
633
+ default="5",
634
+ ),
635
+ ConfigField(
636
+ key="GITPR_FIX_SAFE_EXCLUDED_PATHS",
637
+ label=__("Sensitive Paths"),
638
+ description=__(
639
+ "Semicolon separated glob patterns a patch may not touch and still be safe (migrations, workflows, containers, infrastructure)."
640
+ ),
641
+ category="fix",
642
+ kind=KIND_STR,
643
+ default=(
644
+ "database/migrations/**;**/*.ci.yml;docker/**;terraform/**;.github/workflows/**"
645
+ ),
646
+ ),
647
+ ConfigField(
648
+ key="GITPR_FIX_REQUIRE_CONFIRMATION",
649
+ label=__("Ask Before Applying"),
650
+ description=__(
651
+ "Shows the diff and asks for confirmation before a patch is written to the working tree."
652
+ ),
653
+ category="fix",
654
+ kind=KIND_BOOL,
655
+ default="true",
656
+ ),
657
+ ConfigField(
658
+ key="GITPR_FIX_CREATE_BRANCH_ON_ALL_SAFE",
659
+ label=__("Branch for a Batch"),
660
+ description=__(
661
+ "Creates a new branch when the safe patches are applied in a batch, so the current branch stays untouched."
662
+ ),
663
+ category="fix",
664
+ kind=KIND_BOOL,
665
+ default="true",
666
+ ),
667
+ ConfigField(
668
+ key="GITPR_FIX_BRANCH_NAME_TEMPLATE",
669
+ label=__("Branch Name"),
670
+ description=__(
671
+ "Name of the branch created for a batch. Placeholders: branch and datetime."
672
+ ),
673
+ category="fix",
674
+ kind=KIND_TEMPLATE,
675
+ default="fix/gitpr-{datetime}",
676
+ ),
617
677
  # --------------------------------------------------------------- SCM/Forge
618
678
  # The common settings stay ungrouped and always visible; the fields that
619
679
  # only exist for one forge live under its sub-header and follow the
@@ -330,6 +330,23 @@ def _load_docs_smart_excludes():
330
330
  SMART_EXCLUDES = _load_smart_excludes() + _load_docs_smart_excludes()
331
331
 
332
332
 
333
+ def get_smart_exclude_patterns():
334
+ """
335
+ Return the raw glob patterns behind SMART_EXCLUDES, without the prefix.
336
+
337
+ SMART_EXCLUDES is a list of git pathspecs (":(exclude)*.lock") because that
338
+ is what `git diff` takes on its command line. A diff fetched from a forge
339
+ API never passes through git, so the same exclusions have to be applied in
340
+ Python — this exposes the patterns both paths share, derived from the very
341
+ list the git command uses so the two can never drift apart.
342
+
343
+ GITPR_SKIP_SMART_EXCLUDES is honored upstream in the loaders, so an empty
344
+ SMART_EXCLUDES already yields an empty list here.
345
+ """
346
+ prefix = ":(exclude)"
347
+ return [p[len(prefix):] for p in SMART_EXCLUDES if p.startswith(prefix)]
348
+
349
+
333
350
  def _get_raw_docs_patterns():
334
351
  """
335
352
  Returns raw glob patterns for documentation extensions (e.g. ['*.md', '*.txt']).
@@ -742,8 +759,30 @@ def split_diff_into_chunks(diff_text, max_tokens=90000):
742
759
  return chunks
743
760
 
744
761
 
745
- def generate_pr_content(action_folder, action_type, diff_text, provider="gemini"):
746
- """Sends the diff to the AI using System Instruction and returns a parsed JSON."""
762
+ def generate_pr_content(
763
+ action_folder,
764
+ action_type,
765
+ diff_text,
766
+ provider="gemini",
767
+ cache_scope="",
768
+ store_diff=False,
769
+ ):
770
+ """Sends the diff to the AI using System Instruction and returns a parsed JSON.
771
+
772
+ The engine has no idea where ``diff_text`` came from and does not need one:
773
+ a local ``git diff`` and a pull request fetched over the API are the same
774
+ text by the time they arrive, so both review flows share this function.
775
+
776
+ ``cache_scope`` is appended to the cache key only — never to the prompt the
777
+ model receives — so a review of PR 42 and a review of an identical local
778
+ diff cannot answer each other. The default "" leaves the MD5 of every
779
+ existing local review untouched, so switching a caller to DiffSource
780
+ invalidates no cache.
781
+
782
+ ``store_diff`` records the reviewed diff alongside the response, which is
783
+ what lets ``gitpr fix`` patch against the revision that was reviewed rather
784
+ than the working tree of the day it runs.
785
+ """
747
786
  if not diff_text or not diff_text.strip():
748
787
  click.secho(
749
788
  __("⚠️ No diff found. Make some changes before running the command."),
@@ -870,7 +909,7 @@ def generate_pr_content(action_folder, action_type, diff_text, provider="gemini"
870
909
  pass # Non-critical — never block the main flow for this metadata
871
910
 
872
911
  # TRY TO RETRIEVE FROM CACHE
873
- cached_data = get_cached_response(action_folder, prompt)
912
+ cached_data = get_cached_response(action_folder, prompt + cache_scope)
874
913
  if cached_data:
875
914
  click.secho(__("⚡ Response retrieved from local cache."), fg="green", dim=True)
876
915
  from src.metrics import log_command_metric
@@ -1061,7 +1100,12 @@ def generate_pr_content(action_folder, action_type, diff_text, provider="gemini"
1061
1100
  # SAVE TO CACHE AND RETURN
1062
1101
  if result_json:
1063
1102
  save_cached_response(
1064
- action_folder, action_type, prompt, result_json, meta_raw=total_meta
1103
+ action_folder,
1104
+ action_type,
1105
+ prompt + cache_scope,
1106
+ result_json,
1107
+ meta_raw=total_meta,
1108
+ reviewed_diff=diff_text if store_diff else None,
1065
1109
  )
1066
1110
  # Fire-and-forget metric for successful AI-powered command
1067
1111
  from src.metrics import log_command_metric
@@ -1200,6 +1244,7 @@ def generate_skill_template():
1200
1244
  ".gitpr.blame.md": f"gitpr.blame{lang_suffix}.md",
1201
1245
  ".gitpr.issue.md": f"gitpr.issue{lang_suffix}.md",
1202
1246
  ".gitpr.release.md": f"gitpr.release{lang_suffix}.md",
1247
+ ".gitpr.fix.md": f"gitpr.fix{lang_suffix}.md",
1203
1248
  }
1204
1249
 
1205
1250
  success_count = 0