feat: initial Claude Code configuration scaffold

Comprehensive Claude Code guidance system with:

- 5 agents: tdd-guardian, code-reviewer, security-scanner, refactor-scan, dependency-audit
- 18 skills covering languages (Python, TypeScript, Rust, Go, Java, C#),
  infrastructure (AWS, Azure, GCP, Terraform, Ansible, Docker/K8s, Database, CI/CD),
  testing (TDD, UI, Browser), and patterns (Monorepo, API Design, Observability)
- 3 hooks: secret detection, auto-formatting, TDD git pre-commit
- Strict TDD enforcement with 80%+ coverage requirements
- Multi-model strategy: Opus for planning, Sonnet for execution (opusplan)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 15:47:34 -05:00
commit befb8fbaeb
34 changed files with 12233 additions and 0 deletions

View File

@@ -0,0 +1,574 @@
---
name: rust-async
description: Rust development with Tokio async runtime, cargo test, clippy, and systems programming patterns. Use when writing Rust code, agents, or system utilities.
---
# Rust Development Skill
## Project Structure
```
my-agent/
├── Cargo.toml
├── Cargo.lock
├── src/
│ ├── main.rs # Binary entry point
│ ├── lib.rs # Library root (if dual crate)
│ ├── config.rs # Configuration handling
│ ├── error.rs # Error types
│ ├── client/
│ │ ├── mod.rs
│ │ └── http.rs
│ └── discovery/
│ ├── mod.rs
│ ├── system.rs
│ └── network.rs
├── tests/
│ └── integration/
│ └── discovery_test.rs
└── benches/
└── performance.rs
```
## Cargo Configuration
```toml
# Cargo.toml
[package]
name = "my-agent"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
[dependencies]
# Async runtime
tokio = { version = "1.35", features = ["full"] }
# HTTP client
reqwest = { version = "0.11", features = ["json", "socks"] }
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# Error handling
thiserror = "1.0"
anyhow = "1.0"
# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# System info
sysinfo = "0.30"
[dev-dependencies]
tokio-test = "0.4"
mockall = "0.12"
tempfile = "3.10"
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
codegen-units = 1 # Better optimization
strip = true # Strip symbols
panic = "abort" # Smaller binary
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
all = "deny"
pedantic = "warn"
nursery = "warn"
```
## Error Handling
### Custom Error Types
```rust
// src/error.rs
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AgentError {
#[error("Configuration error: {0}")]
Config(String),
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("Discovery failed: {message}")]
Discovery { message: String, source: Option<Box<dyn std::error::Error + Send + Sync>> },
#[error("Connection timeout after {duration_secs}s")]
Timeout { duration_secs: u64 },
}
pub type Result<T> = std::result::Result<T, AgentError>;
```
### Result Pattern Usage
```rust
// src/client/http.rs
use crate::error::{AgentError, Result};
pub struct HttpClient {
client: reqwest::Client,
base_url: String,
}
impl HttpClient {
pub fn new(base_url: &str, timeout_secs: u64) -> Result<Self> {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(timeout_secs))
.build()
.map_err(AgentError::Network)?;
Ok(Self {
client,
base_url: base_url.to_string(),
})
}
pub async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T> {
let url = format!("{}{}", self.base_url, path);
let response = self
.client
.get(&url)
.send()
.await?
.error_for_status()?
.json::<T>()
.await?;
Ok(response)
}
pub async fn post<T, R>(&self, path: &str, body: &T) -> Result<R>
where
T: serde::Serialize,
R: serde::de::DeserializeOwned,
{
let url = format!("{}{}", self.base_url, path);
let response = self
.client
.post(&url)
.json(body)
.send()
.await?
.error_for_status()?
.json::<R>()
.await?;
Ok(response)
}
}
```
## Async Patterns with Tokio
### Main Entry Point
```rust
// src/main.rs
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
mod config;
mod error;
mod client;
mod discovery;
use crate::config::Config;
use crate::error::Result;
#[tokio::main]
async fn main() -> Result<()> {
// Initialize tracing
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()),
))
.with(tracing_subscriber::fmt::layer())
.init();
let config = Config::from_env()?;
tracing::info!(version = env!("CARGO_PKG_VERSION"), "Starting agent");
run(config).await
}
async fn run(config: Config) -> Result<()> {
let client = client::HttpClient::new(&config.server_url, config.timeout_secs)?;
// Main loop with graceful shutdown
let mut interval = tokio::time::interval(config.poll_interval);
loop {
tokio::select! {
_ = interval.tick() => {
if let Err(e) = discovery::collect_and_send(&client).await {
tracing::error!(error = %e, "Discovery cycle failed");
}
}
_ = tokio::signal::ctrl_c() => {
tracing::info!("Shutdown signal received");
break;
}
}
}
Ok(())
}
```
### Concurrent Operations
```rust
// src/discovery/mod.rs
use tokio::task::JoinSet;
use crate::error::Result;
pub async fn discover_all() -> Result<SystemInfo> {
let mut tasks = JoinSet::new();
// Spawn concurrent discovery tasks
tasks.spawn(async { ("cpu", discover_cpu().await) });
tasks.spawn(async { ("memory", discover_memory().await) });
tasks.spawn(async { ("disk", discover_disk().await) });
tasks.spawn(async { ("network", discover_network().await) });
let mut info = SystemInfo::default();
// Collect results as they complete
while let Some(result) = tasks.join_next().await {
match result {
Ok((name, Ok(data))) => {
tracing::debug!(component = name, "Discovery completed");
info.merge(name, data);
}
Ok((name, Err(e))) => {
tracing::warn!(component = name, error = %e, "Discovery failed");
}
Err(e) => {
tracing::error!(error = %e, "Task panicked");
}
}
}
Ok(info)
}
```
## Testing Patterns
### Unit Tests
```rust
// src/discovery/system.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CpuInfo {
pub cores: usize,
pub usage_percent: f32,
pub model: String,
}
impl CpuInfo {
pub fn is_high_usage(&self) -> bool {
self.usage_percent > 80.0
}
}
#[cfg(test)]
mod tests {
use super::*;
fn get_mock_cpu_info(overrides: Option<CpuInfo>) -> CpuInfo {
let default = CpuInfo {
cores: 4,
usage_percent: 25.0,
model: "Test CPU".to_string(),
};
overrides.unwrap_or(default)
}
#[test]
fn test_is_high_usage_returns_true_above_threshold() {
let cpu = get_mock_cpu_info(Some(CpuInfo {
usage_percent: 85.0,
..get_mock_cpu_info(None)
}));
assert!(cpu.is_high_usage());
}
#[test]
fn test_is_high_usage_returns_false_below_threshold() {
let cpu = get_mock_cpu_info(Some(CpuInfo {
usage_percent: 50.0,
..get_mock_cpu_info(None)
}));
assert!(!cpu.is_high_usage());
}
#[test]
fn test_is_high_usage_returns_false_at_boundary() {
let cpu = get_mock_cpu_info(Some(CpuInfo {
usage_percent: 80.0,
..get_mock_cpu_info(None)
}));
assert!(!cpu.is_high_usage());
}
}
```
### Async Tests
```rust
// tests/integration/client_test.rs
use tokio_test::block_on;
use my_agent::client::HttpClient;
#[tokio::test]
async fn test_client_handles_timeout() {
let client = HttpClient::new("http://localhost:9999", 1).unwrap();
let result: Result<serde_json::Value, _> = client.get("/test").await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_concurrent_requests_complete() {
let client = HttpClient::new("https://httpbin.org", 30).unwrap();
let handles: Vec<_> = (0..5)
.map(|i| {
let client = client.clone();
tokio::spawn(async move {
client.get::<serde_json::Value>(&format!("/delay/{}", i % 2)).await
})
})
.collect();
for handle in handles {
let result = handle.await.unwrap();
assert!(result.is_ok());
}
}
```
### Mocking with Mockall
```rust
// src/discovery/mod.rs
#[cfg_attr(test, mockall::automock)]
pub trait SystemDiscovery {
fn get_cpu_info(&self) -> crate::error::Result<CpuInfo>;
fn get_memory_info(&self) -> crate::error::Result<MemoryInfo>;
}
// src/discovery/collector.rs
pub struct Collector<D: SystemDiscovery> {
discovery: D,
}
impl<D: SystemDiscovery> Collector<D> {
pub fn new(discovery: D) -> Self {
Self { discovery }
}
pub fn collect(&self) -> crate::error::Result<SystemSnapshot> {
let cpu = self.discovery.get_cpu_info()?;
let memory = self.discovery.get_memory_info()?;
Ok(SystemSnapshot { cpu, memory })
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
#[test]
fn test_collector_combines_cpu_and_memory() {
let mut mock = MockSystemDiscovery::new();
mock.expect_get_cpu_info()
.times(1)
.returning(|| Ok(CpuInfo {
cores: 4,
usage_percent: 50.0,
model: "Mock CPU".to_string(),
}));
mock.expect_get_memory_info()
.times(1)
.returning(|| Ok(MemoryInfo {
total_gb: 16.0,
available_gb: 8.0,
}));
let collector = Collector::new(mock);
let snapshot = collector.collect().unwrap();
assert_eq!(snapshot.cpu.cores, 4);
assert_eq!(snapshot.memory.total_gb, 16.0);
}
}
```
## Configuration
```rust
// src/config.rs
use crate::error::{AgentError, Result};
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct Config {
pub server_url: String,
pub timeout_secs: u64,
pub poll_interval: Duration,
pub agent_id: String,
}
impl Config {
pub fn from_env() -> Result<Self> {
let server_url = std::env::var("SERVER_URL")
.map_err(|_| AgentError::Config("SERVER_URL not set".to_string()))?;
let timeout_secs = std::env::var("TIMEOUT_SECS")
.unwrap_or_else(|_| "30".to_string())
.parse()
.map_err(|_| AgentError::Config("Invalid TIMEOUT_SECS".to_string()))?;
let poll_interval_secs: u64 = std::env::var("POLL_INTERVAL_SECS")
.unwrap_or_else(|_| "60".to_string())
.parse()
.map_err(|_| AgentError::Config("Invalid POLL_INTERVAL_SECS".to_string()))?;
let agent_id = std::env::var("AGENT_ID")
.unwrap_or_else(|_| hostname::get()
.map(|h| h.to_string_lossy().to_string())
.unwrap_or_else(|_| "unknown".to_string()));
Ok(Self {
server_url,
timeout_secs,
poll_interval: Duration::from_secs(poll_interval_secs),
agent_id,
})
}
}
```
## Clippy Configuration
```toml
# clippy.toml
cognitive-complexity-threshold = 10
too-many-arguments-threshold = 5
type-complexity-threshold = 200
```
## Commands
```bash
# Building
cargo build # Debug build
cargo build --release # Release build
cargo build --target x86_64-unknown-linux-musl # Static binary
# Testing
cargo test # Run all tests
cargo test -- --nocapture # Show println! output
cargo test test_name # Run specific test
cargo test --test integration # Run integration tests only
# Linting
cargo clippy # Run clippy
cargo clippy -- -D warnings # Treat warnings as errors
cargo clippy --fix # Auto-fix issues
# Formatting
cargo fmt # Format code
cargo fmt --check # Check formatting
# Other
cargo doc --open # Generate and open docs
cargo bench # Run benchmarks
cargo tree # Show dependency tree
cargo audit # Check for vulnerabilities
```
## Anti-Patterns to Avoid
```rust
// BAD: Unwrap without context
let config = Config::from_env().unwrap();
// GOOD: Provide context or use ?
let config = Config::from_env()
.expect("Failed to load configuration from environment");
// Or in functions returning Result:
let config = Config::from_env()?;
// BAD: Clone when not needed
fn process(data: &Vec<String>) {
let cloned = data.clone(); // Unnecessary allocation
for item in cloned.iter() {
println!("{}", item);
}
}
// GOOD: Use references
fn process(data: &[String]) {
for item in data {
println!("{}", item);
}
}
// BAD: String concatenation in loop
let mut result = String::new();
for item in items {
result = result + &item + ", "; // Allocates each iteration
}
// GOOD: Use push_str or join
let result = items.join(", ");
// BAD: Blocking in async context
async fn fetch_data() {
std::thread::sleep(Duration::from_secs(1)); // Blocks runtime!
}
// GOOD: Use async sleep
async fn fetch_data() {
tokio::time::sleep(Duration::from_secs(1)).await;
}
// BAD: Ignoring errors silently
let _ = file.write_all(data);
// GOOD: Handle or propagate errors
file.write_all(data)?;
// Or log if truly ignorable:
if let Err(e) = file.write_all(data) {
tracing::warn!(error = %e, "Failed to write data");
}
```