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.
- diffguard-0.1.3/.github/pull_request_template.md +11 -0
- diffguard-0.1.3/.github/workflows/ci.yml +13 -0
- diffguard-0.1.3/.github/workflows/deploy-docs.yml +21 -0
- diffguard-0.1.3/.github/workflows/docs.yml +24 -0
- diffguard-0.1.3/.github/workflows/publish-to-pypi.yml +102 -0
- diffguard-0.1.3/.gitignore +11 -0
- diffguard-0.1.3/.pre-commit-hooks.yaml +8 -0
- diffguard-0.1.3/AGENTS.md +26 -0
- diffguard-0.1.3/CHANGELOG.md +32 -0
- diffguard-0.1.3/LICENSE +53 -0
- diffguard-0.1.3/PKG-INFO +142 -0
- diffguard-0.1.3/README.md +110 -0
- diffguard-0.1.3/RELEASE.md +67 -0
- diffguard-0.1.3/action.yml +107 -0
- diffguard-0.1.3/docs/agent-integration.md +164 -0
- diffguard-0.1.3/docs/architecture.md +182 -0
- diffguard-0.1.3/docs/claude-md-snippet.md +41 -0
- diffguard-0.1.3/docs/cursor-rule-snippet.md +34 -0
- diffguard-0.1.3/docs/decisions/001-cli-first.md +27 -0
- diffguard-0.1.3/docs/decisions/002-selective-trigger.md +29 -0
- diffguard-0.1.3/docs/decisions/003-bsl-1-1-license.md +31 -0
- diffguard-0.1.3/docs/decisions/004-local-first.md +21 -0
- diffguard-0.1.3/docs/decisions/README.md +43 -0
- diffguard-0.1.3/docs/how-it-works.md +87 -0
- diffguard-0.1.3/docs/index.md +132 -0
- diffguard-0.1.3/docs/llms-ctx.txt +1119 -0
- diffguard-0.1.3/docs/llms.txt +18 -0
- diffguard-0.1.3/docs/quickstart.md +152 -0
- diffguard-0.1.3/docs/real-world-catches.md +151 -0
- diffguard-0.1.3/docs/roadmap.md +98 -0
- diffguard-0.1.3/docs/schema.md +169 -0
- diffguard-0.1.3/docs/validation.md +84 -0
- diffguard-0.1.3/examples/diffguard-workflow.yml +23 -0
- diffguard-0.1.3/mkdocs.yml +47 -0
- diffguard-0.1.3/pyproject.toml +71 -0
- diffguard-0.1.3/scripts/ab_oracle_lite.py +151 -0
- diffguard-0.1.3/scripts/build-llms-ctx.sh +21 -0
- diffguard-0.1.3/src/diffguard/__init__.py +3 -0
- diffguard-0.1.3/src/diffguard/cli.py +676 -0
- diffguard-0.1.3/src/diffguard/engine/__init__.py +1 -0
- diffguard-0.1.3/src/diffguard/engine/_types.py +36 -0
- diffguard-0.1.3/src/diffguard/engine/classifier.py +87 -0
- diffguard-0.1.3/src/diffguard/engine/deps.py +204 -0
- diffguard-0.1.3/src/diffguard/engine/matcher.py +128 -0
- diffguard-0.1.3/src/diffguard/engine/parser.py +47 -0
- diffguard-0.1.3/src/diffguard/engine/pipeline.py +205 -0
- diffguard-0.1.3/src/diffguard/engine/signatures.py +248 -0
- diffguard-0.1.3/src/diffguard/engine/summarizer.py +412 -0
- diffguard-0.1.3/src/diffguard/git.py +323 -0
- diffguard-0.1.3/src/diffguard/languages/__init__.py +52 -0
- diffguard-0.1.3/src/diffguard/languages/_utils.py +13 -0
- diffguard-0.1.3/src/diffguard/languages/go/__init__.py +101 -0
- diffguard-0.1.3/src/diffguard/languages/go/queries.scm +1 -0
- diffguard-0.1.3/src/diffguard/languages/python/__init__.py +204 -0
- diffguard-0.1.3/src/diffguard/languages/python/queries.scm +1 -0
- diffguard-0.1.3/src/diffguard/languages/typescript/__init__.py +184 -0
- diffguard-0.1.3/src/diffguard/languages/typescript/queries.scm +1 -0
- diffguard-0.1.3/src/diffguard/schema.py +97 -0
- diffguard-0.1.3/tests/__init__.py +0 -0
- diffguard-0.1.3/tests/ab_test_matrix.md +37 -0
- diffguard-0.1.3/tests/benchmarks/__init__.py +0 -0
- diffguard-0.1.3/tests/benchmarks/conftest.py +11 -0
- diffguard-0.1.3/tests/benchmarks/test_performance.py +117 -0
- diffguard-0.1.3/tests/benchmarks/test_pipeline_benchmark.py +112 -0
- diffguard-0.1.3/tests/conftest.py +26 -0
- diffguard-0.1.3/tests/fixtures/real_world/.gitkeep +0 -0
- diffguard-0.1.3/tests/fixtures/synthetic/.gitkeep +0 -0
- diffguard-0.1.3/tests/fixtures/synthetic/multi_file_refactor.json +51 -0
- diffguard-0.1.3/tests/fixtures/synthetic/signature_change.json +35 -0
- diffguard-0.1.3/tests/fixtures/synthetic/simple_function_add.json +21 -0
- diffguard-0.1.3/tests/test_classifier.py +152 -0
- diffguard-0.1.3/tests/test_cli.py +160 -0
- diffguard-0.1.3/tests/test_context_cli.py +218 -0
- diffguard-0.1.3/tests/test_deps.py +171 -0
- diffguard-0.1.3/tests/test_git.py +287 -0
- diffguard-0.1.3/tests/test_languages/__init__.py +31 -0
- diffguard-0.1.3/tests/test_languages/test_go.py +55 -0
- diffguard-0.1.3/tests/test_languages/test_python.py +80 -0
- diffguard-0.1.3/tests/test_languages/test_typescript.py +67 -0
- diffguard-0.1.3/tests/test_matcher.py +139 -0
- diffguard-0.1.3/tests/test_parser.py +154 -0
- diffguard-0.1.3/tests/test_pipeline.py +213 -0
- diffguard-0.1.3/tests/test_richer_categories.py +182 -0
- diffguard-0.1.3/tests/test_schema.py +187 -0
- diffguard-0.1.3/tests/test_signatures.py +64 -0
- diffguard-0.1.3/tests/test_summarizer.py +321 -0
- diffguard-0.1.3/uv.lock +1208 -0
|
@@ -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,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
|
diffguard-0.1.3/LICENSE
ADDED
|
@@ -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.
|
diffguard-0.1.3/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://pypi.org/project/diffguard/)
|
|
34
|
+
[](LICENSE)
|
|
35
|
+
[](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
|
+
[](https://pypi.org/project/diffguard/)
|
|
2
|
+
[](LICENSE)
|
|
3
|
+
[](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
|