diff --git a/.agents/skills/exploring_dependency_sources/SKILL.md b/.agents/skills/exploring_dependency_sources/SKILL.md deleted file mode 100644 index 23863d58..00000000 --- a/.agents/skills/exploring_dependency_sources/SKILL.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -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: ` header line. Dependency directories inside - the sources root are symlinks; always pass `--follow` to `rg` (e.g., `rg --follow `). - -## 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: ` 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="", searchType="DECLARATION", projectPath=":")`. -3. Identify the correct file path from the results. -4. Call `read_dependency_sources(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="", 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="", searchType="DECLARATION", sourceSetPath=":buildscript")`. -2. Read plugin implementation: `read_dependency_sources(path="//...", 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="", dependency="", 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="", 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. diff --git a/.agents/skills/exploring_dependency_sources/references/internal_source_research.md b/.agents/skills/exploring_dependency_sources/references/internal_source_research.md deleted file mode 100644 index 2bf164e5..00000000 --- a/.agents/skills/exploring_dependency_sources/references/internal_source_research.md +++ /dev/null @@ -1,126 +0,0 @@ -# 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. diff --git a/.agents/skills/gradle/SKILL.md b/.agents/skills/gradle/SKILL.md deleted file mode 100644 index 8ed6ed0c..00000000 --- a/.agents/skills/gradle/SKILL.md +++ /dev/null @@ -1,345 +0,0 @@ ---- -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 ` 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")`) instead of eager APIs (e.g., `tasks.create("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 ` 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:
`. - -### Idiomatic DSL Patterns - -- **Prefer `register` over `create` (Lazy APIs)**: Use `tasks.register("myTask")` to avoid eager task configuration. -- **Use Type-Safe Accessors**: Prefer `tasks.test { ... }` or `tasks.named("test") { ... }` over `tasks.getByName("test")`. -- **Use Lazy Properties**: Employ `Property` and `Provider` 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 "/src/main/kotlin"`. -3. Add to `settings.gradle.kts`: Append `include(":")`. -4. Create `build.gradle.kts` with idiomatic patterns (apply convention plugins, set up standard configuration). -5. Verify: `gradle(commandLine=["::tasks"], captureTaskOutput="::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`, `Provider`) 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 ", 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. diff --git a/.agents/skills/gradle/references/background_monitoring.md b/.agents/skills/gradle/references/background_monitoring.md deleted file mode 100644 index f4b0b647..00000000 --- a/.agents/skills/gradle/references/background_monitoring.md +++ /dev/null @@ -1,105 +0,0 @@ -# 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. diff --git a/.agents/skills/gradle/references/best_practices.md b/.agents/skills/gradle/references/best_practices.md deleted file mode 100644 index 837ba1f8..00000000 --- a/.agents/skills/gradle/references/best_practices.md +++ /dev/null @@ -1,109 +0,0 @@ -# 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") { ... }` over `tasks.getByName("test")`. -- **Prefer `register` over `create` (Lazy APIs)**: Use `tasks.register("myTask")` to avoid eager task configuration. -- **Use Lazy Properties**: Employ the `Property` and `Provider` 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) diff --git a/.agents/skills/gradle/references/common_build_patterns.md b/.agents/skills/gradle/references/common_build_patterns.md deleted file mode 100644 index e265c232..00000000 --- a/.agents/skills/gradle/references/common_build_patterns.md +++ /dev/null @@ -1,142 +0,0 @@ -# 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().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 - - @TaskAction - fun action() { - println("Message: ${message.get()}") - } -} - -tasks.register("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) diff --git a/.agents/skills/gradle/references/diagnostic_tasks.md b/.agents/skills/gradle/references/diagnostic_tasks.md deleted file mode 100644 index e10ada27..00000000 --- a/.agents/skills/gradle/references/diagnostic_tasks.md +++ /dev/null @@ -1,27 +0,0 @@ -# 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 ` 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. diff --git a/.agents/skills/gradle/references/gradle_docs_research.md b/.agents/skills/gradle/references/gradle_docs_research.md deleted file mode 100644 index 25d62b38..00000000 --- a/.agents/skills/gradle/references/gradle_docs_research.md +++ /dev/null @@ -1,100 +0,0 @@ -# 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) diff --git a/.agents/skills/gradle/references/query_build_diagnostics.md b/.agents/skills/gradle/references/query_build_diagnostics.md deleted file mode 100644 index 7c44075d..00000000 --- a/.agents/skills/gradle/references/query_build_diagnostics.md +++ /dev/null @@ -1,380 +0,0 @@ -# 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. diff --git a/.agents/skills/interacting_with_project_runtime/SKILL.md b/.agents/skills/interacting_with_project_runtime/SKILL.md deleted file mode 100644 index 0d1cbb5c..00000000 --- a/.agents/skills/interacting_with_project_runtime/SKILL.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -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. diff --git a/.agents/skills/managing_gradle_dependencies/SKILL.md b/.agents/skills/managing_gradle_dependencies/SKILL.md deleted file mode 100644 index 0adc95d2..00000000 --- a/.agents/skills/managing_gradle_dependencies/SKILL.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -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`). diff --git a/.agents/skills/verifying_compose_ui/SKILL.md b/.agents/skills/verifying_compose_ui/SKILL.md deleted file mode 100644 index 829ba045..00000000 --- a/.agents/skills/verifying_compose_ui/SKILL.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -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) diff --git a/.agents/skills/verifying_compose_ui/references/troubleshooting.md b/.agents/skills/verifying_compose_ui/references/troubleshooting.md deleted file mode 100644 index 11158fb2..00000000 --- a/.agents/skills/verifying_compose_ui/references/troubleshooting.md +++ /dev/null @@ -1,87 +0,0 @@ -# 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. diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc index 6ed31c91..3ee6e20d 100644 --- a/.opencode/opencode.jsonc +++ b/.opencode/opencode.jsonc @@ -1,17 +1,5 @@ { "$schema": "https://opencode.ai/config.json", "mcp": { - "gradle": { - "type": "local", - "command": [ - "jbang", - "run", - "--java", - "25", - "--quiet", - "--fresh", - "gradle-mcp@rnett" - ] - } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt index d589ebca..b16c284a 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/Buttons.kt @@ -176,7 +176,7 @@ private fun badgeGradient(colors: ButtonColors): Brush = remember(colors) { } @Composable -private fun buttonShadow( +internal fun buttonShadow( shape: androidx.compose.ui.graphics.Shape, pressed: Boolean, primary: Boolean, diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/ChipTabs.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/ChipTabs.kt new file mode 100644 index 00000000..1738ea26 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/ChipTabs.kt @@ -0,0 +1,171 @@ +/* + * 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 . + */ + +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, + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt index 44c53ae4..de3e727e 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/base/TextField.kt @@ -107,7 +107,7 @@ fun TextField( keyboardActions: KeyboardActions = KeyboardActions.Default, visualTransformation: VisualTransformation = VisualTransformation.None, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - textStyle: TextStyle = LocalTextStyle.current, + textStyle: TextStyle = TextStyle.Default, cursorBrush: Color = MaterialTheme.colorScheme.primary, ) { val colors = rememberButtonColors() @@ -167,7 +167,6 @@ fun TextField( if (leadingIcon != null) { leadingIcon() } - Box(modifier = Modifier.weight(1f)) { BasicTextField( value = value, diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/search/SearchScreen.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/search/SearchScreen.kt index 7a529cc1..496286ea 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/search/SearchScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/search/SearchScreen.kt @@ -27,7 +27,6 @@ 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 @@ -38,7 +37,6 @@ 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 @@ -58,7 +56,6 @@ 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 @@ -99,6 +96,7 @@ 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 @@ -613,49 +611,12 @@ private fun SearchTabs( ) { items(types) { type -> val isSelected = selectedType == type - SearchTab( - label = type.tabTitle(), + ChipTab( selected = isSelected, onClick = { onTabSelected(type) } - ) - } - } -} - -@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 - ) + ) { + Text(type.tabTitle()) + } } } }