Compare commits

..

1 Commits

Author SHA1 Message Date
Kingkor Roy Tirtho
9b9de36b97
Merge 28ff68dcc9 into 80626ba4b5 2026-06-30 11:14:33 +00:00
18 changed files with 2220 additions and 178 deletions

View File

@ -0,0 +1,290 @@
---
name: exploring_dependency_sources
description: >
Reads and searches source code across ALL scopes: external library dependencies, plugins (buildscript), and Gradle Build Tool internal source code;
use whenever you need to UNDERSTAND an API — its shape, signature, parameters, overloads, or implementation — before writing any code that calls it;
covers project dependencies (via project/configuration/source set scope), plugins (via `sourceSetPath=":buildscript"`), and Gradle internals (via `gradleSource: true`).
Prefer this over the REPL for all API research; reading source is instantaneous and complete.
Do NOT use for project source code (use grep/tilth), Gradle documentation (use `gradle_docs` via the `gradle` skill), or Maven Central discovery (use `managing_gradle_dependencies`).
license: Apache-2.0
metadata:
author: https://github.com/rnett/gradle-mcp
version: "2.0"
---
# Authoritative Dependency, Plugin & Gradle Internal Source Exploration
Explores, navigates, and analyzes the internal logic, APIs, and symbol implementations of external libraries, Gradle plugins, and the Gradle Build Tool itself with absolute precision using high-performance, indexed searching.
## Constitution
- **ALWAYS** use `search_dependency_sources` as the primary discovery tool for external library, plugin, and Gradle internal code.
- **ALWAYS** prefer reading source code over interactive REPL exploration for understanding unfamiliar library APIs.
- **ALWAYS** provide absolute paths for `projectRoot`.
- **ALWAYS** use the `{group}/{artifact}` prefix for reading specific files (e.g., `path="org.mongodb/mongodb-driver-sync/org/mongodb/client/MongoClient.kt"`). The `dependency` parameter should only be used to filter the search scope for
performance when searching across libraries, not as the primary way to specify a path.
- **ALWAYS** escape Lucene special characters (`:`, `=`, `+`, `-`, `*`, `/`) in `FULL_TEXT` searches using a backslash (e.g., `\:`) or double quotes.
- **ALWAYS** use `read_dependency_sources` once a specific file path has been identified via search.
- **ALWAYS** use `fresh: true` if a search returns a `SearchResponse` with an `error` indicating a missing index.
- **BE AWARE** that indexing and extraction failures (e.g., `ZipException`) are propagated and will cause the tool to fail with a descriptive error.
- **NOTE** that the `dependency` filter targets ONLY the specific library version matched, NOT its transitive dependencies.
- **BE AWARE** that buildscript (plugin) dependencies are excluded from `search_dependency_sources` and `read_dependency_sources` by default to reduce noise.
- **ALWAYS** use `sourceSetPath=":buildscript"` (root project) or `sourceSetPath=":app:buildscript"` (subproject) to search or read plugin source code. This targets the virtual `buildscript` source set which aggregates all classpath
plugins.
- **ALWAYS** use `gradleSource: true` in `search_dependency_sources` to target Gradle's internal engine when probing core Gradle behavior.
- **ALWAYS** scope with a project, configuration, or source set (or use `gradleSource: true`) — unscoped search is no longer supported.
- **ALWAYS** verify against the actual source implementation when researching Gradle internals.
- **NEVER** use generic shell tools like `grep` or `find` to *locate* dependency sources; they reside in remote caches whose paths are not predictable in advance.
- **MAY** use shell tools like `rg` or `ast-grep` to *operate on* a sources root path explicitly returned by `read_dependency_sources` or `search_dependency_sources` in the `Sources root: <path>` header line. Dependency directories inside
the sources root are symlinks; always pass `--follow` to `rg` (e.g., `rg --follow <pattern> <sources-root>`).
## Directives
### Search Modes
- **DECLARATION**: Best for classes, methods, or interfaces. Matches against the simple name (tokenized for CamelCase) and the full path (exact literal). All declaration searches are **case-sensitive**. Do NOT include keywords like `class`,
`interface`, or `fun`. Supports exact names, exact FQNs, glob wildcards (e.g., `*`, `**`), and regular expressions. Use `name:` (discovery) or `fqn:` (precision) prefix for field-specific searches.
- **FULL_TEXT**: Best for literal strings, constants, and complex code patterns using Lucene. **Case-insensitive**.
- **GLOB**: Best for finding specific files (XML, properties, etc.) by name or extension. **Case-insensitive**.
Glob wildcards for FQNs: `*` matches one segment, `**` matches multiple. Example: `fqn:org.gradle.*.Project` or `fqn:org.**.Project`.
Regex: Wrap query in `/` for a full regular expression on the `fqn` field. Example: `fqn:/.*\.internal\..*/` to find all internal declarations.
### Scoping
- **Project Dependencies**: Use `search_dependency_sources` with `projectPath`, `configurationPath`, or `sourceSetPath` to scope to project dependency code.
- **Plugins (Buildscript)**: Use `sourceSetPath=":buildscript"` to target plugin source code.
- **Gradle Internals**: Use `gradleSource: true` to target Gradle Build Tool source code.
### Performance
- **Target Libraries Directly**: Use the `dependency` parameter to filter searches to a single library (`group:name:version:variant`, `group:name:version`, `group:name`, or just `group`). This bypasses project-level index merging for
instantaneous results.
- **Troubleshoot Targeted Searches**: If a targeted search fails, use `inspect_dependencies` first to verify the exact coordinates of the dependency as resolved by Gradle.
- **Refresh Indices**: Use `fresh: true` if project dependencies have recently changed.
- **Use Returned Sources Root**: Every response includes a `Sources root: <absolute-path>` header. Use this path with `rg`, `ast-grep`, or other shell tools for operations not covered by the MCP tools. Always pass `--follow` to `rg` for
symlinked dependency directories.
### Exploration
- **Explore Packages Authoritatively**: Use `read_dependency_sources` with a dot-separated package path (e.g., `org.gradle.api`) to list its direct symbols and sub-packages.
- **Analyze Implementation**: Use `read_dependency_sources` to retrieve implementation logic. Use `pagination` for large files.
- **Trace Symbols Authoritatively**: When encountering an unknown symbol, use `DECLARATION` search to jump directly to its definition.
## When to Use
> **Decision rule**: If the question is *"what does this API look like or how does it work?"* — use this skill. If you need to *run* code to see what it does at runtime — use `interacting_with_project_runtime` (REPL). Read before you run.
- **API & Symbol Discovery**: Finding implementation, signature, or documentation of a class/interface/method from a dependency, plugin, or Gradle itself.
- **Library Usage Research**: Understanding how to use a library's API by reading its internal implementation.
- **Internal Logic Auditing**: Researching how a dependency or Gradle engine handles specific operations.
- **Resource File Location**: Searching for configuration files (XML, JSON, properties) or metadata packaged within library jars.
- **Constant & Literal Research**: Searching for specific constant values, error strings, or literal keys within external code.
- **Gradle Internal Exploration**: Probing Gradle engine classes (e.g., `org.gradle.api.Project`, `Task`, `DependencyHandler`) to understand internal behavior.
- **Plugin Source Auditing**: Examining plugin implementations to understand how they configure the build at runtime.
## Workflows
### 1. Tracing a Symbol from Project Code
1. Identify the symbol name or fully qualified name from an import.
2. Call `search_dependency_sources(query="<SymbolName>", searchType="DECLARATION", projectPath=":")`.
3. Identify the correct file path from the results.
4. Call `read_dependency_sources(path="<path>", projectPath=":")` to analyze the implementation.
### 2. Discovering API Usage through Source
1. Search for a known entry point (constructor, main class) using `DECLARATION` or `FULL_TEXT`.
2. Once found, use `read_dependency_sources` to read the source.
3. Look for internal calls, helper methods, or factory patterns to understand preferred usage.
### 3. Researching Gradle Internal Engine
1. Search for the class: `search_dependency_sources(query="<ClassName>", searchType="DECLARATION", gradleSource=true)`.
2. Use glob wildcards for subpackage searches: `fqn:org.gradle.*.SomeApi` or `fqn:org.**.SomeApi`.
3. Once identified, read the implementation: `read_dependency_sources(path="org/gradle/...", gradleSource=true)`.
4. Check for breaking changes against specific versions via `gradle_docs` (see `gradle` skill).
### 4. Researching Plugin Source Code
1. Search plugins: `search_dependency_sources(query="<PluginName>", searchType="DECLARATION", sourceSetPath=":buildscript")`.
2. Read plugin implementation: `read_dependency_sources(path="<group>/<artifact>/...", sourceSetPath=":buildscript")`.
### 5. Targeted Search for a Single Library
1. Identify dependency coordinates from `inspect_dependencies` or project files.
2. Call `search_dependency_sources(query="<query>", dependency="<group:artifact>", projectPath=":")`.
3. Results are scoped ONLY to that library for maximum speed and relevance.
### 6. Searching for Constants or Error Codes
1. Identify the constant name or error message snippet.
2. Call `search_dependency_sources(query="<text>", projectPath=":")` (defaults to `FULL_TEXT`).
3. Review matches to find where the value is defined or used.
### 7. Exploring Documentation (Gradle Docs)
For official Gradle documentation (User Guide, DSL, Release Notes), use the `gradle_docs` tool via the `gradle` skill. See [Internal Source Research](references/internal_source_research.md) for authoritative research patterns combining docs
and source.
## Examples
### Search for a specific class definition (project deps)
Tool: `search_dependency_sources`
```json
{
"query": "JsonConfiguration",
"searchType": "DECLARATION",
"projectPath": ":"
}
// Reasoning: DECLARATION search for a class name across both name and FQN fields, scoped to the root project.
```
### Search Gradle internal engine source code
Tool: `search_dependency_sources`
```json
{
"query": "Project",
"searchType": "DECLARATION",
"gradleSource": true
}
// Reasoning: Using gradleSource and DECLARATION search to find the ground-truth implementation of the core Project API.
```
### Search for a specific class within a targeted library
Tool: `search_dependency_sources`
```json
{
"query": "MongoClient",
"searchType": "DECLARATION",
"dependency": "org.mongodb:mongodb-driver-sync",
"projectPath": ":"
}
// Reasoning: The 'dependency' parameter targets only the 'mongodb-driver-sync' library for a fast, focused search.
```
### Search plugin source code
Tool: `search_dependency_sources`
```json
{
"query": "KotlinPlugin",
"searchType": "DECLARATION",
"sourceSetPath": ":buildscript"
}
// Reasoning: Targeting the buildscript source set to find plugin implementation classes.
```
### Search with FQN glob wildcards (Gradle internals)
Tool: `search_dependency_sources`
```json
{
"query": "fqn:org.gradle.*.Project",
"searchType": "DECLARATION",
"gradleSource": true
}
// Reasoning: Glob wildcard to find Project declarations in any direct sub-package of org.gradle.
```
### Use regular expressions for internal APIs
Tool: `search_dependency_sources`
```json
{
"query": "fqn:/.*\\.internal\\..*/",
"searchType": "DECLARATION",
"gradleSource": true
}
// Reasoning: Regex on the 'fqn' field to find all internal Gradle declarations.
```
### Search with name-specific discovery (CamelCase)
Tool: `search_dependency_sources`
```json
{
"query": "name:Configuration",
"searchType": "DECLARATION",
"projectPath": ":"
}
// Reasoning: The 'name:' prefix finds classes like 'JsonConfiguration' via CamelCase tokenization.
```
### Search for a constant value assignment
Tool: `search_dependency_sources`
```json
{
"query": "DEFAULT_TIMEOUT_MS \\: 5000",
"projectPath": ":"
}
// Reasoning: FULL_TEXT (default) with escaped colon to find a specific constant assignment.
```
### Locate a specific file by its exact name
Tool: `search_dependency_sources`
```json
{
"query": "**/AndroidManifest.xml",
"searchType": "GLOB",
"projectPath": ":"
}
// Reasoning: GLOB search to find a specific file by name across the dependency graph.
```
### Read sources from a specific dependency
Tool: `read_dependency_sources`
```json
{
"path": "org.jetbrains.kotlinx/kotlinx-coroutines-core/kotlinx/coroutines/Job.kt",
"projectPath": ":"
}
// Reasoning: Reading 'Job.kt' using the recommended `{group}/{artifact}` syntax.
```
### Read a specific Gradle internal class
Tool: `read_dependency_sources`
```json
{
"path": "org/gradle/api/Project.java",
"gradleSource": true
}
// Reasoning: Retrieving the source code for a fundamental Gradle class for high-resolution analysis.
```
### Explore a package via its FQN
Tool: `read_dependency_sources`
```json
{
"path": "org.gradle.api",
"projectPath": ":"
}
// Reasoning: Listing the direct symbols and sub-packages of 'org.gradle.api' using index-backed exploration.
```
## Resources
- [Internal Source Research](references/internal_source_research.md) — Combining `gradle_docs` documentation lookup with `gradleSource: true` source searches for authoritative research.
- **Lucene Query Syntax**: Refer to the tool description for `search_dependency_sources` for details on complex queries and escaping.
- **Troubleshooting Targeted Searches**: If `dependency` filter fails, run `inspect_dependencies` to confirm exact coordinates.

View File

@ -0,0 +1,126 @@
# Gradle Internal Source Research
Guidance for researching Gradle Build Tool's internal implementation and third-party plugin source code using `search_dependency_sources` and `read_dependency_sources`.
## Searching the Gradle Engine
Use `gradleSource = true` and select the appropriate `searchType`:
### DECLARATION: Finding Class/Interface Definitions
```json
{
"query": "DefaultProject",
"searchType": "DECLARATION",
"gradleSource": true
}
```
Search by simple name, FQN, or partial package paths. All declaration searches are **case-sensitive**.
### DECLARATION with FQN Glob Wildcards
```json
{
"query": "fqn:org.gradle.*.Project",
"searchType": "DECLARATION",
"gradleSource": true
}
```
`*` matches one segment, `**` matches multiple. Example: `fqn:org.**.AbstractTask`.
### Regex on FQN for Internal APIs
```json
{
"query": "fqn:/.*\\.internal\\..*/",
"searchType": "DECLARATION",
"gradleSource": true
}
```
### FULL_TEXT: Searching Implementation Patterns
```json
{
"query": "configurationCache",
"gradleSource": true
}
```
**Case-insensitive** text search. Escape Lucene special characters (`:`, `=`, `+`, `-`, `*`, `/`) with backslash.
### GLOB: Finding Internal Resource Files
```json
{
"query": "**/plugin.properties",
"searchType": "GLOB",
"gradleSource": true
}
```
**Case-insensitive** file name search.
## Reading Implementation Details
Once a class is identified via search, read its source:
```json
{
"path": "org/gradle/api/Project.java",
"gradleSource": true
}
```
For large files, use `pagination`:
```json
{
"path": "org/gradle/api/Project.java",
"gradleSource": true,
"pagination": {
"offset": 0,
"limit": 100
}
}
```
## Researching Plugin Source Code
Plugin (buildscript) dependencies are excluded by default. Use `sourceSetPath=":buildscript"` to include them:
### Searching Plugin Classes
```json
{
"query": "KotlinPlugin",
"searchType": "DECLARATION",
"sourceSetPath": ":buildscript"
}
```
### Reading Plugin Source
```json
{
"path": "org.jetbrains.kotlin/kotlin-gradle-plugin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt",
"sourceSetPath": ":buildscript"
}
```
## Tracing Symbols Across Scopes
When encountering an unknown Gradle API or plugin class:
1. Search with `gradleSource: true` for engine classes.
2. Search with `sourceSetPath=":buildscript"` for plugin classes.
3. Read the implementation with `read_dependency_sources`.
4. Cross-reference with official documentation via `gradle_docs` (see `gradle` skill).
## Troubleshooting
- **Source Not Found**: Some Gradle internal modules may not be fully indexed. Try a broader `FULL_TEXT` search or browse the directory structure via `read_dependency_sources` with `gradleSource: true`.
- **Plugin Not Found**: Ensure the plugin is resolved in the buildscript classpath. Use `inspect_dependencies(sourceSetPath=":buildscript")` to verify.
- **Index Error**: Use `fresh: true` if searching after dependency changes.

View File

@ -0,0 +1,345 @@
---
name: gradle
description: >
Provides authoritative guidance for ALL Gradle operations: executing builds, running tests with surgical filtering, introspecting project structure, creating modules, and diagnosing failures;
ALWAYS use instead of raw shell `./gradlew` for build execution, test runs, task introspection, module creation, performance audits, and documentation research.
Do NOT use for dependency graph auditing/updates (use `managing_gradle_dependencies`) or dependency/plugin/Gradle source exploration (use `exploring_dependency_sources`).
license: Apache-2.0
metadata:
author: https://github.com/rnett/gradle-mcp
version: "4.0"
---
# Authoritative Gradle Build Execution, Testing & Project Introspection
Executes builds, runs tests with high-precision filtering, introspects project structure, and diagnoses failures using managed orchestration and structured diagnostics.
## Constitution
- **ALWAYS** use the `gradle` tool instead of `./gradlew` via shell.
- **ALWAYS** provide absolute paths for `projectRoot`.
- **ALWAYS** prefer foreground execution (default) unless the task is persistent (e.g., servers) or extremely long-running (>2 minutes), or you explicitly intend to perform independent research while it proceeds.
- **ALWAYS** use `captureTaskOutput` when you need the isolated output of a specific task (e.g., `help`, `projects`, `tasks`, `properties`, `dependencies`).
- **STRONGLY PREFERRED**: Use `query_build` for all diagnostics. It is more token-efficient than reading raw console logs and provides structured access to failures, problems, and per-test output.
- **ALWAYS** use `query_build` with `kind="TESTS"` and `query="FullTestName"` to access full test output and stack traces.
- **NEVER** use `taskPath` or `captureTaskOutput` to investigate specific test failures; these provide the overall task log which is often truncated and lacks per-test isolation. Per-test output (via `query`) is authoritative and includes
full stack traces.
- **NEVER** use `--rerun-tasks` unless investigating project-wide cache-specific corruption; prefer `--rerun` for individual tasks.
- **NEVER** guess task names or options; use the `help --task <name>` command for authoritative documentation.
- **NEVER** leave background builds running; use `stopBuildId` to release resources when finished.
- **ALWAYS** prefer Kotlin DSL (`.kts`) unless the project explicitly uses Groovy.
- **ALWAYS** use lazy APIs (e.g., `tasks.register<MyTask>("myTask")`) instead of eager APIs (e.g., `tasks.create<MyTask>("myTask")`) to maintain configuration performance.
- **ALWAYS** use version catalogs (`libs.versions.toml`) for dependency management when present.
- **ALWAYS** use `gradle_docs` for authoritative documentation lookup instead of generic web searches.
- **ALWAYS** check for existing conventions in the current project before proposing changes.
- **ALWAYS** use safe navigation (`?.url?.toString()`) and provide fallback values when accessing `ArtifactRepository` URLs in Gradle init scripts or plugins to prevent `NullPointerException`.
- **ALWAYS** use `:properties --property <name>` for surgical property extraction.
## Directives
### Authoritative Task Path Syntax
Gradle uses two ways to identify tasks from the command line. Precision prevents running redundant tasks in multi-project builds.
#### Task Selectors (Recursive Execution)
Providing a task name **without a leading colon** (e.g., `test`, `build`) acts as a selector. Gradle executes that task in **every project** (root and all subprojects) that contains a task with that name.
- **Example**: `gradle(commandLine=["test"])` -> Executes `test` in **all** projects.
#### Absolute Task Paths (Targeted Execution)
Providing a task path **with a leading colon** (e.g., `:test`, `:app:test`) targets a **single specific project**.
- **Root Project Only**: Use a single leading colon. `gradle(commandLine=[":test"])` -> Root project ONLY.
- **Subproject Only**: Use the subproject name(s) separated by colons. `gradle(commandLine=[":app:test"])` -> ':app' subproject ONLY.
### Authoritative Test Selection (`--tests`)
The `--tests` flag supports powerful, high-precision filtering:
- **Exact Class**: `--tests com.example.MyTest`
- **Exact Method**: `--tests com.example.MyTest.myTestMethod`
- **Wildcard Method**: `--tests com.example.MyTest.test*` (All methods starting with 'test')
- **Package Filter**: `--tests com.example.service.*` (All tests in the 'service' package)
- **Class Prefix**: `--tests *IntegrationTest` (All classes ending in 'IntegrationTest')
- **Character Wildcard**: `--tests com.example.Test?` (Matches Test1, TestA, etc.)
- **Multi-Filter**: `gradle(commandLine=["test", "--tests", "ClassA", "--tests", "ClassB"])`
Patterns match against the **fully qualified name** of the test class or method.
### Foreground vs. Background Execution
- **ALWAYS use foreground for authoritative runs**: If you intend to wait for a result, ALWAYS use foreground execution. It provides superior progressive disclosure and simpler control flow.
- **Background ONLY for persistent tasks**: Use `background: true` ONLY for tasks that must remain active (e.g., `bootRun`, continuous builds) or when you intentionally intend to perform independent research while the build proceeds.
- **Foreground is safe**: Do not fear running high-output suites in the foreground. The `gradle` tool uses progressive disclosure to provide concise summaries and structured results, keeping session history clean.
### `captureTaskOutput` Usage
Use `captureTaskOutput` when you need clean, isolated output from a specific task without Gradle's general console noise. This is ideal for introspection tasks:
- `captureTaskOutput: ":projects"` - Clean project list
- `captureTaskOutput: ":app:tasks"` - Task list for a specific project
- `captureTaskOutput: ":help"` - Documentation for a specific task
- `captureTaskOutput: ":properties"` - Single property extraction
- `captureTaskOutput: ":app:dependencyInsight"` - Dependency resolution path
### `gradle_docs` Tag Syntax
Use `gradle_docs` for authoritative documentation. Always scope with tags:
| Tag | Section |
|----------------------|----------------------------------------------------|
| `tag:userguide` | Official Gradle User Guide |
| `tag:dsl` | Gradle DSL Reference (Groovy and Kotlin DSL) |
| `tag:javadoc` | Gradle Java API Reference |
| `tag:samples` | Official Gradle samples and examples |
| `tag:release-notes` | Version-specific release insights |
| `tag:best-practices` | Official best practices and performance guidelines |
Explore sections with `path="."`. Search scoped with `tag:<section> <term>`.
### Idiomatic DSL Patterns
- **Prefer `register` over `create` (Lazy APIs)**: Use `tasks.register<MyTask>("myTask")` to avoid eager task configuration.
- **Use Type-Safe Accessors**: Prefer `tasks.test { ... }` or `tasks.named<Test>("test") { ... }` over `tasks.getByName("test")`.
- **Use Lazy Properties**: Employ `Property<T>` and `Provider<T>` APIs for late binding and configuration cache compatibility.
- **Use Version Catalogs**: Centralize dependencies in `gradle/libs.versions.toml`.
- **Avoid `allprojects`/`subprojects`**: These blocks create tight coupling; use convention plugins and apply them selectively.
- **Enable Configuration Cache**: Ensure build logic avoids accessing the `Project` object inside task actions.
- **Use Specific Annotations**: Properly label task properties with `@Input`, `@OutputFiles`, `@Internal`, etc.
- **Minimize Logic in Build Scripts**: Move complex logic into convention plugins or `build-logic`.
### Resource Management
- Use `query_build()` without arguments to view the build dashboard and ensure no orphaned background builds are consuming system resources.
- Set `invocationArguments: { envSource: "SHELL" }` if Gradle cannot find expected env vars (e.g., `JAVA_HOME`).
### Diagnostic Inspection (See References)
For comprehensive guidance on using `query_build` and `wait_build` for diagnostics, including JSON examples for every inspection mode (DASHBOARD, SUMMARY, FAILURES, PROBLEMS, TASKS, TESTS, CONSOLE, PROGRESS), refer
to: [query_build Diagnostics Reference](references/query_build_diagnostics.md).
## Workflows
### Running a Foreground Build
1. Identify the task(s) to run (e.g., `["clean", "build"]`).
2. Call `gradle(commandLine=["...", "..."])`.
3. If the build fails, the tool returns a high-signal failure summary. Use `query_build` with the `buildId` for deeper diagnostics via [query_build Diagnostics Reference](references/query_build_diagnostics.md).
### Running Specific Tests
1. Identify the project path (e.g., `:app`) and the test filter (e.g., `com.example.MyTestClass*`).
2. Call `gradle(commandLine=[":app:test", "--tests", "com.example.MyTest"])`.
3. If failures are reported, use `query_build` to get detailed test output.
### Orchestrating Background Jobs
1. Start the build with `background: true` to receive a `BuildId`.
2. Use `wait_build(buildId=ID, timeout=..., waitFor=...)` to block until a specific state or log pattern is reached.
3. Use `query_build()` (no arguments) to manage active jobs in the dashboard.
4. Stop the job using `gradle(stopBuildId=ID)` when finished.
### Introspecting Project Structure
1. Run `gradle(commandLine=[":projects"], captureTaskOutput=":projects")` to map the multi-project hierarchy.
2. Run `gradle(commandLine=[":app:tasks", "--all"], captureTaskOutput=":app:tasks")` to discover runnable tasks.
3. Run `gradle(commandLine=[":help", "--task", "test"], captureTaskOutput=":help")` for task-specific documentation.
4. Run `gradle(commandLine=[":properties", "--property", "version"], captureTaskOutput=":properties")` for surgical property extraction.
5. For detailed dependency resolution paths: `gradle(commandLine=[":app:dependencyInsight", "--dependency", "slf4j-api", "--configuration", "compileClasspath"], captureTaskOutput=":app:dependencyInsight")`.
### Creating a New Module
1. Map the project structure: `gradle(commandLine=[":projects"], captureTaskOutput=":projects")` to find the correct parent path.
2. Create directory structure: `New-Item -ItemType Directory -Force -Path "<module-name>/src/main/kotlin"`.
3. Add to `settings.gradle.kts`: Append `include(":<module-name>")`.
4. Create `build.gradle.kts` with idiomatic patterns (apply convention plugins, set up standard configuration).
5. Verify: `gradle(commandLine=[":<module-name>:tasks"], captureTaskOutput=":<module-name>:tasks")`.
### Performance Audit
1. Check configuration cache status: `gradle(commandLine=[":help", "--configuration-cache"])`.
2. Analyze task compatibility and identify violations.
3. Propose fixes: migrate to lazy APIs (`Property<T>`, `Provider<T>`) or use `@Internal`/`@Input` annotations correctly.
4. Verify against latest guidance: `gradle_docs(query="tag:best-practices", projectRoot="/path/to/project")`.
### Documentation Research
1. Search the user guide: `gradle_docs(query="tag:userguide <term>", projectRoot="/path/to/project")`.
2. Navigate the DSL reference: `gradle_docs(path="dsl/org.gradle.api.Project.html", projectRoot="/path/to/project")`.
3. Check for breaking changes: `gradle_docs(query="tag:release-notes", version="8.6")`.
4. Find best practices: `gradle_docs(query="tag:best-practices dependency management", projectRoot="/path/to/project")`.
5. Search for samples: `gradle_docs(query="tag:samples toolchains", projectRoot="/path/to/project")`.
6. Search javadocs: `gradle_docs(query="tag:javadoc Project", projectRoot="/path/to/project")`.
### Investigating Test Failures
1. Identify the `BuildId` from the build result.
2. Use `query_build(buildId=ID, kind="TESTS", outcome="FAILED")` to list all failed tests.
3. Use `query_build(buildId=ID, kind="TESTS", query=TNAME)` to see the full output and stack trace for a specific test.
4. **DO NOT** use `taskPath` or `captureTaskOutput` for test failure investigation.
## When to Use
- **Core Lifecycle Execution**: When you need to execute standard Gradle tasks (`build`, `assemble`, `clean`) with reliable, parseable output.
- **Test Execution & Diagnostics**: When running tests with `--tests` filtering, isolating failures, or retrieving full stack traces.
- **Introspection & Mapping**: When mapping multi-module project hierarchies, discovering runnable tasks, or auditing build configuration.
- **Surgical Property Inspection**: When extracting a specific property value (artifact version, build directory) for use in a subsequent task.
- **Persistent Development Processes**: When starting dev servers (`bootRun`) or continuous builds where background management is required.
- **Task-Specific Information Retrieval**: When you need isolated output from a single task (`help`, `projects`, `tasks`) without build noise.
- **Build Failure Diagnostics**: When performing deep-dive analysis of task failures, problems, or compilation errors.
- **New Module Creation**: When adding a new project or module to a multi-project build.
- **Build Logic Refactoring**: When cleaning up complex build scripts or creating convention plugins.
- **Performance Troubleshooting**: When builds are slow or failing during the configuration phase.
- **Documentation & DSL Research**: When looking up official Gradle syntax, user guide topics, or release notes.
## Examples
### Run build in all projects
Tool: `gradle`
```json
{
"commandLine": ["build"]
}
// Reasoning: Task selector (no colon) verifies build health across the entire multi-project structure.
```
### Run a single test class in a specific subproject
Tool: `gradle`
```json
{
"commandLine": [":app:test", "--tests", "com.example.service.MyServiceTest"]
}
// Reasoning: Absolute task path with exact class filter for the fastest possible feedback loop.
```
### Inspect help output for a specific task
Tool: `gradle`
```json
{
"commandLine": [":app:help", "--task", "test"],
"captureTaskOutput": ":app:help"
}
// Reasoning: Using captureTaskOutput to retrieve clean, isolated documentation.
```
### List all sub-projects in the build
Tool: `gradle`
```json
{
"commandLine": [":projects"],
"captureTaskOutput": ":projects"
}
// Reasoning: Using captureTaskOutput to retrieve the project hierarchy list without startup noise.
```
### Surgically inspect the 'version' property
Tool: `gradle`
```json
{
"commandLine": [":properties", "--property", "version"],
"captureTaskOutput": ":properties"
}
// Reasoning: Using --property to isolate a single value and avoid retrieving thousands of unrelated properties.
```
### Analyze a specific dependency conflict
Tool: `gradle`
```json
{
"commandLine": [
":app:dependencyInsight",
"--dependency",
"com.google.guava:guava",
"--configuration",
"runtimeClasspath"
],
"captureTaskOutput": ":app:dependencyInsight"
}
// Reasoning: Using dependencyInsight to isolate the resolution path for a specific artifact.
```
### Start a dev server and wait for readiness
Tool: `gradle`
```json
// Step 1: Start the server in the background
{
"commandLine": [":app:bootRun"],
"background": true
}
// Response: { "buildId": "build_123" }
// Step 2: Wait for readiness signal
{
"buildId": "build_123",
"timeout": 60,
"waitFor": "Started Application"
}
// Reasoning: Background orchestration allows the server to remain active while waiting for readiness.
```
### Search official Gradle documentation
Tool: `gradle_docs`
```json
{
"query": "tag:dsl signing plugin",
"projectRoot": "/absolute/path/to/project"
}
// Reasoning: Using the DSL tag to find authoritative syntax for the signing plugin configuration.
```
### Create a new sub-project module
Tool: `run_shell_command`
```json
{
"command": "New-Item -ItemType Directory -Force -Path subproject/src/main/kotlin"
}
// Reasoning: Creating the standard directory structure for a Kotlin JVM project using correct PowerShell syntax.
```
### List all failed tests in a build
Tool: `query_build`
```json
{
"buildId": "build_abc123",
"kind": "TESTS",
"outcome": "FAILED"
}
// Reasoning: Isolating only the failures from a large test suite for efficient triage.
```
## Troubleshooting
- **Build Not Found**: If a `BuildId` is not recognized, it may have expired from the recent history cache. Check the dashboard (`query_build()`) for valid active and historical IDs.
- **Task Output Not Captured**: Ensure the path provided to `captureTaskOutput` matches exactly one of the tasks in the `commandLine`.
- **Missing environment variables**: Set `invocationArguments: { envSource: "SHELL" }` if Gradle cannot find expected env vars (e.g., `JAVA_HOME`).
## Resources
- [query_build Diagnostics Reference](references/query_build_diagnostics.md) — Complete diagnostic patterns for DASHBOARD, SUMMARY, FAILURES, PROBLEMS, TASKS, TESTS, CONSOLE, and PROGRESS.
- [Background Monitoring Patterns](references/background_monitoring.md)
- [Authoritative Diagnostic Tasks](references/diagnostic_tasks.md) — Built-in introspection tasks.
- [Best Practices Snapshot](references/best_practices.md) — High-level best practices; always verify with `gradle_docs`.
- [Common Build Patterns](references/common_build_patterns.md) — Idiomatic patterns for multi-project builds, convention plugins, and task registration.
- [Official Gradle Documentation Research](references/gradle_docs_research.md) — Guidance on using `gradle_docs` for authoritative documentation.

View File

@ -0,0 +1,105 @@
# Background Monitoring Patterns
This guide provides advanced patterns for monitoring and managing long-running background builds using `gradle`, `query_build`, and `wait_build`.
## Common Monitoring Patterns
### 1. Waiting for a Log Message
The most common pattern for background builds (like dev servers) is to wait for a specific log message that indicates the build is ready using `wait_build`.
```json
{
"buildId": "BUILD_ID",
"timeout": 60,
"waitFor": "Started Application"
}
```
- **`timeout`**: Max seconds to wait for the message.
- **`waitFor`**: A regex pattern to match in the build logs.
### 2. Waiting for Task Completion
If you want to wait for a specific task to finish in a background build.
```json
{
"buildId": "BUILD_ID",
"timeout": 120,
"waitForTask": ":app:assemble"
}
```
- **`waitForTask`**: The path of the task to wait for.
### 3. Monitoring Progress Without Waiting
To check the current status of a background build without waiting for a specific event using `query_build`.
```json
{
"buildId": "BUILD_ID"
}
```
- This returns a summary of the current build state (e.g., `BUILD IN PROGRESS`, `SUCCESS`, `FAILURE`), failures, and problems.
- **Example**: `query_build(buildId="ID")`
### 4. Inspecting Active Builds (Build Dashboard)
To see all currently running background builds and recent history.
```json
{}
```
- Call `query_build()` with **no arguments** to see the dashboard.
- This is the easiest way to find `BuildId`s for active or recently finished builds.
## Advanced Management
### 1. Stopping a Background Build
Always stop background builds when they are no longer needed to free up resources.
```json
{
"stopBuildId": "BUILD_ID"
}
```
### 2. Continuous Builds
For continuous builds (e.g., `gradle build --continuous`), use the background pattern and wait for the "Waiting for changes" message.
```json
// Start the build
{
"commandLine": ["build", "--continuous"],
"background": true
}
// Wait for the first build to finish
{
"buildId": "BUILD_ID",
"timeout": 120,
"waitFor": "Waiting for changes"
}
```
### 3. Handling Timeouts
If a build takes longer than the `timeout` time, `wait_build` will return the current status. You can then call it again with a new `timeout` time if needed.
## Troubleshooting Background Builds
- **Build Fails Immediately**: If a background build fails quickly, check the `failures` and `console` output using `query_build`.
- **Log Message Not Found**: Ensure the `waitFor` regex is correct and that the message is actually being printed to the console.
- **Resource Exhaustion**: If you have too many background builds running, stop the ones you don't need using `stopBuildId`.
## Functional Identity with Foreground Execution
Monitoring a background build using `query_build` or `wait_build` provides exactly the same rich diagnostic data as a foreground build. The primary difference is control flow: background execution allows you to yield control and perform
other tasks,
whereas foreground execution blocks until completion. Both methods utilize progressive disclosure to ensure session history remains clean and focused.

View File

@ -0,0 +1,109 @@
# Gradle Build Logic Best Practices
**IMPORTANT**: This document provides a high-level snapshot of common best practices. However, Gradle is a rapidly evolving tool. **You MUST use the `gradle_docs` tool to retrieve the most up-to-date and comprehensive best practices
directly from the official documentation.**
## 1. Authoritative Research Workflow
Before implementing significant build logic, always perform a search for the latest recommendations using scoped queries and project context.
### Example: Getting an Index of Best Practices
To find the authoritative index of best practices within the User Guide, first explore the `userguide/` directory to identify the correct files.
Tool: `gradle_docs`
```json
{
"path": "userguide/",
"projectRoot": "/absolute/path/to/project"
}
```
**Reasoning**: This call lists the contents of the `userguide` directory. Look for files starting with `best_practices` (e.g., `best_practices.md`, `best_practices_dependency_management.md`). Once identified, read the main index:
Tool: `gradle_docs`
```json
{
"path": "userguide/best_practices.md",
"projectRoot": "/absolute/path/to/project"
}
```
### Example: Searching for Best Practices
Tool: `gradle_docs`
```json
{
"query": "tag:best-practices dependency management",
"projectRoot": "/absolute/path/to/project"
}
```
**Reasoning**: Using the authoritative `best-practices` tag ensures the returned content is filtered for high-signal architectural recommendations.
### Example: Searching for Specific Guidance
Tool: `gradle_docs`
```json
{
"query": "tag:userguide performance best practices",
"projectRoot": "/absolute/path/to/project"
}
```
---
## 2. Engine-Level Discovery (The "Source of Truth")
To understand the core architectural principles behind best practices, you can explore Gradle's own source documentation.
### Example: Listing Core Concepts and Documentation
Use `gradle_docs` with `path="."` to explore the root documentation tree.
Tool: `gradle_docs`
```json
{
"path": ".",
"projectRoot": "/absolute/path/to/project"
}
```
---
## 3. High-Level Snapshot (Current Guidelines)
The following sections summarize established idiomatic patterns. Use these as a starting point, but verify against the official docs.
### Kotlin DSL Idiomatic Patterns
- **Use Type-Safe Accessors**: Prefer `tasks.test { ... }` or `tasks.named<Test>("test") { ... }` over `tasks.getByName("test")`.
- **Prefer `register` over `create` (Lazy APIs)**: Use `tasks.register<MyTask>("myTask")` to avoid eager task configuration.
- **Use Lazy Properties**: Employ the `Property<T>` and `Provider<T>` APIs for late binding and better configuration cache compatibility.
### Performance & Configuration Cache
- **Enable Configuration Cache**: Ensure build logic avoids accessing the `Project` object inside task actions.
- **Use Specific Annotations**: Properly label task properties with `@Input`, `@OutputFiles`, `@Internal`, etc.
- **Minimize Logic in Build Scripts**: Move complex logic into convention plugins or `buildSrc`.
### Dependency Management
- **Use Version Catalogs**: Centralize dependencies in `gradle/libs.versions.toml`.
- **Avoid `allprojects` and `subprojects`**: These blocks create tight coupling; use convention plugins and apply them selectively instead.
### Project Integrity
- **Reproducible Builds**: Use fixed versions and commit the Gradle wrapper.
- **Surgical Updates**: Only update what is necessary and verify with `check`.
## Resources (Official Source of Truth)
- [Official Gradle Best Practices](https://docs.gradle.org/current/userguide/best_practices.html)
- [Gradle Performance Guide](https://docs.gradle.org/current/userguide/performance.html)
- [Kotlin DSL Primer](https://docs.gradle.org/current/userguide/kotlin_dsl.html)

View File

@ -0,0 +1,142 @@
# Common Gradle Build Patterns & Conventions
A collection of idiomatic patterns for common Gradle build scenarios, including multi-project setups, convention plugins, and task registration.
## 1. Multi-Project Build Structure
Standard project structure with a root project and multiple subprojects.
### Directory Structure
```
root/
├── build.gradle.kts
├── settings.gradle.kts
├── libs.versions.toml
├── app/
│ └── build.gradle.kts
├── core/
│ └── build.gradle.kts
└── build-logic/
├── build.gradle.kts
└── settings.gradle.kts
```
### `settings.gradle.kts`
```kotlin
pluginManagement {
includeBuild("build-logic")
}
rootProject.name = "my-project"
include(":app", ":core")
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("gradle/libs.versions.toml"))
}
}
repositories {
mavenCentral()
}
}
```
## 2. Convention Plugins (The `build-logic` Pattern)
Move common project configuration logic into separate plugins to reduce duplication in `build.gradle.kts` files.
### `build-logic/build.gradle.kts`
```kotlin
plugins {
`kotlin-dsl`
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0")
}
```
### `build-logic/src/main/kotlin/my-convention.gradle.kts`
```kotlin
plugins {
kotlin("jvm")
}
repositories {
mavenCentral()
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile>().configureEach {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}
```
### Usage in Subprojects
```kotlin
plugins {
id("my-convention")
}
```
## 3. Registering Custom Tasks
Use lazy task registration and configure properties with the `Property` and `Provider` APIs.
```kotlin
abstract class MyCustomTask : DefaultTask() {
@get:Input
abstract val message: Property<String>
@TaskAction
fun action() {
println("Message: ${message.get()}")
}
}
tasks.register<MyCustomTask>("myTask") {
message.set("Hello from custom task!")
}
```
## 4. Configuring Standard Plugins
### Java/Kotlin Library
```kotlin
plugins {
`java-library`
kotlin("jvm")
}
dependencies {
api("com.example:some-api:1.0")
implementation("com.example:some-impl:1.0")
testImplementation(kotlin("test"))
}
```
### Application Plugin
```kotlin
plugins {
application
}
application {
mainClass.set("com.example.Main")
}
```
## Resources
- [Multi-project builds](https://docs.gradle.org/current/userguide/multi_project_builds.html)
- [Developing Custom Gradle Plugins](https://docs.gradle.org/current/userguide/custom_plugins.html)
- [Authoring maintainable builds](https://docs.gradle.org/current/userguide/authoring_maintainable_builds.html)

View File

@ -0,0 +1,27 @@
# Authoritative Diagnostic Tasks
Gradle provides built-in tasks for deep introspection. Always use `captureTaskOutput` with these for the best experience.
### 1. Project & Task Discovery
- **`projects`**: Lists the sub-projects in the build.
- `gradle(commandLine=[":projects"], captureTaskOutput=":projects")`
- **`tasks`**: Lists runnable tasks. Use `--all` for a full list (including ungrouped tasks).
- `gradle(commandLine=[":app:tasks", "--all"], captureTaskOutput=":app:tasks")`
- **`help`**: Displays global or task-specific help.
- `gradle(commandLine=[":help", "--task", "test"], captureTaskOutput=":help")`
### 2. Property & Environment Auditing
- **`properties`**: Lists all project properties. Use `--property <name>` for surgical extraction.
- `gradle(commandLine=[":properties", "--property", "version"], captureTaskOutput=":properties")`
- **`javaToolchains`**: Displays detected JVM toolchains and their properties.
- `gradle(commandLine=[":javaToolchains"], captureTaskOutput=":javaToolchains")`
### 3. Low-Level Dependency & Variant Analysis
- **`dependencyInsight`**: Investigates why a specific dependency version was chosen.
- `gradle(commandLine=[":app:dependencyInsight", "--dependency", "slf4j-api", "--configuration", "compileClasspath"], captureTaskOutput=":app:dependencyInsight")`
- **`outgoingVariants`**: Displays the variants the project provides to consumers.
- `gradle(commandLine=[":app:outgoingVariants"], captureTaskOutput=":app:outgoingVariants")`
- **`resolvableConfigurations`**: Displays the configurations available for resolution.

View File

@ -0,0 +1,100 @@
# Official Gradle Documentation Research
Guidance on using the `gradle_docs` tool for authoritative documentation lookup, including the User Guide, DSL Reference, Release Notes, samples, and API reference.
## Searching the User Guide
```json
{
"query": "tag:userguide working with files",
"projectRoot": "/absolute/path/to/project"
}
```
## Navigating the DSL Reference
```json
{
"path": "dsl/org.gradle.api.Project.html",
"projectRoot": "/absolute/path/to/project"
}
```
## Searching for Samples
Official code samples are indexed with the `tag:samples` metadata:
```json
{
"query": "tag:samples toolchains",
"projectRoot": "/absolute/path/to/project"
}
```
## Searching Javadocs
Technical API documentation is indexed with the `tag:javadoc` metadata:
```json
{
"query": "tag:javadoc Project",
"projectRoot": "/absolute/path/to/project"
}
```
## Best Practices
### Getting an Index of Best Practices
First, explore the `userguide/` directory to identify the correct files:
```json
{
"path": "userguide/",
"projectRoot": "/absolute/path/to/project"
}
```
This lists the contents of the `userguide` directory. Look for files starting with `best_practices` (e.g., `best_practices.md`, `best_practices_dependency_management.md`). Once identified, read the main index:
```json
{
"path": "userguide/best_practices.md",
"projectRoot": "/absolute/path/to/project"
}
```
### Searching for Best Practices
```json
{
"query": "tag:best-practices dependency management",
"projectRoot": "/absolute/path/to/project"
}
```
### Searching for Specific Guidance
```json
{
"query": "tag:userguide performance best practices",
"projectRoot": "/absolute/path/to/project"
}
```
## Exploring the Documentation Tree
Use `gradle_docs` with `path="."` to explore the root documentation tree:
```json
{
"path": ".",
"projectRoot": "/absolute/path/to/project"
}
```
## External Resources
- [Official Gradle Best Practices](https://docs.gradle.org/current/userguide/best_practices.html)
- [Gradle Performance Guide](https://docs.gradle.org/current/userguide/performance.html)
- [Kotlin DSL Primer](https://docs.gradle.org/current/userguide/kotlin_dsl.html)

View File

@ -0,0 +1,380 @@
# query_build Diagnostics Reference
Comprehensive guide to inspecting build results, diagnosing failures, and monitoring progress with `query_build` and `wait_build`.
## Quick Reference
| Goal | Tool Call |
|--------------------------|--------------------------------------------------------------------------------|
| See recent/active builds | `query_build()` |
| Build-level summary | `query_build(buildId="ID")` |
| Specific failure | `query_build(buildId="ID", kind="FAILURES", query="F0")` |
| Specific problem | `query_build(buildId="ID", kind="PROBLEMS", query="P1")` |
| Task output | `query_build(buildId="ID", kind="TASKS", query=":app:compileJava")` |
| List failed tests | `query_build(buildId="ID", kind="TESTS", outcome="FAILED")` |
| Per-test stack trace | `query_build(buildId="ID", kind="TESTS", query="com.example.MyTest.myMethod")` |
| Console logs (regex) | `query_build(buildId="ID", kind="CONSOLE", query="ERROR")` |
| Full export to file | `query_build(buildId="ID", kind="CONSOLE", outputFile="path/to/logs.txt")` |
| Wait for log pattern | `wait_build(buildId="ID", timeout=60, waitFor="Started")` |
| Wait for task | `wait_build(buildId="ID", timeout=120, waitForTask=":app:assemble")` |
| Wait for finish | `wait_build(buildId="ID", timeout=600)` |
---
## 1. Build Dashboard (`query_build()`)
Call `query_build()` with no arguments to see the **Build Dashboard** — a list of active background builds and recently completed builds with `BuildId`, status, and failure counts. Use this to discover valid `BuildId`s and ensure no
orphaned background builds are consuming resources.
```json
{}
```
---
## 2. Build Summary (`query_build(buildId="ID")`)
Provide a `buildId` to get a structured summary of that specific build, including:
- Overall build status (SUCCESS, FAILED, etc.)
- Failure IDs and descriptions
- Problem IDs and descriptions
- Test result counts (passed, failed, skipped, etc.)
The summary includes a guide on how to inspect specific details using the appropriate `kind`.
```json
{
"buildId": "BUILD_ID"
}
```
---
## 3. Failure Inspection (`kind="FAILURES"`)
Inspect a specific build failure to see the full error message and stack trace. Find failure IDs (`F0`, `F1`, etc.) in the build summary. If you provide a unique prefix instead of the exact ID, the tool auto-resolves it.
```json
{
"buildId": "BUILD_ID",
"kind": "FAILURES",
"query": "F0"
}
```
---
## 4. Problem Inspection (`kind="PROBLEMS"`)
Inspect a specific compilation or configuration problem. Find problem IDs (`P0`, `P1`, etc.) in the build summary. Provides file locations, error messages, and suggestions where available.
```json
{
"buildId": "BUILD_ID",
"kind": "PROBLEMS",
"query": "P1"
}
```
---
## 5. Task Output Inspection (`kind="TASKS"`)
If the failure is task-related, check the isolated output of a specific task. Supports prefix matching on the task path.
```json
{
"buildId": "BUILD_ID",
"kind": "TASKS",
"query": ":app:compileJava"
}
```
---
## 6. Test Inspection (`kind="TESTS"`)
### Listing Failed Tests (Summary Mode)
Quickly see which tests failed without being overwhelmed by logs:
```json
{
"buildId": "BUILD_ID",
"kind": "TESTS",
"outcome": "FAILED"
}
```
### Getting Detailed Test Output (Details Mode)
**CRITICAL**: Always use `kind="TESTS"` and `query` to see the complete stdout, stderr, and stack trace for a specific test. Supports unique prefix matching on the test name.
```json
{
"buildId": "BUILD_ID",
"kind": "TESTS",
"query": "com.example.MyTest.testMethod"
}
```
### Individual Test Case vs. Task Output
**DO NOT** use `taskPath` or `captureTaskOutput` for investigating specific test failures:
- **Task output** is the aggregated log of the entire test process. It is often truncated, interleaved, and lacks the full stack traces and per-test isolation needed for debugging.
- **Individual test output** (retrieved via `query`) is authoritative, includes full stdout/stderr for just that test case, and provides the complete stack trace for any failure.
### Filtering by Name
Use `query` with summary mode to see all executions of a test across different projects or iterations:
```json
{
"buildId": "BUILD_ID",
"kind": "TESTS",
"query": "MyTest"
}
```
### Monitoring Test Progress
While a build is running, progress notifications provide real-time counts of passed, failed, and skipped tests. Call `query_build(buildId="ID")` repeatedly to see updated test counts: `(5 passed, 1 failed)`.
### Pagination for Large Test Suites
```json
{
"buildId": "BUILD_ID",
"kind": "TESTS",
"pagination": {
"limit": 50,
"offset": 0
},
"query": "com.example.service"
}
```
---
## 7. Console Log Inspection (`kind="CONSOLE"`)
If structured reports are insufficient, examine the raw console output. Use `query` as a regex filter.
### Head (first N lines)
```json
{
"buildId": "BUILD_ID",
"kind": "CONSOLE",
"pagination": {
"limit": 100,
"offset": 0
}
}
```
### Tail (last N lines)
```json
{
"buildId": "BUILD_ID",
"kind": "CONSOLE",
"pagination": {
"limit": 100
}
}
```
### Filtered by Regex
```json
{
"buildId": "BUILD_ID",
"kind": "CONSOLE",
"query": "ERROR|FAILURE"
}
```
---
## 8. Progress Monitoring (`wait_build`)
Use `wait_build` with `timeout`, `waitFor`, or `waitForTask` to block until a condition is met in a background build.
### Waiting for a Log Message
The most common pattern for background builds (dev servers) is waiting for a specific readiness message:
```json
{
"buildId": "BUILD_ID",
"timeout": 60,
"waitFor": "Started Application"
}
```
### Waiting for Task Completion
```json
{
"buildId": "BUILD_ID",
"timeout": 120,
"waitForTask": ":app:assemble"
}
```
### Waiting for Build Completion
If `timeout` is set without a wait condition, the tool waits for the build to finish:
```json
{
"buildId": "BUILD_ID",
"timeout": 600
}
```
### Handling Timeouts
If a build takes longer than the `timeout` value, `wait_build` returns the current status. You can call it again with a new timeout.
### Continuous Builds
For continuous builds, wait for the "Waiting for changes" message after the first build completes:
```json
// Start
{ "commandLine": ["build", "--continuous"], "background": true }
// Wait
{ "buildId": "BUILD_ID", "timeout": 120, "waitFor": "Waiting for changes" }
```
---
## 9. Full Export (`outputFile`)
Use `outputFile="path/to/file.txt"` to write the entire result to a file. This bypasses pagination limits and reduces token usage. Works with all `kind` values.
```json
{
"buildId": "BUILD_ID",
"kind": "CONSOLE",
"outputFile": "C:/temp/build_output.txt"
}
```
---
## 10. Diagnostic Workflow
When a build fails, follow this structured approach:
### Step 1: Get the Build Summary
```json
{ "buildId": "BUILD_ID" }
```
Provides the high-level overview: failures, problems, and failed tests.
### Step 2: Inspect Failures
```json
{ "buildId": "BUILD_ID", "kind": "FAILURES", "query": "F0" }
```
### Step 3: Inspect Problems
```json
{ "buildId": "BUILD_ID", "kind": "PROBLEMS", "query": "P1" }
```
### Step 4: Check Task Outputs
```json
{ "buildId": "BUILD_ID", "kind": "TASKS", "query": ":app:compileJava" }
```
### Step 5: Check Test Failures
```json
{ "buildId": "BUILD_ID", "kind": "TESTS", "outcome": "FAILED" }
```
Then drill into each failed test:
```json
{ "buildId": "BUILD_ID", "kind": "TESTS", "query": "com.example.MyTest.shouldWork" }
```
### Step 6: Fall Back to Console Logs
```json
{ "buildId": "BUILD_ID", "kind": "CONSOLE", "pagination": { "limit": 100 } }
```
---
## 11. Build Failures vs. Test Failures
Sometimes a test run fails because the build itself failed (compilation error, configuration error, task dependency failure), not because a test failed.
1. Check the build summary: `query_build(buildId="ID")`.
2. If failures or problems are listed, inspect them directly via `kind="FAILURES"` or `kind="PROBLEMS"`.
3. If the build failed but no tests are reported, focus on build-level failures and problems.
---
## 12. Common Failure Scenarios
### Compilation Errors
- Look for problems with the specific `severity: ERROR` in the build summary.
- Check `kind="PROBLEMS"` for file location and error details.
### Dependency Resolution Issues
- Check `kind="FAILURES"` for messages like "Could not resolve all dependencies".
- Use `inspect_dependencies` to investigate the dependency graph.
### Task Execution Failures
- Check `kind="TASKS"` to see which task failed and its output.
### Build Script Errors
- Usually appear in the `kind="FAILURES"` section with a stack trace and line reference.
### Assertion Failures
- Check `kind="TESTS"` with the specific test `query` for expected vs. actual values.
### Timeouts
- Tests that time out may be marked as ERROR or FAILED. Check console output for "Timeout" messages.
### Infrastructure Issues
- If many tests fail with similar errors (`NoClassDefFoundError`, `DatabaseConnectionException`), check build-level failures and problems.
---
## 13. Stopping Background Builds
Always stop background builds when they are no longer needed:
```json
{
"stopBuildId": "BUILD_ID"
}
```
---
## 14. Foreground vs. Background Identity
Monitoring a background build using `query_build` or `wait_build` provides exactly the same rich diagnostic data as a foreground build, including progressive disclosure. The difference is control flow: background allows non-blocking work
while the build proceeds; foreground blocks until completion.

View File

@ -0,0 +1,119 @@
---
name: interacting_with_project_runtime
description: >
Executes Kotlin code interactively within the project's full JVM classpath.
Use when you need to RUN code: verify runtime behavior, experiment with logic, or render Compose UI previews.
Do NOT use to understand an API's shape or signature — read its source with `exploring_dependency_sources` instead.
license: Apache-2.0
metadata:
author: https://github.com/rnett/gradle-mcp
version: "2.3"
---
# Authoritative Project Runtime Interaction
Runs Kotlin code interactively within the project's exact JVM classpath — for when you need to execute, not just read.
## Constitution
- **The core decision rule**: If the question is *"what does this API look like or how does it work?"* → use `search_dependency_sources` / `read_dependency_sources` (read the source). If the question is *"what happens when I run this?"*
use `kotlin_repl`.
- **NEVER** start a REPL session to learn about an API. Reading indexed sources is instantaneous, shows the full implementation with all overloads, and requires no JVM process.
- **ALWAYS** use `kotlin_repl` instead of a standalone Kotlin REPL for project-aware interaction.
- **ALWAYS** provide absolute paths for `projectRoot`.
- **ALWAYS** start a REPL session with the correct `projectPath` and `sourceSet` (e.g., `main`, `test`).
- **ALWAYS** restart the REPL (`stop` then `start`) after modifying project source code to pick up changes in the classpath.
- **ALWAYS** use the `responder` API for rich output (images, markdown) to improve diagnostic visibility.
- **NEVER** leave a REPL session running indefinitely; use `stop` when finished.
- **REPL Session Management**: Explicitly terminate previous REPL sessions in `ReplTools` before starting new ones when session IDs are regenerated. This prevents leaking worker processes and ensures stable session management during
concurrent or sequential tool calls.
## Directives
- **Read to understand, run to verify**: If you need to understand what an API does — its signature, parameters, overloads, or implementation — read its source via `search_dependency_sources` / `read_dependency_sources`. Only reach for the
REPL once you know what you want to call and need to observe actual runtime output.
- **ALWAYS use project-aware REPL**: Only the `kotlin_repl` tool provides full access to the project's exact classpath, dependencies, and source sets. NEVER attempt to use standalone runners for project-internal logic.
- **Identify the environment**: When starting a session, ALWAYS ensure you select the appropriate `projectPath` (e.g., `:app`) and `sourceSet` (e.g., `main` for application code, `test` for test utility access).
- **Pick up source changes**: The REPL uses a static snapshot of the classpath. If you change project code, you MUST `stop` and then `start` the session again to pick up the updated classes.
- **Utilize the `responder`**: ALWAYS use `responder.render()` or specialized methods (`markdown`, `image`, `html`) to return rich content.
- **Import necessary classes**: ALWAYS provide explicit imports for project-specific and library classes.
- **Use `envSource: SHELL` if environment variables are missing**: If the REPL fails to find expected environment variables (e.g., `JAVA_HOME` or specific JDKs), it may be because the host process started before the shell environment was
fully loaded. Set `env: { envSource: "SHELL" }` when calling `start` to force a new shell process to query the environment.
- **Resolve `{baseDir}` manually**: If your environment does not automatically resolve the `{baseDir}` placeholder in reference links, treat it as the absolute path to the directory containing this `SKILL.md` file.
## When to Use (you need to RUN code)
- **Behavior Verification**: You know the API, you've written the call, and you need to observe the actual runtime output or side-effects.
- **Logic Prototyping**: Experimenting with an algorithm or snippet of your own code before committing it to source.
- **Visual Component Auditing**: Rendering Compose UI components to images for visual review.
- **Dynamic Data Probing**: One-off data transformations using your project's existing utilities where the output depends on runtime state.
## When NOT to Use (you need to READ source instead)
Ask yourself: *"Am I trying to understand this API, or run it?"*
If you're trying to understand it — what methods it has, what its parameters are, how it's implemented — **stop and use `exploring_dependency_sources` first**. The REPL cannot tell you what you don't already know to ask; source reading can.
Examples of what belongs in source reading, not the REPL:
- "What methods does `SomeClass` have?" → `search_dependency_sources` DECLARATION search
- "What does this function do internally?" → `read_dependency_sources`
- "What are the parameters / overloads of this function?" → `read_dependency_sources`
- "Does this library have a class for X?" → `search_dependency_sources` FULL_TEXT or DECLARATION search
## Workflows
### Starting an Authoritative Session
1. Identify the project module (e.g., `:app`) and source set (e.g., `main`).
2. Call `kotlin_repl(command="start")`.
3. Optionally provide `env` for environment variables or `additionalDependencies` if you need external libraries not currently in the project.
### Probing Code & State
1. Use `kotlin_repl(command="run")` with your Kotlin code.
2. Use `responder.render()` for rich diagnostics.
3. Review the returned text or image content.
### Lifecycle Management
1. Use `kotlin_repl(command="stop")` once your investigation is complete to release system resources.
## Examples
### Probing a project utility function
```json
// Start the session
{
"command": "start",
"projectPath": ":my-project",
"sourceSet": "main"
}
// Execute the probe
{
"command": "run",
"code": "import com.example.utils.MyHelper\nMyHelper.calculateSum(1, 2)"
}
// Reasoning: Using kotlin_repl to verify a utility function in the context of the main source set.
```
### Visualizing a UI Component
```kotlin
import androidx.compose.ui.test.*
import com.example.ui.MyComposable
runComposeUiTest {
setContent { MyComposable() }
val bitmap = onRoot().captureToImage()
responder.render(bitmap)
}
// Reasoning: Using the responder API to retrieve a high-resolution image of a Compose component.
```
## Troubleshooting
- **REPL Not Started**: You must call `start` successfully before calling `run`.
- **ClassNotFoundException**: Ensure the project has been built at least once and that you have selected the correct `sourceSet` (e.g., `test` if the class is in `src/test/kotlin`).
- **Changes Not Reflected**: If your code changes aren't appearing, `stop` and `start` the REPL to refresh the classpath.

View File

@ -0,0 +1,166 @@
---
name: managing_gradle_dependencies
description: >
Audits and manages Gradle dependency graphs with high-resolution update checks, transitive tree analysis, and Maven Central discovery;
use for dependency auditing, finding stable updates, and resolving GAV coordinates.
Do NOT use for exploring dependency source code (use `exploring_dependency_sources`) or running builds/tests (use `gradle`).
license: Apache-2.0
metadata:
author: https://github.com/rnett/gradle-mcp
version: "3.4"
---
# Authoritative Dependency Intelligence & Maven Central Search
Audits project dependencies, performs high-resolution update checks, and discovers new libraries on Maven Central with powerful, integrated search tools.
## Constitution
- **ALWAYS** use `inspect_dependencies` for querying project dependency information instead of raw Gradle tasks.
- **ALWAYS** provide absolute paths for `projectRoot`.
- **ALWAYS** use `updatesOnly: true` to quickly identify available library updates.
- **ALWAYS** use `lookup_maven_versions` to find exact GAV coordinates for new libraries.
- **NEVER** add a dependency to a project without verifying its authoritative version and existence on Maven Central.
- **ALWAYS** use the `projectPath` argument to target specific modules in multi-project builds.
## Directives
- **Identify authoritative paths**: ALWAYS use the Gradle project path (e.g., `:app`) when querying dependencies.
- **Inspect plugins and build scripts**: Build script dependencies (like plugins) are automatically included in `inspect_dependencies` output under configurations prefixed with `buildscript:` (e.g. `buildscript:classpath`).
- **Monitor for updates**: ALWAYS use `updatesOnly: true` in `inspect_dependencies` to retrieve a flat, high-signal report of available library updates: `group:artifact: current → latest` with the project paths where each dep is used.
Configuration and source-set detail is intentionally omitted; use `inspect_dependencies` with a specific `dependency` filter if that detail is needed.
- **Target dependencies surgically**: Use the `dependency` parameter in `inspect_dependencies` to target a single library with a full-string Kotlin regex over `group:name:version[:variant]` coordinates.
- **Efficient Transitive Isolation**: When isolating a single library, filter the flattened list of resolved components using the dependency filter rather than traversing the dependency graph. This naturally and efficiently excludes
transitive dependencies that do not match the targeted filter.
- **Discover libraries surgically**: ALWAYS use `lookup_maven_versions` to check the version history of an existing artifact.
- **Use `gradle` for diagnostics**: For built-in tasks like `dependencyInsight`, ALWAYS use the `gradle` tool with `captureTaskOutput`.
- **Audit full trees**: ALWAYS use `onlyDirect: false` in `inspect_dependencies` when you need to visualize the complete transitive dependency graph.
## When to Use
- **Dependency Tree Auditing**: When you need to visualize the full dependency graph for a specific project, configuration, or source set.
- **Automated Update Detection**: When performing maintenance and you want a concise report on available stable or pre-release updates.
- **Precision Artifact Discovery**: When looking for new libraries on Maven Central and you need to find exact GAV coordinates or explore an artifact's full version history.
- **Version Conflict Resolution**: When you need to identify why a specific version of a library is being resolved and look for compatible alternatives.
- **Targeted Audit**: When you only care about a specific library and want to bypass the cost of a full project resolution.
## Workflows
### 1. Auditing Dependencies
1. Identify the project module (e.g., `:app`).
2. Call `inspect_dependencies(projectPath=":app")`.
3. Optionally filter by `configuration` (e.g., `runtimeClasspath`) or `sourceSet` (e.g., `test`).
### 2. Checking for Stable Updates
1. Call `inspect_dependencies(updatesOnly=true, stableOnly=true)`.
2. Review the flat list of upgradeable dependencies. Each entry shows `group:artifact: current → latest` and the project paths where it is used.
### 3. Discovering New Libraries
1. Use `lookup_maven_versions(coordinates="group:artifact")` to see all available versions for a specific library.
### 4. Targeted Dependency Inspection
1. Identify the dependency you want to check (e.g., `org.mongodb:mongodb-driver-sync`).
2. Call `inspect_dependencies(dependency="^org\\.mongodb:mongodb-driver-sync(:.*)?$")`.
3. The report will be focused ONLY on that library across all matched configurations.
### 5. Adding a New Dependency
1. **Search Maven Central**: Use `lookup_maven_versions(coordinates="group:artifact")` to find the artifact and its latest version.
2. **Update Version Catalog**: Add the dependency coordinates to `gradle/libs.versions.toml`:
```toml
[versions]
my-lib = "X.Y.Z"
[libraries]
my-lib = { group = "com.example", name = "my-lib", version.ref = "my-lib" }
```
3. **Apply to `build.gradle.kts`**: Use the type-safe catalog accessor (e.g., `implementation(libs.my.lib)`) in the appropriate dependency configuration.
4. **Verify Resolution**: Run `inspect_dependencies(fresh: true)` to confirm the dependency resolves correctly.
#### Example: Adding a dependency to a subproject
```json
// Step 1: Discover the library
{
"coordinates": "com.squareup.retrofit2:retrofit"
}
// Step 2: Update libs.versions.toml with the version and library entry
// Step 3: Add `implementation(libs.retrofit)` to the subproject's build.gradle.kts
// Step 4: Verify
{
"projectPath": ":app",
"dependency": "^com\\.squareup\\.retrofit2:retrofit(:.*)?$"
}
// Reasoning: Adding the Retrofit library to the 'app' module with full resolution verification.
```
### 6. Verifying Plugin Dependencies
Build script dependencies (plugins) are automatically reported under `buildscript:` configurations. To specifically verify plugin resolution:
1. Call `inspect_dependencies(sourceSetPath=":buildscript")` for the root project, or `sourceSetPath=":app:buildscript"` for a subproject.
2. Review the `buildscript:classpath` configuration for plugin dependencies.
3. Use `fresh: true` if plugins were recently added or updated.
#### Example: Verifying a specific plugin
```json
{
"sourceSetPath": ":buildscript",
"dependency": "^org\\.jetbrains\\.kotlin:kotlin-gradle-plugin(:.*)?$"
}
// Reasoning: Verifying the Kotlin plugin is properly resolved in the buildscript classpath.
```
## Examples
### List dependencies for a specific module
```json
{
"projectPath": ":app"
}
// Reasoning: Auditing the direct and transitive dependencies of the 'app' module to understand its runtime footprint.
```
### Check for updates for a specific library
```json
{
"dependency": "^org\\.jetbrains\\.kotlinx:kotlinx-coroutines-core(:.*)?$",
"updatesOnly": true
}
// Reasoning: Surgically checking if a specific library has available updates.
```
### Check for stable updates across the project
```json
{
"updatesOnly": true,
"stableOnly": true
}
// Reasoning: Performing a high-signal update audit that ignores unstable pre-release versions.
```
### List all versions of a specific library
```json
{
"coordinates": "org.jetbrains.kotlinx:kotlinx-serialization-json"
}
// Reasoning: Retrieving the full version history of an artifact to identify the latest stable or specific version required.
```
## Troubleshooting
- **Dependency Not Found**: Verify the `projectPath` using the `projects` task in the `gradle` skill.
- **Update Not Showing**: If a known update is missing, ensure `stableOnly` is set correctly and check if a `versionFilter` is active.
- **[UPDATE CHECK SKIPPED]**: This annotation means the dep was in scope for update checking but its resolution genuinely failed — it does NOT appear for dependencies intentionally excluded from the update-check scope (e.g., transitive deps
when `onlyDirect=true`, or deps excluded by a `dependency` filter).
- **Maven Search No Results**: Use broader search terms or verify the `group:artifact` format for version searches.
- **Missing environment variables**: Set `invocationArguments: { envSource: "SHELL" }` if Gradle cannot find expected env vars (e.g., `JAVA_HOME`).

View File

@ -0,0 +1,165 @@
---
name: verifying_compose_ui
description: >
Visually verifies Compose UI components by rendering @Composable/@Preview functions to images from the project's JVM runtime;
STRONGLY PREFERRED for rapid UI iteration and visual feedback on any composable.
Do NOT use for build lifecycle tasks or dependency auditing.
license: Apache-2.0
metadata:
author: https://github.com/rnett/gradle-mcp
version: "2.2"
---
## ⚠️ FUNDAMENTAL ANDROID LIMITATION
**IMPORTANT: `runComposeUiTest` and image capture methods (`node.captureToImage()`) are NOT supported on Android and CANNOT work.**
### Why This Cannot Work
The `runComposeUiTest` function requires a JVM-based test runtime with desktop Compose rendering (via Skiko on JVM/Desktop). Android's ART (Android Runtime) does not support the desktop Compose testing APIs, and image capture via
`captureToImage()` relies on desktop-specific rendering pipelines that are fundamentally incompatible with Android.
### The Solution: Use a JVM Target
**For visual verification of Composables, you MUST use a JVM or Desktop target source set.** This is not a workaround—it is the only supported method.
Recommended approach:
1. **Put your Composables in a common source set** (`commonMain`) that is shared across all targets
2. **Create or use a JVM target** (e.g., `jvmMain`, `desktopMain`) that depends on `commonMain`
3. **Run the REPL on the JVM target** (`sourceSet: "jvmMain"` or `sourceSet: "jvmTest"`)
This is the standard KMP pattern and allows you to verify your UI code without needing an Android device or emulator.
### What WILL NOT Work
- ❌ Running the REPL with `sourceSet: "androidMain"`
- ❌ Running the REPL with `sourceSet: "androidTest"`
- ❌ Any attempt to use `runComposeUiTest` on Android
- ❌ Any attempt to use `captureToImage()` on Android
### What WILL Work
- ✅ Running the REPL with `sourceSet: "jvmMain"` or `sourceSet: "jvmTest"`
- ✅ Running the REPL with `sourceSet: "desktopMain"` or `sourceSet: "desktopTest"`
- ✅ Using `runComposeUiTest` with `captureToImage()` on JVM/Desktop targets
---
# Authoritative Compose UI Preview & Visual Verification
Visually verifies and renders any @Composable or @Preview directly to high-quality images from the project-aware REPL for instant, authoritative visual feedback.
## Constitution
- **ALWAYS** use `kotlin_repl` to render Compose components instead of running the full application for visual checks.
- **ALWAYS** provide absolute paths for `projectRoot`.
- **ALWAYS** use `node.captureToImage()` and `responder.render(bitmap)` to return the visual output.
- **ALWAYS** ensure the correct Compose UI testing dependencies are on the classpath, using `additionalDependencies` if necessary.
- **NEVER** assume a UI component renders correctly without visual verification.
- **ALWAYS** search for existing `@Preview` functions in the project source code before creating new ones.
## Directives
- **ALWAYS provide absolute `projectRoot`**: Ensure `projectRoot` is an **absolute file system path** for all `kotlin_repl` calls.
- **Ensure dependencies**: ALWAYS ensure Compose UI testing dependencies (e.g., `androidx.compose.ui:ui-test-junit4`) are on the classpath.
- **Note on versions**: ALWAYS check your project's version catalog and existing tests for the correct imports for `runComposeUiTest`.
- **Render as image**: ALWAYS use `node.captureToImage()` and `responder.render(bitmap)` to return the visual output.
- **Kotlin Multiplatform (KMP) Note**: The `kotlin_repl` currently only supports **JVM-based** source sets. ALWAYS select a JVM or Desktop target source set for visual checks.
- **Use `envSource: SHELL` if environment variables are missing**: Set `env: { envSource: "SHELL" }` (REPL start) or `invocationArguments: { envSource: "SHELL" }` (Gradle tasks) if expected env vars (e.g., `JAVA_HOME`) are not found.
## When to Use
- **Rapid UI Prototyping & Iteration**: When you need to see the visual result of a Composable change instantly without the latency of a full application launch.
- **Authoritative @Preview Verification**: When you want to verify the visual correctness of existing `@Preview` functions.
- **Complex UI State & Interaction Testing**: When you need to capture visual state before and after interactions (like clicks or state changes).
- **Multi-Configuration Visual Auditing**: When checking how a component renders across different data states or configurations (e.g., different view models or mock data).
## Workflows
### 1. Identifying the Component
1. Find the fully qualified name of the `@Composable` or `@Preview` function.
2. Search for existing previews in the source code via `grep_search(pattern="@Preview")`.
### 2. Orchestrating the Session
1. Start the REPL with the `test` source set (preferred) or `main` with `additionalDependencies`.
2. Use `kotlin_repl(command="start")`.
3. Use `kotlin_repl(command="run")` to execute the rendering script.
### 3. Rendering & Verifying
1. Execute a script that uses `runComposeUiTest` to render and capture the component.
2. Inspect the returned image for visual correctness.
## Examples
### Viewing a simple Composable
```kotlin
import androidx.compose.ui.test.*
import com.example.ui.MyButton
runComposeUiTest {
setContent {
MyButton(text = "Click Me")
}
val node = onRoot()
responder.render(node.captureToImage())
}
// Reasoning: Using kotlin_repl to render a specific component and retrieve its visual representation via the responder API.
```
### Viewing an existing @Preview
```kotlin
import androidx.compose.ui.test.*
import com.example.ui.MyButtonPreview // Top-level preview function
runComposeUiTest {
setContent {
MyButtonPreview()
}
val node = onRoot()
responder.render(node.captureToImage())
}
// Reasoning: Reusing an existing authoritative preview function to verify its visual correctness.
```
### Capturing State Transitions
```kotlin
import androidx.compose.ui.test.*
import com.example.ui.MyCounter
import com.example.viewmodel.MyViewModel
runComposeUiTest {
val viewModel = MyViewModel()
setContent {
MyCounter(viewModel)
}
// Capture state before interaction
responder.render("State before: ${viewModel.count}")
responder.render(onRoot().captureToImage())
// Perform interaction
onNodeWithText("Increment").performClick()
// Capture state after interaction
responder.render("State after: ${viewModel.count}")
responder.render(onRoot().captureToImage())
}
// Reasoning: Capturing visual snapshots before and after an interaction to verify state-dependent UI changes.
```
## Troubleshooting
- **No Image Returned**: Ensure you are calling `responder.render(bitmap)`.
- **ClassNotFoundException**: Check if you have the correct imports and that the required testing dependencies are on the classpath.
- **Empty Image**: If the Composable is empty or has zero size, the image will be empty. Verify your Composable's modifiers.
## Resources
- [Troubleshooting]({baseDir}/references/troubleshooting.md)

View File

@ -0,0 +1,87 @@
# Troubleshooting Compose in REPL
Common issues when rendering Compose components in the REPL and how to resolve them.
## Kotlin Multiplatform (KMP) Issues
The Gradle REPL only supports JVM-based source sets.
- **Issue**: Attempting to start the REPL on a common, iOS, or Android source set fails.
- **Solution**: Select a JVM or Desktop target source set. Recommended names: `jvmMain`, `jvmTest`, `desktopMain`, `desktopTest`.
- **Issue**: Dependencies from `commonMain` are not resolving.
- **Solution**: Starting the REPL on a JVM-specific source set that *depends* on `commonMain` (which is standard KMP structure) will include all inherited dependencies. Ensure the project is built before starting.
## Android Limitation (FUNDAMENTAL)
**IMPORTANT: `runComposeUiTest` and `captureToImage()` CANNOT work on Android.**
This is not a bug or limitation that can be fixed—it is a fundamental architectural incompatibility:
- `runComposeUiTest` requires desktop Compose runtime with Skiko rendering
- Android's ART does not support desktop Compose testing APIs
- `captureToImage()` relies on desktop-specific rendering pipelines
**Solution**: Put your Composables in `commonMain` and run the REPL on a JVM target (`jvmMain`, `jvmTest`, `desktopMain`, or `desktopTest`).
## `ClassNotFoundException: androidx.compose.ui.test.junit4.DesktopComposeTestRule`
This occurs when the Compose UI Test dependencies are not on the REPL's classpath.
The REPL uses the runtime classpath of the selected `sourceSet`.
If you are using `sourceSet: "main"`, but the test dependencies are in `testImplementation`, they won't be included.
**Solutions:**
1. **(Preferred)** Start the REPL with `sourceSet: "test"`. This usually includes the required test dependencies.
2. Add the dependency manually using `additionalDependencies` if using `sourceSet: "main"`:
```json
{
"command": "start",
"projectPath": ":my-app",
"sourceSet": "main",
"additionalDependencies": ["org.jetbrains.compose.ui:ui-test-junit4-desktop:1.7.0"]
}
```
## `java.lang.NoClassDefFoundError: org/jetbrains/skiko/SkiaLayer`
Skiko is the rendering engine for Compose Desktop. It might be missing if the project is not correctly configured for Compose Desktop or if dependencies are incomplete.
**Solution:**
Ensure `compose.desktop.currentOs` is in the project's dependencies or add it to `additionalDependencies`.
## Composable Renders but No Image is Shown
The `project_repl` tool only returns content that is explicitly rendered via `responder.render(value)` or the result of the last expression in the script.
**Solution:**
Ensure you call `responder.render(bitmap)` inside your `runComposeUiTest` block.
```kotlin
runComposeUiTest {
setContent { MyComposable() }
responder.render(onRoot().captureToImage()) // <--- Important!
}
```
## `Unresolved reference: runComposeUiTest`
The `runComposeUiTest` function may be located in different packages depending on your version of Compose or whether you are using JetBrains Compose.
**Solutions:**
1. Check the correct import for your project. Common ones:
- `androidx.compose.ui.test.runComposeUiTest`
- `org.jetbrains.compose.ui.test.runComposeUiTest` (for older or specific JetBrains Compose versions)
2. Ensure you are using a compatible version of the `ui-test` library. Some older versions used `runDesktopComposeUiTest` or similar.
## `Unresolved reference: responder`
The `responder` property is automatically injected into the REPL session by the worker.
If it is unresolved, check if you have a custom `providedProperties` in your script configuration (usually not an issue for users).
Ensure you are using the `project_repl` tool which uses the custom REPL worker.
## `java.lang.IllegalStateException: runComposeUiTest { ... } needs to be called from the main thread`
On some platforms, Compose UI tests might require being run on the main/UI thread.
The REPL evaluates snippets on a background thread. `runComposeUiTest` usually handles its own thread management, but if you encounter this, try wrapping your code in a `SwingUtilities.invokeLater` (for Desktop) if applicable, though
`runComposeUiTest` should be sufficient.

View File

@ -1,5 +1,17 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"gradle": {
"type": "local",
"command": [
"jbang",
"run",
"--java",
"25",
"--quiet",
"--fresh",
"gradle-mcp@rnett"
]
}
}
}

View File

@ -176,7 +176,7 @@ private fun badgeGradient(colors: ButtonColors): Brush = remember(colors) {
}
@Composable
internal fun buttonShadow(
private fun buttonShadow(
shape: androidx.compose.ui.graphics.Shape,
pressed: Boolean,
primary: Boolean,

View File

@ -1,171 +0,0 @@
/*
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package dev.krtirtho.spotube.core.ui.base
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
private val ChipTabShape = RoundedCornerShape(10.dp)
private val ChipTabMinHeight = 36.dp
@Composable
fun ChipTab(
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
contentPadding: PaddingValues = PaddingValues(horizontal = 14.dp, vertical = 4.dp),
content: @Composable RowScope.() -> Unit,
) {
val colors = rememberButtonColors()
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val isHovered by interactionSource.collectIsHoveredAsState()
val gradient = if (selected) {
primaryGradient(colors, isPressed)
} else {
outlinedGradient(colors, isPressed)
}
val borderColor = if (selected) {
colors.accent
} else {
colors.border.copy(alpha = if (isPressed) 0.7f else 1f)
}
val contentColor = if (selected) colors.onAccent else colors.onContainer
Box(
modifier = modifier
.defaultMinSize(minHeight = ChipTabMinHeight)
.hoverable(interactionSource = interactionSource, enabled = enabled)
.then(
buttonShadow(
shape = ChipTabShape,
pressed = isPressed,
primary = selected,
colors = colors,
hovered = isHovered,
)
)
.clip(ChipTabShape)
.background(gradient, ChipTabShape)
.border(BorderStroke(0.5.dp, borderColor), ChipTabShape)
.clickable(
enabled = enabled,
interactionSource = interactionSource,
indication = ripple(),
onClick = onClick,
)
.drawWithCache {
val highlight = if (selected) {
Color.White.copy(alpha = 0.25f)
} else {
colors.highlight
}
val highlightBrush = Brush.verticalGradient(
colors = listOf(highlight, Color.Transparent),
startY = 0f,
endY = size.height * 0.5f,
)
onDrawWithContent {
drawContent()
drawRect(
brush = highlightBrush,
topLeft = androidx.compose.ui.geometry.Offset.Zero,
size = size,
)
}
}
.padding(contentPadding),
contentAlignment = Alignment.Center,
) {
CompositionLocalProvider(
LocalContentColor provides contentColor,
LocalTextStyle provides LocalTextStyle.current.copy(
fontWeight = FontWeight.SemiBold,
fontSize = 14.sp,
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
content = content,
)
}
}
}
@Composable
fun ChipTab(
text: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null,
contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
) {
ChipTab(
selected = selected,
onClick = onClick,
modifier = modifier,
enabled = enabled,
contentPadding = contentPadding,
) {
if (leadingIcon != null) {
leadingIcon()
}
Text(
text = text,
maxLines = 1,
softWrap = false,
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Normal,
)
}
}

View File

@ -107,7 +107,7 @@ fun TextField(
keyboardActions: KeyboardActions = KeyboardActions.Default,
visualTransformation: VisualTransformation = VisualTransformation.None,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
textStyle: TextStyle = TextStyle.Default,
textStyle: TextStyle = LocalTextStyle.current,
cursorBrush: Color = MaterialTheme.colorScheme.primary,
) {
val colors = rememberButtonColors()
@ -167,6 +167,7 @@ fun TextField(
if (leadingIcon != null) {
leadingIcon()
}
Box(modifier = Modifier.weight(1f)) {
BasicTextField(
value = value,

View File

@ -27,6 +27,7 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -37,6 +38,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@ -56,6 +58,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -96,7 +99,6 @@ import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user.MetadataUser
import dev.krtirtho.spotube.core.audioplayer.AudioPlayerQueue
import dev.krtirtho.spotube.core.audioplayer.QueueEntry
import dev.krtirtho.spotube.core.share.ShareService
import dev.krtirtho.spotube.core.ui.base.ChipTab
import dev.krtirtho.spotube.core.ui.component.AlbumCard
import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar
import dev.krtirtho.spotube.core.ui.component.ArtistCard
@ -611,14 +613,51 @@ private fun SearchTabs(
) {
items(types) { type ->
val isSelected = selectedType == type
ChipTab(
SearchTab(
label = type.tabTitle(),
selected = isSelected,
onClick = { onTabSelected(type) }
) {
Text(type.tabTitle())
)
}
}
}
@Composable
private fun SearchTab(
label: String,
selected: Boolean,
onClick: () -> Unit,
) {
Surface(
onClick = onClick,
shape = TabShape,
color = if (selected)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.surface,
contentColor = if (selected)
MaterialTheme.colorScheme.onPrimaryContainer
else
MaterialTheme.colorScheme.onSurfaceVariant,
border = if (selected)
null
else
BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant),
modifier = Modifier.height(32.dp)
) {
Box(
modifier = Modifier
.padding(horizontal = 14.dp, vertical = 6.dp),
contentAlignment = Alignment.Center
) {
Text(
text = label,
style = MaterialTheme.typography.labelLarge,
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Medium,
maxLines = 1
)
}
}
}
@Composable