Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions AGENTS-JAVA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Embedded Agents Inventory

## Goal

Provide a quick checklist of the embedded agents available for installation in this repository.

## Embedded agents

| Agent | Primary purpose |
| --- | --- |
| `robot-business-analyst` | Analyze requirements, user stories, and implementation plans. |
| `robot-coordinator` | Coordinate Java enterprise implementation flow across specialized coders. |
| `robot-java-coder` | Implement framework-agnostic Java changes and refactors. |
| `robot-micronaut-coder` | Implement Micronaut-specific code and architecture changes. |
| `robot-quarkus-coder` | Implement Quarkus-specific code and architecture changes. |
| `robot-spring-boot-coder` | Implement Spring Boot-specific code and architecture changes. |

## Installation target options

- `.cursor/agents`
- `.claude/agents`
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.14.0] 2026-04-12

### Added

- **Skills**:
- `@001-skills-inventory` checklist skill that emits `SKILLS-JAVA.md` in the project root using the embedded system-prompts template
- `@002-agents-inventory` checklist skill that emits `AGENTS-JAVA.md` with the embedded agents table and installation targets
- `@003-agents-installation` interactive installer that copies the six embedded robot agents into `.cursor/agents` or `.claude/agents`
- Add INVEST validation to agile user-story workflow in `@014-agile-user-story` (#633)
- Maven Central search guidance skill (`@114-java-maven-search`) (#605)
- OpenSpec adoption for project change management in `@042-planning-openspec` (#620, #621, #616)
- GitHub issue management workflow support`@043-planning-github` (#607)
- Jira planning support and guidance improvements in `@044-planning-jira` (#631, #641, #642)
- OpenAPI guidance skill `@701-technologies-openapi` (#635)
- WireMock guidance skill `@702-technologies-wiremock` (#636)
- Fuzz testing skill `@703-technologies-fuzzing-testing` and CATS example coverage (#634, #646)
- Cursor and Claude plugin support documentation/workflow (#622)

- **Rules:**
- Created an ADR to drop support for Rules in favor of Skills.

- **Examples:**
- Reduce CI Pipeline times, removing non essential examples

## [0.13.0] 2026-03-30

### Added
Expand Down Expand Up @@ -326,6 +350,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Added initial cursor rules (Java, Effective Java, Concurrency, Functional programming, Data-Oriented programming & Spring Boot)

[0.14.0]: https://github.com/jabrena/cursor-rules-java/compare/0.13.0...0.14.0
[0.13.0]: https://github.com/jabrena/cursor-rules-java/compare/0.12.0...0.13.0
[0.12.0]: https://github.com/jabrena/cursor-rules-java/compare/0.11.0...0.12.0
[0.11.0]: https://github.com/jabrena/cursor-rules-java/compare/0.10.0...0.11.0
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Read [AGENTS.md](./AGENTS.md) for the full contributor guide (tech stack, bounda
The unified `skills-generator` module holds all XML sources and Java code used to build **agent skills** under `skills/`.

- [System prompt XML files](./skills-generator/src/main/resources/system-prompts/) use the PML Schema ([pml.xsd](https://jabrena.github.io/pml/schemas/0.5.0/pml.xsd)). They are transformed with [CursorRulesGenerator.java](./skills-generator/src/main/java/info/jab/pml/CursorRulesGenerator.java) and [system-prompts.xsl](./skills-generator/src/main/resources/system-prompts.xsl) when producing reference content for skills.
- [Skill summaries and inventory](./skills-generator/src/main/resources/) (`skills/`, `skill-inventory.json`) drive `SKILL.md` generation.
- [Skill summaries and inventory](./skills-generator/src/main/resources/) (`skills/`, `skill-inventory.xml`) drive `SKILL.md` generation.

If you have the idea to contribute, review the whole process in detail:

Expand All @@ -22,7 +22,7 @@ If you have the idea to contribute, review the whole process in detail:
npx skill-check skills # Validate generated skills
```

Keep `skill-inventory.json` aligned with `skills/` and `system-prompts/` when adding or changing skills.
Keep `skill-inventory.xml` aligned with `skills/` and `system-prompts/` when adding or changing skills.

When you feel confident with the process, fork the repository and try to create new XML documents. Models will help you because an XML file is more rigid than natural language and it has `a common vocabulary` to create prompts.

Expand Down
147 changes: 147 additions & 0 deletions SKILLS-JAVA.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions documentation/MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ Can you analyze the last Java version, Java 26 from @All-JEPS.md if exist some J
Review that the list doesn´t any broken link to @/.cursor with .md files

# Prompt to provide a release changelog
Can you update the current changelog for 0.13.0 comparing git commits in relation to 0.12.0 tag. Use @https://keepachangelog.com/en/1.1.0/ rules
Can you update the current changelog for 0.14.0 comparing git commits in relation to 0.13.0 tag. Use @https://keepachangelog.com/en/1.1.0/ rules

#Bump to a new snapshot
@resources/ update version to 0.13.0 and pom.xml and maven modules
@resources/ update version to 0.14.0 and pom.xml, maven modules and finally regenerate the skills
```

## Release process
Expand Down Expand Up @@ -64,5 +64,5 @@ git push --tags
## Add a new Skills

```bash
review if exist a new id in @skills-generator/src/main/resources/skill-inventory.json to review compare with the content of @skills-generator/src/main/resources/skills and if exist add a new skill summary in @skills-generator/src/main/resources/skills . to elaborate the skill review the content of the id with @skills-generator/src/main/resources/system-prompts when finish, validate generation with ./mvnw clean install -pl skills-generator and validate the skill with npx skill-check skills
review if exist a new id in @skills-generator/src/main/resources/skill-inventory.xml to review compare with the content of @skills-generator/src/main/resources/skills and if exist add a new skill summary in @skills-generator/src/main/resources/skills . to elaborate the skill review the content of the id with @skills-generator/src/main/resources/system-prompts when finish, validate generation with ./mvnw clean install -pl skills-generator and validate the skill with npx skill-check skills
```
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The new skill SHALL complement, not duplicate, framework REST skills: framework

### Requirement: Generator Integration

New sources for **701** MUST be registered in both `skill-inventory.json` and `system-prompt-inventory.json`, and the `skills-generator` module MUST build successfully after the change.
New sources for **701** MUST be registered in both `skill-inventory.xml` and `system-prompt-inventory.xml`, and the `skills-generator` module MUST build successfully after the change.

#### Scenario: Maven verify passes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The new skill SHALL complement, not replace, framework integration-test skills:

### Requirement: Generator Integration

New sources for **702** MUST be registered in both `skill-inventory.json` and `system-prompt-inventory.json`, and the `skills-generator` module MUST build successfully after the change.
New sources for **702** MUST be registered in both `skill-inventory.xml` and `system-prompt-inventory.xml`, and the `skills-generator` module MUST build successfully after the change.

#### Scenario: Maven verify passes

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>info.jab</groupId>
<artifactId>cursor-rules-java</artifactId>
<version>0.14.0-SNAPSHOT</version>
<version>0.14.0</version>
<packaging>pom</packaging>
<name>cursor-rules-java</name>
<description>The project provides a curated collection of System prompts and Skills for modern
Expand Down
2 changes: 1 addition & 1 deletion site-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<parent>
<groupId>info.jab</groupId>
<artifactId>cursor-rules-java</artifactId>
<version>0.14.0-SNAPSHOT</version>
<version>0.14.0</version>
</parent>

<artifactId>cursor-rules-java-site</artifactId>
Expand Down
12 changes: 3 additions & 9 deletions skills-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,14 @@
<parent>
<groupId>info.jab</groupId>
<artifactId>cursor-rules-java</artifactId>
<version>0.14.0-SNAPSHOT</version>
<version>0.14.0</version>
</parent>

<groupId>info.jab.pml</groupId>
<artifactId>cursor-rules-java-skills-generator</artifactId>
<packaging>jar</packaging>

<dependencies>
<!-- JSON parsing for skill-inventory.json -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<!-- Logging dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
Expand Down Expand Up @@ -77,11 +71,11 @@
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>skill-inventory.json</include>
<include>skill-inventory.xml</include>
<include>skills/**</include>
<include>system-prompts/**</include>
<include>system-prompts.xsl</include>
<include>system-prompt-inventory.json</include>
<include>system-prompt-inventory.xml</include>
</includes>
</resource>
</resources>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package info.jab.pml;

import java.io.InputStream;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;

/** Loads small local inventory documents with safe parser settings. */
final class InventoryXmlLoader {

private InventoryXmlLoader() {}

static Document parse(InputStream in) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(false);
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
try {
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
} catch (IllegalArgumentException ignored) {
// not all parsers support these attributes
}
try {
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (ParserConfigurationException ignored) {
// optional hardening
}
DocumentBuilder builder = dbf.newDocumentBuilder();
return builder.parse(in);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* <p>
* Reuses CursorRulesGenerator for full rule content. SKILL.md is sourced from
* {@code skills/{numericId}-skill.md} (user-editable), where numericId is extracted from skillId (e.g. 110 from 110-java-maven-best-practices).
* The list of skills to generate is defined in {@code skill-inventory.json}; each must have a
* The list of skills to generate is defined in {@code skill-inventory.xml}; each must have a
* matching skill summary in {@code skills/} and a matching system-prompt in {@code system-prompts/}.
*/
public final class SkillsGenerator {
Expand Down
88 changes: 52 additions & 36 deletions skills-generator/src/main/java/info/jab/pml/SkillsInventory.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package info.jab.pml;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -16,18 +13,22 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
* Inventory of skills to generate, loaded from {@code skill-inventory.json}.
* Inventory of skills to generate, loaded from {@code skill-inventory.xml}.
* <p>
* Each entry has an {@code id} (numeric or string like "010"). When {@code requiresSystemPrompt}
* is true (default), the skillId is derived by matching system-prompts with prefix {@code {id}-}.
* When false, the entry must specify {@code skillId} and no system-prompt is required.
* Each skill must have a summary in {@code skills/{id}-skill.md}.
* Each skill must have a summary in {@code skills/{id}-skill.md} or {@code skills/{id}-skill.xml}
* when {@code xml="true"} on the entry.
*/
public final class SkillsInventory {

private static final String INVENTORY_RESOURCE = "skill-inventory.json";
private static final String INVENTORY_RESOURCE = "skill-inventory.xml";
private static final String SYSTEM_PROMPTS_PREFIX = "system-prompts/";

private SkillsInventory() {}
Expand Down Expand Up @@ -150,64 +151,79 @@ private static URL getResourceUrl(String name) {
}

/**
* Loads and parses skill-inventory.json.
* Loads and parses skill-inventory.xml.
*/
public static List<InventoryEntry> loadInventory() {
try (InputStream stream = getResource(INVENTORY_RESOURCE)) {
if (stream == null) {
throw new RuntimeException("Skill inventory not found: " + INVENTORY_RESOURCE);
}
String json = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
return parseInventory(json);
return parseInventory(stream);
} catch (Exception e) {
throw new RuntimeException("Failed to load skill inventory", e);
}
}

private static List<InventoryEntry> parseInventory(String json) {
private static List<InventoryEntry> parseInventory(InputStream in) {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
if (!root.isArray()) {
throw new RuntimeException("Skill inventory must be a JSON array");
Document doc = InventoryXmlLoader.parse(in);
Element root = doc.getDocumentElement();
if (!"skill-inventory".equals(root.getNodeName())) {
throw new RuntimeException("Skill inventory root must be <skill-inventory>");
}

NodeList skillNodes = root.getElementsByTagName("skill");
List<InventoryEntry> entries = new ArrayList<>();
for (JsonNode node : root) {
String numericId = node.get("id").isTextual()
? node.get("id").asText()
: String.valueOf(node.get("id").asInt());
boolean requiresSystemPrompt = node.has("requiresSystemPrompt")
? node.get("requiresSystemPrompt").asBoolean()
: true;
String skillId = node.has("skillId") ? node.get("skillId").asText() : null;
for (int i = 0; i < skillNodes.getLength(); i++) {
if (!(skillNodes.item(i) instanceof Element skillEl)) {
continue;
}
if (skillEl.getParentNode() != root) {
continue;
}
String numericId = skillEl.getAttribute("id");
if (numericId == null || numericId.isBlank()) {
throw new RuntimeException("skill-inventory entry missing id attribute");
}
boolean requiresSystemPrompt = parseBooleanAttribute(skillEl, "requiresSystemPrompt", true);
String skillId = skillEl.hasAttribute("skillId")
? skillEl.getAttribute("skillId").trim()
: null;
if (skillId != null && skillId.isEmpty()) {
skillId = null;
}

if (!requiresSystemPrompt && (skillId == null || skillId.isBlank())) {
throw new RuntimeException("Entry with id " + numericId
+ " has requiresSystemPrompt=false but no skillId specified.");
}
boolean useXml = parseXmlFlag(node);
boolean useXml = parseXmlAttribute(skillEl);
entries.add(new InventoryEntry(numericId, requiresSystemPrompt, skillId, useXml));
}
if (entries.isEmpty()) {
throw new RuntimeException("Skill inventory must contain at least one <skill> entry");
}
return entries;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Failed to parse skill inventory", e);
}
}

private static boolean parseXmlFlag(JsonNode node) {
if (!node.has("xml")) {
return false;
}
JsonNode xmlNode = node.get("xml");
if (xmlNode.isBoolean()) {
return xmlNode.asBoolean();
private static boolean parseBooleanAttribute(Element el, String name, boolean defaultValue) {
if (!el.hasAttribute(name)) {
return defaultValue;
}
if (xmlNode.isTextual()) {
String s = xmlNode.asText().toLowerCase();
return "true".equals(s) || "yes".equals(s) || "1".equals(s);
String v = el.getAttribute(name).trim().toLowerCase();
return "true".equals(v) || "yes".equals(v) || "1".equals(v);
}

private static boolean parseXmlAttribute(Element skillEl) {
if (!skillEl.hasAttribute("xml")) {
return false;
}
return false;
String s = skillEl.getAttribute("xml").trim().toLowerCase();
return "true".equals(s) || "yes".equals(s) || "1".equals(s);
}

private static void validateSkillSummaryExists(String numericId, boolean useXml) {
Expand Down Expand Up @@ -241,7 +257,7 @@ private static InputStream getResource(String name) {
}

/**
* Single entry from skill-inventory.json. When requiresSystemPrompt is true,
* Single entry from skill-inventory.xml. When requiresSystemPrompt is true,
* skillId is derived by matching system-prompts with prefix {@code {numericId}-}.
* When false, skillId must be provided and no system-prompt is required.
* When useXml is true, skill summary is loaded from skills/{numericId}-skill.xml
Expand Down
Loading