Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
90aa8d7
Bump Mill to 1.1.3 (was 1.1.2) (#4169)
Gedochao Mar 9, 2026
e4acce5
Bump @algolia/client-search in /website in the npm-dependencies group…
dependabot[bot] Mar 10, 2026
9c2adc0
Bump the github-actions group with 4 updates (#4172)
dependabot[bot] Mar 10, 2026
4763be0
Update Scala 3 Next RC to 3.8.3-RC2 (#4175)
Gedochao Mar 12, 2026
1631f8c
Refer the Scala 3 compiler policy on usage of LLM-based tools in cont…
Gedochao Mar 12, 2026
03ec06a
Add a PR template
Gedochao Mar 12, 2026
48fa471
Support `--cross` with the `package` sub-command (#4171)
Gedochao Mar 12, 2026
6c20fe0
Allow to `--watch` extra paths with `--watching` (#4174)
Gedochao Mar 12, 2026
2c22e11
Merge pull request #4177 from Gedochao/dx/contributions
Gedochao Mar 13, 2026
f0fc0c3
Bump undici from 7.18.2 to 7.24.1 in /website (#4182)
dependabot[bot] Mar 15, 2026
bdaa4f4
Use targeted Java/Scala mappings with the `doc` sub-command (#4180)
Gedochao Mar 15, 2026
bd1b92e
Bump webfactory/ssh-agent in the github-actions group (#4187)
dependabot[bot] Mar 17, 2026
c1d45d7
Correct Native test bridge error message and parse META-INF service f…
Gedochao Mar 17, 2026
ab8f4b9
Bump `coursier` to 2.1.25-M24 (#4184)
Gedochao Mar 17, 2026
4c529d6
Add support for `--cross` in the `doc` sub-command (#4183)
Gedochao Mar 17, 2026
bc4f0b0
Bump sass in /website in the npm-dependencies group (#4188)
dependabot[bot] Mar 17, 2026
7df6c78
Warn when `.java` & `.scala` sources are used in a mixed compilation …
Gedochao Mar 17, 2026
2352850
Add AGENTS.md & `agentskills` for directives & integration tests (#4178)
Gedochao Mar 18, 2026
5ed4f79
Add support for local `.m2` in `publish local` (#4179)
Gedochao Mar 18, 2026
039e6e1
Add WASM support: --wasm flag with Node.js and Deno runtimes
Mar 6, 2026
40df564
Review fixes: remove runtime download and unsupported standalone runt…
Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!-- Fixes #XYZ (where XYZ is the issue number from the issue tracker) -->

<!-- TODO description of the change -->

<!-- if the PR is still a WIP, create it as a draft PR (or convert it into one) -->

## Checklist
- [ ] tested the solution locally and it works
- [ ] ran the code formatter (`scala-cli fmt .`)
- [ ] ran `scalafix` (`./mill -i __.fix`)
- [ ] ran reference docs auto-generation (`./mill -i 'generate-reference-doc[]'.run`)

## How much have your relied on LLM-based tools in this contribution?

<!--
State clearly in the pull request description,
whether LLM-based tools were used and to what extent

(extensively/moderately/minimally/not at all)
-->

<!--
Refer to our [LLM usage policy](https://github.com/scala/scala3/blob/main/LLM_POLICY.md) for rules and guidelines
regarding usage of LLM-based tools in contributions.
-->

## How was the solution tested?

<!--
If automated tests are included, mention it.
If they are not, explain why and how the solution was tested.
-->

## Additional notes

<!-- Placeholder for any extra context regarding this contribution. -->

<!-- When in doubt, check[our contribution guide](https://github.com/VirtusLab/scala-cli/blob/main/CONTRIBUTING.md) -->
2 changes: 1 addition & 1 deletion .github/scripts/get-latest-cs.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -e

CS_VERSION="2.1.25-M23"
CS_VERSION="2.1.25-M24"

DIR="$(cs get --archive "https://github.com/coursier/coursier/releases/download/v$CS_VERSION/cs-x86_64-pc-win32.zip")"

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,7 @@ jobs:
path: test-report.xml
- name: Login to GitHub Container Registry
if: startsWith(github.ref, 'refs/tags/v')
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Expand Down Expand Up @@ -1178,7 +1178,7 @@ jobs:
path: test-report.xml
- name: Login to GitHub Container Registry
if: startsWith(github.ref, 'refs/tags/v')
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Expand Down Expand Up @@ -1456,7 +1456,7 @@ jobs:
MILL_PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
MILL_SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
MILL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
- uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555
with:
ssh-private-key: |
${{ secrets.SSH_PRIVATE_KEY_SCALA_CLI }}
Expand Down Expand Up @@ -1624,7 +1624,7 @@ jobs:
- name: Display structure of downloaded files
run: ls -R
working-directory: artifacts/
- uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555
with:
ssh-private-key: |
${{ secrets.SCALA_CLI_PACKAGES_KEY }}
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
uses: actions/checkout@v6

- name: Log in to the Container registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_LOGIN }}
Expand All @@ -44,19 +44,19 @@ jobs:
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4

# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
file: ${{ env.DOCKERFILE }}
Expand Down Expand Up @@ -106,18 +106,18 @@ jobs:
merge-multiple: true

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4

- name: Log in to the Container registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_LOGIN }}
password: ${{ env.REGISTRY_PASSWORD }}

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

Expand Down
2 changes: 1 addition & 1 deletion .mill-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.2
1.1.3
165 changes: 165 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# AGENTS.md — Guidance for AI agents contributing to Scala CLI

Short reference for AI agents. For task-specific guidance (directives, integration tests), load skills from *
*[agentskills/](agentskills/)** when relevant.

> **LLM Policy**: All AI-assisted contributions must comply with the
> [LLM usage policy](https://github.com/scala/scala3/blob/HEAD/LLM_POLICY.md). The contributor (human) is responsible
> for every line. State LLM usage in the PR description. See [LLM_POLICY.md](LLM_POLICY.md).

## Human-facing docs

- **[DEV.md](DEV.md)** — Setup, run from source, tests, launchers, GraalVM.
- **[CONTRIBUTING.md](CONTRIBUTING.md)** — PR workflow, formatting, reference doc generation.
- **[INTERNALS.md](INTERNALS.md)** — Modules, `Inputs → Sources → Build`, preprocessing.

## Build system

The project uses [Mill](https://mill-build.org/). Mill launchers ship with the repo (`./mill`). JVM 17 required.
Cross-compilation: default `Scala.defaultInternal`; `[]` = default version, `[_]` = all.

### Key build files

| File | Purpose |
|---------------------------------|------------------------------------------------------------------------------------------|
| `build.mill` | Root build definition: all module declarations, CI helper tasks, integration test wiring |
| `project/deps/package.mill` | Dependency versions and definitions (`Deps`, `Scala`, `Java` objects) |
| `project/settings/package.mill` | Shared traits, utils (`HasTests`, `CliLaunchers`, `FormatNativeImageConf`, etc.) |
| `project/publish/package.mill` | Publishing settings |
| `project/website/package.mill` | Website-related build tasks |

### Essential commands

```bash
./mill -i clean # Clean Mill context
./mill -i scala …args… # Run Scala CLI from source
./mill -i __.compile # Compile everything
./mill -i unitTests # All unit tests
./mill -i 'build-module[].test' # Unit tests for a specific module
./mill -i 'build-module[].test' 'scala.build.tests.BuildTestsScalac.*' # Filter by suite
./mill -i 'build-module[].test' 'scala.build.tests.BuildTests.simple' # Single test by name
./mill -i integration.test.jvm # Integration tests (JVM launcher)
./mill -i integration.test.jvm 'scala.cli.integration.RunTestsDefault.*' # Integration: filter by suite
./mill -i 'generate-reference-doc[]'.run # Regenerate reference docs
./mill -i __.fix # Fix import ordering (scalafix)
scala-cli fmt . # Format all code (scalafmt)
```

## Project modules

Modules live under `modules/`. The dependency graph flows roughly as:

```
specification-level → config → core → options → directives → build-module → cli
```

### Module overview

The list below may not be exhaustive — check `modules/` and `build.mill` for the current set.

| Module | Purpose |
|-----------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| `specification-level` | Defines `SpecificationLevel` (MUST / SHOULD / IMPLEMENTATION / RESTRICTED / EXPERIMENTAL) for SIP-46 compliance. |
| `config` | Scala CLI configuration keys and persistence. |
| `build-macros` | Compile-time macros (e.g. `EitherCps`). |
| `core` | Core types: `Inputs`, `Sources`, build constants, Bloop integration, JVM/JS/Native tooling. |
| `options` | `BuildOptions`, `SharedOptions`, and all option types. |
| `directives` | Using directive handlers — the bridge between `//> using` directives and `BuildOptions`. |
| `build-module` (aliased from `build` in mill) | The main build pipeline: preprocessing, compilation, post-processing. Most business logic lives here. |
| `cli` | Command definitions, argument parsing (CaseApp), the `ScalaCli` entry point. Packaged as the native image. |
| `runner` | Lightweight app that runs a main class and pretty-prints exceptions. Fetched at runtime. |
| `test-runner` | Discovers and runs test frameworks/suites. Fetched at runtime. |
| `tasty-lib` | Edits file names in `.tasty` files for source mapping. |
| `scala-cli-bsp` | BSP protocol types. |
| `integration` | Integration tests (see dedicated section below). |
| `docs-tests` | Tests that validate documentation (`Sclicheck`). |
| `generate-reference-doc` | Generates reference documentation from CLI option/directive metadata. |

## Specification levels

Every command, CLI option, and using directive has a `SpecificationLevel`. This is central to how features are exposed.

| Level | In the Scala Runner spec? | Available without `--power`? | Stability |
|------------------|---------------------------|------------------------------|---------------------------------|
| `MUST` | Yes | Yes | Stable |
| `SHOULD` | Yes | Yes | Stable |
| `IMPLEMENTATION` | No | Yes | Stable |
| `RESTRICTED` | No | No (requires `--power`) | Stable |
| `EXPERIMENTAL` | No | No (requires `--power`) | Unstable — may change/disappear |

**New features contributed by agents should generally be marked `EXPERIMENTAL`** unless the maintainers explicitly
request otherwise. This applies to new sub-commands, options, and directives alike.

The specification level is set via:

- **Directives**: `@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)` annotation on the directive case class.
- **CLI options**: `@Tag(tags.experimental)` annotation on option fields.
- **Commands**: Override `scalaSpecificationLevel` in the command class.

## Using directives

Using directives are in-source configuration comments:

```scala
//> using scala 3
//> using dep com.lihaoyi::os-lib:0.11.4
//> using test.dep org.scalameta::munit::1.1.1
```

Directives are parsed by `using_directives`, then `ExtractedDirectives` → `DirectivesPreprocessor` → `BuildOptions`/
`BuildRequirements`. **CLI options override directive values.** To add a new directive,
see [agentskills/adding-directives/](agentskills/adding-directives/SKILL.md).

## Testing

> **Every contribution that changes logic must include automated tests.** A PR without tests for
> new or changed behavior will not be accepted. If testing is truly infeasible, explain why in the
> PR description — but this should be exceptional.

> **Unit tests are always preferred over integration tests.** Unit tests are faster, more reliable,
> easier to debug, and cheaper to run on CI. Only add integration tests when the behavior cannot be
> adequately verified at the unit level (e.g. end-to-end CLI invocation, launcher-specific behavior,
> cross-process interactions).

> **Always re-run and verify tests locally before submitting.** After any logic change, run the
> relevant test suites on your machine and confirm they pass. Do not rely on CI to catch failures —
> CI resources are shared, and broken PRs waste maintainer time.

**Unit tests**: munit, in each module’s `test` submodule. Run commands above; add tests in `modules/build/.../tests/` or
`modules/cli/src/test/scala/`. Prefer unit over integration.

**Integration tests**: `modules/integration/`; they run the CLI as a subprocess.
See [agentskills/integration-tests/](agentskills/integration-tests/SKILL.md) for structure and how to add tests.

## Pre-PR checklist

1. Code compiles: `./mill -i __.compile`
2. Tests added and passing locally (unit tests first, integration if needed)
3. Code formatted: `scala-cli fmt .`
4. Imports ordered: `./mill -i __.fix`
5. Reference docs regenerated (if options/directives changed): `./mill -i 'generate-reference-doc[]'.run`
6. PR template filled, LLM usage stated

## Code style

Code style is enforced.

**Scala 3**: Prefer `if … then … else`, `for … do`/`yield`, `enum`, `extension`, `given`/`using`, braceless blocks,
top-level defs. Use union/intersection types when they simplify signatures. Always favor Scala 3 idiomatic syntax.

**Functional**: Prefer `val`, immutable collections, `case class`.copy(). Prefer expressions over statements; prefer
`map`/`flatMap`/`fold`/`for`-comprehensions over loops. Use `@tailrec` for tail recursion. Avoid `null`; use `Option`/
`Either`/`EitherCps` (build-macros). Keep functions small; extract helpers.

**No duplication**: Extract repeated logic into shared traits or utils (`*Options` traits, companion helpers,
`CommandHelpers`, `TestUtil`). Check for existing abstractions before copying.

**Logging**: Use the project `Logger` only — never `System.err` or `System.out`. Logger respects verbosity (`-v`, `-q`).
Use `logger.message(msg)` (default), `logger.log(msg)` (verbose), `logger.debug(msg)` (debug), `logger.error(msg)` (
always). In commands: `options.shared.logging.logger`; in build code it is passed in; in tests use `TestLogger`.

**Mutability**: OK in hot paths or when a Java API requires it; keep scope minimal.

## Further reference

[DEV.md](DEV.md), [CONTRIBUTING.md](CONTRIBUTING.md), [INTERNALS.md](INTERNALS.md).
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ A subsequent PR from `stable` back to `main` is created automatically.
Whenever reasonable, we try to follow the following set of rules when merging code to the repository. Following those
will save you from getting a load of comments and speed up the code review.

- If you are using LLM-based tools to assist you in your contribution, state that clearly in the PR description
and refer to our [LLM usage policy](LLM_POLICY.md) for rules and guidelines regarding usage of LLM-based tools
in contributions.
- If the PR is meant to be merged as a single commit (`squash & merge`), please make sure that you modify only one
thing.
- This means such a PR shouldn't include code clean-up, a secondary feature or bug fix, just the single thing
Expand All @@ -54,7 +57,7 @@ will save you from getting a load of comments and speed up the code review.

Other notes:

- give a short explanation on what the PR is meant to achieve in the description, unless covered by the PR title;
- fill the pull request template;
- make sure to add tests wherever possible;
- favor unit tests over integration tests where applicable;
- try to add scaladocs for key classes and functions;
Expand Down
7 changes: 7 additions & 0 deletions LLM_POLICY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Policy regarding LLM-generated code in contributions to Scala CLI

Scala CLI accepts contributions containing code produced with AI assistance. This means that using LLM-based
tooling aiding software development (like Cursor, Claude Code, Copilot or whatever else) is allowed.

All such contributions are regulated by the policy defined in the Scala 3 compiler repository, which can be found at:
https://github.com/scala/scala3/blob/main/LLM_POLICY.md
5 changes: 5 additions & 0 deletions agentskills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Agent skills (Scala CLI)

This directory holds **agent skills** — task-specific guidance loaded on demand by AI coding agents. The layout is tool-agnostic; Cursor, Claude Code, Codex, and other tools that support a standard skill directory can use this (e.g. by configuring or symlinking to `.agents/skills/` if required).

Each subdirectory contains a `SKILL.md` with frontmatter and instructions. See [agentskills/agentskills](https://github.com/agentskills/agentskills) for the open standard.
21 changes: 21 additions & 0 deletions agentskills/adding-directives/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: scala-cli-adding-directives
description: Add or change using directives in Scala CLI. Use when adding a new //> using directive, registering a directive handler, or editing directive preprocessing.
---

# Adding a new directive (Scala CLI)

1. **Create a case class** in `modules/directives/src/main/scala/scala/build/preprocessing/directives/` extending one of:
- `HasBuildOptions` — produces `BuildOptions` directly
- `HasBuildOptionsWithRequirements` — produces `BuildOptions` with scoped requirements (e.g. `test.dep`)
- `HasBuildRequirements` — produces `BuildRequirements` (for `//> require`)

2. **Annotate**: `@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)`, `@DirectiveDescription("…")`, `@DirectiveUsage("…")`, `@DirectiveExamples("…")`, `@DirectiveName("key")` on fields.

3. **Companion**: `val handler: DirectiveHandler[YourDirective] = DirectiveHandler.derive`

4. **Register** in `modules/build/.../DirectivesPreprocessingUtils.scala` in the right list: `usingDirectiveHandlers`, `usingDirectiveWithReqsHandlers`, or `requireDirectiveHandlers`.

5. **Regenerate reference docs**: `./mill -i 'generate-reference-doc[]'.run`

CLI options always override directive values when both set the same thing.
Loading
Loading