gerenuk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. gerenuk-0.1.0/.claude/settings.json +2 -0
  2. gerenuk-0.1.0/.claude/skills/rust-review/SKILL.md +95 -0
  3. gerenuk-0.1.0/.github/dependabot.yml +38 -0
  4. gerenuk-0.1.0/.github/workflows/audit.yml +29 -0
  5. gerenuk-0.1.0/.github/workflows/cargo-deny.yml +35 -0
  6. gerenuk-0.1.0/.github/workflows/codeql.yml +42 -0
  7. gerenuk-0.1.0/.github/workflows/docs.yml +57 -0
  8. gerenuk-0.1.0/.github/workflows/release.yml +114 -0
  9. gerenuk-0.1.0/.github/workflows/review.yml +84 -0
  10. gerenuk-0.1.0/.gitignore +81 -0
  11. gerenuk-0.1.0/CLAUDE.md +105 -0
  12. gerenuk-0.1.0/Cargo.lock +817 -0
  13. gerenuk-0.1.0/Cargo.toml +86 -0
  14. gerenuk-0.1.0/LICENSE +21 -0
  15. gerenuk-0.1.0/Makefile +76 -0
  16. gerenuk-0.1.0/PKG-INFO +161 -0
  17. gerenuk-0.1.0/README.md +133 -0
  18. gerenuk-0.1.0/clippy.toml +10 -0
  19. gerenuk-0.1.0/deny.toml +27 -0
  20. gerenuk-0.1.0/docs/book.toml +23 -0
  21. gerenuk-0.1.0/docs/custom.css +260 -0
  22. gerenuk-0.1.0/docs/dev/ARCHITECTURE.md +103 -0
  23. gerenuk-0.1.0/docs/gen-version.sh +34 -0
  24. gerenuk-0.1.0/docs/generate-llms-txt.sh +56 -0
  25. gerenuk-0.1.0/docs/inject-shared.sh +100 -0
  26. gerenuk-0.1.0/docs/lint-mermaid.mjs +76 -0
  27. gerenuk-0.1.0/docs/mermaid-init.js +45 -0
  28. gerenuk-0.1.0/docs/mermaid.min.js +2609 -0
  29. gerenuk-0.1.0/docs/package-lock.json +173 -0
  30. gerenuk-0.1.0/docs/package.json +10 -0
  31. gerenuk-0.1.0/docs/plans/gerenuk-pitch-phase1-diff-to-symbols.md +180 -0
  32. gerenuk-0.1.0/docs/plans/phase1-implementation-plan.md +191 -0
  33. gerenuk-0.1.0/docs/shared/claude-snippet.md +15 -0
  34. gerenuk-0.1.0/docs/src/SUMMARY.md +11 -0
  35. gerenuk-0.1.0/docs/src/commands/audit.md +83 -0
  36. gerenuk-0.1.0/docs/src/commands/changed-symbols.md +168 -0
  37. gerenuk-0.1.0/docs/src/commands/doctor.md +26 -0
  38. gerenuk-0.1.0/docs/src/commands/overview.md +35 -0
  39. gerenuk-0.1.0/docs/src/how-it-works.md +83 -0
  40. gerenuk-0.1.0/docs/src/index.md +39 -0
  41. gerenuk-0.1.0/docs/src/setup.md +71 -0
  42. gerenuk-0.1.0/docs/src/troubleshooting.md +74 -0
  43. gerenuk-0.1.0/docs/theme/head.hbs +3 -0
  44. gerenuk-0.1.0/docs/version.js +24 -0
  45. gerenuk-0.1.0/prek.toml +38 -0
  46. gerenuk-0.1.0/pyproject.toml +44 -0
  47. gerenuk-0.1.0/release.toml +25 -0
  48. gerenuk-0.1.0/rust-toolchain.toml +3 -0
  49. gerenuk-0.1.0/rustfmt.toml +3 -0
  50. gerenuk-0.1.0/src/analyze.rs +367 -0
  51. gerenuk-0.1.0/src/changed.rs +1176 -0
  52. gerenuk-0.1.0/src/cli.rs +300 -0
  53. gerenuk-0.1.0/src/config.rs +192 -0
  54. gerenuk-0.1.0/src/diff.rs +432 -0
  55. gerenuk-0.1.0/src/git.rs +447 -0
  56. gerenuk-0.1.0/src/lib.rs +26 -0
  57. gerenuk-0.1.0/src/main.rs +42 -0
  58. gerenuk-0.1.0/src/model.rs +310 -0
  59. gerenuk-0.1.0/src/modpath.rs +207 -0
  60. gerenuk-0.1.0/src/pysource.rs +462 -0
  61. gerenuk-0.1.0/src/report.rs +187 -0
  62. gerenuk-0.1.0/src/tyf.rs +228 -0
  63. gerenuk-0.1.0/src/workspace.rs +163 -0
  64. gerenuk-0.1.0/tests/audit.rs +206 -0
  65. gerenuk-0.1.0/tests/changed_symbols.rs +521 -0
  66. gerenuk-0.1.0/tests/cli.rs +115 -0
  67. gerenuk-0.1.0/tests/common/mod.rs +223 -0
  68. gerenuk-0.1.0/tests/fixtures/sample_pkg/pyproject.toml +16 -0
  69. gerenuk-0.1.0/tests/fixtures/sample_pkg/sample_pkg/__init__.py +12 -0
  70. gerenuk-0.1.0/tests/fixtures/sample_pkg/sample_pkg/cli.py +28 -0
  71. gerenuk-0.1.0/tests/fixtures/sample_pkg/sample_pkg/models.py +38 -0
  72. gerenuk-0.1.0/tests/fixtures/sample_pkg/sample_pkg/service.py +45 -0
  73. gerenuk-0.1.0/tests/fixtures/sample_pkg/tests/__init__.py +0 -0
  74. gerenuk-0.1.0/tests/fixtures/sample_pkg/tests/test_models.py +34 -0
  75. gerenuk-0.1.0/tests/fixtures/sample_pkg/tests/test_service.py +32 -0
  76. gerenuk-0.1.0/tests/fixtures/sample_pkg/uv.lock +155 -0
  77. gerenuk-0.1.0/tests/snapshots/changed_symbols__the_full_json_report_matches_its_snapshot.snap +31 -0
@@ -0,0 +1,2 @@
1
+ {
2
+ }
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: rust-review
3
+ context: fork
4
+ description: Deep Rust code quality review. Auto-invoke when finishing a task, before marking work complete, when the user asks to review code, or when preparing a PR. Covers error handling, async correctness, duplicated logic, test quality, performance patterns, idiomatic Rust, docs-code alignment, and API design beyond what clippy catches.
5
+ ---
6
+
7
+ # Deep Code Review for gerenuk
8
+
9
+ Perform a thorough code quality review of the changes in this project. Go beyond what clippy and rustfmt catch. Focus on the areas below and report findings grouped by severity: 🔴 Must Fix, 🟡 Should Fix, 🟢 Suggestion.
10
+
11
+ First, run `cargo fmt --all -- --check` and `cargo clippy --all-targets --all-features -- -D warnings` to confirm the automated checks pass. If they don't, fix those first before proceeding with the deep review.
12
+
13
+ ## 1. Error Handling Quality
14
+
15
+ - `.unwrap()` outside tests is forbidden. Flag any instance.
16
+ - Check that `.context("message")` is used when propagating errors from external crates with `?` — bare `?` loses context about what operation failed.
17
+ - Flag `Result` return types where the function never actually returns `Err` (remove the wrapper — clippy catches this as `unnecessary_wraps` but double-check).
18
+ - Flag `.map_err(|_| ...)` that silently discards error information.
19
+ - Verify error types are specific enough — no `String` as error type, no `anyhow::Error` leaking into library-style modules.
20
+ - Check that `thiserror` error variants have `#[from]` or manual `From` impls for their sources.
21
+
22
+ ## 2. Async / Tokio Correctness
23
+
24
+ - Flag any `MutexGuard` or `RwLockGuard` held across an `.await` point — this can deadlock or prevent `Send` futures.
25
+ - Flag blocking operations (std::fs, std::process::Command, heavy computation) running directly in async context without `spawn_blocking`.
26
+ - Check that spawned tasks (`tokio::spawn`) have their `JoinHandle` either awaited or explicitly dropped with a comment explaining why.
27
+ - Verify `tokio::select!` branches handle cancellation correctly (partially completed work).
28
+ - Check for unbounded channels or queues that could cause memory issues under load.
29
+
30
+ ## 3. Duplicated Logic
31
+
32
+ - Search for functions or code blocks that do substantially the same thing with minor variations. Flag them and suggest extraction into a shared function, trait, or generic.
33
+ - Pay special attention to:
34
+ - LSP message construction patterns (likely repeated across commands)
35
+ - Error handling boilerplate that could be a helper
36
+ - Serialization/deserialization patterns
37
+ - Similar match arms across different functions
38
+ - Suggest concrete refactoring: which function to extract, what parameters it should take.
39
+
40
+ ## 4. Test Quality
41
+
42
+ - **Tests that don't assert**: Flag any test that just calls code without asserting on the result. Running without panicking is not a test.
43
+ - **Tests that assert too little**: `assert!(result.is_ok())` without checking the value inside is weak. Suggest asserting on the actual content.
44
+ - **Overly verbose tests**: Tests with excessive setup that obscure what's actually being tested. Suggest extracting test helpers or fixtures.
45
+ - **Missing edge cases**: For each public function, check if tests cover: empty input, error paths, boundary conditions.
46
+ - **Test naming**: Test names should describe the behavior being tested, not the implementation (`test_returns_error_on_missing_file` not `test_function_xyz`).
47
+ - **Integration vs unit**: Verify that unit tests (in `#[cfg(test)] mod tests`) test internal logic, while `tests/integration/` tests exercise the public CLI interface.
48
+ - Suggest adding `proptest` for any function that transforms data (roundtrip properties).
49
+
50
+ ## 5. Performance Patterns
51
+
52
+ - Flag unnecessary `.clone()` — suggest borrowing or restructuring ownership.
53
+ - Flag `.collect::<Vec<_>>()` immediately followed by `.iter()` — the intermediate collection is usually unnecessary.
54
+ - Prefer `&str` over `String`, `&[T]` over `Vec<T>`, `&Path` over `PathBuf` in function parameters when ownership isn't needed.
55
+ - Flag large structs being passed by value instead of reference.
56
+ - Check for `Arc::clone(&x)` vs `x.clone()` — the former is required by our lint config for clarity.
57
+ - In async code, flag futures that capture large amounts of data across await points.
58
+
59
+ ## 6. Idiomatic Rust
60
+
61
+ - Prefer iterator chains over manual `for` loops with `push`.
62
+ - Use `if let` / `let else` instead of `match` with a single interesting arm and a wildcard.
63
+ - Suggest `impl From<X> for Y` instead of standalone conversion functions.
64
+ - Suggest `Display` implementations instead of `to_string()` methods.
65
+ - Flag `&String`, `&Vec<T>`, `&Box<T>` in function signatures — use `&str`, `&[T]`, `&T` instead.
66
+ - Check doc comments on all public types and functions.
67
+
68
+ ## 7. Documentation ↔ Code Alignment
69
+
70
+ - **Check that `docs/src/how-it-works.md` still matches the actual architecture.** In particular:
71
+ - Do the diagrams (daemon layers, request lifecycle, client pool flow) reflect the current code paths in `src/daemon/` and `src/lsp/`?
72
+ - Are the RPC method names listed in the "Available RPC methods" table still accurate? Cross-check against the actual enum/dispatch in the daemon server code.
73
+ - Do the described concurrency patterns (single-pipe, batch processing, sequential LSP requests) still hold?
74
+ - Are timeouts, retry counts, and backoff intervals mentioned in prose consistent with the constants in the code?
75
+ - **Check that `docs/src/commands/*.md` describe the current CLI interface.** Compare the documented flags and subcommands against the `clap` definitions in `src/cli/`.
76
+ - **Check that `docs/src/performance.md` benchmarks and claims are not contradicted by recent changes.**
77
+ - Flag any mismatch as 🔴 Must Fix — stale docs are worse than no docs.
78
+
79
+ ## 8. API and Module Design
80
+
81
+ - Is `main.rs` thin? It should just parse args and call into library code.
82
+ - Are module boundaries clean? Each module should have a clear responsibility.
83
+ - Check for any `pub` items that don't need to be public.
84
+ - Flag circular dependencies between modules.
85
+
86
+ ## Output Format
87
+
88
+ Group all findings by severity, then by area. For each finding:
89
+ - State **what** the issue is and **where** (file:line or function name)
90
+ - Explain **why** it matters
91
+ - Suggest a **concrete fix** (not just "improve this")
92
+
93
+ End with a summary: X must-fix, Y should-fix, Z suggestions.
94
+
95
+ $ARGUMENTS
@@ -0,0 +1,38 @@
1
+ version: 2
2
+ updates:
3
+ # Rust (Cargo) dependencies
4
+ - package-ecosystem: "cargo"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "weekly"
8
+ day: "monday"
9
+ open-pull-requests-limit: 10
10
+ labels:
11
+ - "dependencies"
12
+ - "rust"
13
+ groups:
14
+ minor-and-patch:
15
+ update-types:
16
+ - "minor"
17
+ - "patch"
18
+
19
+ # GitHub Actions
20
+ - package-ecosystem: "github-actions"
21
+ directory: "/"
22
+ schedule:
23
+ interval: "weekly"
24
+ labels:
25
+ - "dependencies"
26
+ - "ci"
27
+
28
+ # Python dependencies. `ty-find` is a runtime dependency in pyproject.toml,
29
+ # and it is the project gerenuk drives, so a new release matters here.
30
+ # Weekly is enough — there is no lockfile at the root to keep in step.
31
+ - package-ecosystem: "pip"
32
+ directory: "/"
33
+ schedule:
34
+ interval: "weekly"
35
+ open-pull-requests-limit: 10
36
+ labels:
37
+ - "dependencies"
38
+ - "python"
@@ -0,0 +1,29 @@
1
+ name: "Cargo Audit"
2
+
3
+ on:
4
+ push:
5
+ paths:
6
+ - "Cargo.toml"
7
+ - "Cargo.lock"
8
+ pull_request:
9
+ paths:
10
+ - "Cargo.toml"
11
+ - "Cargo.lock"
12
+ schedule:
13
+ - cron: "0 0 * * *" # Daily at midnight UTC
14
+ workflow_dispatch:
15
+
16
+ permissions:
17
+ contents: read
18
+ issues: write
19
+
20
+ jobs:
21
+ audit:
22
+ name: Security Audit
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Checkout repository
26
+ uses: actions/checkout@v7
27
+
28
+ - name: Run cargo-audit
29
+ uses: actions-rust-lang/audit@v1
@@ -0,0 +1,35 @@
1
+ name: "Cargo Deny"
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ paths:
7
+ - "Cargo.lock"
8
+ - "Cargo.toml"
9
+ - "deny.toml"
10
+ pull_request:
11
+ paths:
12
+ - "Cargo.lock"
13
+ - "Cargo.toml"
14
+ - "deny.toml"
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ deny:
21
+ name: ${{ matrix.check }}
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ check: [bans, licenses, sources]
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v7
30
+
31
+ - name: Run cargo-deny (${{ matrix.check }})
32
+ uses: EmbarkStudios/cargo-deny-action@v2
33
+ with:
34
+ command: check ${{ matrix.check }}
35
+ arguments: --all-features
@@ -0,0 +1,42 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ paths:
7
+ - "src/**"
8
+ - "Cargo.toml"
9
+ - "Cargo.lock"
10
+ pull_request:
11
+ branches: ["main"]
12
+ paths:
13
+ - "src/**"
14
+ - "Cargo.toml"
15
+ - "Cargo.lock"
16
+ schedule:
17
+ - cron: "0 6 * * 1" # Weekly on Monday at 06:00 UTC
18
+
19
+ permissions:
20
+ security-events: write
21
+ packages: read
22
+ actions: read
23
+ contents: read
24
+
25
+ jobs:
26
+ analyze:
27
+ name: Analyze Rust
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - name: Checkout repository
31
+ uses: actions/checkout@v7
32
+
33
+ - name: Initialize CodeQL
34
+ uses: github/codeql-action/init@v4
35
+ with:
36
+ languages: rust
37
+ build-mode: none
38
+
39
+ - name: Perform CodeQL Analysis
40
+ uses: github/codeql-action/analyze@v4
41
+ with:
42
+ category: "/language:rust"
@@ -0,0 +1,57 @@
1
+ name: Deploy docs
2
+ on:
3
+ push:
4
+ branches: ["main"]
5
+ paths:
6
+ - "docs/**"
7
+ - "src/**" # rebuild when CLI changes (help text may change)
8
+
9
+ permissions:
10
+ contents: read
11
+ pages: write
12
+ id-token: write
13
+
14
+ concurrency:
15
+ group: "pages"
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ deploy:
20
+ runs-on: ubuntu-latest
21
+ environment:
22
+ name: github-pages
23
+ url: ${{ steps.deployment.outputs.page_url }}
24
+ steps:
25
+ - uses: actions/checkout@v7
26
+
27
+ - uses: actions/setup-node@v6
28
+ with:
29
+ node-version: "22"
30
+
31
+ - name: Lint mermaid diagrams
32
+ run: |
33
+ cd docs && npm ci --silent
34
+ cd .. && node docs/lint-mermaid.mjs
35
+
36
+ - name: Install mdBook and preprocessors
37
+ run: |
38
+ mkdir -p bin
39
+ curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.5.2/mdbook-v0.5.2-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
40
+ curl -sSL https://github.com/badboy/mdbook-mermaid/releases/download/v0.17.0/mdbook-mermaid-v0.17.0-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
41
+ echo "$PWD/bin" >> $GITHUB_PATH
42
+
43
+ - name: Sync docs version badge with Cargo.toml
44
+ run: bash docs/gen-version.sh
45
+
46
+ - name: Build book
47
+ run: mdbook build docs
48
+
49
+ - name: Generate llms.txt
50
+ run: bash docs/generate-llms-txt.sh
51
+
52
+ - uses: actions/upload-pages-artifact@v5
53
+ with:
54
+ path: docs/book/html
55
+
56
+ - uses: actions/deploy-pages@v5
57
+ id: deployment
@@ -0,0 +1,114 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: write
11
+
12
+ jobs:
13
+ build-wheels:
14
+ name: Build wheels on ${{ matrix.os }}
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ matrix:
18
+ os: [ubuntu-latest, macos-14, windows-latest]
19
+
20
+ steps:
21
+ - uses: actions/checkout@v7
22
+
23
+ - name: Build wheels
24
+ uses: PyO3/maturin-action@v1
25
+ with:
26
+ args: --release --strip
27
+ manylinux: 2_28
28
+
29
+ - name: Upload wheels
30
+ uses: actions/upload-artifact@v7
31
+ with:
32
+ name: wheels-${{ matrix.os }}
33
+ path: target/wheels/*.whl
34
+
35
+ build-sdist:
36
+ name: Build source distribution
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@v7
40
+
41
+ - name: Set up Rust
42
+ uses: dtolnay/rust-toolchain@stable
43
+
44
+ - name: Set up Python
45
+ uses: actions/setup-python@v6
46
+ with:
47
+ python-version: '3.11'
48
+
49
+ - name: Install maturin
50
+ run: pip install maturin
51
+
52
+ - name: Build sdist
53
+ run: maturin sdist
54
+
55
+ - name: Upload sdist
56
+ uses: actions/upload-artifact@v7
57
+ with:
58
+ name: sdist
59
+ path: target/wheels/*.tar.gz
60
+
61
+ create-release:
62
+ name: Create GitHub Release
63
+ needs: [build-wheels, build-sdist]
64
+ runs-on: ubuntu-latest
65
+ if: startsWith(github.ref, 'refs/tags/')
66
+ steps:
67
+ - uses: actions/checkout@v7
68
+
69
+ - name: Download all artifacts
70
+ uses: actions/download-artifact@v8
71
+ with:
72
+ path: dist
73
+
74
+ - name: Flatten artifacts
75
+ run: |
76
+ mkdir -p wheels
77
+ find dist -name '*.whl' -exec cp {} wheels/ \;
78
+ find dist -name '*.tar.gz' -exec cp {} wheels/ \;
79
+
80
+ - name: Create Release
81
+ uses: softprops/action-gh-release@v3
82
+ with:
83
+ files: wheels/*
84
+ generate_release_notes: true
85
+ draft: false
86
+ prerelease: false
87
+
88
+ publish-to-pypi:
89
+ name: Publish to PyPI
90
+ needs: [build-wheels, build-sdist, create-release]
91
+ runs-on: ubuntu-latest
92
+ if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
93
+ environment:
94
+ name: pypi
95
+ url: https://pypi.org/p/gerenuk
96
+ permissions:
97
+ id-token: write
98
+ steps:
99
+ - name: Download all artifacts
100
+ uses: actions/download-artifact@v8
101
+ with:
102
+ path: dist
103
+
104
+ - name: Flatten artifacts
105
+ run: |
106
+ mkdir -p wheels
107
+ find dist -name '*.whl' -exec cp {} wheels/ \;
108
+ find dist -name '*.tar.gz' -exec cp {} wheels/ \;
109
+
110
+ - name: Publish to PyPI
111
+ uses: pypa/gh-action-pypi-publish@release/v1
112
+ with:
113
+ packages-dir: wheels/
114
+ skip-existing: true
@@ -0,0 +1,84 @@
1
+ name: Code Review
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "src/**"
8
+ - "tests/**"
9
+ - "docs/**"
10
+ - "Cargo.toml"
11
+ - "Cargo.lock"
12
+ - "rust-toolchain.toml"
13
+ - "clippy.toml"
14
+ - "rustfmt.toml"
15
+ pull_request:
16
+ paths:
17
+ - "src/**"
18
+ - "tests/**"
19
+ - "docs/**"
20
+ - "Cargo.toml"
21
+ - "Cargo.lock"
22
+ - "rust-toolchain.toml"
23
+ - "clippy.toml"
24
+ - "rustfmt.toml"
25
+
26
+ concurrency:
27
+ group: ${{ github.workflow }}-${{ github.ref }}
28
+ cancel-in-progress: true
29
+
30
+ env:
31
+ CARGO_TERM_COLOR: always
32
+
33
+ jobs:
34
+ fmt:
35
+ name: Formatting
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v7
39
+ - uses: dtolnay/rust-toolchain@stable
40
+ with:
41
+ components: rustfmt
42
+ - run: cargo fmt --all --check
43
+
44
+ clippy:
45
+ name: Clippy
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v7
49
+ - uses: dtolnay/rust-toolchain@stable
50
+ with:
51
+ components: clippy
52
+ - uses: Swatinem/rust-cache@v2
53
+ - run: cargo clippy --all-targets --all-features -- -D warnings
54
+
55
+ test:
56
+ name: Tests
57
+ runs-on: ubuntu-latest
58
+ steps:
59
+ - uses: actions/checkout@v7
60
+ - uses: dtolnay/rust-toolchain@stable
61
+ - uses: Swatinem/rust-cache@v2
62
+ # The suite stubs `tyf` via GERENUK_TYF, so neither ty-find nor ty is
63
+ # needed here. See tests/common/mod.rs.
64
+ - run: cargo test --all-features
65
+
66
+ fixture:
67
+ name: Fixture package tests
68
+ runs-on: ubuntu-latest
69
+ steps:
70
+ - uses: actions/checkout@v7
71
+ - uses: astral-sh/setup-uv@v7
72
+ - name: Run the fixture package's pytest suite
73
+ working-directory: tests/fixtures/sample_pkg
74
+ run: uv run --with pytest --with hatchling python -m pytest -q
75
+
76
+ docs:
77
+ name: Docs are in sync
78
+ runs-on: ubuntu-latest
79
+ steps:
80
+ - uses: actions/checkout@v7
81
+ - name: Version badge matches Cargo.toml
82
+ run: bash docs/gen-version.sh --check
83
+ - name: Shared snippets are injected
84
+ run: bash docs/inject-shared.sh --check
@@ -0,0 +1,81 @@
1
+ # Rust
2
+ /target/
3
+ **/*.rs.bk
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+ cf/
30
+ # Virtual environments
31
+ .env
32
+ .venv
33
+ env/
34
+ venv/
35
+ ENV/
36
+ env.bak/
37
+ venv.bak/
38
+
39
+ # Claude Code
40
+ .claude/worktrees/
41
+
42
+ # IDE
43
+ .vscode/
44
+ .idea/
45
+ *.swp
46
+ *.swo
47
+ *~
48
+
49
+ # OS
50
+ .DS_Store
51
+ .DS_Store?
52
+ ._*
53
+ .Spotlight-V100
54
+ .Trashes
55
+ ehthumbs.db
56
+ Thumbs.db
57
+
58
+ # Logs
59
+ *.log
60
+
61
+ # Temporary files
62
+ *.tmp
63
+ *.temp
64
+ .cache/
65
+
66
+ # maturin / PyO3
67
+ *.so
68
+ *.pyd
69
+ .eggs/
70
+
71
+ # Testing
72
+ .pytest_cache/
73
+ .coverage
74
+ htmlcov/
75
+
76
+
77
+ # Fixture package virtualenvs
78
+ tests/fixtures/**/.venv/
79
+
80
+ # mdBook build output (docs/book.toml build-dir)
81
+ docs/book/
@@ -0,0 +1,105 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with
4
+ code in this repository.
5
+
6
+ ## Project Overview
7
+
8
+ `gerenuk` is a CLI that audits Python symbols by driving `tyf` (from
9
+ [ty-find](https://github.com/mojzis/ty-find)). It is a hybrid Rust/Python
10
+ project: a Rust binary packaged as a Python wheel via maturin.
11
+
12
+ It reports two things per file: symbols nothing references, and symbols only
13
+ tests reach. A second command, `changed-symbols`, maps the working tree's git
14
+ diff to the Python symbols it changed — the first stage of impact-based pytest
15
+ selection, and the one part of the crate that needs `git` rather than `tyf`.
16
+ Architecture details: `docs/dev/ARCHITECTURE.md`.
17
+
18
+ ## Prerequisites
19
+
20
+ - **`tyf`** on `PATH` for `audit`/`doctor` (`uv add --dev ty ty-find`). Not
21
+ needed for the test suite — the tests stub it.
22
+ - **`git`** on `PATH` for `changed-symbols` and its tests, or `GERENUK_GIT`
23
+ pointing at it. That is all it needs.
24
+ - **`uv`** for the fixture package's pytest suite.
25
+
26
+ ## Common Commands
27
+
28
+ ```sh
29
+ # Pre-commit checks (always run before committing)
30
+ cargo fmt --all -- --check && cargo clippy --all-targets --all-features -- -D warnings && cargo test --all-features
31
+
32
+ make review # the above, plus the fixture pytest suite, audit and deny
33
+ make review-quick # skip the network checks
34
+ make test-fixture # just the Python fixture package's pytest suite
35
+ make docs # build the mdBook site + llms.txt
36
+ ```
37
+
38
+ If formatting fails, fix it with `cargo fmt --all` and re-run.
39
+
40
+ ## Development Workflow
41
+
42
+ All features and bug fixes follow TDD (red-green-refactor). No implementation
43
+ code without a failing test first. Bug fixes must include a regression test that
44
+ fails without the fix.
45
+
46
+ ## Test Changes Require Deliberation
47
+
48
+ When a test fails during implementation:
49
+
50
+ 1. **Stop and diagnose.** Understand WHY it fails before changing anything. Is
51
+ the test wrong, or is the implementation wrong?
52
+ 2. **Default assumption: the test is right.** Fix the implementation first.
53
+ 3. **If the test genuinely needs updating** (requirements changed, API evolved),
54
+ explain what changed and why the old assertion is no longer correct before
55
+ modifying it.
56
+ 4. **Never weaken an assertion just to make it pass.** Making a test more
57
+ permissive without understanding the failure is not a fix.
58
+ 5. **If uncertain, ask.** A 2-line question is cheaper than a silent wrong
59
+ decision.
60
+
61
+ ## Key Invariants
62
+
63
+ - **`tyf::Runner::run` and `git::Git::run` are the only functions that spawn a
64
+ process.** Everything downstream takes parsed data. Keep it that way — it is
65
+ what makes `analyze`, `changed` and `report` testable with no `tyf`, no `ty`,
66
+ no `git` and no Python. Adding a third seam needs a very good reason.
67
+ - **`changed-symbols` must never construct a `tyf::Runner`.** Discovery happens
68
+ inside the `Audit` and `Doctor` arms of `Cli::run`, not before the match. A
69
+ test in `tests/changed_symbols.rs` runs with an empty `PATH` to enforce this.
70
+ - **`changed::Sources` is the seam the classification is tested through.** New
71
+ inputs go behind that trait, not into `changed::analyze` directly, or the unit
72
+ tests stop being able to run without a repository.
73
+ - **`main.rs` stays thin.** Argument parsing, tracing setup, exit-code mapping.
74
+ Command bodies belong in `cli.rs`.
75
+ - **Exit codes are part of the contract.** `0` clean, `1` findings, `2` the run
76
+ could not complete. Do not collapse `1` and `2`. `changed-symbols` is an
77
+ inventory rather than a verdict, so it returns `0` or `2` and never `1`.
78
+ - **The fixture package and the canned payloads must agree.** Changing the call
79
+ graph in `tests/fixtures/sample_pkg` means updating both its pytest suite and
80
+ the stub payloads in `tests/audit.rs`.
81
+ - **`clippy::unwrap_used` / `expect_used` are denied outside tests.** Use
82
+ `anyhow::Context` on every `?` that crosses an I/O or parsing boundary.
83
+ - **A symbol's `added`/`modified`/`deleted` verdict comes from whether it exists
84
+ on each side of the diff, not from which side the hunk touched.** Deleting
85
+ lines from a function that still exists is a modification; its callers were
86
+ never orphaned. Renames are deliberately *not* paired — a moved module is a
87
+ new module, so the old path's symbols are `deleted` and the new path's
88
+ `added`.
89
+ - **Do not trust `tyf`'s production/test split.** Its heuristic reads the whole
90
+ absolute path, so a project under a `tests/` directory has every reference
91
+ filed as a test. `analyze::split_refs` re-derives the buckets from paths
92
+ relative to the workspace root, and drops the symbol's own definition. The
93
+ stub payloads in `tests/audit.rs` reproduce both quirks — keep them that way.
94
+
95
+ ## Docs
96
+
97
+ The mdBook site under `docs/` deploys to GitHub Pages on push to `main`
98
+ (`.github/workflows/docs.yml`), along with `llms.txt` and `llms-full.txt`.
99
+
100
+ - Shared prose lives in `docs/shared/` and is injected into `README.md` and
101
+ `docs/src/setup.md` by `docs/inject-shared.sh`. Edit the shared file, then run
102
+ the script — never edit between the `<!-- BEGIN SHARED:... -->` markers.
103
+ - `docs/gen-version.sh` syncs the docs version badge with `Cargo.toml`.
104
+ - Adding a page means adding it to `docs/src/SUMMARY.md`; the `llms.txt`
105
+ generator reads that file.