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.
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/PKG-INFO +9 -5
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/README.md +8 -4
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/PKG-INFO +9 -5
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/SOURCES.txt +15 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/cache.py +63 -2
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/config.py +52 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/config_schema.py +60 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/core.py +49 -4
- gitpr_cli-1.2.0/src/diff_parser.py +321 -0
- gitpr_cli-1.2.0/src/fix/__init__.py +16 -0
- gitpr_cli-1.2.0/src/fix/apply_fix.py +479 -0
- gitpr_cli-1.2.0/src/fix/fix_history.py +153 -0
- gitpr_cli-1.2.0/src/fix/patch_applier.py +172 -0
- gitpr_cli-1.2.0/src/fix/patch_extractor.py +61 -0
- gitpr_cli-1.2.0/src/fix/patch_provenance.py +96 -0
- gitpr_cli-1.2.0/src/fix/patch_safety_classifier.py +101 -0
- gitpr_cli-1.2.0/src/fix/rollback_fix.py +69 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/azure_devops_provider.py +13 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/base.py +29 -1
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/bitbucket_provider.py +9 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/github_provider.py +77 -4
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/gitlab_provider.py +50 -3
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/linter_engine.py +11 -2
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/main.py +469 -56
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/mcp_server.py +263 -0
- gitpr_cli-1.2.0/src/review/__init__.py +13 -0
- gitpr_cli-1.2.0/src/review/diff_normalizer.py +84 -0
- gitpr_cli-1.2.0/src/review/diff_source.py +69 -0
- gitpr_cli-1.2.0/src/review/remote_pr.py +319 -0
- gitpr_cli-1.2.0/src/review/render.py +75 -0
- gitpr_cli-1.2.0/src/reviewer_resolution.py +159 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/reviewer_suggestion.py +19 -3
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/suggest_reviewers.py +19 -12
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/chat_app.py +3 -31
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/pr_publish_app.py +222 -38
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/updater.py +2 -2
- gitpr_cli-1.2.0/tests/test_diff_parser.py +359 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_main_suggest_reviewers.py +40 -4
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_mcp_server.py +227 -4
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_pr_publish_app.py +146 -5
- gitpr_cli-1.2.0/tests/test_reviewer_resolution.py +255 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_reviewer_suggestion.py +23 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_suggest_reviewers.py +32 -1
- gitpr_cli-1.1.0/src/diff_parser.py +0 -159
- gitpr_cli-1.1.0/tests/test_diff_parser.py +0 -204
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/LICENSE +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/dependency_links.txt +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/entry_points.txt +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/requires.txt +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/gitpr_cli.egg-info/top_level.txt +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/pyproject.toml +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/setup.cfg +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/__init__.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ai_providers.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/blame_engine.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/changelog_builder.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/chat_memory.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/commit_classifier.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/doc_links.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/github_api.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/i18n.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/__init__.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/__init__.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/infrastructure/scm/factory.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/issue_engine.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/linter_wizard.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/metrics.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/net.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/release_engine.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/security.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/spinner.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/tui_issue.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/__init__.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/config_app.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/help_screen.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/issue_app.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/linter_app.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/metrics_app.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/ui/pr_publish_help.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/usage_log.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/src/version_bump.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_blame_engine_ranges.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_blame_metrics.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_changelog_builder.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_chat_backend.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_commit_classifier.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_app.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_cli.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_schema.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_store.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_suggest_reviewers.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_config_validation.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_core.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_external_linters.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_i18n.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_install_wizard.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_issue_engine.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_linter_metrics.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_linter_presets.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_mcp_prompts.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_mcp_server_e2e.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_metrics.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_net_timeouts.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_plugins.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_pr_publish_linter_modal.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_pre_save.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_release_cli.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_release_engine.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_skill_command.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_skill_context.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_smart_excludes.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_thinking_words.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_updater.py +0 -0
- {gitpr_cli-1.1.0 → gitpr_cli-1.2.0}/tests/test_usage_log.py +0 -0
- {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.
|
|
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
|
|
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`)
|
|
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
|
|
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`)
|
|
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
|
|
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`)
|
|
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
|
|
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`)
|
|
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.
|
|
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
|
|
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`)
|
|
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
|
|
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`)
|
|
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,
|
|
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(
|
|
746
|
-
|
|
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,
|
|
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
|