Skip to content

Native worksheet replacement: run scripts against a build module's classpath via BSP #4321

Description

@Awethon

Is your feature request related to a problem? Please describe.

I'm migrating off IntelliJ's Scala worksheets (.sc) and there's no native scala-cli equivalent for what made them useful. In a real project, a worksheet can execute scratch code against an existing build module's full classpath. That means the module's compiled classes plus every resolved dependency, including private artifacts from authenticated repositories, while staying completely outside the build's compiled sources. That isolation is the key property. A broken or outdated worksheet never breaks compile or CI, so nobody is forced to fix throwaway debug code.

To reproduce this with scala-cli today I have to manually export the classpath from the build tool and wire it up myself:

CP=$(sbt -batch -error "export myModule/Runtime/fullClasspath" | tail -1)
scala-cli run scratch.sc --scala 2.13.18 --classpath "$CP"

This works, but it requires a wrapper script, manual caching of the exported classpath, pinning the Scala version to match the module, and there's a stale-classes caveat (you have to remember to recompile the project for the script to see code changes). It's enough friction that it doesn't feel like a first-class worksheet replacement.

Describe the solution you'd like

A native way to point a scala-cli script at an existing build module and inherit its classpath automatically, ideally over BSP so it works across sbt, mill, and others. Something like:

scala-cli run scratch.sc --bsp-module domain
# or
scala-cli run scratch.sc --sbt-project http --module-classpath runtime

scala-cli would discover the module via the existing BSP connection, use its already-resolved dependency classpath (private repos included, so resolution stays the build tool's job) and its compiled output, compile the script standalone against that, and run it. The script itself would stay outside the build's source set, which preserves the worksheet-style isolation. As a bonus, it could trigger a recompile of the module first (so the script always sees current code) and match the module's Scala version automatically.

Describe alternatives you've considered

  • Manual --classpath from sbt export (my current approach): functional, but needs a wrapper, classpath caching, version pinning, and has the stale-classes gotcha.
  • Promoting scratch files into a compiled build module (for example an object Main run via sbt runMain): this loses the whole point, because the build and CI now compile them, so a stale debug script breaks everyone's build.
  • sbt console or Ammonite: REPL-oriented, not file-based, version-controllable, or easily repeatable. Ammonite also has to re-resolve the build's private dependencies itself, which is the painful part.
  • Staying on IntelliJ worksheets: ties the workflow to one IDE, which is exactly what I'm trying to avoid.

Additional context

The dependencies in these projects come from an authenticated, private artifact registry that the build tool is already configured to resolve. Re-declaring or re-resolving them in scala-cli (via //> using dep and //> using repository plus credentials) is the most painful part, so the goal is specifically to reuse the build's existing resolution rather than duplicate it. BSP seems like the natural mechanism, since scala-cli already speaks it, and IDE worksheets effectively rely on the same build-server information today.

Metadata

Metadata

Assignees

No one assigned

    Labels

    IDEbspIssues tied to the implementation of BSP (Build Server Protocol)enhancementNew feature or requestpriority: low

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions