gitpr-cli 1.3.0__py3-none-any.whl → 1.4.1__py3-none-any.whl

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 (98) hide show
  1. {gitpr_cli-1.3.0.dist-info → gitpr_cli-1.4.1.dist-info}/METADATA +87 -4
  2. gitpr_cli-1.4.1.dist-info/RECORD +173 -0
  3. src/application/use_cases/activate_policy_pack.py +122 -0
  4. src/application/use_cases/baseline_gate.py +415 -0
  5. src/application/use_cases/baseline_summary.py +259 -0
  6. src/application/use_cases/calculate_risk.py +309 -0
  7. src/application/use_cases/compare_against_baseline.py +41 -0
  8. src/application/use_cases/create_baseline.py +709 -0
  9. src/application/use_cases/generate_mentor_explanation.py +279 -0
  10. src/application/use_cases/install_policy_pack.py +71 -0
  11. src/application/use_cases/publish_pull_request.py +270 -0
  12. src/application/use_cases/resolve_effective_policy.py +208 -0
  13. src/application/use_cases/run_check.py +446 -0
  14. src/application/use_cases/suppress_finding.py +483 -0
  15. src/application/use_cases/validate_policy_pack.py +263 -0
  16. src/commit_classifier.py +45 -0
  17. src/config.py +298 -3
  18. src/config_schema.py +117 -0
  19. src/core.py +104 -8
  20. src/domain/baseline/__init__.py +143 -0
  21. src/domain/baseline/baseline_comparator.py +268 -0
  22. src/domain/baseline/baseline_fingerprint.py +103 -0
  23. src/domain/baseline/baseline_manifest.py +291 -0
  24. src/domain/baseline/baseline_types.py +289 -0
  25. src/domain/baseline/suppression_policy.py +661 -0
  26. src/domain/check/__init__.py +31 -0
  27. src/domain/check/check_config.py +122 -0
  28. src/domain/check/check_output.py +112 -0
  29. src/domain/check/check_types.py +165 -0
  30. src/domain/finding/__init__.py +5 -0
  31. src/domain/finding/finding_types.py +25 -0
  32. src/domain/linter/sast_finding_mapper.py +5 -1
  33. src/domain/mentor/__init__.py +15 -0
  34. src/domain/mentor/mentor_explanation_builder.py +228 -0
  35. src/domain/policy/__init__.py +116 -0
  36. src/domain/policy/policy_checksum.py +60 -0
  37. src/domain/policy/policy_compatibility.py +210 -0
  38. src/domain/policy/policy_manifest.py +694 -0
  39. src/domain/policy/policy_resolver.py +706 -0
  40. src/domain/policy/policy_types.py +237 -0
  41. src/domain/risk/__init__.py +18 -0
  42. src/domain/risk/risk_calculator.py +144 -0
  43. src/domain/risk/risk_explanation.py +78 -0
  44. src/domain/risk/risk_rules.py +261 -0
  45. src/domain/risk/risk_types.py +113 -0
  46. src/infrastructure/baseline/__init__.py +28 -0
  47. src/infrastructure/baseline/local_baseline_repository.py +355 -0
  48. src/infrastructure/ci/__init__.py +11 -0
  49. src/infrastructure/ci/junit_builder.py +102 -0
  50. src/infrastructure/ci/masking.py +65 -0
  51. src/infrastructure/ci/sarif_builder.py +152 -0
  52. src/infrastructure/git/identity.py +155 -0
  53. src/infrastructure/git/risk_history_reader.py +93 -0
  54. src/infrastructure/git/test_matcher.py +78 -0
  55. src/infrastructure/linter/external/__init__.py +1 -1
  56. src/infrastructure/linter/external/bandit_bridge.py +1 -1
  57. src/infrastructure/linter/external/base_bridge.py +3 -13
  58. src/infrastructure/linter/external/gitleaks_bridge.py +1 -1
  59. src/infrastructure/linter/external/semgrep_bridge.py +1 -1
  60. src/infrastructure/policy/__init__.py +45 -0
  61. src/infrastructure/policy/local_policy_repository.py +142 -0
  62. src/infrastructure/policy/policy_pack_loader.py +240 -0
  63. src/infrastructure/scm/azure_devops_provider.py +73 -10
  64. src/infrastructure/scm/base.py +90 -4
  65. src/infrastructure/scm/bitbucket_provider.py +76 -9
  66. src/infrastructure/scm/github_provider.py +77 -19
  67. src/infrastructure/scm/gitlab_provider.py +62 -9
  68. src/ledger.py +1124 -0
  69. src/linter_engine.py +192 -8
  70. src/main.py +2682 -184
  71. src/mcp_server.py +497 -5
  72. src/metrics.py +1105 -628
  73. src/policy_packs/laravel-quality/CHANGELOG.md +25 -0
  74. src/policy_packs/laravel-quality/README.md +41 -0
  75. src/policy_packs/laravel-quality/linter.yml +61 -0
  76. src/policy_packs/laravel-quality/policy.yml +110 -0
  77. src/policy_packs/node-quality/README.md +27 -0
  78. src/policy_packs/node-quality/linter.yml +57 -0
  79. src/policy_packs/node-quality/policy.yml +82 -0
  80. src/policy_packs/php-security/README.md +33 -0
  81. src/policy_packs/php-security/linter.yml +69 -0
  82. src/policy_packs/php-security/policy.yml +75 -0
  83. src/policy_packs/vue-quality/README.md +29 -0
  84. src/policy_packs/vue-quality/linter.yml +50 -0
  85. src/policy_packs/vue-quality/policy.yml +78 -0
  86. src/release_engine.py +65 -1
  87. src/review/remote_pr.py +33 -2
  88. src/review/render.py +18 -10
  89. src/ui/metrics_app.py +230 -224
  90. src/ui/metrics_migration_app.py +173 -0
  91. src/ui/pr_publish_app.py +58 -8
  92. src/updater.py +3 -3
  93. src/usage_log.py +13 -61
  94. gitpr_cli-1.3.0.dist-info/RECORD +0 -108
  95. {gitpr_cli-1.3.0.dist-info → gitpr_cli-1.4.1.dist-info}/WHEEL +0 -0
  96. {gitpr_cli-1.3.0.dist-info → gitpr_cli-1.4.1.dist-info}/entry_points.txt +0 -0
  97. {gitpr_cli-1.3.0.dist-info → gitpr_cli-1.4.1.dist-info}/licenses/LICENSE +0 -0
  98. {gitpr_cli-1.3.0.dist-info → gitpr_cli-1.4.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpr-cli
3
- Version: 1.3.0
3
+ Version: 1.4.1
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
@@ -15,6 +15,7 @@ Requires-Dist: pyyaml
15
15
  Requires-Dist: textual
16
16
  Requires-Dist: requests
17
17
  Requires-Dist: mcp>=1.0.0
18
+ Requires-Dist: packaging>=23.0
18
19
  Dynamic: license-file
19
20
 
20
21
 
@@ -163,11 +164,16 @@ You can pass the following *flags* for specific actions:
163
164
  * `gitpr fix [<finding_id>]`: **Review findings patch application & rollback.** Turns findings from the last code review (`gitpr -r`) into safe, reviewable unified diffs. Includes safety classification (`safe`, `review_required`, `experimental`), `--all-safe` batching, `--force` validation, and `--rollback <patch-id>`. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/fix-command.md)
164
165
  * `gitpr split`: **Atomic commit splitter.** Reads uncommitted working tree changes, groups hunks by intent using AI, and proposes ordered atomic commits without altering your working tree files (`--dry-run`, `--apply`). 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/split-command.md)
165
166
  * `gitpr tests generate`: **AI test suite generation & scaffolding.** Analyzes your diff, a target file (`--file`), or a review finding (`--finding`) and generates complete, executable test suites (supporting pytest, jest, vitest, phpunit, pest). Use `--apply` to write to disk.
167
+ * `gitpr mentor [<finding_id>]`: **Junior Mentor Pedagogical Explanations.** Breaks down code review findings with educational context ("why this matters", real-world analogy, guided questions, and key takeaway) to foster growth and mentor developers. Works standalone over the latest review findings or with `--finding <id>`. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/mentor-mode.md)
166
168
  * `gitpr explain`: **Reviewer Guide generator.** Generates a concise summary explaining what changed, why it changed, where reviewers should focus, and potential regression risks. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/pull-request-publication.md)
167
169
  * `gitpr review-pr <pr_number>`: **Remote Pull Request review.** Fetches and reviews an open pull request directly from the forge (GitHub, GitLab, Bitbucket, Azure DevOps) without checking out the branch locally. Use `--post-comment` to post the review directly to the PR. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/review-pr.md)
170
+ * `gitpr risk`: **Local Risk Scoring for Diffs.** Computes a deterministic, explainable risk score (0.0 to 100.0) and badge (`LOW 🟢`, `MEDIUM 🟡`, `HIGH 🟠`, `CRITICAL 🔴`) using local repository signals (critical paths, missing tests, migrations, bug history, and static findings). Use `--file <path>`, `--format json`, `--base <ref>`, or `--no-history`. Automatically included in code reviews (`GITPR_RISK_INCLUDE_IN_REVIEW=true`). 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/risk-scoring.md)
171
+ * `gitpr policy`: **Shared quality policy for the repository.** Adopts a versioned YAML **Policy Pack** — review skills, linter rules, severity overrides, critical paths, risk weights and PR/commit conventions — and records the choice in `.gitpr/policy.lock.yml`, so the review, the linter and the risk score all run under the same policy, with a checksum per pack. `list`, `validate`, `show`, `use`, `init`, `install`, `off`. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/policy-packs.md)
172
+ * `gitpr baseline`: **Baseline and auditable suppressions for legacy adoption.** Records the findings a repository already has in `.gitpr/baseline.json`, so a run classifies every finding as `new`, `existing`, `resolved`, `ignored` or `accepted_debt` and **only `new` blocks**. Every suppression carries a reason, and accepted debt carries an owner and an optional deadline. `create`, `show`, `validate`, `update`, `suppress`, `unsuppress`. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/baseline-suppressions.md)
173
+ * `gitpr check`: **Unified CI check command.** Consolidates static linter, secret scanning, external bridges (Semgrep, Gitleaks, Bandit), optional AI code review, risk scoring, and baseline into a single CI pipeline command. Emits machine-readable artifacts in **SARIF 2.1.0**, **JSON**, or **JUnit XML** with deterministic exit codes (`0`, `1`, `2`). 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/check-ci.md)
168
174
  * `-c` or `--commit`: Runs a local `git diff` and displays **only the suggested commit message**.
169
- * `-r` or `--review`: Performs a detailed **Code Review** of local changes.
170
- * `-f` or `--fullreview`: Performs a **Full Code Review** analyzing all changes since the remote branch.
175
+ * `-r` or `--review`: Performs a detailed **Code Review** of local changes. Use with `--mentor` to append pedagogical mentoring explanations for each finding.
176
+ * `-f` or `--fullreview`: Performs a **Full Code Review** analyzing all changes since the remote branch. Use with `--mentor` to append pedagogical mentoring explanations.
171
177
  * `-i <file>` or `--input <file>`: **Full File Audit.** Must be used together with `-r` or `-f`; it ignores git history and does a Code Review of the entire file. Excellent for acting as a consultant on legacy code refactoring.
172
178
  * `--provider <gemini|deepseek|ollama>`: Forces the use of a specific AI only for this execution, ignoring your default saved in `.env`.
173
179
  * `--lang <code>`: Forces the interface language for this execution (e.g.: `en_us`, `pt_br`). Overrides `GITPR_LANG` in `.env` without persisting the change.
@@ -179,7 +185,7 @@ You can pass the following *flags* for specific actions:
179
185
  * `--explain`: Includes the Reviewer Guide section ("Explain my PR") in the Pull Request description.
180
186
  * `--init`: **Interactive SCM forge wizard.** Detects the repository's forge (GitHub, GitLab, Bitbucket Cloud, Azure DevOps), configures the provider, and stores the access token encrypted. 📖 [Full docs](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/scm-multiforge.md)
181
187
  * `--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)
182
- * `--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.
188
+ * `--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 16 annotated tools, 19 resources, and 7 pre-built prompts directly inside your IDE. Also available as the standalone `gitpr-mcp` command.
183
189
  * `--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)
184
190
  * `--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.
185
191
  * `-ih` or `--installhooks`: Automatically installs **local Git Hooks** (`pre-commit` and `prepare-commit-msg`) in your repository.
@@ -237,6 +243,74 @@ The wizard shows pre-configured presets (PHPCS, ESLint, Stylelint — controlled
237
243
 
238
244
  Every run — manual via `--linter` or automatic before commits — consolidates the Regex Rules, Embedded Security Rules, and External Linters into a single Markdown report saved at `.gitpr/reports/linter/` (customizable via `OUTPUT_FILE_NAME_LINTER`). The report is generated only when violations are found — clean runs create no files.
239
245
 
246
+ ## ⚡ Local Risk Scoring (gitpr risk)
247
+
248
+ GitPR computes a local, deterministic, and explainable risk score (from `0.0` to `100.0`) for individual modified files and the pull request as a whole. It requires no network calls, consumes zero AI tokens, and evaluates repository and Git signals offline:
249
+
250
+ * **Critical Paths & Security (`+25`):** Authentication, billing, credentials, and sensitive operational modules.
251
+ * **Database & Infra (`+20`):** Migrations, schema definitions, CI/CD workflows, Docker, and Kubernetes configs.
252
+ * **Missing Tests (`+15`):** Executable production code modified without matching test updates in the diff.
253
+ * **History Signals (`+10` to `+20`):** Files associated with recent bug fixes, reverts, or high commit churn.
254
+ * **Findings:** Blocker (`+25`) or critical (`+15`) findings reported by static linters and secret scanners.
255
+ * **Test Presence (`-10`):** Mitigating credit when test files are added or modified alongside code changes.
256
+
257
+ ### Risk Levels & Badges
258
+
259
+ | Level | Score Range | Badge | Meaning |
260
+ |---|---|---|---|
261
+ | **LOW** | 0.0 – 24.0 | `LOW 🟢` | Routine changes with low regression likelihood |
262
+ | **MEDIUM** | 25.0 – 49.0 | `MEDIUM 🟡` | Moderate changes requiring normal review vigilance |
263
+ | **HIGH** | 50.0 – 79.0 | `HIGH 🟠` | Significant changes touching critical areas or lacking tests |
264
+ | **CRITICAL** | 80.0 – 100.0 | `CRITICAL 🔴` | High regression exposure, blockers, or sensitive architectural impacts |
265
+
266
+ ### Aggregation Formula (50 / 30 / 20)
267
+
268
+ The aggregate PR score combines:
269
+ * **50%**: Maximum individual file score
270
+ * **30%**: Weighted average of file scores by lines changed
271
+ * **20%**: Aggregated critical evidence score
272
+
273
+ ### Review Integration
274
+
275
+ When enabled (`GITPR_RISK_INCLUDE_IN_REVIEW=true` in `~/.gitpr/.env`), GitPR automatically appends a `## ⚡ Risk Assessment` section to generated code review reports (`-r`, `-f`, `gitpr review-pr`) and primes the AI review prompt with critical risk areas.
276
+
277
+ ```bash
278
+ gitpr risk # Compute risk for current diff
279
+ gitpr risk --file src/auth.py # Decompose risk for a single file
280
+ gitpr risk --format json # Output JSON for CI/CD pipelines
281
+ gitpr risk --base main # Evaluate against specific git base ref
282
+ ```
283
+
284
+ 📖 **Full documentation:** [docs/risk-scoring.md](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/risk-scoring.md)
285
+
286
+ ## 🧾 Adopting GitPR in a Legacy Repository (gitpr baseline)
287
+
288
+ Adopting GitPR in a repository that already carries hundreds of findings is the moment the tool is least useful: the gate fails on problems nobody in this change introduced, and the alternatives are turning it off or spending a sprint fixing code nobody is touching. A **baseline** is the record of what the repository already has, committed to Git, so that every run classifies what it finds instead of starting from zero.
289
+
290
+ ```bash
291
+ gitpr baseline create # Record the findings of the current diff
292
+ gitpr baseline show # The findings, the decisions and the counts
293
+ gitpr baseline validate # Every defect in the file — exit 1 on any
294
+ gitpr baseline update # Re-record today's diff, keeping decisions
295
+ gitpr baseline suppress <id> --reason "…" # A decision about one finding
296
+ gitpr baseline unsuppress <id> # Take a decision back
297
+ ```
298
+
299
+ A suppression is not a silent filter: it carries a **reason**, an author and a date, and accepted debt carries an **owner** and an optional **deadline**. `--scope finding|line|file|rule` widens a decision beyond one finding, and anything wider than a single finding lives in `.gitpr/baseline.overrides.yml`, where it is reviewed with the code. A checksum over the record catches an edit made outside GitPR — `gitpr baseline validate` names the divergence instead of applying the file. A **Policy Pack** may carry a `baseline:` block of its own, applied in memory and attributed to the pack.
300
+
301
+ Four variables in `~/.gitpr/.env`, also editable through `gitpr config` in the section **Baseline**:
302
+
303
+ | Key | Default | Effect |
304
+ |---|---|---|
305
+ | `GITPR_BASELINE_ENABLED` | `true` | `false` → no run reads the baseline: the behaviour of every earlier version, byte for byte |
306
+ | `GITPR_BASELINE_PATH` | *(empty)* | `.gitpr/baseline.json`; a relative path resolves from the repository root |
307
+ | `GITPR_BASELINE_REQUIRE_LOCKFILE_CHECKSUM_MATCH` | `true` | A divergent checksum makes the record unusable: the blocking flows refuse and exit non-zero |
308
+ | `GITPR_BASELINE_ALLOW_LOCAL_OVERRIDES` | `true` | `false` → `.gitpr/baseline.overrides.yml` is not read |
309
+
310
+ Only a `new` error-level finding exits non-zero, and only `new` findings carry risk points — everything else reaches the risk report as zero-point evidence with its status. The baseline is never sent to the AI, and no entry stores code or a secret: the file is committed, so only a digest of the offending line is kept.
311
+
312
+ 📖 **Full documentation:** [docs/baseline-suppressions.md](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/baseline-suppressions.md)
313
+
240
314
  ## 🤝 Co-Author Signature
241
315
 
242
316
  Every commit message generated by GitPR automatically carries the co-author trailer:
@@ -284,6 +358,7 @@ Instead of hiding AI instructions in the source code, GitPR uses local Markdown
284
358
  * `.gitpr.filereview.md`: Defines strict cohesion and coupling rules for full file auditing (used with `--input`).
285
359
  * `.gitpr.issue.md`: Defines the structure and level of detail required for generating standardized Issues (used with `--issue`).
286
360
  * `.gitpr.blame.md`: Defines the focus of archaeological analysis for legacy code tracing (used with `--blame`).
361
+ * `.gitpr.mentor.md`: Defines pedagogical coaching style, analogy framing, and tone for Junior Mentor Mode (used with `gitpr mentor` or `--mentor`).
287
362
 
288
363
  ## 🌐 Internationalization (i18n)
289
364
 
@@ -384,6 +459,8 @@ Once configured, use natural language in your editor's AI chat:
384
459
  | `analyze_unstaged_diff` | Unstaged diff only (working tree vs index) |
385
460
  | `list_fix_candidates` | Fix candidates of the last review: patch, classification, id (read-only) |
386
461
  | `review_remote_pr` | AI review of a pull request already open on the forge, fetched by number (read-only) |
462
+ | `run_check` | Structured CI/Editor quality gate returning normalized findings and pass/fail status |
463
+ | `publish_pull_request` | Publish or update a Pull Request on the remote forge from title and body |
387
464
 
388
465
  ### Direct CLI Invocation
389
466
 
@@ -404,6 +481,8 @@ This is useful for scripting, CI/CD pipelines, and one-off queries where you don
404
481
 
405
482
  📖 **Full documentation:** [docs/mcp-integration.md](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/mcp-integration.md) — available in 5 languages (EN, PT-BR, PT-PT, ES, FR).
406
483
 
484
+ > 🧩 **Official VS Code & Cursor Extension** — Prefer native editor commands, SCM toolbar buttons, and inline `Problems` diagnostics? Install the thin-client extension in [`extensions/vscode/`](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/editor-extension.md), powered by `gitpr-mcp`.
485
+
407
486
  > 💬 **MCP Prompts** — GitPR also exposes 7 pre-defined message templates (prompts) for common flows like "Review PR", "Generate Commit Message", and "Create Issue from Diff". See the [MCP Prompts guide](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/mcp-prompts.md) for the full list.
408
487
 
409
488
  ## 🎯 Smart Excludes (Token Optimization)
@@ -490,9 +569,13 @@ If you want to implement GitPR as an automated quality barrier in your team, che
490
569
  * [**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 from the delta of the previous version, links every commit, pull request and contributor, suggests the next semantic version and publishes releases on the forge.
491
570
  * [**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.
492
571
  * [**Split Command (gitpr split)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/split-command.md) — How the `gitpr split` subcommand reads a working tree holding several concerns, groups the hunks by intent with AI, and turns them into ordered atomic commits without ever writing to your files.
572
+ * [**Local Risk Scoring (gitpr risk)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/risk-scoring.md) — How the `gitpr risk` command evaluates regression risk, decomposes signals (50/30/20 formula), and highlights high-risk areas in code reviews offline.
573
+ * [**Policy Packs (gitpr policy)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/policy-packs.md) — How a repository adopts a versioned Policy Pack that carries skills, linter rules, severity overrides, critical paths and risk weights together, the precedence ladder that combines packs, the lockfile the team commits, and the three failures that abort instead of silently degrading.
574
+ * [**Baseline & Auditable Suppressions (gitpr baseline)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/baseline-suppressions.md) — How a legacy repository records the findings it already has, how a run classifies them as `new`, `existing`, `resolved`, `ignored` or `accepted_debt`, why only `new` blocks, what the fingerprint hashes and what it deliberately leaves out, the four scopes of a suppression, the checksum that catches a hand edit, and the `baseline:` block a Policy Pack may carry.
493
575
 
494
576
  ### Configuration & Infrastructure
495
577
 
578
+ * [**Editor Extension (VS Code & Cursor)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/editor-extension.md) — Thin-client VSIX extension driving `gitpr-mcp` over stdio for SCM commit generation, Markdown code reviews, two-step PR publishing, and inline `Problems` diagnostics.
496
579
  * [**Interactive Configuration TUI (gitpr config)**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/config-tui.md) — Master-detail terminal interface to view, edit, and validate settings in `~/.gitpr/.env`.
497
580
  * [**Multi-Forge SCM Integration**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/scm-multiforge.md) — Unified SCM provider layer for GitHub, GitLab, Bitbucket Cloud, and Azure DevOps with `--init` wizard.
498
581
  * [**Suggested Reviewers**](https://github.com/gitpr-cli/gitpr.git/blob/main/docs/suggested-reviewers.md) — Automatic reviewer suggestion via Git blame of added lines mapped to forge logins.
@@ -0,0 +1,173 @@
1
+ gitpr_cli-1.4.1.dist-info/licenses/LICENSE,sha256=UQzLC6mwqb-ZNDkBBAqOxC-SMQcEID0gaM6oFcu6u48,27030
2
+ src/__init__.py,sha256=WZW9x8P5CYnru29UiarFa8q1nQzuwYWyIodSOlruCWI,29
3
+ src/ai_providers.py,sha256=QyFEOsXyA59lZziO5bl-4nlYFJpxBcxU9aLhcTBnkWE,14633
4
+ src/blame_engine.py,sha256=Zov1hxrt7Dcmf56ovJKz3QmHIfzDKtSub2iItXeg9-A,17104
5
+ src/cache.py,sha256=GC2ZT0ThAtH22aRRZapIeauATv_nqBfiRsXjqeMhAiI,6368
6
+ src/changelog_builder.py,sha256=8GicvIhREWo3B8XhQugVi2_OWzhN_ik_2GxpxmiJVTg,9650
7
+ src/chat_memory.py,sha256=PcohnyspAfIwwwbsp9U2Wk_1AylJMFEMoWqRSJe_w30,8949
8
+ src/commit_classifier.py,sha256=pgZRj8JvkCBgwgzDFNjjNKJKzddVB4HoPBcFM1F0zjE,6742
9
+ src/config.py,sha256=5bs6jxxg-CwmtGfJB2eoMUbwRddOnc_MCcadOOcMbHg,53637
10
+ src/config_schema.py,sha256=z-JC5rULAVkaQG4ppmVcdeNKLGpuXFqglzvCBgZjHVo,43308
11
+ src/core.py,sha256=IO7HM3LaKNfs0rKWnDafAQLxrM3O7nwEWTJ6JJttyPM,85522
12
+ src/diff_parser.py,sha256=62X69ves4tlzNgVKXNsfWdOMwqn2h16RkUOPqszMU7k,11252
13
+ src/doc_links.py,sha256=DuovU-RF9kjbisBdEoGAsuavQ2y0MYDhqy6Sz6vbrGI,1157
14
+ src/github_api.py,sha256=ILm7e03UP80swc3XuJvIJJvhLp81-V_3kQQ1EuAcN8Q,3836
15
+ src/i18n.py,sha256=PdUmtZlH1zogbiDc3j6xiwtnvUd7XiNbiS-xKjYK4WM,4844
16
+ src/issue_engine.py,sha256=XwW9C5uX84eOWWZMerm0DvE8LpLRcjhrLsXtt7yftak,10872
17
+ src/ledger.py,sha256=s10Sm0qcA1b916X7m8d2osDThCmSK0UQnmUrkyj8Eao,43663
18
+ src/linter_engine.py,sha256=hIxht8M09Fnm6NiJZOqjI6dyqnFoHUDVA8Ve4dWZV4s,25214
19
+ src/linter_wizard.py,sha256=7J_nQFghVwCaKG6QI3K45KcOaxHSH1oTCAvP_6tnCQM,7424
20
+ src/main.py,sha256=wekm-Q0IBFU6c-rQXoz_56Yxygs-jmNrxHXusbMkBgw,214065
21
+ src/mcp_server.py,sha256=gi4LmMYVN4CZWHQgb-5aV5UD2O-tRYzc_BqNg7t-EFA,111654
22
+ src/metrics.py,sha256=M4Yqa__giuhmkElT-XOWrTfHGsjK_URVLhIZvErD2f0,39560
23
+ src/net.py,sha256=f0MOkXyIxi4HVetsybtWpV_yHjiC17T2JMWySBdoV2I,2774
24
+ src/release_engine.py,sha256=xgsoZUYnFohxemwzRSPymoLvgYsK0Y4HRRtZaItkZEg,37433
25
+ src/reviewer_resolution.py,sha256=lZ3csSJHN6Y5TJ3U3p9uuXikOgG7fH4qqx7X9-4F7pc,5754
26
+ src/reviewer_suggestion.py,sha256=jk9FAyp7WkkJbi-yepz7LDF1_tSHxJEEjcajMKF5HdY,8501
27
+ src/security.py,sha256=fkWy-r4tdISH0xOyWZyR8ZCFyLuYqu8fn9sKsIeNAso,1351
28
+ src/security_ruleset.py,sha256=L754S4RlVoZGWm0nuCoeli9-49mKydZpwi31V319DQ0,4337
29
+ src/spinner.py,sha256=63pEotiDa3TVwBr-sn0MJoDPQ_uWJTTwINXieJIDDKc,8964
30
+ src/suggest_reviewers.py,sha256=-wRe0HYksx4FWQZ-eDvU9Ghdk5gqtEt457KQP7sE31Y,5181
31
+ src/tui_issue.py,sha256=WEFxT4kzWQ_KbSDsOnS_iuYXasZySB-68jryGNm0peg,10331
32
+ src/updater.py,sha256=nRI22O8pIku2xu2eWxZOR-eyswPw5DtV4youbyTwtpA,4771
33
+ src/usage_log.py,sha256=FdfSmZKL_I5BcG750rD4gl3pNHx0jqV8Shij5XkAnzc,4381
34
+ src/version_bump.py,sha256=OjqJe9OL1-1dT5od7-SgU5ZN_dTtR_aKX_oSjSY64YQ,2954
35
+ src/application/__init__.py,sha256=0GADzpCB3CdCzw-d3az0DVOtTvXfCppt-0hY-Js5a1U,29
36
+ src/application/use_cases/__init__.py,sha256=7rwBoDbGxnsjVe32j7yj9yCA9g2E-DWGeokOfDbl_O0,284
37
+ src/application/use_cases/activate_policy_pack.py,sha256=lsR_AbIEykN7xWBB7qWNJpstnYFm3FKH117VZJx9jlM,4505
38
+ src/application/use_cases/baseline_gate.py,sha256=akVGhWKen5Cv_JExHu05yH6ahEc0AiPRc-rCStzKbkw,16319
39
+ src/application/use_cases/baseline_summary.py,sha256=SBcSv8EPjacckKOZjBpwjq07TRS9hY8CvUgUGxuknvc,10291
40
+ src/application/use_cases/calculate_risk.py,sha256=oh327qPomI_sO8JXErAHATafu_21KQKV4O_LLLHQ-tk,13204
41
+ src/application/use_cases/compare_against_baseline.py,sha256=qgidzsloF5b84LXGb42ae4bDdRGBbGTRepLm1Yb9FmA,1875
42
+ src/application/use_cases/create_baseline.py,sha256=LpXHlF9IkD4P4FL4VKJOh5YyCgfAD6hjt_0bQLy1pqI,25928
43
+ src/application/use_cases/generate_mentor_explanation.py,sha256=KNj5cDm-i7aPJxxbbLm4Hvtv7g5CI59-pbX7tHPcEtM,9428
44
+ src/application/use_cases/generate_pr_explanation.py,sha256=5BYUPgGBdR29P-9zt75IcKO4KOwGhZnapws7Y6A4574,3202
45
+ src/application/use_cases/generate_test_file.py,sha256=WlHnhwCWHe-XBrL9m1xvJOkn1wqr8vnsE3s3x9wgUi0,8615
46
+ src/application/use_cases/install_policy_pack.py,sha256=U1XvkHt0elXvyM2VRc6TPNukhUomSI2BSWj09hmLC_8,2745
47
+ src/application/use_cases/publish_pull_request.py,sha256=DPK8ERbO2fsjoWUhcLQs10z1WhHVhsfTvtGumn50uIA,9369
48
+ src/application/use_cases/resolve_effective_policy.py,sha256=nTbjRR4di1GnCRV7l3yDNw27CQV3heAd45sftHxNyNc,7563
49
+ src/application/use_cases/run_check.py,sha256=NEhuOZmdWgPFrOPWNku6MwS1Lp0OArk3OMd78M8_EcE,15246
50
+ src/application/use_cases/suppress_finding.py,sha256=juj2jjk2BLjrAcx6WTKJNr5VMTJQ4Gb3GaEjDNGjM64,17809
51
+ src/application/use_cases/validate_policy_pack.py,sha256=g8jt8HYUcGae69tzfT84A22usbvF9JB3T2ZoG5oyw0A,9643
52
+ src/branding/__init__.py,sha256=6ORCsJHtd422jUiQNMCf6eL8kwW4QMf6dJ9Z4Gf9bEc,80
53
+ src/branding/badge_builder.py,sha256=ZO-rXMPoSPYodvILfY2wHufYs0ZQwgFxSs0SkHd6m4A,4899
54
+ src/branding/badge_data.py,sha256=eamUtEKeg3NwF9MCipb5EXFlwL8vgJnR6yUw2TrFlEw,1925
55
+ src/demo/__init__.py,sha256=lF-kdDOqylPXRiuSmT3XwqdghMlRIiOuzpNYs7ElGiE,370
56
+ src/demo/demo_runner.py,sha256=lqnJZB27GSJan7bwV3Gf1wnCBYn7Wws_DEOFdB5PADc,10721
57
+ src/demo/fake_ai_provider.py,sha256=9slMebDqKvHv6wmhms_YhEqPhQJBlxYwAiCzehpkR3A,4038
58
+ src/demo/scenarios/__init__.py,sha256=t8PRSdgNM9f0tb53lBSkehiQ_liI-azts7BdhMdUZrs,4041
59
+ src/demo/scenarios/laravel_bug_fix.py,sha256=lU3XoKaGvcDIlMmzZFeWvEI6tGjgz5bWGflBbUNvgHU,20461
60
+ src/demo/scenarios/security_issue.py,sha256=sI8YykXcYOkpgHygAOX6pYLHwSkHZoE5OjrYsRqOCXQ,23821
61
+ src/domain/baseline/__init__.py,sha256=g-zsXskjG1FY4ok0N4CRWxRLPh-fowr-2_sTiFj9kLA,3802
62
+ src/domain/baseline/baseline_comparator.py,sha256=dB8ydnKFiSSq4qPalLKq4RNpDi27ZB6mSx42fBhE5WA,8702
63
+ src/domain/baseline/baseline_fingerprint.py,sha256=06i-rxVcElr0md4eveKycl7etsHaECdI9vOZ_TAWPYQ,4370
64
+ src/domain/baseline/baseline_manifest.py,sha256=jYy_AHBNP6adFr8FNfZtqQmdZteGNzTlToXHg-BP3RI,10429
65
+ src/domain/baseline/baseline_types.py,sha256=_su74m5HuBu0Z5jxm_elva_CX7EP_6025lbX5EjH2o8,11220
66
+ src/domain/baseline/suppression_policy.py,sha256=GNZ0WN7ww4HcUq82vtiqd8Ere7EuP51VuSCoPzEkLOU,22489
67
+ src/domain/check/__init__.py,sha256=Ngd91XHlsZVj7NByY5DzLhI75W9ZqvpAOmt6GQ2crM8,681
68
+ src/domain/check/check_config.py,sha256=C2RB2CHkpRhEpb8QI7MEuSmwD5LskVNSVj2doz6b818,3679
69
+ src/domain/check/check_output.py,sha256=5W7q0S8ojeoXBDbhgdfTLk8bOYvZmO2rbMMNNzyMi8A,3834
70
+ src/domain/check/check_types.py,sha256=k3UvkjWNsFt-7PTHQzE1F-6SXRni72XHHyXS761g1t4,5056
71
+ src/domain/finding/__init__.py,sha256=Y9n2tmT8zybkv3zkBAoI_2k8C4ode2CWHm6l28BhH9w,166
72
+ src/domain/finding/finding_types.py,sha256=Nocd7SmXMAk3q0gVM6yeh2AW6rmJS6CxOCOIQsmsF-Y,833
73
+ src/domain/linter/__init__.py,sha256=WoLci3LhRc7S2IlytrveRPbEQVrAwI4bqazVmGP08Ak,236
74
+ src/domain/linter/sast_finding_mapper.py,sha256=C7SdwVz7-pJNCPeF5kchWjAmL4qs4m1yJQoAKrO-omM,3336
75
+ src/domain/mentor/__init__.py,sha256=4Ii3I9g-fQWv5wgxd5xe5sRdtTinF0Uvqd_b1CxL9ao,357
76
+ src/domain/mentor/mentor_explanation_builder.py,sha256=VGvHEWQuneZDA8epa2txRD5w2U4vU6rgVzmxM5tChg0,6776
77
+ src/domain/policy/__init__.py,sha256=rXn1lKH67Lx-p-9imYIjwqfEp3XAPLxq7djSWFusOIs,3501
78
+ src/domain/policy/policy_checksum.py,sha256=SIwVmMpb3FH1ziWBc9NJN6jqDqBLoagdf-QGO-Qf6N0,2282
79
+ src/domain/policy/policy_compatibility.py,sha256=rd90TLg4wyB9gI-RKMI94NxwxeFk9uKxBsJ9Tm9QM3c,8267
80
+ src/domain/policy/policy_manifest.py,sha256=hXIM4u-tKYXTcEu0rBEGJYBuv4dysn9NEjhxXVPSkk4,26292
81
+ src/domain/policy/policy_resolver.py,sha256=5oTVHysPIwRJibRtRp3U3zOJpS07EBRfkhsmpfI6wcE,28883
82
+ src/domain/policy/policy_types.py,sha256=lhGxapRFAPiVCjmZtwZ9EVViSkDYCJ6UiKNVsaPKvDw,9875
83
+ src/domain/pr/__init__.py,sha256=O9C2dQpbMnbF3mOXTMUvHPmIAF0G-QxhDlVXjB_wShs,274
84
+ src/domain/pr/explain_section_builder.py,sha256=RMPErD_X8nLhbfCwxHCBmI3AFLilO_eNPpEDZE6pyns,3799
85
+ src/domain/risk/__init__.py,sha256=9vbLfhIomCarCbEw_DCZzri9lzV-lFIXBN-FAq6oSPY,283
86
+ src/domain/risk/risk_calculator.py,sha256=zjIoLIRdXi3pHNbq0QcRwOznArtNtQQWV8ReLaW6Zn4,5037
87
+ src/domain/risk/risk_explanation.py,sha256=YuPMBT27x7rnZnmvttIWBsfbTjsIXjMUEP9WLU4VlvE,3005
88
+ src/domain/risk/risk_rules.py,sha256=2hueo89tvHOpLcCfKVEro8Z8AMgVeyUYG8xf6wcpcAI,8705
89
+ src/domain/risk/risk_types.py,sha256=O3l6woRgnpocGffucFiI22LoOrxM-qS70HDg2hSHGO4,4041
90
+ src/domain/tests_generation/__init__.py,sha256=OcHvtEilwu_8z_PV_prq2x_GGBGxmlsCW2XM1xU8dl0,251
91
+ src/domain/tests_generation/framework_detector.py,sha256=DyJZcttvpRkZMEx8a9X3ksveJ-PNub49XXNzqERQSU4,4439
92
+ src/domain/tests_generation/test_content_types.py,sha256=jnPywEMAGtm340e8V1kuHbx9Nxa9nhC7mKnubduxEZM,1019
93
+ src/domain/tests_generation/test_scaffold_builder.py,sha256=NkTbYswWiuWikLfz6nRi34zU8e7HvzMzkbj3q2W__Ws,3800
94
+ src/fix/__init__.py,sha256=haZFtazOc4v9SmtqBtyPNUvYrEQXNaChTIZiOpZlNSc,937
95
+ src/fix/apply_fix.py,sha256=yMinMW50Fw62V-k7hRwN1lsikCJbz6xV5iX4IH0sdxY,17912
96
+ src/fix/fix_history.py,sha256=pHXHpt-SstitbLDmlPloDVKlH79FUD8hqKjVK5xKLMA,5626
97
+ src/fix/patch_applier.py,sha256=3tIXwp8yPwelngXvhmQhvCYVpEOPfvCUwrnG_B9Yf0c,1235
98
+ src/fix/patch_extractor.py,sha256=gEiwSvxGNHM74TnubTN22NBpbXDwvNpB2vMqgpF7hQQ,2514
99
+ src/fix/patch_provenance.py,sha256=sF934ntcZjJCTzzhtuR2Pi-vMUdXFElKcuw5riwPnUk,3195
100
+ src/fix/patch_safety_classifier.py,sha256=StZvh_0Y-ZCdf_LwFoVAdfhpi0b7v1TOxTRoG86_63M,4121
101
+ src/fix/rollback_fix.py,sha256=rMBYewuvlhJOFdo-Ke_SPfu1O-yd6NJzEYiO5o-LcKk,2501
102
+ src/infrastructure/__init__.py,sha256=zTIcY3cscsdkXogSGXoJbH0mVGFbKYTRK32fg26wr9U,81
103
+ src/infrastructure/baseline/__init__.py,sha256=VCnAq7MgyvIAwjd2jznSEvyY42Xg5tmnz4VWBxFA5YI,614
104
+ src/infrastructure/baseline/local_baseline_repository.py,sha256=lE9f2D-6XBPRNi5e32jUA6aG6TUxgAN12ihz-tH3KaM,13460
105
+ src/infrastructure/ci/__init__.py,sha256=weQNs0JnGo0nAB_NKvDzcUvOWx_pQQyWucd63Q0ShLA,334
106
+ src/infrastructure/ci/junit_builder.py,sha256=u3DE_nUq0yntvNHRufFUu35aIkgLKfvj-Tz2a2lWBXk,3819
107
+ src/infrastructure/ci/masking.py,sha256=uzH9uanMzye-kKH0IOfKqdEFUAPcCev17BhGSPzqBoc,2153
108
+ src/infrastructure/ci/sarif_builder.py,sha256=XLkmX5j_dmOWy3ycT98WbP35tuYjDgRwaxenhN9jiIE,5233
109
+ src/infrastructure/git/__init__.py,sha256=j9zQUXKNp5NcOzTK-qHGNh75YkufGlw5rgH1wgRknX4,87
110
+ src/infrastructure/git/identity.py,sha256=fClwWTkGIypquE19dUjwwDQCifdsGQmmRMgVobuBIa8,5722
111
+ src/infrastructure/git/patch_applier.py,sha256=RgbrpAp-6x401Eydz6yLMTOXKn5E9LQzV9ZB_n7E90M,7988
112
+ src/infrastructure/git/risk_history_reader.py,sha256=EqIvVPqw5gCFHnHSjIioMSg3BTSk9fXvynZJi0ajfyY,2733
113
+ src/infrastructure/git/selective_stager.py,sha256=sMRPZmq2EWipcabCCETeMu_0in66BFSzQAqZ7h1y2hc,8038
114
+ src/infrastructure/git/test_matcher.py,sha256=wsbMrPAdBqfRz8W_DNjdIE1yQi3Y56Mi9Jfv0DOeMos,2586
115
+ src/infrastructure/linter/external/__init__.py,sha256=6qz3sZ-ObiSOiTfakgGoIRefJnHaWSh4M9Hvh1XNWxw,317
116
+ src/infrastructure/linter/external/bandit_bridge.py,sha256=gTixkOj0wERfe4Bp53w1hjpSrEH4F6bxCfEwYX0Cy0M,4330
117
+ src/infrastructure/linter/external/base_bridge.py,sha256=sHiKZnLitCMoMhMy9tVXVggIQWhE5IcpECYx_JQ_FnY,3063
118
+ src/infrastructure/linter/external/gitleaks_bridge.py,sha256=rgsYTcfvHDeekWuXU2D73apq-4NyMlVi_E5S5cHbOWU,5881
119
+ src/infrastructure/linter/external/semgrep_bridge.py,sha256=gifKH4T22ZunoYRZROD2mwv46CCYgGLT6ikmO9VnnrM,4504
120
+ src/infrastructure/policy/__init__.py,sha256=37VsNF-kYx3Z7UQQo_LT8nQhrfU-v4JUwaLGBKgU11Y,1039
121
+ src/infrastructure/policy/local_policy_repository.py,sha256=bBRWrz8cDJB34c_QwVnCUFK4NESjQTKUiPPuJ4uDpII,5644
122
+ src/infrastructure/policy/policy_pack_loader.py,sha256=sutt_l-_SGBkR0VAaeZ9YwFPC4WAcX4H89J7E7_Br8s,9319
123
+ src/infrastructure/scm/__init__.py,sha256=kNiYljRCJRwi5WoPckn8NbZt6qbEQZFMwaBY3-kepRo,1825
124
+ src/infrastructure/scm/azure_devops_provider.py,sha256=L9nKlwayUj80jZxOeeGFKbgos3He-lVRVh5U7Nkoopg,17920
125
+ src/infrastructure/scm/base.py,sha256=wbHFUNBtxXrRcEBiT0qmgBScWr-Ep_eJSIeP279KOQI,13921
126
+ src/infrastructure/scm/bitbucket_provider.py,sha256=ooHt7qilk_LHwOSJK81WMm-ZPBZJomyIZL5NEWeMoak,15151
127
+ src/infrastructure/scm/factory.py,sha256=Qeearq8eWnF_4boI68rVBkoGEiS-xRGD4V-O_FF0204,3856
128
+ src/infrastructure/scm/github_provider.py,sha256=Ju1QzU6oiUft1hGT1zqaIx1vUJIr0oy3GCYm3y0YfcM,19342
129
+ src/infrastructure/scm/gitlab_provider.py,sha256=06jWqEk_A8ERYf0KalGxXdLZBL4ZemYz9KuDMiK7YcY,16595
130
+ src/infrastructure/scm/web_links.py,sha256=9DHFCwYc6TVzO3INe0rvEicgoD2-kxQSrgq3vt_p5Ow,3621
131
+ src/policy_packs/laravel-quality/CHANGELOG.md,sha256=ztrTTZhzScFdPEWOSkQoQ7vnaTu5LkqyJZc43k6G8qg,1129
132
+ src/policy_packs/laravel-quality/README.md,sha256=GSeluNUTR80dwhm1qZrrYFaaPjrdhTkWmfJdv3hDWes,1811
133
+ src/policy_packs/laravel-quality/linter.yml,sha256=OoJg-i5jDnaXwVYd_nZe2HMinVt0n2op7KvUzG7fqKQ,3130
134
+ src/policy_packs/laravel-quality/policy.yml,sha256=z41BBUM_rAWLTIstNoI3E5pa4Y_Uy21lsrPBWOuH_VY,3703
135
+ src/policy_packs/node-quality/README.md,sha256=eGb8bZBBwniM4fKBDQoHhrpXxcijHpxEiwffJupgvZg,1286
136
+ src/policy_packs/node-quality/linter.yml,sha256=LqKDpnIcO4jSe2VihaEAfncevZNl9fJYQF7n_vpNLAw,2817
137
+ src/policy_packs/node-quality/policy.yml,sha256=MVXsPk-AYx2mchum0lOnKQqCfy_3HdkVPogAas-Qcmk,3137
138
+ src/policy_packs/php-security/README.md,sha256=7R7NrqoEN0WjVCJf6l38zxurBI9af7veWj9NvI1XTAc,1456
139
+ src/policy_packs/php-security/linter.yml,sha256=3aSXdmLZCkVwE7vRl4doXEO1yohc9A-GLmcRCWk-UTY,3191
140
+ src/policy_packs/php-security/policy.yml,sha256=sycGc-AhsHrx_kYwcbaVwAO5gMmj9zmC-9YdjBpMg0A,3183
141
+ src/policy_packs/vue-quality/README.md,sha256=hHKAPxNMfkBOdqe4lhL5-LLYEUM82mJ10zdhxP4yQxE,1264
142
+ src/policy_packs/vue-quality/linter.yml,sha256=UFZY6G7N7G484mStiQVL0u9p6yzoVPzdvdYTHc5ABpc,2458
143
+ src/policy_packs/vue-quality/policy.yml,sha256=WMfnCoWxWAqwrPn28QJ1oOvY0emoxoLEXmxzGlDLq_4,3021
144
+ src/review/__init__.py,sha256=XVyQZFf6UAqIKi8Gb1pQ4dKjcrC50soUvNuKu1DBspc,687
145
+ src/review/diff_normalizer.py,sha256=PRXQ7b81EBH1nx_8_Tm9G7Kjdax_6hIVtFgEixicT0E,3551
146
+ src/review/diff_source.py,sha256=O8bIlff0Zr7wPMzCKAVk_LF2pMDr3sc9KAuyA3OvHG0,2664
147
+ src/review/remote_pr.py,sha256=BswcOgZA1GZdfQ1NXKA9lWsZA60fKmUUndMIG97CJg0,13319
148
+ src/review/render.py,sha256=Wrfp3gPfzT7_IS-yScABYI2ptSb0ntoJkpEw27xpzhA,3111
149
+ src/split/__init__.py,sha256=JuXxPxoG1NrWtLAQARSBh59tsNXvrUoS-T-vHcHeDPM,912
150
+ src/split/apply_split_plan.py,sha256=-ESEEl973Ai9czEOVfS697Dy196QX8EKbVrNZlgrQhY,10678
151
+ src/split/generate_split_plan.py,sha256=I370yLCxqeZRemIIcLswYVI07fet5JJMRxxht21HlOA,8786
152
+ src/split/hunk_grouper.py,sha256=rNH64CJQ80ue6YjTTaVxwtQug1tY6yCjEEScjj8wL2o,14249
153
+ src/split/hunk_parser.py,sha256=Lvmpd8qPYT2a0pw-XCSvUJqm_b9P5tAw9Kj_DlbBWTk,10010
154
+ src/split/split_plan.py,sha256=rim_iUlyQO8EpqXjN-YzRFv5mbo2pwZlXdrkuZ7PGxM,6419
155
+ src/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
+ src/ui/chat_app.py,sha256=O21d4-_6u87pHeFKtBJbTpQxkBQWmaNa617CO0f4-0Q,26777
157
+ src/ui/config_app.py,sha256=xgppZbEBFrFijgLGo1b1PN1vTGCg8xpI8gpMYBv2KzU,73417
158
+ src/ui/help_screen.py,sha256=EvbP8kFZ6Sdehi7bJ6Wo_YcTTk5MTkgneeidWwefYrA,1890
159
+ src/ui/issue_app.py,sha256=eINMmamloeopbtwyS1rCPwosaHeZW5oLebnEV4TfamY,9281
160
+ src/ui/linter_app.py,sha256=Y5vwU7D5PQIR_HaY6t40jZy7Guc8z2w-auyosCQqoro,1566
161
+ src/ui/metrics_app.py,sha256=r5JSS70Fkir0qQ4Q03T6c1R2-WGrvYMNTlHMhjAR2Tc,16303
162
+ src/ui/metrics_migration_app.py,sha256=-56HhWZhtVpGCO53NhUnOpZI9bDesfwy2_w7f31cMsU,5923
163
+ src/ui/pr_publish_app.py,sha256=KAtq0aKtSx7d_J4mTaOBAjIK0z_ftJRsNGGbIo0ZhL8,69540
164
+ src/ui/pr_publish_help.py,sha256=fBiUtD97ngxbSfAvPVGGXU19r5kbALCKyi4QeKqyQ2c,1891
165
+ src/ui/demo/__init__.py,sha256=PGXenIwpS1lVDE164CZ55q8_65XtJq5H300NIpnqOh0,58
166
+ src/ui/demo/demo_app.py,sha256=EDeuqxh_v9KrPhib49TyirmFlHZN7I5NQkg9AjDGhcs,3074
167
+ src/ui/demo/demo_help_screen.py,sha256=m8SuvhzGmkMsahUBA6Aq7ull4W65nS0KOX4hLx-GGps,2056
168
+ src/ui/demo/demo_screens.py,sha256=3Ntp-A7OB43yEtniRqV1UKWc-p5lHRyxeadpe8bxJpM,1937
169
+ gitpr_cli-1.4.1.dist-info/METADATA,sha256=sNb--Fg6TdLtXclPH_pTKhQvVNAmG4irqz8wDlhATUs,52571
170
+ gitpr_cli-1.4.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
171
+ gitpr_cli-1.4.1.dist-info/entry_points.txt,sha256=TstGnMUzdOI5qNmpM97EPDBPuMqOZxoo6EoLxHSOMSQ,71
172
+ gitpr_cli-1.4.1.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
173
+ gitpr_cli-1.4.1.dist-info/RECORD,,
@@ -0,0 +1,122 @@
1
+ """Use case: record which pack a repository follows.
2
+
3
+ The record is ``.gitpr/policy.lock.yml``, a file meant to be committed: it is
4
+ what makes the policy the same for everyone on the team and what lets a later
5
+ run notice that a pack changed under it.
6
+
7
+ Activation is **one pack per repository**. Choosing a second one replaces the
8
+ first rather than adding to it, so there is never a question about which pack
9
+ owns a value — the graph below the root is reached through ``extends``, and only
10
+ through it.
11
+
12
+ Building the lockfile and writing it are separate functions because the CLI has
13
+ to show the user what will be written *before* asking whether to write it. A
14
+ command that writes first and asks afterwards is not asking.
15
+ """
16
+
17
+ import os
18
+ from datetime import datetime, timezone
19
+ from typing import Any
20
+
21
+ from src.domain.policy import set_active_policy
22
+ from src.domain.policy.policy_resolver import resolve_graph
23
+ from src.i18n import __
24
+ from src.infrastructure.policy.local_policy_repository import (
25
+ LOCKFILE_SCHEMA_VERSION,
26
+ lockfile_path,
27
+ write_lockfile,
28
+ )
29
+ from src.infrastructure.policy.policy_pack_loader import (
30
+ load_pack_by_reference,
31
+ make_loader,
32
+ )
33
+
34
+ from src.application.use_cases.validate_policy_pack import split_reference
35
+
36
+
37
+ def _relative_to_repo(absolute: str, repo_path: str) -> str | None:
38
+ """*absolute* as a repository-relative POSIX path, or None when outside.
39
+
40
+ Only packs that live inside the repository are recorded by path. A pack in
41
+ ``~/.gitpr/policies/`` is recorded by name and version instead, so the
42
+ lockfile stays valid for a teammate who has it installed somewhere else.
43
+ """
44
+ try:
45
+ relative = os.path.relpath(absolute, repo_path)
46
+ except ValueError:
47
+ # Different drives on Windows.
48
+ return None
49
+ parts = relative.split(os.sep)
50
+ if not relative or parts[0] == "..":
51
+ return None
52
+ return relative.replace(os.sep, "/")
53
+
54
+
55
+ def _lockfile_entry(pack: Any, repo_path: str) -> dict[str, Any]:
56
+ entry: dict[str, Any] = {
57
+ "name": pack.reference.name,
58
+ "version": pack.reference.version,
59
+ "source": pack.reference.source.value,
60
+ "checksum": pack.reference.checksum,
61
+ }
62
+ if pack.reference.path:
63
+ relative = _relative_to_repo(pack.reference.path, repo_path)
64
+ if relative:
65
+ entry["path"] = relative
66
+ return entry
67
+
68
+
69
+ def build_lockfile(root_reference: str, repo_path: str | None = None) -> dict[str, Any]:
70
+ """Resolves *root_reference* and the lockfile that would pin it.
71
+
72
+ Raises ``PolicyError`` when the pack cannot be found or its graph does not
73
+ resolve, and writes nothing. The CLI calls this first, shows the result, and
74
+ only then decides whether to call :func:`activate_policy_pack`.
75
+ """
76
+ from src.updater import __version__
77
+
78
+ repo = os.path.abspath(repo_path or os.getcwd())
79
+ name, version_range = split_reference(root_reference)
80
+ root = load_pack_by_reference(name, version_range, repo)
81
+ packs = resolve_graph(root, make_loader(repo))
82
+
83
+ return {
84
+ "schema_version": LOCKFILE_SCHEMA_VERSION,
85
+ "gitpr_version": __version__,
86
+ "activated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
87
+ "root": _lockfile_entry(root, repo),
88
+ "packs": [_lockfile_entry(pack, repo) for pack in packs],
89
+ }
90
+
91
+
92
+ def activate_policy_pack(root_reference: str, repo_path: str | None = None) -> str:
93
+ """Pins *root_reference* for the repository and returns the lockfile path."""
94
+ data = build_lockfile(root_reference, repo_path)
95
+ return write_lockfile(data, repo_path)
96
+
97
+
98
+ def deactivate_policy_pack(repo_path: str | None = None) -> bool:
99
+ """Removes the lockfile. Returns whether there was one to remove.
100
+
101
+ The overrides file and the packs themselves are left alone: they are the
102
+ user's material, and a decision to stop following a pack is not a decision to
103
+ delete it.
104
+ """
105
+ target = lockfile_path(repo_path)
106
+ if not os.path.isfile(target):
107
+ return False
108
+ os.remove(target)
109
+ set_active_policy(None)
110
+ return True
111
+
112
+
113
+ def describe_lockfile(data: dict[str, Any]) -> str:
114
+ """A one-line summary for the confirmation prompt."""
115
+ root = data.get("root") or {}
116
+ dependencies = [p for p in data.get("packs", []) if p.get("name") != root.get("name")]
117
+ return __(
118
+ "{pack}@{version} with {count} dependency pack(s)",
119
+ pack=root.get("name", "?"),
120
+ version=root.get("version", "?"),
121
+ count=len(dependencies),
122
+ )