diffguard 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. diffguard-0.1.3/.github/pull_request_template.md +11 -0
  2. diffguard-0.1.3/.github/workflows/ci.yml +13 -0
  3. diffguard-0.1.3/.github/workflows/deploy-docs.yml +21 -0
  4. diffguard-0.1.3/.github/workflows/docs.yml +24 -0
  5. diffguard-0.1.3/.github/workflows/publish-to-pypi.yml +102 -0
  6. diffguard-0.1.3/.gitignore +11 -0
  7. diffguard-0.1.3/.pre-commit-hooks.yaml +8 -0
  8. diffguard-0.1.3/AGENTS.md +26 -0
  9. diffguard-0.1.3/CHANGELOG.md +32 -0
  10. diffguard-0.1.3/LICENSE +53 -0
  11. diffguard-0.1.3/PKG-INFO +142 -0
  12. diffguard-0.1.3/README.md +110 -0
  13. diffguard-0.1.3/RELEASE.md +67 -0
  14. diffguard-0.1.3/action.yml +107 -0
  15. diffguard-0.1.3/docs/agent-integration.md +164 -0
  16. diffguard-0.1.3/docs/architecture.md +182 -0
  17. diffguard-0.1.3/docs/claude-md-snippet.md +41 -0
  18. diffguard-0.1.3/docs/cursor-rule-snippet.md +34 -0
  19. diffguard-0.1.3/docs/decisions/001-cli-first.md +27 -0
  20. diffguard-0.1.3/docs/decisions/002-selective-trigger.md +29 -0
  21. diffguard-0.1.3/docs/decisions/003-bsl-1-1-license.md +31 -0
  22. diffguard-0.1.3/docs/decisions/004-local-first.md +21 -0
  23. diffguard-0.1.3/docs/decisions/README.md +43 -0
  24. diffguard-0.1.3/docs/how-it-works.md +87 -0
  25. diffguard-0.1.3/docs/index.md +132 -0
  26. diffguard-0.1.3/docs/llms-ctx.txt +1119 -0
  27. diffguard-0.1.3/docs/llms.txt +18 -0
  28. diffguard-0.1.3/docs/quickstart.md +152 -0
  29. diffguard-0.1.3/docs/real-world-catches.md +151 -0
  30. diffguard-0.1.3/docs/roadmap.md +98 -0
  31. diffguard-0.1.3/docs/schema.md +169 -0
  32. diffguard-0.1.3/docs/validation.md +84 -0
  33. diffguard-0.1.3/examples/diffguard-workflow.yml +23 -0
  34. diffguard-0.1.3/mkdocs.yml +47 -0
  35. diffguard-0.1.3/pyproject.toml +71 -0
  36. diffguard-0.1.3/scripts/ab_oracle_lite.py +151 -0
  37. diffguard-0.1.3/scripts/build-llms-ctx.sh +21 -0
  38. diffguard-0.1.3/src/diffguard/__init__.py +3 -0
  39. diffguard-0.1.3/src/diffguard/cli.py +676 -0
  40. diffguard-0.1.3/src/diffguard/engine/__init__.py +1 -0
  41. diffguard-0.1.3/src/diffguard/engine/_types.py +36 -0
  42. diffguard-0.1.3/src/diffguard/engine/classifier.py +87 -0
  43. diffguard-0.1.3/src/diffguard/engine/deps.py +204 -0
  44. diffguard-0.1.3/src/diffguard/engine/matcher.py +128 -0
  45. diffguard-0.1.3/src/diffguard/engine/parser.py +47 -0
  46. diffguard-0.1.3/src/diffguard/engine/pipeline.py +205 -0
  47. diffguard-0.1.3/src/diffguard/engine/signatures.py +248 -0
  48. diffguard-0.1.3/src/diffguard/engine/summarizer.py +412 -0
  49. diffguard-0.1.3/src/diffguard/git.py +323 -0
  50. diffguard-0.1.3/src/diffguard/languages/__init__.py +52 -0
  51. diffguard-0.1.3/src/diffguard/languages/_utils.py +13 -0
  52. diffguard-0.1.3/src/diffguard/languages/go/__init__.py +101 -0
  53. diffguard-0.1.3/src/diffguard/languages/go/queries.scm +1 -0
  54. diffguard-0.1.3/src/diffguard/languages/python/__init__.py +204 -0
  55. diffguard-0.1.3/src/diffguard/languages/python/queries.scm +1 -0
  56. diffguard-0.1.3/src/diffguard/languages/typescript/__init__.py +184 -0
  57. diffguard-0.1.3/src/diffguard/languages/typescript/queries.scm +1 -0
  58. diffguard-0.1.3/src/diffguard/schema.py +97 -0
  59. diffguard-0.1.3/tests/__init__.py +0 -0
  60. diffguard-0.1.3/tests/ab_test_matrix.md +37 -0
  61. diffguard-0.1.3/tests/benchmarks/__init__.py +0 -0
  62. diffguard-0.1.3/tests/benchmarks/conftest.py +11 -0
  63. diffguard-0.1.3/tests/benchmarks/test_performance.py +117 -0
  64. diffguard-0.1.3/tests/benchmarks/test_pipeline_benchmark.py +112 -0
  65. diffguard-0.1.3/tests/conftest.py +26 -0
  66. diffguard-0.1.3/tests/fixtures/real_world/.gitkeep +0 -0
  67. diffguard-0.1.3/tests/fixtures/synthetic/.gitkeep +0 -0
  68. diffguard-0.1.3/tests/fixtures/synthetic/multi_file_refactor.json +51 -0
  69. diffguard-0.1.3/tests/fixtures/synthetic/signature_change.json +35 -0
  70. diffguard-0.1.3/tests/fixtures/synthetic/simple_function_add.json +21 -0
  71. diffguard-0.1.3/tests/test_classifier.py +152 -0
  72. diffguard-0.1.3/tests/test_cli.py +160 -0
  73. diffguard-0.1.3/tests/test_context_cli.py +218 -0
  74. diffguard-0.1.3/tests/test_deps.py +171 -0
  75. diffguard-0.1.3/tests/test_git.py +287 -0
  76. diffguard-0.1.3/tests/test_languages/__init__.py +31 -0
  77. diffguard-0.1.3/tests/test_languages/test_go.py +55 -0
  78. diffguard-0.1.3/tests/test_languages/test_python.py +80 -0
  79. diffguard-0.1.3/tests/test_languages/test_typescript.py +67 -0
  80. diffguard-0.1.3/tests/test_matcher.py +139 -0
  81. diffguard-0.1.3/tests/test_parser.py +154 -0
  82. diffguard-0.1.3/tests/test_pipeline.py +213 -0
  83. diffguard-0.1.3/tests/test_richer_categories.py +182 -0
  84. diffguard-0.1.3/tests/test_schema.py +187 -0
  85. diffguard-0.1.3/tests/test_signatures.py +64 -0
  86. diffguard-0.1.3/tests/test_summarizer.py +321 -0
  87. diffguard-0.1.3/uv.lock +1208 -0
@@ -0,0 +1,11 @@
1
+ ## What Changed
2
+
3
+ ## Test Evidence
4
+
5
+ ## Schema Impact
6
+ - [ ] No schema changes
7
+ - [ ] Schema changed — migration notes below:
8
+
9
+ ## Performance Impact
10
+ - [ ] No performance impact
11
+ - [ ] Benchmark results:
@@ -0,0 +1,13 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ check:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - uses: astral-sh/setup-uv@v5
9
+ - run: uv sync --dev
10
+ - run: uv run ruff check
11
+ - run: uv run ruff format --check
12
+ - run: uv run mypy src/
13
+ - run: uv run pytest
@@ -0,0 +1,21 @@
1
+ name: Deploy Docs
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ paths:
6
+ - 'docs/**'
7
+ - 'mkdocs.yml'
8
+ permissions:
9
+ contents: write
10
+ jobs:
11
+ deploy:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: '3.12'
20
+ - run: pip install mkdocs-material
21
+ - run: mkdocs gh-deploy --force
@@ -0,0 +1,24 @@
1
+ name: Deploy docs
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ paths: ['docs/**', 'mkdocs.yml']
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ concurrency:
12
+ group: deploy-docs
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ deploy:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: '3.13'
23
+ - run: pip install mkdocs-material
24
+ - run: mkdocs gh-deploy --force
@@ -0,0 +1,102 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Build & Test
11
+ runs-on: ubuntu-latest
12
+ timeout-minutes: 10
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.x"
22
+
23
+ - name: Validate version against tag
24
+ run: |
25
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
26
+ PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
27
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
28
+ echo "::error::Tag version ($TAG_VERSION) does not match pyproject.toml version ($PKG_VERSION)"
29
+ exit 1
30
+ fi
31
+ echo "Version validated: $PKG_VERSION"
32
+
33
+ - name: Install dev dependencies & run tests
34
+ run: |
35
+ pip install -e .
36
+ pip install pytest pytest-cov pytest-benchmark ruff mypy
37
+ pytest
38
+ timeout-minutes: 5
39
+
40
+ - name: Build package
41
+ run: |
42
+ pip install build
43
+ python -m build
44
+
45
+ - name: Upload dist artifacts
46
+ uses: actions/upload-artifact@v4
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+
51
+ publish-to-testpypi:
52
+ name: Publish to TestPyPI
53
+ needs: build
54
+ runs-on: ubuntu-latest
55
+ environment: testpypi
56
+ permissions:
57
+ id-token: write
58
+ steps:
59
+ - name: Download dist artifacts
60
+ uses: actions/download-artifact@v4
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+
65
+ - name: Publish to TestPyPI
66
+ uses: pypa/gh-action-pypi-publish@release/v1
67
+ with:
68
+ repository-url: https://test.pypi.org/legacy/
69
+
70
+ publish-to-pypi:
71
+ name: Publish to PyPI
72
+ needs: build # TestPyPI is optional; don't block production publish
73
+ runs-on: ubuntu-latest
74
+ environment: pypi
75
+ permissions:
76
+ id-token: write
77
+ steps:
78
+ - name: Download dist artifacts
79
+ uses: actions/download-artifact@v4
80
+ with:
81
+ name: dist
82
+ path: dist/
83
+
84
+ - name: Publish to PyPI
85
+ uses: pypa/gh-action-pypi-publish@release/v1
86
+
87
+ smoke-test:
88
+ name: Smoke Test
89
+ needs: publish-to-pypi
90
+ runs-on: ubuntu-latest
91
+ timeout-minutes: 5
92
+ steps:
93
+ - name: Set up Python
94
+ uses: actions/setup-python@v5
95
+ with:
96
+ python-version: "3.x"
97
+
98
+ - name: Install and verify
99
+ run: |
100
+ sleep 30 # allow PyPI index to propagate
101
+ pip install diffguard
102
+ diffguard --version
@@ -0,0 +1,11 @@
1
+ .coverage
2
+ *.pyc
3
+ __pycache__/
4
+ .mypy_cache/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ dist/
8
+ *.egg-info/
9
+ .venv/
10
+ site/
11
+ STRESS_TEST_RESULTS.md
@@ -0,0 +1,8 @@
1
+ - id: diffguard-review
2
+ name: DiffGuard Review
3
+ description: Run diffguard review to detect breaking API changes
4
+ entry: diffguard review HEAD
5
+ language: python
6
+ pass_filenames: false
7
+ stages: [pre-push]
8
+ always_run: true
@@ -0,0 +1,26 @@
1
+ # AGENTS.md — DiffGuard v2
2
+
3
+ ## Module Boundaries
4
+ - `engine/parser.py` — tree-sitter parsing ONLY. No git logic, no matching.
5
+ - `engine/matcher.py` — symbol matching ONLY. Takes old+new symbol lists, returns matches.
6
+ - `engine/classifier.py` — change classification ONLY. Takes matches, returns classified changes.
7
+ - `engine/signatures.py` — signature comparison ONLY. Takes old+new signatures, detects breaking.
8
+ - `engine/summarizer.py` — summary generation ONLY. Takes classified changes, produces tiered text.
9
+ - `schema.py` — Pydantic models. THE contract. Changes require migration notes in PR.
10
+ - `git.py` — all git subprocess calls live here. Nothing else touches git.
11
+ - `languages/{lang}/` — per-language tree-sitter config + queries.scm.
12
+
13
+ ## Conventions
14
+ - Type hints everywhere. `mypy --strict` must pass.
15
+ - Tests mirror source: `test_parser.py` tests `parser.py`, etc.
16
+ - Fixtures: synthetic for unit tests, real-world for integration tests. Both live in `tests/fixtures/`.
17
+ - All JSON output must validate against schema.py models.
18
+ - No print statements — use structured logging (logging module).
19
+ - Functions over classes unless state is genuinely needed.
20
+
21
+ ## What NOT to Do
22
+ - Don't import across engine modules horizontally (parser doesn't import matcher).
23
+ - Don't shell out to git anywhere except `git.py`.
24
+ - Don't add dependencies without justification in PR description.
25
+ - Don't modify schema.py without migration notes.
26
+ - Don't write tests that depend on network or external repos — use fixtures.
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.2] - 2026-02-15
11
+
12
+ ### Fixed
13
+ - Updated package description to match current positioning
14
+ - Changed license field format for hatchling compatibility
15
+
16
+ ### Added
17
+ - GitHub Action for CI integration
18
+
19
+ ## [0.1.1] - 2026-02-11
20
+
21
+ ### Fixed
22
+ - Fixed fabricated README example
23
+
24
+ ### Added
25
+ - Added content verification to release process
26
+
27
+ ## [0.1.0] - 2026-02-11
28
+
29
+ ### Added
30
+ - Initial PyPI release
31
+ - `review` and `summarize` commands
32
+ - Python, TypeScript, and Go support via tree-sitter
@@ -0,0 +1,53 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: ostehost
6
+ Licensed Work: DiffGuard v0.1.1 (and all subsequent versions)
7
+ Additional Use Grant: You may use the Licensed Work for any purpose other
8
+ than offering the functionality of the Licensed Work
9
+ to third parties as a managed or hosted service.
10
+ Change Date: 2029-02-11
11
+ Change License: Apache License 2.0
12
+
13
+ Terms
14
+
15
+ The Licensor hereby grants you the right to copy, modify, create derivative
16
+ works, redistribute, and make non-production use of the Licensed Work. The
17
+ Licensor may make an Additional Use Grant, above, permitting limited
18
+ production use.
19
+
20
+ Effective on the Change Date, or the fourth anniversary of the first publicly
21
+ available distribution of a specific version of the Licensed Work under this
22
+ License, whichever comes first, the Licensor hereby grants you rights under
23
+ the terms of the Change License, and the rights granted in the paragraph
24
+ above terminate.
25
+
26
+ If your use of the Licensed Work does not comply with the requirements
27
+ currently in effect as described in this License, you must purchase a
28
+ commercial license from the Licensor, its affiliated entities, or authorized
29
+ resellers, or you must refrain from using the Licensed Work.
30
+
31
+ All copies of the original and modified Licensed Work, and derivative works
32
+ of the Licensed Work, are subject to this License. This License applies
33
+ separately for each version of the Licensed Work and the Change Date may vary
34
+ for each version of the Licensed Work released by Licensor.
35
+
36
+ You must conspicuously display this License on each original or modified copy
37
+ of the Licensed Work. If you receive the Licensed Work in original or
38
+ modified form from a third party, the terms and conditions set forth in this
39
+ License apply to your use of that work.
40
+
41
+ Any use of the Licensed Work in violation of this License will automatically
42
+ terminate your rights under this License for the current and all other
43
+ versions of the Licensed Work.
44
+
45
+ This License does not grant you any right in any trademark or logo of
46
+ Licensor or its affiliates (provided that you may use a trademark or logo of
47
+ Licensor as expressly required by this License).
48
+
49
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
50
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
51
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
52
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
53
+ TITLE.
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: diffguard
3
+ Version: 0.1.3
4
+ Summary: Catches the structural breaks that pass code review
5
+ Project-URL: Homepage, https://github.com/ostehost/diffguard
6
+ Project-URL: Repository, https://github.com/ostehost/diffguard
7
+ Project-URL: Issues, https://github.com/ostehost/diffguard/issues
8
+ Author: ostehost
9
+ License: BSL-1.1
10
+ License-File: LICENSE
11
+ Keywords: agents,ai,code-intelligence,code-review,git,tree-sitter
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: Other/Proprietary License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Quality Assurance
21
+ Classifier: Topic :: Software Development :: Version Control :: Git
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: click>=8.0
24
+ Requires-Dist: pydantic>=2.0
25
+ Requires-Dist: rich>=13.0
26
+ Requires-Dist: tree-sitter-go
27
+ Requires-Dist: tree-sitter-javascript
28
+ Requires-Dist: tree-sitter-python
29
+ Requires-Dist: tree-sitter-typescript>=0.23.2
30
+ Requires-Dist: tree-sitter>=0.24
31
+ Description-Content-Type: text/markdown
32
+
33
+ [![PyPI](https://img.shields.io/pypi/v/diffguard)](https://pypi.org/project/diffguard/)
34
+ [![License](https://img.shields.io/badge/license-BSL%201.1-blue)](LICENSE)
35
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://pypi.org/project/diffguard/)
36
+
37
+ # DiffGuard
38
+
39
+ **Catches the structural breaks that pass code review.**
40
+
41
+ ## A real bug, in one line
42
+
43
+ This diff shipped in Flask ([PR #5898](https://github.com/pallets/flask/pull/5898)):
44
+
45
+ ```diff
46
+ -def redirect(location, code=302, ...):
47
+ +def redirect(location, code=303, ...):
48
+ ```
49
+
50
+ One line. Looks fine. A reviewer approves it.
51
+
52
+ **The real impact:** 7 endpoints silently change HTTP behavior. POST-to-POST redirects become POST-to-GET. No errors. No warnings. Just broken APIs in production.
53
+
54
+ **DiffGuard catches it:**
55
+
56
+ ```
57
+ $ diffguard review eca5fd1d~1..eca5fd1d
58
+
59
+ ⚠ DiffGuard: 2 changes need review
60
+
61
+ DEFAULT VALUE CHANGED: redirect(location, code=302) → redirect(location, code=303)
62
+ src/flask/helpers.py:241 — 7 callers rely on the default
63
+
64
+ DEFAULT VALUE CHANGED: App.redirect(self, location, code=302) → App.redirect(self, location, code=303)
65
+ src/flask/sansio/app.py:935 — 7 callers rely on the default
66
+ ```
67
+
68
+ Tree-sitter AST analysis. No LLM. No network calls. Runs in seconds.
69
+
70
+ ## What it catches
71
+
72
+ Function signature changes, removed/renamed symbols, default value changes — and shows you every caller affected.
73
+
74
+ ## What it doesn't catch
75
+
76
+ Logic bugs, behavioral changes beyond signatures, performance issues, security vulnerabilities. DiffGuard detects **structural breaks**, not all bugs.
77
+
78
+ When there's nothing structural to report, it stays silent (exit code 0, no output).
79
+
80
+ ## Quick Start
81
+
82
+ ```bash
83
+ pip install diffguard
84
+ diffguard review main..feature
85
+ ```
86
+
87
+ Exit codes: `0` = nothing noteworthy, `1` = findings, `2` = error.
88
+
89
+ ## How It Works
90
+
91
+ 1. **Parses the diff** using tree-sitter AST analysis (not regex)
92
+ 2. **Extracts symbols** — functions, classes, signatures
93
+ 3. **Detects high-signal changes** — signature changes, removed symbols, default value changes
94
+ 4. **Scans for callers** — finds every file that references changed symbols
95
+ 5. **Outputs actionable context** — or stays silent if nothing matters
96
+
97
+ ## Agent Integration
98
+
99
+ Works with **Claude Code**, **Cursor**, **GitHub Actions**, or any agent that can run a CLI command.
100
+
101
+ Add one line to your agent config — DiffGuard is silent when nothing matters.
102
+
103
+ See the full [Agent Integration Guide](docs/agent-integration.md) for hooks, CI patterns, and snippets for [Claude Code](docs/claude-md-snippet.md) and [Cursor](docs/cursor-rule-snippet.md).
104
+
105
+ ## GitHub Action
106
+
107
+ ```yaml
108
+ # .github/workflows/diffguard.yml
109
+ name: DiffGuard PR Review
110
+ on:
111
+ pull_request:
112
+ types: [opened, synchronize, reopened]
113
+ permissions:
114
+ contents: read
115
+ pull-requests: write
116
+ jobs:
117
+ diffguard:
118
+ runs-on: ubuntu-latest
119
+ steps:
120
+ - uses: actions/checkout@v4
121
+ with:
122
+ fetch-depth: 0
123
+ - uses: ostehost/diffguard@main
124
+ ```
125
+
126
+ ## Languages
127
+
128
+ - **Python** (most mature — extensive real-world validation)
129
+ - TypeScript / JavaScript
130
+ - Go
131
+ - More planned (Rust, Java, C#)
132
+
133
+ ## Philosophy
134
+
135
+ 1. **Silence is a feature.** No findings? No output. Most diffs don't need structural analysis.
136
+ 2. **Local-first.** Your code never leaves your machine. No SaaS, no API keys, no accounts.
137
+ 3. **Agent-native.** CLI + JSON output. `pip install` and go.
138
+ 4. **Precision over recall.** We'd rather miss a minor issue than cry wolf on every PR.
139
+
140
+ ## License
141
+
142
+ BSL 1.1 — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,110 @@
1
+ [![PyPI](https://img.shields.io/pypi/v/diffguard)](https://pypi.org/project/diffguard/)
2
+ [![License](https://img.shields.io/badge/license-BSL%201.1-blue)](LICENSE)
3
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://pypi.org/project/diffguard/)
4
+
5
+ # DiffGuard
6
+
7
+ **Catches the structural breaks that pass code review.**
8
+
9
+ ## A real bug, in one line
10
+
11
+ This diff shipped in Flask ([PR #5898](https://github.com/pallets/flask/pull/5898)):
12
+
13
+ ```diff
14
+ -def redirect(location, code=302, ...):
15
+ +def redirect(location, code=303, ...):
16
+ ```
17
+
18
+ One line. Looks fine. A reviewer approves it.
19
+
20
+ **The real impact:** 7 endpoints silently change HTTP behavior. POST-to-POST redirects become POST-to-GET. No errors. No warnings. Just broken APIs in production.
21
+
22
+ **DiffGuard catches it:**
23
+
24
+ ```
25
+ $ diffguard review eca5fd1d~1..eca5fd1d
26
+
27
+ ⚠ DiffGuard: 2 changes need review
28
+
29
+ DEFAULT VALUE CHANGED: redirect(location, code=302) → redirect(location, code=303)
30
+ src/flask/helpers.py:241 — 7 callers rely on the default
31
+
32
+ DEFAULT VALUE CHANGED: App.redirect(self, location, code=302) → App.redirect(self, location, code=303)
33
+ src/flask/sansio/app.py:935 — 7 callers rely on the default
34
+ ```
35
+
36
+ Tree-sitter AST analysis. No LLM. No network calls. Runs in seconds.
37
+
38
+ ## What it catches
39
+
40
+ Function signature changes, removed/renamed symbols, default value changes — and shows you every caller affected.
41
+
42
+ ## What it doesn't catch
43
+
44
+ Logic bugs, behavioral changes beyond signatures, performance issues, security vulnerabilities. DiffGuard detects **structural breaks**, not all bugs.
45
+
46
+ When there's nothing structural to report, it stays silent (exit code 0, no output).
47
+
48
+ ## Quick Start
49
+
50
+ ```bash
51
+ pip install diffguard
52
+ diffguard review main..feature
53
+ ```
54
+
55
+ Exit codes: `0` = nothing noteworthy, `1` = findings, `2` = error.
56
+
57
+ ## How It Works
58
+
59
+ 1. **Parses the diff** using tree-sitter AST analysis (not regex)
60
+ 2. **Extracts symbols** — functions, classes, signatures
61
+ 3. **Detects high-signal changes** — signature changes, removed symbols, default value changes
62
+ 4. **Scans for callers** — finds every file that references changed symbols
63
+ 5. **Outputs actionable context** — or stays silent if nothing matters
64
+
65
+ ## Agent Integration
66
+
67
+ Works with **Claude Code**, **Cursor**, **GitHub Actions**, or any agent that can run a CLI command.
68
+
69
+ Add one line to your agent config — DiffGuard is silent when nothing matters.
70
+
71
+ See the full [Agent Integration Guide](docs/agent-integration.md) for hooks, CI patterns, and snippets for [Claude Code](docs/claude-md-snippet.md) and [Cursor](docs/cursor-rule-snippet.md).
72
+
73
+ ## GitHub Action
74
+
75
+ ```yaml
76
+ # .github/workflows/diffguard.yml
77
+ name: DiffGuard PR Review
78
+ on:
79
+ pull_request:
80
+ types: [opened, synchronize, reopened]
81
+ permissions:
82
+ contents: read
83
+ pull-requests: write
84
+ jobs:
85
+ diffguard:
86
+ runs-on: ubuntu-latest
87
+ steps:
88
+ - uses: actions/checkout@v4
89
+ with:
90
+ fetch-depth: 0
91
+ - uses: ostehost/diffguard@main
92
+ ```
93
+
94
+ ## Languages
95
+
96
+ - **Python** (most mature — extensive real-world validation)
97
+ - TypeScript / JavaScript
98
+ - Go
99
+ - More planned (Rust, Java, C#)
100
+
101
+ ## Philosophy
102
+
103
+ 1. **Silence is a feature.** No findings? No output. Most diffs don't need structural analysis.
104
+ 2. **Local-first.** Your code never leaves your machine. No SaaS, no API keys, no accounts.
105
+ 3. **Agent-native.** CLI + JSON output. `pip install` and go.
106
+ 4. **Precision over recall.** We'd rather miss a minor issue than cry wolf on every PR.
107
+
108
+ ## License
109
+
110
+ BSL 1.1 — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,67 @@
1
+ # Release Process
2
+
3
+ ## Roles
4
+
5
+ **Release owner:** Oste (manager). Tags, pushes, monitors CI, verifies install.
6
+ When a release agent exists, it takes over this role.
7
+
8
+ ## Release Blockers
9
+
10
+ A release MUST NOT ship if any of these are true:
11
+ - Tests fail (`pytest`)
12
+ - Linting fails (`ruff check src/ tests/`)
13
+ - Version not bumped in `pyproject.toml`
14
+ - README.md is stale or misaligned with current positioning
15
+
16
+ ## Pre-release Checklist
17
+
18
+ - [ ] All tests pass (`pytest`)
19
+ - [ ] Linting passes (`ruff check src/ tests/`)
20
+ - [ ] Version bumped in `pyproject.toml`
21
+ - [ ] README.md examples verified against real tool output (run every example command, diff against what's written)
22
+ - [ ] Docs examples verified against real tool output
23
+ - [ ] CHANGELOG updated (if maintained)
24
+
25
+ ## Cutting a Release
26
+
27
+ ```bash
28
+ # 1. Bump version in pyproject.toml
29
+ # e.g. version = "0.2.0"
30
+
31
+ # 2. Commit the version bump
32
+ git commit -am "release: v0.2.0"
33
+
34
+ # 3. Create the tag
35
+ git tag v0.2.0
36
+
37
+ # 4. Push commit and tag
38
+ git push origin main --tags
39
+
40
+ # 5. GitHub Actions handles:
41
+ # build & test → TestPyPI → PyPI → smoke test
42
+
43
+ # 6. Verify the release
44
+ pip install diffguard && diffguard --version
45
+ ```
46
+
47
+ The workflow uses [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (OIDC) — no API tokens needed.
48
+
49
+ ## Post-release Checklist
50
+
51
+ - [ ] Verify package on [pypi.org/project/diffguard](https://pypi.org/project/diffguard/)
52
+ - [ ] Test install in a clean venv:
53
+ ```bash
54
+ python -m venv /tmp/test-diffguard && source /tmp/test-diffguard/bin/activate
55
+ pip install diffguard
56
+ diffguard --version
57
+ deactivate && rm -rf /tmp/test-diffguard
58
+ ```
59
+ - [ ] Create a [GitHub Release](https://github.com/ostehost/diffguard/releases/new) from the tag (optional)
60
+
61
+ ## Rollback
62
+
63
+ PyPI does not allow re-uploading the same version. If a broken package ships:
64
+
65
+ 1. **Yank the version:** `pip install diffguard` won't grab yanked versions by default
66
+ 2. **Bump to patch version** (e.g., v0.1.0 → v0.1.1) with the fix
67
+ 3. **Cut a patch release** following the same process above