A Cargo-like build tool for Scala projects.
sinter new <name>— create a new Scala projectsinter init— initialize a workspacesinter build— build the project with dependency resolutionsinter run— run a Scala application (single file or project)sinter add <dep>— add a dependency toproject.tomlsinter test— run testssinter workspace add <path>— add a member to the workspace- Plugin system — extend sinter with custom commands via
CommandHandlertrait
git clone https://github.com/s0raLin/sinter.git
cd sinter
cargo build --release
# Add target/release/sinter to your PATH- Rust (latest stable)
- Scala CLI (
scala-cli) — for compilation and execution. Install:curl -fL https://github.com/VirtusLab/scala-cli/releases/latest/download/scala-cli-x86_64-pc-linux.gz | gzip -d > scala-cli && chmod +x scala-cli && mv scala-cli ~/.local/bin/
# Create a new project
sinter new hello-scala
cd hello-scala
# Add a dependency (Scala format — group::artifact:version)
sinter add org.typelevel::cats-core:2.10.0
# Build and run
sinter build
sinter runCreates a new Scala project with the following structure:
hello-scala/
├── project.toml
└── src/main/scala/
└── Main.scala
Initializes a workspace by creating a project.toml with a [workspace] section in the current directory.
Adds an existing project to the workspace. <path> is relative to the workspace root.
Compiles all Scala sources. Automatically resolves transitive dependencies and sets up BSP for IDE integration.
- In a workspace: builds all members
- In a workspace sub-directory: builds only the current member
- Standalone project: builds the single project
Runs a Scala application.
- Without arguments: runs the main file specified in
project.toml(mainfield in[package]) sinter run src/main/scala/App.scala— runs a specific filesinter run --lib— compile only (library mode)
Runs tests in the project's test directory (test_dir in project.toml, default src/test/scala).
Adds a dependency to project.toml and validates it.
Dependency format:
| Format | Example | Type |
|---|---|---|
group::artifact:version |
org.typelevel::cats-core:2.10.0 |
Scala (with ::) |
group:artifact:version |
com.google.guava:guava:31.1-jre |
Java |
sbt:path |
sbt:../my-sbt-project |
SBT sub-project |
- For Scala dependencies you may specify a Scala version:
group::artifact@2.13:version - Dependencies added inside a workspace root are added as workspace dependencies
[package]
name = "my-project"
version = "0.1.0"
main = "Main"
scala_version = "2.13"
source_dir = "src/main/scala"
target_dir = "target"
test_dir = "src/test/scala"
backend = "scala-cli"
[dependencies]
"org.typelevel::cats-core" = "2.10.0"| Field | Default | Description |
|---|---|---|
name |
— | Project name (required) |
version |
— | Project version (required) |
main |
Main |
Main class name (without .scala) |
scala_version |
2.13 |
Scala version (2.13 or 3.x) |
source_dir |
src/main/scala |
Source directory |
target_dir |
target |
Output directory |
test_dir |
src/test/scala |
Test directory |
backend |
scala-cli |
Build backend (scala-cli/sbt/gradle/maven) |
Workspaces define a [workspace] section instead of [package]:
[workspace]
members = ["member-a", "member-b"]
[workspace.dependencies]
"org.typelevel::cats-core" = "2.10.0"Sinter has a lightweight plugin architecture. Plugin commands are registered at startup via the builder pattern:
use sinter::{Sinter, CommandHandler};
use sinter_plugins::jsp_plugin;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
Sinter::new()
.plugin(jsp_plugin())
.run()
.await
}Built-in plugin: JSP project generator (sinter jsp <name>)
To create your own plugin, implement the CommandHandler trait:
use async_trait::async_trait;
use clap::{Arg, ArgMatches, Command};
use sinter::CommandHandler;
use std::path::PathBuf;
pub struct MyPlugin;
#[async_trait]
impl CommandHandler for MyPlugin {
fn name(&self) -> &'static str { "mycommand" }
fn about(&self) -> &'static str { "Description of my command" }
fn configure(&self, cmd: Command) -> Command {
cmd.about(self.about())
.arg(Arg::new("input").help("Input value").required(true))
}
async fn execute(&self, matches: &ArgMatches, cwd: &PathBuf) -> anyhow::Result<()> {
let input = matches.get_one::<String>("input").unwrap();
println!("Processing: {}", input);
Ok(())
}
}crates/
├── sinter-core/ # Core library — models, deps, build, CLI, plugin trait
├── sinter-plugins/ # Official plugins (JSP generator)
└── sinter-cli/ # Binary entry point
cargo test # 53 unit tests across models, deps, and CLI parsing- Scala CLI not found: Install it (see Prerequisites). Sinter will attempt auto-download once.
sinter buildhangs: On first run, Scala CLI may download Coursier metadata (~30s timeout). Wait or press Ctrl+C and try again.- Dependency format error: Use
group::artifact:version(Scala) orgroup:artifact:version(Java). - "latest" version rejected: Sinter requires explicit version numbers for reproducibility.
MIT — see LICENSE.