diff --git a/docs/superpowers/plans/2026-06-14-minimessage-config.md b/docs/superpowers/plans/2026-06-14-minimessage-config.md
new file mode 100644
index 0000000..6eabea9
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-14-minimessage-config.md
@@ -0,0 +1,217 @@
+# MiniMessage Config Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Let player-visible configuration text support MiniMessage while keeping existing legacy `&` and `#RRGGBB` formatting compatible.
+
+**Architecture:** Keep `ColorUtil` as the single text-formatting boundary. Add Adventure text serializers there, return legacy `String` values to preserve current command, quest, reward, and API signatures.
+
+**Tech Stack:** Java 21, Maven, Paper API Adventure classes, JUnit 5 for utility tests.
+
+---
+
+## File Structure
+
+- Modify `pom.xml`: add test dependencies for JUnit 5 and Surefire so Maven can run unit tests.
+- Create `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java`: focused tests for legacy compatibility and MiniMessage parsing.
+- Modify `src/main/java/com/io/yaohun/questengine/util/ColorUtil.java`: implement MiniMessage parsing while retaining legacy output.
+
+### Task 1: Add ColorUtil Regression Tests
+
+**Files:**
+- Modify: `pom.xml`
+- Create: `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java`
+
+- [ ] **Step 1: Add JUnit test dependencies**
+
+Add these dependencies under `` in `pom.xml`:
+
+```xml
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.10.3
+ test
+
+```
+
+Add this plugin under `` in `pom.xml`:
+
+```xml
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+```
+
+- [ ] **Step 2: Write failing tests**
+
+Create `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java`:
+
+```java
+package com.io.yaohun.questengine.util;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ColorUtilTest {
+
+ @Test
+ void colorKeepsLegacyAmpersandFormatting() {
+ assertEquals("§a任务 §f完成", ColorUtil.color("&a任务 &f完成"));
+ }
+
+ @Test
+ void colorKeepsLegacyHexFormatting() {
+ assertEquals("§x§1§2§3§4§5§6任务", ColorUtil.color("#123456任务"));
+ }
+
+ @Test
+ void colorSupportsMiniMessageNamedColors() {
+ assertEquals("§a任务", ColorUtil.color("任务"));
+ }
+
+ @Test
+ void colorSupportsMiniMessageDecorations() {
+ assertEquals("§l加粗", ColorUtil.color("加粗"));
+ }
+
+ @Test
+ void colorParsesEveryListEntry() {
+ assertEquals(
+ List.of("§a领取", "§c完成"),
+ ColorUtil.color(List.of("领取", "&c完成"))
+ );
+ }
+}
+```
+
+- [ ] **Step 3: Run tests to verify RED**
+
+Run:
+
+```bash
+mvn -q -Dtest=ColorUtilTest test
+```
+
+Expected: tests compile, legacy tests pass, MiniMessage tests fail because tags are not parsed yet.
+
+### Task 2: Implement MiniMessage Support
+
+**Files:**
+- Modify: `src/main/java/com/io/yaohun/questengine/util/ColorUtil.java`
+- Test: `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java`
+
+- [ ] **Step 1: Replace ColorUtil implementation**
+
+Update `ColorUtil.java` to:
+
+```java
+package com.io.yaohun.questengine.util;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.ChatColor;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class ColorUtil {
+
+ private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");
+ private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
+ private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection();
+
+ public static String color(String text) {
+ if (text == null) {
+ return "";
+ }
+ String legacyText = translateLegacyColors(text);
+ Component component = MINI_MESSAGE.deserialize(legacyText);
+ return LEGACY_SERIALIZER.serialize(component);
+ }
+
+ public static List color(List list) {
+ return list.stream().map(ColorUtil::color).collect(Collectors.toList());
+ }
+
+ private static String translateLegacyColors(String text) {
+ Matcher matcher = HEX_PATTERN.matcher(text);
+ while (matcher.find()) {
+ String hexCode = text.substring(matcher.start(), matcher.end());
+ String replaceSharp = hexCode.replace('#', 'x');
+ char[] ch = replaceSharp.toCharArray();
+ StringBuilder builder = new StringBuilder();
+ for (char c : ch) {
+ builder.append("&").append(c);
+ }
+ text = text.replace(hexCode, builder.toString());
+ matcher = HEX_PATTERN.matcher(text);
+ }
+
+ return ChatColor.translateAlternateColorCodes('&', text);
+ }
+}
+```
+
+- [ ] **Step 2: Run focused tests to verify GREEN**
+
+Run:
+
+```bash
+mvn -q -Dtest=ColorUtilTest test
+```
+
+Expected: all `ColorUtilTest` tests pass.
+
+### Task 3: Verify Plugin Build
+
+**Files:**
+- Verify: full project
+
+- [ ] **Step 1: Run full test suite**
+
+Run:
+
+```bash
+mvn test
+```
+
+Expected: build succeeds and `ColorUtilTest` passes.
+
+- [ ] **Step 2: Run compile**
+
+Run:
+
+```bash
+mvn compile
+```
+
+Expected: production code compiles.
+
+- [ ] **Step 3: Review changed files**
+
+Run:
+
+```bash
+git diff --check
+git status --short
+```
+
+Expected: no whitespace errors; only planned files are modified or added.
+
+- [ ] **Step 4: Commit implementation**
+
+Run:
+
+```bash
+git add pom.xml src/main/java/com/io/yaohun/questengine/util/ColorUtil.java src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java docs/superpowers/plans/2026-06-14-minimessage-config.md
+git commit -m "feat: 支持配置 MiniMessage 文本"
+```
diff --git a/pom.xml b/pom.xml
index dfa04c0..cf0afc9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,6 +85,12 @@
3.6.52
provided
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.10.3
+ test
+
@@ -124,6 +130,11 @@
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
diff --git a/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java b/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java
index 7ad7216..6ab503d 100644
--- a/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java
+++ b/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java
@@ -1,5 +1,8 @@
package com.io.yaohun.questengine.util;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.ChatColor;
import java.util.List;
@@ -10,11 +13,28 @@ import java.util.stream.Collectors;
public class ColorUtil {
private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");
+ private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
+ private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection();
public static String color(String text) {
if (text == null) {
return "";
}
+ if (text.indexOf('§') >= 0) {
+ Component component = LEGACY_SERIALIZER.deserialize(text);
+ return LEGACY_SERIALIZER.serialize(component);
+ }
+ String hexText = translateHexColors(text);
+ Component component = MINI_MESSAGE.deserialize(hexText);
+ String serialized = LEGACY_SERIALIZER.serialize(component);
+ return ChatColor.translateAlternateColorCodes('&', serialized);
+ }
+
+ public static List color(List list) {
+ return list.stream().map(ColorUtil::color).collect(Collectors.toList());
+ }
+
+ private static String translateHexColors(String text) {
Matcher matcher = HEX_PATTERN.matcher(text);
while (matcher.find()) {
String hexCode = text.substring(matcher.start(), matcher.end());
@@ -28,10 +48,6 @@ public class ColorUtil {
matcher = HEX_PATTERN.matcher(text);
}
- return ChatColor.translateAlternateColorCodes('&', text);
+ return text;
}
-
- public static List color(List list) {
- return list.stream().map(ColorUtil::color).collect(Collectors.toList());
- }
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java b/src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java
new file mode 100644
index 0000000..8ceb83c
--- /dev/null
+++ b/src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java
@@ -0,0 +1,53 @@
+package com.io.yaohun.questengine.util;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ColorUtilTest {
+
+ @Test
+ void colorKeepsLegacyAmpersandFormatting() {
+ assertEquals("§a任务 §f完成", ColorUtil.color("&a任务 &f完成"));
+ }
+
+ @Test
+ void colorKeepsLegacyHexFormatting() {
+ assertEquals("§x§1§2§3§4§5§6任务", ColorUtil.color("#123456任务"));
+ }
+
+ @Test
+ void colorSupportsMiniMessageNamedColors() {
+ assertEquals("§a任务", ColorUtil.color("任务"));
+ }
+
+ @Test
+ void colorSupportsMiniMessageDecorations() {
+ assertEquals("§l加粗", ColorUtil.color("加粗"));
+ }
+
+ @Test
+ void colorSupportsMixedMiniMessageAndLegacyFormatting() {
+ assertEquals("§a领取§r §c奖励", ColorUtil.color("领取 &c奖励"));
+ }
+
+ @Test
+ void colorCanBeCalledOnAlreadyColoredText() {
+ assertEquals("§a任务", ColorUtil.color(ColorUtil.color("任务")));
+ }
+
+ @Test
+ void colorLeavesUnknownMiniMessageTagsVisible() {
+ assertEquals("任务", ColorUtil.color("任务"));
+ }
+
+ @Test
+ void colorParsesEveryListEntry() {
+ assertEquals(
+ List.of("§a领取", "§c完成"),
+ ColorUtil.color(List.of("领取", "&c完成"))
+ );
+ }
+}