CLI tool
VirtusLab/scala-cli avatar
VirtusLab/scala-cli

scala-cli is now the official scala command, and it is not trying to be a build tool

Scala CLI is a command-line tool to interact with the Scala language. It lets you compile, run, test, and package your Scala code (and more!)

644 stars170 forksScalaApache-2.0

At a glance

What is it?
VirtusLab/scala-cli is an Apache-2.0 command line tool that compiles, runs, tests and packages Scala code, and since Scala 3.5.0 it is the official scala runner of the language under SIP-46. The README is explicit about the scope it declines: it does not aim at supporting multi-module projects, nor to be extended via a task system, which is the trade you are making when you pick it.
Who is it for?
Adopt scala-cli if you want a single command to run, test and package a Scala file without a build definition, and especially if you are working with the scala command that the language now ships through, because that path is the one the project supports. Do not adopt it as the build tool for a multi-module codebase or if you need custom tasks, since the README says it does not aim at either.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Scala, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

It became the scala command, which changes what it is for

Scala CLI is a command line tool to interact with the Scala language, letting you compile, run, test and package your Scala code, and more. The detail that reframes it is in the next paragraph of the README. As of Scala 3.5.0, Scala CLI has become the official scala runner of the language, under SIP-46, with a separate document covering how to use it through the scala command. That is a much stronger position than a third party tool, because it means the entry point most people type is the one this project implements. For a reader deciding whether to care, this is the fact to start from. The tool is not a popular alternative that might disappear, it is the implementation behind a command the language itself now resolves to, and the documentation site at scala-cli.virtuslab.org is where that behaviour is described.

The scope it declines is the scope you are choosing

The README says Scala CLI shares some similarities with build tools, but it does not aim at supporting multi-module projects, nor to be extended via a task system. That single sentence is the most important thing in the file, because it tells you exactly when not to use it. A single file, a script, a small playground, a repository where each file stands alone, or a snippet you want to run before deciding where it lives, all fit. A codebase with a dependency graph across modules, or a build where the release process is a sequence of custom steps, does not, and forcing it is a fight. The related consequence is that the tool is deliberately small in what it does. It has no task graph to learn and no plugin story to maintain, which is exactly why it can be the thing that runs when you type scala, and exactly why it will not replace sbt or Mill in a large repository.

The repository is a Mill build with separate launcher and source trees

The top level shows how the project is organised. The build is Mill, with build.mill, the mill and millw wrapper scripts, mill.bat, a .mill-version file and .mill-jvm-opts, plus a mill-build directory and a mill-native-image directory. There are two source trees, scala-cli and scala-cli-src, which matches the split between the launcher that users run and the implementation behind it. Then there is the modules directory for the components, project/ for shared build configuration, website/ for the documentation site, gcbenchmark/ for the garbage collection benchmark, gifs/ for the assets, and agentskills/ for agent skills. The quality tooling is configured in the repository too, with .scalafmt.conf, two .scalafix configurations and a .scala-steward.conf for dependency updates, and the governance files are CONTRIBUTING.md, CODE_OF_CONDUCT.md, AGENTS.md and LLM_POLICY.md. The last of those is worth reading before you contribute, since it tells you what the project expects from machine generated changes.

The Dockerfile is really a packaging test across Scala versions

The container definition is more interesting than a normal build image, because it uses the tool to test the tool. The build stage starts from eclipse-temurin:17 and builds a native image through the Mill task named for it:

dockerfile
FROM eclipse-temurin:17 as build
RUN apt update && apt install build-essential libz-dev clang procps git -y
WORKDIR /workdir
COPY . .
RUN ./mill -i copyTo --task 'cli[].base-image.nativeImage' --dest "./docker-out/scala-cli" 1>&2

The runtime stage is debian:stable-slim with the binary copied to /usr/bin/scala-cli, and then the image runs the tool three times, once per supported Scala version, which is the version list stated in the build:

dockerfile
RUN \
 echo "println(1)" | scala-cli -S 3 - -v -v -v && \
 echo "println(1)" | scala-cli -S 2.13 - -v -v -v && \
 echo "println(1)" | scala-cli -S 2.12 - -v -v -v

The next step exercises the two packaging modes, one for a native binary and one for a native image, again with a one line program piped in. The image therefore fails to build if any of those paths break, which is a neat way to keep a launcher for three Scala versions honest in one file.

Docs are split into user, developer and internals

The README is a signpost rather than a manual, and the destinations are worth mapping. There is user facing documentation at scala-cli.virtuslab.org, and separately a contributing guide, developer docs in DEV.md, app internals in INTERNALS.md, the website readme, a docs gifs readme, a sclicheck readme in modules/docs-tests, a gcbenchmark readme, the release procedure in .github/release, and the code of conduct. The split tells you where to look for a given question. If you want to run a file, the user site is the place. If you want to change the tool, DEV.md and the Mill build are the place. If you want to know how the launcher decides which JVM, which Scala version and which build to use, INTERNALS.md is where that is described, and the existence of a separate app internals document is a sign that the launcher is a substantial program in its own right rather than a thin wrapper.

When scala-cli is the wrong tool

There are two clear cases, and both are stated rather than inferred. The first is a multi-module project, which the README says the tool does not aim to support. The second is a build that needs a task system, which it also does not aim to provide. In practice the failure mode is not an error message but a workaround: you end up calling an external build tool from inside your workflow, which means you now maintain two build descriptions and you have lost the simplicity that made the tool appealing in the first place. The other limit is version coverage, since the packaging paths are exercised for 2.12, 2.13 and 3 and a codebase pinned to an older line is outside what the image tests. For those cases a conventional build tool is the right answer, and the difference in approach is the whole point. A build tool models a project as a graph of modules with tasks you can extend, and accepts the configuration that requires. Scala CLI models a project as a file you run, and accepts the constraint that keeps it small.

Apache-2.0, a nightly channel, and a project that moves weekly

The licence is Apache-2.0, with the LICENSE file at the repository root, which is the permissive choice for a tool that ends up in developer setups and CI images. The release history shows a steady line: v1.17.1 on 2026-09-17 and v1.18.0 on 2026-10-06, alongside a nightly tag that is described as the latest launchers and is dated 2021-08-13, so the nightly channel is long standing even though the tag itself has not moved recently. The repository is not archived and the last push was on 2026-10-20, two days before the current date, which for a project of this size says the work is continuous. The Maven Central badge points at the cli_3 artifact, so the launcher is also published as a library rather than only as a binary, and the .scala-steward.conf file in the root indicates that dependency updates are handled by a bot rather than by hand.

Editorial conclusion

Adopt scala-cli if you want a single command to run, test and package a Scala file without a build definition, and especially if you are working with the scala command that the language now ships through, because that path is the one the project supports. Do not adopt it as the build tool for a multi-module codebase or if you need custom tasks, since the README says it does not aim at either. Before you commit, check which Scala versions your build needs, since the packaging paths are exercised for 2.12, 2.13 and 3, and read LLM_POLICY.md and AGENTS.md in the repository if you intend to contribute.

Frequently asked questions

What is scala-cli and what can it do?

It is a command line tool to interact with the Scala language, letting you compile, run, test and package your Scala code, and more. The README notes that it shares some similarities with build tools but does not aim at supporting multi-module projects or being extended via a task system.

Is scala-cli the official scala command?

Yes, as of Scala 3.5.0. The README states that Scala CLI has become the official scala runner of the language, referring to SIP-46, with a separate document covering how to use it through the scala command.

Which Scala versions does scala-cli support?

The Docker image exercises the tool against Scala 3, 2.13 and 2.12 by piping a one line program into scala-cli with the corresponding -S option, so those three lines are the versions the packaging paths are tested against.

How is the scala-cli project built?

With Mill. The repository root carries build.mill, the mill and millw wrapper scripts, a .mill-version file and a mill-build directory, and the Dockerfile builds a native image through the Mill task cli[].base-image.nativeImage before copying it into a debian:stable-slim runtime image.

Can scala-cli handle a multi-module project?

No, and the README says so directly. The tool does not aim at supporting multi-module projects, nor to be extended via a task system, which is the boundary that makes it small enough to be the official scala runner.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. VirtusLab/scala-cli on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/virtuslab-scala-cli.svg)](https://hysenlabs.com/projects/virtuslab-scala-cli)

Was this useful?