Skip to content

Commit ada50bb

Browse files
committed
feat: add Rust language support to codebase-readiness plugin
Add first-class Rust support to the codebase-readiness assessment plugin: - Create rust.md language reference with all 8 dimension sections covering ownership/borrow checker signals, cargo test detection, clippy/rustfmt tooling, and Rust-specific architecture patterns - Update recon.sh to detect Cargo.toml, count .rs source files, find clippy.toml/rustfmt.toml linter configs, and detect Rust integration tests - Add rust to SKILL.md supported languages list and README examples - Bump plugin version to 1.6.0
1 parent 35f771f commit ada50bb

6 files changed

Lines changed: 241 additions & 9 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"name": "codebase-readiness",
5252
"source": "./plugins/codebase-readiness",
5353
"description": "Score your codebase's readiness for autonomous AI agent work across 8 dimensions, framed around the Stripe benchmark of 1k+ AI-generated PRs per week",
54-
"version": "1.5.0"
54+
"version": "1.6.0"
5555
},
5656
{
5757
"name": "agent-ready",

plugins/codebase-readiness/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codebase-readiness",
3-
"version": "1.5.0",
3+
"version": "1.6.0",
44
"description": "Agent-Ready Codebase Assessment — scores your codebase across 8 dimensions and generates an actionable improvement roadmap framed around the Stripe AI benchmark",
55
"author": {
66
"name": "Damian Galarza",

plugins/codebase-readiness/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ The assessment spawns 4 parallel agents, scores all 8 dimensions using language-
3535

3636
## What Gets Scored
3737

38-
8 dimensions, weighted by language (dynamic languages like Ruby and Python weight tests higher; static languages like TypeScript and Go weight type safety higher):
38+
8 dimensions, weighted by language (dynamic languages like Ruby and Python weight tests higher; static languages like TypeScript, Go, and Rust weight type safety higher):
3939

4040
| Dimension | What it measures |
4141
|---------------------------|-------------------------------------------------------------------------|

plugins/codebase-readiness/skills/codebase-readiness/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: codebase-readiness
3-
description: This skill should be used to run an Agent-Ready Codebase Assessment — scoring a codebase across 8 dimensions with parallel agents, producing a weighted score (0-100), band rating, and improvement roadmap. Supports Ruby, Python, TypeScript, JavaScript, Go, Java, and Scala.
3+
description: This skill should be used to run an Agent-Ready Codebase Assessment — scoring a codebase across 8 dimensions with parallel agents, producing a weighted score (0-100), band rating, and improvement roadmap. Supports Ruby, Python, TypeScript, JavaScript, Go, Java, Scala, and Rust.
44
---
55

66
# Codebase Readiness Assessment
@@ -43,7 +43,7 @@ After reviewing the output, format a **Codebase Snapshot**:
4343

4444
Determine `PRIMARY_LANGUAGE` and `LANGUAGE_TIER` from the snapshot. These values drive which language reference file to load.
4545

46-
**Supported languages:** ruby, python, typescript, javascript, go, java, scala.
46+
**Supported languages:** ruby, python, typescript, javascript, go, java, scala, rust.
4747

4848
If the primary language is not in the supported list, inform the user which languages are supported. Offer to proceed with the closest supported language file if the user agrees, or assess dimensions generically without language-specific criteria.
4949

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# Rust — Language-Specific Assessment Criteria
2+
3+
## Language Classification
4+
- **Tier:** statically-typed
5+
- **Primary safety mechanism:** Ownership system + compile-time type checking + `Result<T,E>` error handling
6+
- **Key principle:** Rust's ownership/borrow checker and type system eliminate entire classes of bugs at compile time. Assess quality of type usage, error handling discipline, and minimization of escape hatches (`unsafe`, `unwrap()`).
7+
8+
## Type Safety
9+
### What to Examine
10+
- Use of `unsafe {}` blocks — lower frequency is better; each should have a `// SAFETY:` comment
11+
- `unwrap()` / `expect()` in non-test code — panic-on-failure anti-pattern; `clippy::unwrap_used` / `clippy::expect_used` lints enforce discipline
12+
- `dyn Any` usage — type erasure escape hatch
13+
- `transmute` usage — dangerous type-level escape hatch
14+
- Raw pointer usage (`*const T`, `*mut T`) — another unsafe primitive
15+
- Custom newtypes for domain concepts (`struct UserId(u64)`, `struct Email(String)`)
16+
- Trait bounds and generic constraints — well-bounded generics signal strong type discipline
17+
- `#[must_use]` annotations — strong API discipline signal
18+
- `From`/`Into` trait implementations — idiomatic type conversions
19+
- Error handling: `thiserror`/`snafu` (libraries) vs `anyhow`/`color-eyre` (binaries), custom error enums with `Result<T,E>`
20+
- `Result<T,E>` vs bare panics — explicit error propagation with `?` operator
21+
22+
### Evidence Commands
23+
```bash
24+
echo "=== unsafe usage count ==="
25+
grep -r "unsafe " --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
26+
27+
echo "=== SAFETY comments on unsafe ==="
28+
grep -r "// SAFETY:" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
29+
30+
echo "=== unwrap()/expect() in non-test code (approx — includes in-file test modules) ==="
31+
grep -rE "\.unwrap\(\)|\.expect\(" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | grep -v "/tests/" | wc -l
32+
33+
echo "=== Clippy unwrap lint enabled ==="
34+
grep -rE "deny\(clippy::(unwrap_used|expect_used)" --include="*.rs" --include="Cargo.toml" --include="clippy.toml" --include=".clippy.toml" . 2>/dev/null | grep -v target | head -5
35+
36+
echo "=== transmute usage ==="
37+
grep -r "transmute" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
38+
39+
echo "=== Raw pointer usage ==="
40+
grep -rE "\*const |\*mut " --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
41+
42+
echo "=== dyn Any usage ==="
43+
grep -r "dyn Any" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
44+
45+
echo "=== Custom newtypes ==="
46+
grep -rE "^(pub(\([^)]+\))? )?struct [A-Z][a-zA-Z]+\(.+\)" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
47+
48+
echo "=== Error handling libraries ==="
49+
grep -rE "thiserror|anyhow|color-eyre|snafu" --include="Cargo.toml" . 2>/dev/null | grep -v target | head -5
50+
51+
echo "=== Result usage ==="
52+
grep -r "Result<" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
53+
54+
echo "=== Trait definitions ==="
55+
grep -rE "^(pub(\([^)]+\))? )?trait " --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
56+
57+
echo "=== #[must_use] annotations ==="
58+
grep -r "#\[must_use\]" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
59+
60+
echo "=== From/Into implementations ==="
61+
grep -rE "impl .* From<" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
62+
```
63+
64+
### Scoring Criteria
65+
- **0-20**: Widespread `unsafe`, excessive `unwrap()`, no custom types, bare panics in library code
66+
- **21-40**: Minimal newtypes, frequent `unwrap()` outside tests, unstructured error handling
67+
- **41-60**: Some newtypes, `thiserror`/`anyhow` present, moderate `unwrap()` usage with `expect()` messages
68+
- **61-80**: Well-defined newtypes, structured error enums, `unsafe` blocks documented with `// SAFETY:`, minimal `unwrap()`
69+
- **81-100**: Comprehensive newtype usage, zero undocumented `unsafe`, `unwrap()`-free non-test code, rich trait bounds, `Result<T,E>` throughout
70+
71+
### Language-Specific Notes
72+
- Rust's ownership system prevents data races and memory errors at compile time — the type system does more work than most languages.
73+
- `unsafe {}` is Rust's escape hatch — treat it like `any` in TypeScript or `interface{}` in Go. Every `unsafe` block should have a `// SAFETY:` comment.
74+
- `unwrap()` in non-test code is a panic-on-failure anti-pattern; `expect("reason")` is slightly better but still panics. Prefer `?` propagation. Projects enabling `clippy::unwrap_used` / `clippy::expect_used` lints demonstrate strong discipline.
75+
- `transmute` and raw pointers are deeper escape hatches than `unsafe` blocks alone — their presence warrants close inspection.
76+
- Error handling discipline (`thiserror`/`snafu` for libraries, `anyhow`/`color-eyre` for binaries, `?` operator) is a key Rust quality signal.
77+
- `#[deny(warnings)]` in source code is a known anti-pattern (causes breakage on compiler upgrades) — prefer CI-only `-D warnings` via rustflags.
78+
79+
## Test Foundation
80+
### Tooling & Detection
81+
- **Framework:** cargo test (built-in, standard)
82+
- **Coverage:** cargo-tarpaulin, cargo-llvm-cov
83+
- **Mutation testing:** cargo-mutants
84+
- **Property-based testing:** proptest, quickcheck
85+
- **Snapshot testing:** insta (cargo-insta)
86+
- **Parametric testing:** rstest
87+
- **Benchmarking:** criterion, divan
88+
- **Mocking:** mockall
89+
- **CLI testing:** assert_cmd, assert_fs
90+
- **Parallel runner:** cargo-nextest
91+
- **File convention:** `#[cfg(test)] mod tests` in-file + `tests/*.rs` integration tests + doc tests in `///` and `//!` comments
92+
93+
### Evidence Commands
94+
```bash
95+
echo "=== In-file test modules ==="
96+
grep -r "#\[cfg(test)\]" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
97+
98+
echo "=== Integration test files ==="
99+
find . -path "*/tests/*.rs" 2>/dev/null | grep -v target | grep -v .git | wc -l
100+
101+
echo "=== Source file count ==="
102+
find . -name "*.rs" 2>/dev/null | grep -v target | grep -v .git | grep -v "/tests/" | wc -l
103+
104+
echo "=== Doc tests ==="
105+
grep -rE "/// ```|//! ```" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
106+
107+
echo "=== Testing/coverage in CI ==="
108+
grep -rE "tarpaulin|llvm-cov|cargo test|nextest" .github/ .circleci/ .buildkite/ Makefile 2>/dev/null | head -5
109+
110+
echo "=== Snapshot/property/benchmark/mock testing ==="
111+
grep -rE "insta|proptest|quickcheck|criterion|divan|mockall|rstest|assert_cmd|assert_fs" --include="Cargo.toml" . 2>/dev/null | grep -v target | head -10
112+
113+
echo "=== Ignored tests ==="
114+
grep -r "#\[ignore\]" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
115+
```
116+
117+
### Scoring Notes
118+
- **Skip patterns:** `#[ignore]` attribute on test functions
119+
- **Mock indicators:** mockall or trait-based test doubles
120+
- Rust's in-file `#[cfg(test)] mod tests` pattern means test count requires grepping, not just file counting
121+
- Doc tests (`/// ````) are a strong positive signal — they ensure examples stay compilable
122+
- `cargo test` runs unit tests, integration tests, and doc tests by default
123+
- `insta` snapshot testing is widely used in the Rust ecosystem and a strong positive signal
124+
- `cargo-nextest` for parallel test execution is both a feedback loop and test foundation signal
125+
126+
## Feedback Loops
127+
### Tooling
128+
- **Pre-commit:** lefthook, pre-commit framework
129+
- **Linter:** cargo clippy (official lint tool, highly configurable)
130+
- **Formatter:** rustfmt (official, universally adopted)
131+
- **Fast checking:** cargo check (type checking without full compilation)
132+
- **Watch mode:** cargo-watch, bacon (auto-rerun on file change)
133+
- **Parallel test runner:** cargo-nextest (faster parallel test execution)
134+
- **Security scanners:** cargo-audit, cargo-deny
135+
136+
### Evidence Commands
137+
```bash
138+
ls lefthook.yml .pre-commit-config.yaml 2>/dev/null
139+
grep -rE "clippy|cargo-audit|cargo-deny|nextest|cargo check" .github/ .circleci/ .buildkite/ Makefile 2>/dev/null | head -5
140+
```
141+
142+
## Code Clarity
143+
### Conventions
144+
- **Extensions:** .rs
145+
- **Idiomatic file size:** Moderate files; one type per module is idiomatic
146+
- **Naming:** snake_case for functions/modules/variables, PascalCase for types/traits, SCREAMING_SNAKE_CASE for constants
147+
- **File organization:** Module system — `module/mod.rs` (2015 style) or `module.rs` + `module/` subdirectory (2018+ style)
148+
149+
## Consistency & Conventions
150+
### Tooling
151+
- **Linter:** clippy (clippy.toml or .clippy.toml for configuration)
152+
- **Formatter:** rustfmt (rustfmt.toml for configuration)
153+
- **Toolchain pinning:** rust-toolchain.toml
154+
- **Dependency policy:** cargo-deny (deny.toml)
155+
- **Cargo configuration:** .cargo/config.toml
156+
- **Observability:** tracing, log (ecosystem standard crates)
157+
158+
### Evidence Commands
159+
```bash
160+
echo "=== Formatter/linter config ==="
161+
ls rustfmt.toml clippy.toml .clippy.toml .cargo/config.toml rust-toolchain.toml deny.toml 2>/dev/null
162+
163+
echo "=== CI linting ==="
164+
grep -rE "clippy|rustfmt|cargo fmt" .github/ .circleci/ .buildkite/ Makefile 2>/dev/null | head -5
165+
166+
echo "=== Clippy deny warnings ==="
167+
grep -rE "\-D warnings|\-D clippy|deny\(clippy" .cargo/config.toml Makefile .github/ 2>/dev/null | head -5
168+
169+
echo "=== deny(warnings) anti-pattern ==="
170+
grep -r "deny(warnings)" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | head -5
171+
172+
echo "=== missing_docs enforcement ==="
173+
grep -rE "warn\(missing_docs\)|deny\(missing_docs\)" --include="*.rs" . 2>/dev/null | grep -v target | head -5
174+
175+
echo "=== Observability ==="
176+
grep -rE "tracing|\"log\"" --include="Cargo.toml" . 2>/dev/null | grep -v target | head -5
177+
```
178+
179+
## Architecture
180+
### Framework Conventions
181+
- **Standard layout:** `src/` (source), `tests/` (integration tests), `benches/` (benchmarks), `examples/` (example programs)
182+
- **Entry points:** `src/main.rs` (binary), `src/lib.rs` (library)
183+
- **Workspace structure:** `[workspace]` in root `Cargo.toml` with member crates; `[workspace.dependencies]` for dependency inheritance (modern practice)
184+
- **Web frameworks:** actix-web, axum (current standard), rocket
185+
- **Async runtimes:** tokio (dominant), async-std
186+
- **Build scripts:** `build.rs` for code generation or native dependencies
187+
188+
### Evidence Commands
189+
```bash
190+
echo "=== Standard layout ==="
191+
ls src/main.rs src/lib.rs tests/ benches/ examples/ 2>/dev/null
192+
193+
echo "=== Workspace structure ==="
194+
grep -A5 "\[workspace\]" Cargo.toml 2>/dev/null | head -10
195+
196+
echo "=== Directory structure ==="
197+
find . -maxdepth 2 -type d 2>/dev/null | grep -v target | grep -v .git | head -20
198+
```
199+
200+
### Co-Change Notes
201+
- Cargo.lock churn is expected on dependency updates and should not penalize
202+
- `src/` + `tests/` co-changes for the same feature are expected
203+
- Workspace member crates may have coordinated version bumps
204+
205+
## Change Safety
206+
### Tooling
207+
- **Feature flags:** Cargo feature flags (`#[cfg(feature = "...")]`), compile-time feature gating
208+
- **Security scanning:** cargo-audit (advisories), cargo-deny (license + advisories + bans)
209+
- **Database migrations:** diesel, sqlx, sea-orm-migration
210+
- **Dependency management:** Cargo.lock (committed for binaries; optional for libraries)
211+
- **Toolchain pinning:** rust-toolchain.toml (specific version like `1.75.0` is a stronger signal than just `stable`)
212+
- **MSRV policy:** `rust-version` field in Cargo.toml indicates backward compatibility maintenance
213+
214+
### Evidence Commands
215+
```bash
216+
echo "=== Feature flags ==="
217+
grep -r "cfg(feature" --include="*.rs" . 2>/dev/null | grep -v target | grep -v .git | wc -l
218+
219+
echo "=== Security scanning ==="
220+
grep -rE "cargo-audit|cargo-deny" .github/ .circleci/ .buildkite/ Makefile 2>/dev/null | head -5
221+
222+
echo "=== Migration tools ==="
223+
grep -rE "diesel|sqlx|sea-orm" --include="Cargo.toml" . 2>/dev/null | grep -v target | head -5
224+
find . -path "*/migrations/*" -name "*.sql" 2>/dev/null | grep -v target | wc -l
225+
226+
echo "=== Reproducibility ==="
227+
[ -f "Cargo.lock" ] && echo "Cargo.lock present" || echo "Cargo.lock absent"
228+
cat rust-toolchain.toml 2>/dev/null || echo "No rust-toolchain.toml"
229+
grep -r "rust-version" --include="Cargo.toml" . 2>/dev/null | grep -v target | head -3
230+
grep -r 'edition = ' --include="Cargo.toml" . 2>/dev/null | grep -v target | head -3
231+
```

plugins/codebase-readiness/skills/codebase-readiness/scripts/recon.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ basename "$(pwd)"
1111
echo ""
1212
echo "=== LANGUAGE/FRAMEWORK DETECTION ==="
1313
manifests=""
14-
for f in package.json Gemfile pyproject.toml go.mod requirements.txt pom.xml build.sbt; do
14+
for f in package.json Gemfile pyproject.toml go.mod requirements.txt pom.xml build.sbt Cargo.toml; do
1515
[ -f "$f" ] && manifests="$manifests $f"
1616
done
1717
if [ -n "$manifests" ]; then
@@ -24,6 +24,7 @@ head -5 Gemfile 2>/dev/null || true
2424
head -5 pyproject.toml 2>/dev/null || true
2525
head -3 go.mod 2>/dev/null || true
2626
head -5 build.sbt 2>/dev/null || true
27+
head -5 Cargo.toml 2>/dev/null || true
2728

2829
echo ""
2930
echo "=== SIZE METRICS ==="
@@ -32,13 +33,13 @@ git rev-list --count HEAD 2>/dev/null || echo "Not a git repo or no commits"
3233
echo -n "Contributors: "
3334
git shortlog -sn HEAD 2>/dev/null | wc -l
3435
echo -n "Source files: "
35-
find . -name "*.rb" -o -name "*.ts" -o -name "*.tsx" -o -name "*.py" -o -name "*.go" -o -name "*.js" -o -name "*.scala" -o -name "*.java" 2>/dev/null \
36+
find . -name "*.rb" -o -name "*.ts" -o -name "*.tsx" -o -name "*.py" -o -name "*.go" -o -name "*.js" -o -name "*.scala" -o -name "*.java" -o -name "*.rs" 2>/dev/null \
3637
| grep -v node_modules | grep -v .git | grep -v vendor | grep -v target | grep -v spec | grep -v test | wc -l
3738

3839
echo ""
3940
echo "=== TEST FILES ==="
4041
echo -n "Test file count: "
41-
find . -name "*_spec*" -o -name "*_test*" -o -name "*.spec.*" -o -name "*.test.*" -o -name "test_*.py" -o -name "*Spec.scala" -o -name "*Test.scala" -o -name "*Suite.scala" 2>/dev/null \
42+
find . -name "*_spec*" -o -name "*_test*" -o -name "*.spec.*" -o -name "*.test.*" -o -name "test_*.py" -o -name "*Spec.scala" -o -name "*Test.scala" -o -name "*Suite.scala" -o -path "*/tests/*.rs" 2>/dev/null \
4243
| grep -v node_modules | grep -v .git | grep -v target | wc -l
4344

4445
echo ""
@@ -62,7 +63,7 @@ find . -name "CLAUDE.md" -exec wc -l {} \; 2>/dev/null | grep -v node_modules ||
6263
echo ""
6364
echo "=== LINTING/FORMATTING ==="
6465
linters=""
65-
for f in .eslintrc* .rubocop.yml .flake8 ruff.toml .golangci.yml .prettierrc* .scalafmt.conf .scalafix.conf; do
66+
for f in .eslintrc* .rubocop.yml .flake8 ruff.toml .golangci.yml .prettierrc* .scalafmt.conf .scalafix.conf clippy.toml rustfmt.toml; do
6667
# Use compgen to handle globs that don't match
6768
compgen -G "$f" > /dev/null 2>&1 && linters="$linters $f"
6869
done

0 commit comments

Comments
 (0)