The server writes .mcp.json with its current port on startup and during worktree creation. Tracking it in git causes QA gate failures when the worktree copy diverges from master (e.g. different port). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Story Kit
This app runs as a single Rust web server binary that serves the Vite/React frontend and exposes APIs.
The frontend lives in the frontend/ directory.
You can also run the frontend and backend separately in development (Vite dev server + Rust API).
Running it in development
# Build the frontend
cd frontend
npm install
npm run dev
# In another terminal - run the server (serves embedded frontend/dist/)
cargo run
Production
# Build the release binary (also builds the frontend via build.rs)
cargo build --release
# Run the server (serves embedded frontend/dist/)
./target/release/story-kit
Cross-Platform Distribution
Story Kit ships as a single self-contained binary with the React frontend embedded via
rust-embed. No Rust toolchain, Node.js, or extra libraries are required on the target machine.
macOS
# Native build – no extra tools required beyond Rust + npm
make build-macos
# Output: target/release/story-kit
# Verify only system frameworks are linked (Security.framework, libSystem.B.dylib, etc.)
otool -L target/release/story-kit
Linux (static x86_64, zero dynamic deps)
The Linux build uses the x86_64-unknown-linux-musl target to produce a fully static binary.
Prerequisites:
# Install cross – a Rust cross-compilation tool backed by Docker
cargo install cross
# Ensure Docker Desktop (or Docker Engine) is running
Build:
make build-linux
# Output: target/x86_64-unknown-linux-musl/release/story-kit
# Verify the binary is statically linked
file target/x86_64-unknown-linux-musl/release/story-kit
# Expected: ELF 64-bit LSB executable, x86-64, statically linked
ldd target/x86_64-unknown-linux-musl/release/story-kit
# Expected: not a dynamic executable
Running on any Linux x86_64 machine:
# No Rust, Node, glibc, or any other library needed – just copy and run
./story-kit
Releasing
Builds both macOS and Linux binaries locally, tags the repo, and publishes a Gitea release with a changelog.
One-time setup:
- Create a Gitea API token at
https://code.crashlabs.io/user/settings/applications(needs repository read/write) - Add it to
.env(gitignored):GITEA_TOKEN=your_token - Ensure
crossis installed (cargo install cross) and Docker is running
To release:
make release V=0.2.0
This will:
- Build macOS arm64 (native) and Linux amd64 (static musl via cross/Docker)
- Generate a changelog from commits since the last tag
- Tag the repo as
v0.2.0and push the tag - Create a Gitea release with both binaries and the changelog attached
Testing
Frontend Tests
The frontend uses Vitest for unit tests and Playwright for end-to-end tests.
cd frontend
# Run unit tests
npm test
# Run end-to-end tests
npm run test:e2e
Backend Tests
This project uses nextest for running tests and cargo-llvm-cov for code coverage.
Install Tools
cargo install cargo-nextest cargo-llvm-cov
Run Tests
# Run all tests
cargo nextest run
# Run specific module
cargo nextest run search_files
# Run with verbose output
cargo nextest run --no-capture
Generate Coverage
# HTML report (opens in browser)
cargo llvm-cov nextest --html --open
# Terminal output
cargo llvm-cov nextest
# LCOV format (for CI)
cargo llvm-cov nextest --lcov --output-path lcov.info
# Clean coverage data
cargo llvm-cov clean
Configuration
- Nextest config:
.config/nextest.toml - Coverage output:
target/llvm-cov/html/index.html
Current Coverage (search_files module)
Module: commands/search.rs
├── Region Coverage: 75.36%
├── Function Coverage: 69.05%
└── Line Coverage: 72.55%
Available Test Profiles
# Development (default)
cargo nextest run
# CI with retries
cargo nextest run --profile ci
# Coverage optimized
cargo nextest run --profile coverage