|
| 1 | +# AGENTS.md — Guidance for AI agents contributing to Scala CLI |
| 2 | + |
| 3 | +Short reference for AI agents. For task-specific guidance (directives, integration tests), load skills from * |
| 4 | +*[agentskills/](agentskills/)** when relevant. |
| 5 | + |
| 6 | +> **LLM Policy**: All AI-assisted contributions must comply with the |
| 7 | +> [LLM usage policy](https://github.com/scala/scala3/blob/HEAD/LLM_POLICY.md). The contributor (human) is responsible |
| 8 | +> for every line. State LLM usage in the PR description. See [LLM_POLICY.md](LLM_POLICY.md). |
| 9 | +
|
| 10 | +## Human-facing docs |
| 11 | + |
| 12 | +- **[DEV.md](DEV.md)** — Setup, run from source, tests, launchers, GraalVM. |
| 13 | +- **[CONTRIBUTING.md](CONTRIBUTING.md)** — PR workflow, formatting, reference doc generation. |
| 14 | +- **[INTERNALS.md](INTERNALS.md)** — Modules, `Inputs → Sources → Build`, preprocessing. |
| 15 | + |
| 16 | +## Build system |
| 17 | + |
| 18 | +The project uses [Mill](https://mill-build.org/). Mill launchers ship with the repo (`./mill`). JVM 17 required. |
| 19 | +Cross-compilation: default `Scala.defaultInternal`; `[]` = default version, `[_]` = all. |
| 20 | + |
| 21 | +### Key build files |
| 22 | + |
| 23 | +| File | Purpose | |
| 24 | +|---------------------------------|------------------------------------------------------------------------------------------| |
| 25 | +| `build.mill` | Root build definition: all module declarations, CI helper tasks, integration test wiring | |
| 26 | +| `project/deps/package.mill` | Dependency versions and definitions (`Deps`, `Scala`, `Java` objects) | |
| 27 | +| `project/settings/package.mill` | Shared traits, utils (`HasTests`, `CliLaunchers`, `FormatNativeImageConf`, etc.) | |
| 28 | +| `project/publish/package.mill` | Publishing settings | |
| 29 | +| `project/website/package.mill` | Website-related build tasks | |
| 30 | + |
| 31 | +### Essential commands |
| 32 | + |
| 33 | +```bash |
| 34 | +./mill -i clean # Clean Mill context |
| 35 | +./mill -i scala …args… # Run Scala CLI from source |
| 36 | +./mill -i __.compile # Compile everything |
| 37 | +./mill -i unitTests # All unit tests |
| 38 | +./mill -i 'build-module[].test' # Unit tests for a specific module |
| 39 | +./mill -i 'build-module[].test' 'scala.build.tests.BuildTestsScalac.*' # Filter by suite |
| 40 | +./mill -i 'build-module[].test' 'scala.build.tests.BuildTests.simple' # Single test by name |
| 41 | +./mill -i integration.test.jvm # Integration tests (JVM launcher) |
| 42 | +./mill -i integration.test.jvm 'scala.cli.integration.RunTestsDefault.*' # Integration: filter by suite |
| 43 | +./mill -i 'generate-reference-doc[]'.run # Regenerate reference docs |
| 44 | +./mill -i __.fix # Fix import ordering (scalafix) |
| 45 | +scala-cli fmt . # Format all code (scalafmt) |
| 46 | +``` |
| 47 | + |
| 48 | +## Project modules |
| 49 | + |
| 50 | +Modules live under `modules/`. The dependency graph flows roughly as: |
| 51 | + |
| 52 | +``` |
| 53 | +specification-level → config → core → options → directives → build-module → cli |
| 54 | +``` |
| 55 | + |
| 56 | +### Module overview |
| 57 | + |
| 58 | +The list below may not be exhaustive — check `modules/` and `build.mill` for the current set. |
| 59 | + |
| 60 | +| Module | Purpose | |
| 61 | +|-----------------------------------------------|------------------------------------------------------------------------------------------------------------------| |
| 62 | +| `specification-level` | Defines `SpecificationLevel` (MUST / SHOULD / IMPLEMENTATION / RESTRICTED / EXPERIMENTAL) for SIP-46 compliance. | |
| 63 | +| `config` | Scala CLI configuration keys and persistence. | |
| 64 | +| `build-macros` | Compile-time macros (e.g. `EitherCps`). | |
| 65 | +| `core` | Core types: `Inputs`, `Sources`, build constants, Bloop integration, JVM/JS/Native tooling. | |
| 66 | +| `options` | `BuildOptions`, `SharedOptions`, and all option types. | |
| 67 | +| `directives` | Using directive handlers — the bridge between `//> using` directives and `BuildOptions`. | |
| 68 | +| `build-module` (aliased from `build` in mill) | The main build pipeline: preprocessing, compilation, post-processing. Most business logic lives here. | |
| 69 | +| `cli` | Command definitions, argument parsing (CaseApp), the `ScalaCli` entry point. Packaged as the native image. | |
| 70 | +| `runner` | Lightweight app that runs a main class and pretty-prints exceptions. Fetched at runtime. | |
| 71 | +| `test-runner` | Discovers and runs test frameworks/suites. Fetched at runtime. | |
| 72 | +| `tasty-lib` | Edits file names in `.tasty` files for source mapping. | |
| 73 | +| `scala-cli-bsp` | BSP protocol types. | |
| 74 | +| `integration` | Integration tests (see dedicated section below). | |
| 75 | +| `docs-tests` | Tests that validate documentation (`Sclicheck`). | |
| 76 | +| `generate-reference-doc` | Generates reference documentation from CLI option/directive metadata. | |
| 77 | + |
| 78 | +## Specification levels |
| 79 | + |
| 80 | +Every command, CLI option, and using directive has a `SpecificationLevel`. This is central to how features are exposed. |
| 81 | + |
| 82 | +| Level | In the Scala Runner spec? | Available without `--power`? | Stability | |
| 83 | +|------------------|---------------------------|------------------------------|---------------------------------| |
| 84 | +| `MUST` | Yes | Yes | Stable | |
| 85 | +| `SHOULD` | Yes | Yes | Stable | |
| 86 | +| `IMPLEMENTATION` | No | Yes | Stable | |
| 87 | +| `RESTRICTED` | No | No (requires `--power`) | Stable | |
| 88 | +| `EXPERIMENTAL` | No | No (requires `--power`) | Unstable — may change/disappear | |
| 89 | + |
| 90 | +**New features contributed by agents should generally be marked `EXPERIMENTAL`** unless the maintainers explicitly |
| 91 | +request otherwise. This applies to new sub-commands, options, and directives alike. |
| 92 | + |
| 93 | +The specification level is set via: |
| 94 | + |
| 95 | +- **Directives**: `@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)` annotation on the directive case class. |
| 96 | +- **CLI options**: `@Tag(tags.experimental)` annotation on option fields. |
| 97 | +- **Commands**: Override `scalaSpecificationLevel` in the command class. |
| 98 | + |
| 99 | +## Using directives |
| 100 | + |
| 101 | +Using directives are in-source configuration comments: |
| 102 | + |
| 103 | +```scala |
| 104 | +//> using scala 3 |
| 105 | +//> using dep com.lihaoyi::os-lib:0.11.4 |
| 106 | +//> using test.dep org.scalameta::munit::1.1.1 |
| 107 | +``` |
| 108 | + |
| 109 | +Directives are parsed by `using_directives`, then `ExtractedDirectives` → `DirectivesPreprocessor` → `BuildOptions`/ |
| 110 | +`BuildRequirements`. **CLI options override directive values.** To add a new directive, |
| 111 | +see [agentskills/adding-directives/](agentskills/adding-directives/SKILL.md). |
| 112 | + |
| 113 | +## Testing |
| 114 | + |
| 115 | +> **Every contribution that changes logic must include automated tests.** A PR without tests for |
| 116 | +> new or changed behavior will not be accepted. If testing is truly infeasible, explain why in the |
| 117 | +> PR description — but this should be exceptional. |
| 118 | +
|
| 119 | +> **Unit tests are always preferred over integration tests.** Unit tests are faster, more reliable, |
| 120 | +> easier to debug, and cheaper to run on CI. Only add integration tests when the behavior cannot be |
| 121 | +> adequately verified at the unit level (e.g. end-to-end CLI invocation, launcher-specific behavior, |
| 122 | +> cross-process interactions). |
| 123 | +
|
| 124 | +> **Always re-run and verify tests locally before submitting.** After any logic change, run the |
| 125 | +> relevant test suites on your machine and confirm they pass. Do not rely on CI to catch failures — |
| 126 | +> CI resources are shared, and broken PRs waste maintainer time. |
| 127 | +
|
| 128 | +**Unit tests**: munit, in each module’s `test` submodule. Run commands above; add tests in `modules/build/.../tests/` or |
| 129 | +`modules/cli/src/test/scala/`. Prefer unit over integration. |
| 130 | + |
| 131 | +**Integration tests**: `modules/integration/`; they run the CLI as a subprocess. |
| 132 | +See [agentskills/integration-tests/](agentskills/integration-tests/SKILL.md) for structure and how to add tests. |
| 133 | + |
| 134 | +## Pre-PR checklist |
| 135 | + |
| 136 | +1. Code compiles: `./mill -i __.compile` |
| 137 | +2. Tests added and passing locally (unit tests first, integration if needed) |
| 138 | +3. Code formatted: `scala-cli fmt .` |
| 139 | +4. Imports ordered: `./mill -i __.fix` |
| 140 | +5. Reference docs regenerated (if options/directives changed): `./mill -i 'generate-reference-doc[]'.run` |
| 141 | +6. PR template filled, LLM usage stated |
| 142 | + |
| 143 | +## Code style |
| 144 | + |
| 145 | +Code style is enforced. |
| 146 | + |
| 147 | +**Scala 3**: Prefer `if … then … else`, `for … do`/`yield`, `enum`, `extension`, `given`/`using`, braceless blocks, |
| 148 | +top-level defs. Use union/intersection types when they simplify signatures. Always favor Scala 3 idiomatic syntax. |
| 149 | + |
| 150 | +**Functional**: Prefer `val`, immutable collections, `case class`.copy(). Prefer expressions over statements; prefer |
| 151 | +`map`/`flatMap`/`fold`/`for`-comprehensions over loops. Use `@tailrec` for tail recursion. Avoid `null`; use `Option`/ |
| 152 | +`Either`/`EitherCps` (build-macros). Keep functions small; extract helpers. |
| 153 | + |
| 154 | +**No duplication**: Extract repeated logic into shared traits or utils (`*Options` traits, companion helpers, |
| 155 | +`CommandHelpers`, `TestUtil`). Check for existing abstractions before copying. |
| 156 | + |
| 157 | +**Logging**: Use the project `Logger` only — never `System.err` or `System.out`. Logger respects verbosity (`-v`, `-q`). |
| 158 | +Use `logger.message(msg)` (default), `logger.log(msg)` (verbose), `logger.debug(msg)` (debug), `logger.error(msg)` ( |
| 159 | +always). In commands: `options.shared.logging.logger`; in build code it is passed in; in tests use `TestLogger`. |
| 160 | + |
| 161 | +**Mutability**: OK in hot paths or when a Java API requires it; keep scope minimal. |
| 162 | + |
| 163 | +## Further reference |
| 164 | + |
| 165 | +[DEV.md](DEV.md), [CONTRIBUTING.md](CONTRIBUTING.md), [INTERNALS.md](INTERNALS.md). |
0 commit comments