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
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ public SkillsGenerator() {

/**
* Generates SKILL.md and reference content for all skills in the inventory.
* Validates that each skill has a summary in {@code skills/} and a matching system-prompt.
* Skills with {@code requiresSystemPrompt=false} get SKILL.md only (no reference content).
*
* @return stream of generated skill outputs
*/
public Stream<SkillOutput> generateAllSkills() {
return SkillsInventory.skillIds()
.map(this::generateSkill);
return SkillsInventory.skillDescriptors()
.map(d -> generateSkill(d.skillId(), d.requiresSystemPrompt()));
}

/**
Expand All @@ -46,8 +46,20 @@ public Stream<SkillOutput> generateAllSkills() {
* @throws RuntimeException if resources cannot be loaded or generation fails
*/
public SkillOutput generateSkill(String skillId) {
SkillMetadata metadata = parseMetadata(skillId);
String referenceContent = generateReferenceContent(skillId, metadata);
return generateSkill(skillId, true);
}

/**
* Generates SKILL.md and reference content for a given skill.
*
* @param skillId the skill identifier (e.g. 110-java-maven-best-practices)
* @param requiresSystemPrompt when false, skips system-prompt XML and reference generation
* @return the generated skill output
*/
public SkillOutput generateSkill(String skillId, boolean requiresSystemPrompt) {
String referenceContent = requiresSystemPrompt
? generateReferenceContent(skillId, parseMetadata(skillId))
: "";
String skillMdContent = loadSkillSummary(skillId);
return new SkillOutput(skillId, skillMdContent, referenceContent);
}
Expand Down
71 changes: 54 additions & 17 deletions skills-generator/src/main/java/info/jab/pml/SkillsInventory.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
/**
* Inventory of skills to generate, loaded from {@code skill-inventory.json}.
* <p>
* Each entry has an {@code id}. The corresponding skillId is derived by matching
* system-prompts with prefix {@code {id}-} (e.g. id 110 matches 110-java-maven-best-practices.xml).
* Each skill must have a summary in {@code skills/{id}-skill.md} and a matching
* system-prompt in {@code system-prompts/}.
* 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}.
*/
public final class SkillsInventory {

Expand All @@ -33,9 +33,9 @@ public final class SkillsInventory {
private SkillsInventory() {}

/**
* Returns the skill IDs from the inventory. For each id, resolves the skillId
* by matching system-prompts with prefix {@code {id}-}. Validates that each
* skill has a summary in {@code skills/} and a matching system-prompt.
* Returns the skill IDs from the inventory. For each entry, validates the skill summary
* exists. When {@code requiresSystemPrompt} is true, resolves skillId from system-prompts;
* when false, uses the provided {@code skillId}.
*
* @return stream of skill IDs (e.g. 110-java-maven-best-practices)
* @throws RuntimeException if the inventory cannot be loaded or validation fails
Expand All @@ -45,14 +45,39 @@ public static Stream<String> skillIds() {
List<String> skillIds = new ArrayList<>();

for (InventoryEntry entry : entries) {
validateSkillSummaryExists(entry.id());
String skillId = resolveSkillIdFromPrefix(entry.id());
String numericId = entry.numericId();
validateSkillSummaryExists(numericId);
String skillId = entry.requiresSystemPrompt()
? resolveSkillIdFromPrefix(Integer.parseInt(numericId))
: entry.skillId();
skillIds.add(skillId);
}

return skillIds.stream();
}

/**
* Returns skill descriptors (skillId + requiresSystemPrompt) for generator use.
*/
public static Stream<SkillDescriptor> skillDescriptors() {
List<InventoryEntry> entries = loadInventory();
List<SkillDescriptor> descriptors = new ArrayList<>();
for (InventoryEntry entry : entries) {
String numericId = entry.numericId();
validateSkillSummaryExists(numericId);
String skillId = entry.requiresSystemPrompt()
? resolveSkillIdFromPrefix(Integer.parseInt(numericId))
: entry.skillId();
descriptors.add(new SkillDescriptor(skillId, entry.requiresSystemPrompt()));
}
return descriptors.stream();
}

/**
* Skill ID and whether it requires a system prompt for reference generation.
*/
public record SkillDescriptor(String skillId, boolean requiresSystemPrompt) {}

/**
* Resolves skillId by finding the system-prompt XML that starts with {@code {id}-}.
*
Expand Down Expand Up @@ -149,21 +174,32 @@ private static List<InventoryEntry> parseInventory(String json) {

List<InventoryEntry> entries = new ArrayList<>();
for (JsonNode node : root) {
int id = node.required("id").asInt();
entries.add(new InventoryEntry(id));
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;

if (!requiresSystemPrompt && (skillId == null || skillId.isBlank())) {
throw new RuntimeException("Entry with id " + numericId
+ " has requiresSystemPrompt=false but no skillId specified.");
}
entries.add(new InventoryEntry(numericId, requiresSystemPrompt, skillId));
}
return entries;
} catch (Exception e) {
throw new RuntimeException("Failed to parse skill inventory", e);
}
}

private static void validateSkillSummaryExists(int id) {
String resourceName = "skills/" + id + "-skill.md";
private static void validateSkillSummaryExists(String numericId) {
String resourceName = "skills/" + numericId + "-skill.md";
try (InputStream stream = getResource(resourceName)) {
if (stream == null) {
throw new RuntimeException("Skill summary not found: " + resourceName
+ ". Add skills/" + id + "-skill.md for each skill in the inventory.");
+ ". Add skills/" + numericId + "-skill.md for each skill in the inventory.");
}
} catch (Exception e) {
if (e instanceof RuntimeException re) {
Expand All @@ -186,8 +222,9 @@ private static InputStream getResource(String name) {
}

/**
* Single entry from skill-inventory.json: numeric id only. skillId is derived
* by matching system-prompts with prefix {@code {id}-}.
* Single entry from skill-inventory.json. 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.
*/
public record InventoryEntry(int id) {}
public record InventoryEntry(String numericId, boolean requiresSystemPrompt, String skillId) {}
}
1 change: 1 addition & 0 deletions skills-generator/src/main/resources/skill-inventory.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[
{"id": "010", "requiresSystemPrompt": false, "skillId": "010-java-design-plans"},
{"id": 110},
{"id": 111},
{"id": 112},
Expand Down
162 changes: 162 additions & 0 deletions skills-generator/src/main/resources/skills/010-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
name: 010-java-design-plans
description: Use when it is creatin a plan using Plan model and enchance the creation of structured design plans in Cursor Plan mode for Java implementations. Use when the user wants to create a plan, design an implementation, structure a development plan, or use plan mode for outside-in TDD, feature implementation, or refactoring work.
---

# Java Design Plan Creation

Guides the process of creating a structured plan using Cursor Plan mode. Plans follow a consistent section structure suitable for Java feature implementation, refactoring, or API design.

---

## Plan Mode Workflow

1. **Enter Plan mode** (or use plan-related commands) before creating the plan.
2. **Gather context**: Read specs, existing code, and acceptance criteria.
3. **Draft the plan** using the structure below.
4. **Iterate**: Refine tasks, dependencies, and file checklist as needed.

---

## Plan File Structure

Plans use Markdown with YAML frontmatter. Save to `.cursor/plans/YYYY-MM-DD_<plan_name>.plan.md` (prefix with creation date).

### YAML Frontmatter

```yaml
---
name: <Short Plan Name>
overview: "<One-line description: what, approach, key constraints.>"
todos: []
isProject: false
---
```

### Required Sections

| Section | Purpose | Content |
|---------|---------|---------|
| **Title** | Problem/feature identifier | `# Problem N: [Name] Implementation Plan` |
| **Requirements Summary** | Business context | User story, key business rules, acceptance criteria |
| **Approach** | Strategy and flow | Named approach (e.g., London Style TDD), diagram (Mermaid) |
| **Task List** | Ordered implementation steps | Table with #, Phase, Task, TDD, Status |
| **Execution Instructions** | How agents must execute | Update task Status after each task before advancing |
| **File Checklist** | What files and when | Order, File path, When (TDD phase) |
| **Notes** | Extra context | Package layout, conventions, edge cases |

---

## Section Templates

### Requirements Summary

```markdown
## Requirements Summary

**User Story:** [One sentence describing the user goal.]

**Key Business Rules:**
- **[Rule name]:** [Concrete rule]
- **[Filtering / Conversion / Timeout]:** [Behavior]
- **Expected result:** [Specific value or behavior when applicable]
```

### Approach (with Diagram)

````markdown
## [Approach Name] (e.g., London Style Outside-In TDD)

[Brief description of the strategy.]

```mermaid
flowchart LR
subgraph [Flow Name]
A[Step 1] --> B[Step 2]
B --> C[Step 3]
end
```
````

### Task List Table

```markdown
## Task List ([Approach] Order)

| # | Phase | Task | TDD | Status |
| --- | ------- | ------------------------------------------------------------- | ---- | ------ |
| 1 | Setup | [First task] | | |
| 2 | RED | [Write failing test] | Test | |
| 3 | GREEN | [Implement minimal solution] | Impl | |
| 4 | Refactor| [Polish, verify] | | |
```

**Phases:** Setup, RED (write failing test), GREEN (pass test), Refactor. Use the **Status** column to track completion (e.g., `✔`, `Done`, or `✓` when finished).

### Execution Instructions (Required)

Include this section in every plan. It reminds agents to update the task list during execution:

```markdown
## Execution Instructions

When executing this plan:
1. Complete the current task.
2. **Update the Task List**: set the Status column for that task (e.g., ✔ or Done).
3. Only then proceed to the next task.
4. Repeat for all tasks. Never advance without updating the plan.
```

### File Checklist Table

```markdown
## File Checklist ([Approach] Order)

| Order | File | When (TDD) |
| ----- | ------------------------------------------------- | ----------------------- |
| 1 | `path/to/File1.java` | Setup |
| 2 | `path/to/Test.java` | RED — write first |
| 3 | `path/to/Impl.java` | GREEN — implement |
```

---

## London Style (Outside-In) TDD Pattern

For feature implementation, prefer **outside-in** order:

1. **Acceptance/integration test** (RED) — defines API and expected behavior.
2. **Delegate/controller** (GREEN) — minimal wiring.
3. **Service unit test** (RED) — business logic in isolation.
4. **Service implementation** (GREEN) — with fake/stub dependencies.
5. **Client test** (RED) — external calls.
6. **Client implementation** (GREEN) — wire real client.
7. **Refactor** — remove fakes, add error handling, verify `mvn clean verify`.

---

## Plan Execution Workflow

When **executing** a plan, follow this discipline for every task:

1. **Run** the current task (e.g., Task 1).
2. **When the task finishes**, immediately update the plan file: set the Status column for that task (e.g., ✔ or Done or ✓).
3. **Then** proceed to the next task.
4. Repeat steps 1–3 for all tasks in order.

Never advance to the next task without updating the task list. This keeps progress visible and lets the plan file reflect the current state.

---

## Plan Creation Checklist

Before finalizing:

- [ ] Frontmatter has `name`, `overview`, `todos`, `isProject`.
- [ ] Requirements Summary includes user story and key business rules.
- [ ] Approach section names the strategy and includes a Mermaid diagram.
- [ ] Task list is ordered (Setup → RED → GREEN → Refactor) with Status column.
- [ ] **Execution Instructions** section is included (update Status after each task before advancing).
- [ ] File checklist maps files to TDD phases.
- [ ] Notes cover package layout, conventions, and constraints.
- [ ] Plan file path is `.cursor/plans/YYYY-MM-DD_<name>.plan.md`.
Loading
Loading