-
Notifications
You must be signed in to change notification settings - Fork 11
Maven repositories + improved SKILL.md #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package cellar | ||
|
|
||
| import coursierapi.{MavenRepository, Repository} | ||
|
|
||
| private[cellar] object ExtraRepositories: | ||
|
|
||
| /** Configured repository URLs first, command-line `-r` values appended. | ||
| * | ||
| * Duplicates collapse to their first occurrence, ignoring a trailing slash. The surviving URL | ||
| * reaches Coursier verbatim — no other normalization. | ||
| */ | ||
| def effective(configured: List[String], commandLine: List[String]): List[Repository] = | ||
| (configured ++ commandLine).distinctBy(_.stripSuffix("/")).map(MavenRepository.of) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package cellar | ||
|
|
||
| import cats.effect.IO | ||
| import cats.syntax.all.* | ||
| import fs2.io.file.{Files => Fs2Files, Path} | ||
| import munit.CatsEffectSuite | ||
| import org.typelevel.otel4s.trace.Tracer.Implicits.noop | ||
|
|
||
| class ExtraRepositoriesTest extends CatsEffectSuite: | ||
|
|
||
| private def urls(configured: List[String], commandLine: List[String]): List[String] = | ||
| ExtraRepositories.effective(configured, commandLine).map { | ||
| case maven: coursierapi.MavenRepository => maven.getBase() | ||
| case other => fail(s"Expected a MavenRepository, got $other") | ||
| } | ||
|
|
||
| private def withConfigFiles(userConfig: Option[String], projectConfig: Option[String])( | ||
| test: Config => IO[Unit] | ||
| ): IO[Unit] = | ||
| Fs2Files[IO].tempDirectory.use { dir => | ||
| def write(name: String, contents: String): IO[Path] = | ||
| val file = dir.resolve(name) | ||
| fs2.Stream.emit(contents).through(Fs2Files[IO].writeUtf8(file)).compile.drain.as(file) | ||
|
|
||
| for | ||
| userPath <- userConfig.traverse(write("user.conf", _)) | ||
| projectPath <- projectConfig.traverse(write("project.conf", _)) | ||
| _ <- test(Config.loadFrom(userPath, projectPath)) | ||
| yield () | ||
| } | ||
|
|
||
| test("configured repositories come first, command-line values append"): | ||
| assertEquals( | ||
| urls(List("https://configured.example/maven"), List("https://cli.example/maven")), | ||
| List("https://configured.example/maven", "https://cli.example/maven") | ||
| ) | ||
|
|
||
| test("duplicates collapse to their first occurrence, ignoring a trailing slash"): | ||
| assertEquals( | ||
| urls(List("https://repo.example/maven"), List("https://repo.example/maven/", "https://other.example/maven")), | ||
| List("https://repo.example/maven", "https://other.example/maven") | ||
| ) | ||
|
|
||
| test("no configured repositories leaves command-line values untouched"): | ||
| assertEquals(urls(Nil, List("https://cli.example/maven")), List("https://cli.example/maven")) | ||
|
|
||
| test("default configuration has no extra repositories"): | ||
| withConfigFiles(None, None)(config => IO(assertEquals(config.maven.repositories, Nil))) | ||
|
|
||
| test("empty project list clears repositories inherited from the user config"): | ||
| withConfigFiles( | ||
| userConfig = Some("""maven.repositories = ["https://configured.example/maven"]"""), | ||
| projectConfig = Some("maven.repositories = []") | ||
| )(config => IO(assertEquals(config.maven.repositories, Nil))) | ||
|
|
||
| test("configured repository resolves a fixture artifact without a command-line repository"): | ||
| TestFixtures.assumeFixturesAvailable() | ||
| withConfigFiles( | ||
| userConfig = Some(s"""maven.repositories = ["file://${TestFixtures.localM2}"]"""), | ||
| projectConfig = None | ||
| ) { config => | ||
| CoursierFetchClient | ||
| .fetchClasspath(TestFixtures.scala3Coord, ExtraRepositories.effective(config.maven.repositories, Nil)) | ||
| .map(paths => assert(paths.nonEmpty, "Expected the fixture JAR to resolve through the configured repository")) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,108 +12,75 @@ description: > | |
|
|
||
| # Cellar | ||
|
|
||
| Use cellar to look up the API of JVM dependencies instead of guessing or manually downloading, unpacking, and searching JAR files for type signatures. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| Run `cellar --version` to verify cellar is on PATH. If not found, the user | ||
| needs to install it: https://github.com/VirtusLab/cellar#installation | ||
| Look up JVM dependency APIs from the terminal. NEVER download or unpack JARs manually. | ||
| If `cellar` isn't on PATH: https://github.com/VirtusLab/cellar#installation | ||
|
|
||
| ## Project-aware commands (run from project root) | ||
|
|
||
| Query the current project's code and all its dependencies. Cellar auto-detects the build tool (Mill, sbt, scala-cli). | ||
|
|
||
| cellar get [--module <name>] <fqn> # single symbol (signature, members, docs) | ||
| cellar list [--module <name>] <package> # list symbols in a package or class | ||
| cellar search [--module <name>] <query> # case-insensitive substring search | ||
| Queries the current project and its dependencies. Auto-detects Mill, sbt, scala-cli. | ||
|
|
||
| - Mill/sbt projects: `--module` is required (e.g. `--module lib`, `--module core`) | ||
| - scala-cli projects: omit `--module` | ||
| - `--no-cache`: skip classpath cache, re-extract from build tool | ||
| - `--java-home <path>`: override JRE classpath | ||
| - `-l`, `--limit <N>`: max results for `list`/`search` (default: 50), max members for `get` | ||
| - `--hide-inherited`: show only members declared on the type itself (`get` commands) | ||
| - `--group-inherited`: group members by declaring type with section headers (`get` commands) | ||
| ```sh | ||
| cellar get [--module <name>] <fqn> # signature, members, docs | ||
| cellar list [--module <name>] <package> # symbols in a package or class | ||
| cellar search [--module <name>] <query> # case-insensitive substring search | ||
| ``` | ||
|
|
||
| ## External commands (query arbitrary Maven coordinates) | ||
| - Mill/sbt: `--module` required (e.g. `--module core`) | ||
| - scala-cli: omit `--module` | ||
| - `--no-cache` — re-extract classpath from the build tool | ||
|
|
||
| Query any published artifact by explicit coordinate (`group:artifact:version`): | ||
| ## External (any Maven coordinate) | ||
|
|
||
| cellar get-external <coordinate> <fqn> # single symbol | ||
| cellar list-external <coordinate> <package> # list symbols | ||
| cellar search-external <coordinate> <query> # search by name | ||
| cellar get-source <coordinate> <fqn> # fetch source code | ||
| cellar deps <coordinate> # dependency tree | ||
| ```sh | ||
| cellar get-external <coordinate> <fqn> | ||
| cellar list-external <coordinate> <package> | ||
| cellar search-external <coordinate> <query> | ||
| cellar get-source <coordinate> <fqn> | ||
| cellar deps <coordinate> | ||
| cellar meta <coordinate> | ||
| ``` | ||
|
|
||
| - Coordinates must be explicit: `group:artifact_3:version` (no `::` shorthand) | ||
| - For sbt plugins, use the full Scala and sbt suffix: `group:artifact_2.12_1.0:version` (e.g. `org.scala-native:sbt-scala-native_2.12_1.0:latest`) | ||
| - For compiler plugins and other artifacts with full Scala version suffixes, use the full version: `group:artifact_3.3.8:version` | ||
| - Use `latest` as the version to resolve the most recent release | ||
| - `-r`, `--repository <url>`: extra Maven repository (repeatable) | ||
| - sbt plugins: full Scala + sbt suffix, e.g. `org.scala-native:sbt-scala-native_2.12_1.0:latest` | ||
| - Full Scala-version artifacts: e.g. `group:artifact_3.3.8:version` | ||
| - Version may be `latest` | ||
| - `-r <url>` — extra Maven repository (repeatable). Only for a repository that is not already | ||
| listed under `maven.repositories` in `~/.cellar/cellar.conf` or `.cellar/cellar.conf`; those | ||
| apply automatically. Never ask for, inspect, or pass repository credentials. | ||
|
|
||
| ## Flags for both | ||
|
|
||
| - `-l N` — limit `list`/`search` results (default 50) or `get` members | ||
| - `--hide-inherited` / `--group-inherited` — control inherited members on `get` | ||
| - `--java-home <path>` — override JRE classpath | ||
|
|
||
| ## Workflow | ||
|
|
||
| 1. **Don't know the package?** → `cellar search` / `cellar search-external` | ||
| 2. **Know the package, not the type?** → `cellar list` / `cellar list-external` | ||
| 3. **Know the type?** → `cellar get` / `cellar get-external` | ||
| 4. **Need the implementation?** → `cellar get-source` | ||
| 1. Package/name unknown → `search` / `search-external` | ||
| 2. Package or enclosing class known, type unknown → `list` / `list-external` | ||
| 3. Type known → `get` / `get-external` | ||
| 4. Implementation needed → `get-source` | ||
| 5. Coordinate/POM only → `meta` or `deps` | ||
|
|
||
| ## Examples | ||
|
|
||
| ```sh | ||
| # Look up a Scala 3 trait | ||
| cellar get-external org.typelevel:cats-core_3:2.10.0 cats.Monad | ||
|
|
||
| # Look up a Java class | ||
| cellar get-external org.apache.commons:commons-lang3:3.14.0 org.apache.commons.lang3.StringUtils | ||
|
|
||
| # List a package | ||
| cellar get-external --hide-inherited org.typelevel:cats-core_3:2.10.0 cats.Monad | ||
| cellar list-external io.circe:circe-core_3:0.14.6 io.circe | ||
|
|
||
| # Search for a method | ||
| cellar search-external org.typelevel:cats-core_3:2.10.0 flatMap | ||
|
|
||
| # Get source code | ||
| cellar get-source org.typelevel:cats-core_3:2.10.0 cats.Monad | ||
|
|
||
| # Dependency tree | ||
| cellar deps org.typelevel:cats-effect_3:3.5.4 | ||
|
|
||
| # sbt plugin (use full Scala + sbt suffix) | ||
| cellar deps org.scala-native:sbt-scala-native_2.12_1.0:latest | ||
|
|
||
| # Project-aware (from a Mill project root) | ||
| cellar get --module lib cats.Monad | ||
| cellar list --module core cats | ||
| cellar search --module lib flatMap | ||
| ``` | ||
|
|
||
| ## Example output | ||
|
|
||
| `cellar get-external --hide-inherited org.typelevel:cats-core_3:2.10.0 cats.Monad` | ||
|
|
||
| ```markdown | ||
| ## cats.Monad | ||
| `trait Monad[F] extends FlatMap[F] with Applicative[F]` | ||
| **Flags:** abstract | ||
| **Origin:** cats.Monad | ||
| **Members:** | ||
| def iterateWhile[A](f: F[A]): (p: A => Boolean): F[A] | ||
| def untilM[G, A](f: F[A]): (cond: => F[Boolean]): (G: Alternative[G]): F[G[A]] | ||
| def whileM_[A](p: F[Boolean]): (body: => F[A]): F[Unit] | ||
| def iterateUntil[A](f: F[A]): (p: A => Boolean): F[A] | ||
| … (+ 7 more) | ||
| **Companion members:** trait Ops[F, A], def apply[F](instance: Monad[F]): Monad[F], … | ||
| ``` | ||
|
|
||
| Use `--hide-inherited` to get only own members. Without it, all inherited members are shown (can be large for deep hierarchies). | ||
|
|
||
| ## When Metals MCP is also available | ||
| ## Metals vs cellar | ||
|
|
||
| Prefer cellar **only** for external dependency API lookups (`cellar get-external` vs Metals `inspect`/`get-docs`): cellar requires no project import and works with any published Maven coordinate. For all other tasks — goto definition, find references, rename, diagnostics, compilation — use Metals. | ||
| cellar reads APIs, nothing else. | ||
| External coordinate → cellar. | ||
| Project classpath → Metals `inspect`/`get-docs`/`glob-search`, or cellar's project-aware commands when Metals is unavailable. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have to be extra careful with metals. I got some comments / feedback that when they used it with IJ, it tried to install metals or something because of our skill. I'd be very explicit here so something more like: Project classpath: Compile, references, tests, formatting are not supported by cellar |
||
| Compile, references, tests, formatting → Metals. | ||
|
|
||
| ## Output | ||
|
|
||
| - **stdout**: Markdown — ready to consume directly | ||
| - **stderr**: diagnostics (warnings, truncation notices) | ||
| - **Exit 0**: success, **Exit 1**: error | ||
| stdout is Markdown, stderr is diagnostics. Exit 0 success, 1 error. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skill-creator suggested that leaving a reason will hold better and have better results