No code in the build file.
A build file describes a project. It doesn’t run one. bleep.yaml is data: readable top to bottom by anyone on the team. Logic lives in your code, in your repo, where you can git blame it.
Two decades of Maven, Gradle, and sbt is a long time to watch build tools grow incredibly complex. We built one that won’t. Bleep does precisely what a build is for: compile, test, sourcegen, then package, link, publish what comes out. It refuses the rest. Your container build is code you write. So is your doc generation, your sidecar boot, your CI orchestration. All of it.
A real bleep.yaml. Not pseudocode. Not a marketing render. Plain YAML you can read, grep, diff, and rewrite. The same model bleep itself uses.
$schema: https://raw.githubusercontent.com/oyvindberg/bleep/master/schema.json
$version: 1.0.0-M9
jvm:
name: graalvm-community:25.0.1
projects:
myapp:
extends: template-common
platform:
mainClass: com.example.MainKt
myapp-test:
dependencies:
- io.kotest:kotest-runner-junit5-jvm:5.8.0
- org.junit.jupiter:junit-jupiter:5.10.1
dependsOn: myapp
extends: template-common
isTestProject: true
templates:
template-common:
kotlin:
jvmTarget: '25'
version: 2.3.0
platform:
name: jvm
A project is a project is a project. Code is code is code. Everything explicit, everything simple. Here’s how we got there.
A build file describes a project. It doesn’t run one. bleep.yaml is data: readable top to bottom by anyone on the team. Logic lives in your code, in your repo, where you can git blame it.
No autoplugins, no requires graphs, no Plugin<Project> registration. Bleep doesn’t have one. Code goes in your repo, where you can read it.
A test project is a project. A scripts project (your build code) is a project. Your production app is a project. Same fields, same dependency model, same bleep compile and bleep test, no second category. No Test/test/itTest/Compile scope dance grafted onto the project graph. A project is a project is a project.
There’s no user-definable task DAG. The build does compile, test, sourcegen. Everything else is a script: a main class you call when you want to. Composable like programs, debuggable like programs, no special layer between you and the JVM.
Two integration points cover what build plugins ever did. Most of that didn’t belong in the build to begin with.
Rules you don’t write, settings you can’t see, order you don’t control. Debugged with println.
Signing, containers, docs, CI glue: distribution. None of it runs when you save a file; none of it needs to be coupled to compile and test. Write a script, run it when you want it.
Bring a build plugin’s logic into bleep and it becomes one of two things.
sourcegen before compile.We verified this model three ways: by analyzing each of the top 50 Maven plugins, by implementing the hardest case (Spring Boot), and by shipping codebases of millions of lines on it.
Cut the code, the build plugins, the scopes, the task graph. The inner loop stops being something you wait for.
Native CLI binary. Reads bleep.yaml, resolves dependencies through Coursier’s local cache, builds the full project model. Done. No JVM startup, no configuration phase, no “loading projects…” progress bar. The compile daemon (bleep-bsp) is the JVM-heavy bit, and it stays hot between invocations.
Open a project the first time. Switch a branch with a different Kotlin version and reload. In Gradle or sbt that’s a configuration phase, plugin loading, dep resolution, and IDE model rebuild: minutes on real projects. Bleep reads bleep.yaml, builds the BSP model, syncs to the IDE. Initial import: a second or two. Branch reload: milliseconds.
One file changed in a 200-class module. Maven recompiles all 200, slowly. Bleep does file-level incremental compilation: one file changed, one (or two) recompiled, in milliseconds. The save-to-result loop stays tight.
The same simplification pays off again at CI scale. Build only what changed, pull the rest from cache: two commands, and your CI bill stops being a thing you complain about.
bleep build invalidated loads the build at two git refs, digests each project from config plus sources plus transitive deps, and prints the ones that differ. Both loads are instant because the build is data and dependency resolution is cached. Scope the rest of your CI run to those projects. Everything else is already green from the last build.
bleep remote-cache push uploads compile outputs to S3, keyed by a SHA-256 over config plus sources plus transitive deps. bleep remote-cache pull fetches them on the next run. Skip the compile entirely for projects that haven’t changed.
No transparent freshness checks across the network. You push when you want a cache populated, you pull when you want to use it. The fail-hard error model stays clean, your CI logs stay grep-able.
Build-as-data has one more payoff: bleep can rewrite its own input. update-deps, project-rename,templates-reapply: each reads the file, transforms the model, writes it back. No DSL to interpret, no build plugin lifecycle to mutate, just a small library of commands operating on the same model bleep itself uses.
Failures show up the moment they happen. Suites compile and run in parallel across every CPU, the terminal stays live, and the summary at the end is short enough to act on. No two-minute pause. No fifty-thousand-line transcript.
Test suites run in forked JVMs across every available CPU. Each test project gets its own classpath, its own JVM, its own lifecycle. The bottleneck is your hardware, not the build tool.
The terminal shows which suites are compiling, which are running, which finished, which failed. Failures land the instant they happen, not at the end of the run. Pass --no-tui for plain CI logs.
When the run ends, you get exact suite and test names, pass/fail counts per project, and the diff against the previous run, not a wall of stdout you have to grep through. JUnit XML is one flag away (--junit-report).
Claude Code, Cursor, and the next generation of dev tools talk to bleep through MCP, the Model Context Protocol. One command (bleep setup-mcp-server) and an agent can compile, test, run, and inspect your build through 18 structured tool calls. The design assumptions are blunt: more than one agent will be running at once, tokens are scarce, and any tool that takes seconds blocks every agent attached to it.
Four parallel agents on the same build mean four parallel tool calls. If the build tool is slow, every one of them stalls, aggregate latency multiplies. Bleep’s MCP server runs in-process against the BSP daemon. Every call is sub-second after warmup, sub-100ms once warm.
bleep setup-mcp-server writes.mcp.json in your build root. Any MCP client picks it up automatically. No adapter to configure, no protocol to translate, bleep speaks MCP natively.
Every tool returns a compact JSON summary by default : error counts, failure suites, the diff against the previous run. Full diagnostics are one extra call away. Per-project errors stream as MCP notifications the instant a project finishes, not at the end. Latency floor for surfacing a real problem: milliseconds.
Bleep reads your existing build and writes the equivalent bleep.yaml. Project graph derived, dependencies preserved, common configuration lifted into templates. You should have a compiling, testing build after one command.
bleep import for sbt projects, bleep import-maven for Maven. Both load your existing build, derive the project graph, infer templates from repeated configuration, and write bleep.yaml. Compile and test run immediately.
Import takes the generated files it finds on disk and freezes them as static sourcegen output. Compile works on day one. But once your schemas, grammars, or templates change, the frozen output is stale; you write a real sourcegen script then, calling the generator (protoc, antlr, openapi-generator, JAXB) directly. Typically tens of lines.
Coming from Gradle? Gradle import may come later; hand-porting is the path today.
Bleep is open source under Apache 2.0. Java, Kotlin, and Scala on the JVM. Cross-build to JS and Native if you want. Or don’t.