We label issues that would be good for a first time contributor as
good first issue.
These usually do not require significant experience with Rust or the fastskill code base.
We label issues that we think are a good opportunity for subsequent contributions as
help wanted.
These require varying levels of experience with Rust and fastskill. Often, we want to accomplish these
tasks but do not have the resources to do so ourselves.
You don't need our permission to start on an issue we have labeled as appropriate for community contribution as described above. However, it's a good idea to indicate that you are going to work on an issue to avoid concurrent attempts to solve the same problem.
Please check in with us before starting work on an issue that has not been labeled as appropriate for community contribution. We're happy to receive contributions for other issues, but it's important to make sure we have consensus on the solution to the problem first.
Outside of issues with the labels above, issues labeled as
bug are the
best candidates for contribution. In contrast, issues labeled with needs-decision or
needs-design are not good candidates for contribution. Please do not open pull requests for
issues with these labels.
Please do not open pull requests for new features without prior discussion. While we appreciate exploration of new features, we will almost always close these pull requests immediately. Adding a new feature to fastskill creates a long-term maintenance burden and requires strong consensus from the fastskill team before it is appropriate to begin work on an implementation.
Welcome! Here's the fastest path to get fastskill building and running locally:
- Rust nightly (MSRV defined in
rust-toolchain.toml- required for latest dependencies)
# Clone and enter the repository
git clone https://github.com/gofastskill/fastskill.git
cd fastskill
# Build fastskill (rust-toolchain.toml ensures consistent Rust version)
cargo build
# Run tests
cargo nextest run
# Format and lint your code
cargo fmt --all
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
# Run fastskill locally
cargo run --bin fastskill -- --help
# Run snapshot tests (if you modify output)
cargo insta test --accept --test-runner nextest# Add a skill from local path
cargo run --bin fastskill -- add ./path/to/skill
# Search for skills
cargo run --bin fastskill -- search "query"
# Start the web server
cargo run --bin fastskill -- serveThis quickstart reduces contributor drop-off by getting you productive in under 10 minutes.
- Keep changes scoped to one crate responsibility at a time.
- Reuse
fastskill-coreservices instead of duplicating domain logic in CLI code. - Preserve stable public APIs for library crates (
fastskill-core,evals-core). - Add tests with behavior changes and keep docs aligned with new usage.
- Prefer small, reviewable pull requests with clear problem statements.
Rust nightly is required to build fastskill. The exact version and components are pinned in rust-toolchain.toml to ensure consistent builds across contributors, CI, and releases.
fastskill uses pure Rust dependencies and does not require a C compiler for building. The SQLite dependency (rusqlite) uses the bundled feature, which compiles SQLite from source using the Rust compiler.
To run tests effectively, install the recommended testing tools:
# Install nextest for fast, parallel test execution
cargo install cargo-nextest
# Install insta for snapshot testing
cargo install cargo-instaFor running tests, we recommend nextest, a fast, parallel test runner with excellent output and caching.
To run all tests:
cargo nextest runTo run a specific test by name:
cargo nextest run -E 'test(test_name)'To run tests in a specific file:
cargo nextest run --run-ignored --package <test_package>To run tests with different output formats:
cargo nextest run --verbose # Detailed output
cargo nextest run --quiet # Minimal outputfastskill uses insta for snapshot testing CLI output. This provides reliable validation of command output by capturing expected results.
Key benefits:
- Catches unintended output changes automatically
- Easier maintenance than brittle
.contains()assertions - Clear diff visualization when outputs change
- Supports content normalization (paths, timestamps, etc.)
Helper utilities are available in tests/cli/snapshot_helpers.rs for consistent snapshot testing:
use super::snapshot_helpers::{
run_fastskill_command, cli_snapshot_settings, assert_snapshot_with_settings
};
#[test]
fn test_command_output() {
let result = run_fastskill_command(&["--help"], None);
assert_snapshot_with_settings("help_output", &result.stdout, &cli_snapshot_settings());
}Snapshot workflow:
- Run
cargo insta reviewto review and accept changes - Use
cargo insta acceptto accept all pending changes - Snapshots are stored in
tests/cli/snapshots/directory and committed to git
fastskill supports several feature flags that affect available functionality:
filesystem-storage(default): Enables local filesystem storage for skillshot-reload(optional): Enables file watching for automatic skill reloading during development
| Test Type | Command | Description |
|---|---|---|
| Fast local checks | cargo nextest run |
Unit tests, integration tests with default features |
| Full CI equivalent | cargo nextest run --all-features |
All tests with optional features enabled |
| Feature-specific | cargo nextest run --features hot-reload |
Tests requiring specific optional features |
Tests requiring optional features will be skipped if those features are not enabled.
You can invoke your development version of fastskill with cargo run --bin fastskill -- <args>. For example:
# Add a skill from a local path
cargo run --bin fastskill -- add ./path/to/skill
# Search for skills
cargo run --bin fastskill -- search "data processing"
# Start the HTTP API server
cargo run --bin fastskill -- serve
# List installed skills
cargo run --bin fastskill -- show# Run all tests (recommended - much faster than cargo test)
cargo nextest run
# Run specific test suites
cargo nextest run --package cli_tests
cargo nextest run --package integration_tests
# Run tests matching a pattern
cargo nextest run search
# Run with different feature flags
cargo nextest run --features hot-reload
cargo nextest run --all-features
# Run tests in CI mode (fail fast, different retry settings)
cargo nextest run --profile ciSome tests use insta for snapshot testing, which captures expected output for validation:
# Review snapshot changes (run this when tests fail due to output changes)
cargo insta review
# Accept all snapshot changes (use carefully!)
cargo insta accept
# Accept changes for specific test
cargo insta accept --test test_name
# Check snapshots without running tests (for CI validation)
cargo insta test --checkSnapshot Review Process:
- When a test fails with snapshot differences, run
cargo insta review - Review the diff between expected and actual output
- Accept legitimate changes with
cargo insta accept - Reject unexpected changes and fix the underlying issue
Snapshot Best Practices:
- Snapshots are committed to version control
- Review snapshot changes carefully - they represent contract changes
- Use snapshot redactions for dynamic content (paths, timestamps, etc.)
- Keep snapshots focused on the essential output being tested
# Rust
cargo fmt --all
# Markdown, YAML, and other files (requires Node.js)
npx prettier --write .
# or in Docker
docker run --rm -v .:/src/ -w /src/ node:alpine npx prettier --write .Linting requires shellcheck and cargo-shear to be installed separately.
# Rust
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
# Shell scripts
shellcheck <script>
# Spell checking
typos
# Unused Rust dependencies
cargo shearTo run clippy for a Windows target from Linux or macOS, you can use cargo-xwin:
# Install cargo-xwin
cargo install cargo-xwin --locked
# Add the Windows target
rustup target add x86_64-pc-windows-msvc
# Run clippy for Windows
cargo xwin clippy --workspace --all-targets --all-features --locked -- -D warningsNote: fastskill's release process uses cross-compilation for the Windows target, but this is typically only needed for CI/release builds, not local development.
fastskill is a multi-crate workspace:
crates/fastskill-cli: CLI entry point and command handlers.crates/fastskill-core: shared services, domain logic, validation, storage, and HTTP components.crates/evals-core: reusable evaluation suite/check/runner/artifact infrastructure.
Use crate-local docs when contributing:
crates/fastskill-cli/CONTRIBUTING.mdcrates/fastskill-core/CONTRIBUTING.mdcrates/evals-core/CONTRIBUTING.md
For dependency visualization, you can use cargo-tree:
cargo install cargo-tree
cargo treeSkills can potentially execute arbitrary code when loaded. For security when testing skills from untrusted sources, consider running fastskill in a container:
# Build a containerized version for testing
docker build -t fastskill-testing -f Dockerfile .
docker run --rm -it -v $(pwd)/skills:/app/skills fastskill-testing
# Or use the musl binary in a minimal container for maximum security
docker run --rm -it -v $(pwd)/skills:/app/skills \
-v $(pwd)/target/x86_64-unknown-linux-musl/release:/app/bin \
alpine:latest /app/bin/fastskillWe recommend using containers when testing skills from untrusted sources or when you want to isolate skill execution from your development environment.
fastskill uses tracing for structured logging. You can enable detailed logging using the RUST_LOG environment variable:
# Enable trace-level logging for fastskill
RUST_LOG=fastskill=trace cargo run --bin fastskill -- <command>
# Enable debug logging for all components
RUST_LOG=debug cargo run --bin fastskill -- <command>
# Enable info logging for specific components
RUST_LOG=fastskill::core=info,fastskill::http=debug cargo run --bin fastskill -- <command>fastskill uses thiserror for structured error types and anyhow for error propagation:
use thiserror::Error;
use anyhow::{Context, Result};
#[derive(Error, Debug)]
pub enum SkillError {
#[error("Skill not found: {id}")]
NotFound { id: String },
#[error("Invalid skill format: {reason}")]
InvalidFormat { reason: String },
}
pub fn load_skill(id: &str) -> Result<SkillDefinition> {
// Use ? for propagation, Context for additional info
let content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read skill file: {}", path))?;
// ...
}Use structured logging with appropriate levels:
use tracing::{info, warn, error, debug, instrument};
#[instrument(skip(skill))]
pub async fn execute_skill(skill: &SkillDefinition) -> Result<()> {
info!(skill_id = %skill.id, "Executing skill");
if let Err(e) = do_execution(skill).await {
error!(error = %e, "Skill execution failed");
return Err(e);
}
debug!("Skill executed successfully");
Ok(())
}- Keep modules focused on single responsibilities
- Use
pub(crate)for internal APIs,pubonly for public interfaces - Prefer associated functions over free functions where appropriate
- Avoid allocations in hot paths
- Use
Arcfor shared ownership when needed - Prefer stack allocation for small, temporary data
- Unit tests for individual functions
- Integration tests for CLI commands and HTTP endpoints
- Snapshot tests for command output and API responses
- Add tests when fixing bugs or adding features
- Keep PRs focused and small (under 500 lines preferred)
- Include tests for new functionality
- Update documentation for API changes
- Squash commits when merging (GitHub's "Squash and merge" is preferred)
For messaging conventions, see STYLE.md.
fastskill uses automated release workflows to ensure consistent builds across platforms.
- Pushes to
maintrigger the auto-release workflow - Version is auto-bumped (patch increment) if not already updated in the PR
- Can be skipped with commit messages containing
[skip release],[no release], or[skip ci] - Can also be skipped by adding a
no-releaselabel to the PR
- Create a version tag (
v1.2.3) or use workflow dispatch - Triggers the release workflow which:
- Verifies version matches Cargo.toml
- Builds binaries for 3 targets:
x86_64-unknown-linux-gnu(glibc/dynamic linking)x86_64-unknown-linux-musl(static linking, maximum compatibility)x86_64-pc-windows-msvc(Windows)
- Creates GitHub release with artifacts
- Updates Homebrew (macOS) and Scoop (Windows) package managers
The rust-toolchain.toml file ensures all builds (local development, CI, releases) use the same Rust version and components for reproducible builds.
| Binary | Target | Best For | Compatibility |
|---|---|---|---|
fastskill-x86_64-unknown-linux-musl.tar.gz |
musl static | Universal compatibility | Works on any Linux, containers, CI/CD |
fastskill-x86_64-unknown-linux-gnu.tar.gz |
glibc dynamic | FIPS/compliance | Requires glibc 2.38+ |
fastskill-x86_64-pc-windows-msvc.zip |
Windows MSVC | Windows users | Windows 10+ |
All releases include:
- Pre-built binaries for Linux (2 variants) and Windows
- SHA256 checksums
- Installation instructions in release notes