$npx -y skills add AdamBien/airails --skill zcfgIntegrate zcfg (Zero Dependency Configuration Utility) into Java applications. Use when adding configuration loading, reading properties files, setting up application configuration, or integrating zcfg into a Java project. Triggers on "zcfg", "add configuration", "load properties
| 1 | Integrate zcfg into a Java application using $ARGUMENTS. Apply all rules below. |
| 2 | |
| 3 | ## What is zcfg |
| 4 | |
| 5 | zcfg is a zero-dependency, single-class Java configuration loader. It reads standard Java properties files from multiple sources with defined precedence and provides type-safe access to configuration values. |
| 6 | |
| 7 | - Source: https://github.com/AdamBien/zcfg |
| 8 | - Requires: Java 21+ |
| 9 | |
| 10 | ## Source Integration |
| 11 | |
| 12 | zcfg is integrated by copying the source file directly into the target project — no Maven dependency required. |
| 13 | |
| 14 | **Source location:** https://github.com/AdamBien/zcfg/blob/main/src/main/java/airhacks/zcfg/ZCfg.java |
| 15 | |
| 16 | **For zb projects:** Download the file preserving its package structure: |
| 17 | ``` |
| 18 | curl -sf https://raw.githubusercontent.com/AdamBien/zcfg/main/src/main/java/airhacks/zcfg/ZCfg.java -o src/main/java/airhacks/zcfg/ZCfg.java --create-dirs |
| 19 | ``` |
| 20 | The `package airhacks.zcfg;` declaration stays as-is. Import with `import airhacks.zcfg.*;`. |
| 21 | |
| 22 | **For other projects:** |
| 23 | |
| 24 | 1. Determine the target project's base package (e.g., `com.example.myapp`) |
| 25 | 2. Create the file `ZCfg.java` in a `zcfg` sub-package under the base package (e.g., `src/main/java/com/example/myapp/zcfg/ZCfg.java` or `src/com/example/myapp/zcfg/ZCfg.java`) |
| 26 | 3. Write the following source with the `package` declaration adjusted to match the target location: |
| 27 | |
| 28 | ```java |
| 29 | package <base-package>.zcfg; |
| 30 | |
| 31 | import java.io.IOException; |
| 32 | import java.nio.file.Files; |
| 33 | import java.nio.file.Path; |
| 34 | import java.util.List; |
| 35 | import java.util.Properties; |
| 36 | import java.util.stream.Stream; |
| 37 | |
| 38 | public class ZCfg { |
| 39 | |
| 40 | static final String PROPERTIES_FILE = "app.properties"; |
| 41 | static Properties CACHE; |
| 42 | |
| 43 | public static void load(String appName) { |
| 44 | CACHE = loadProperties(appName); |
| 45 | } |
| 46 | |
| 47 | static Properties loadProperties(String appName) { |
| 48 | var properties = new Properties(); |
| 49 | var userHome = System.getProperty("user.home"); |
| 50 | var globalConfig = Path.of(userHome, "." + appName, PROPERTIES_FILE); |
| 51 | if (Files.exists(globalConfig)) { |
| 52 | loadFromFile(globalConfig, properties); |
| 53 | } |
| 54 | var localConfig = Path.of(PROPERTIES_FILE); |
| 55 | if (Files.exists(localConfig)) { |
| 56 | loadFromFile(localConfig, properties); |
| 57 | } |
| 58 | properties.putAll(System.getProperties()); |
| 59 | return properties; |
| 60 | } |
| 61 | |
| 62 | static void loadFromFile(Path file, Properties properties) { |
| 63 | try (var is = Files.newBufferedReader(file)) { |
| 64 | properties.load(is); |
| 65 | } catch (IOException e) { |
| 66 | throw new IllegalStateException("Cannot load properties from: " + file, e); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | public static String string(String key) { |
| 71 | if (CACHE == null) |
| 72 | throw new IllegalStateException("Call ZCfg.load(appName) first"); |
| 73 | return CACHE.getProperty(key); |
| 74 | } |
| 75 | |
| 76 | public static String string(String key, String defaultValue) { |
| 77 | if (CACHE == null) |
| 78 | throw new IllegalStateException("Call ZCfg.load(appName) first"); |
| 79 | return CACHE.getProperty(key, defaultValue); |
| 80 | } |
| 81 | |
| 82 | public static int integer(String key, int defaultValue) { |
| 83 | if (CACHE == null) |
| 84 | throw new IllegalStateException("Call ZCfg.load(appName) first"); |
| 85 | var value = CACHE.getProperty(key); |
| 86 | return value != null ? Integer.parseInt(value) : defaultValue; |
| 87 | } |
| 88 | |
| 89 | public static boolean bool(String key, boolean defaultValue) { |
| 90 | if (CACHE == null) |
| 91 | throw new IllegalStateException("Call ZCfg.load(appName) first"); |
| 92 | var value = CACHE.getProperty(key); |
| 93 | return value != null ? Boolean.parseBoolean(value) : defaultValue; |
| 94 | } |
| 95 | |
| 96 | public static List<String> strings(String key) { |
| 97 | if (CACHE == null) |
| 98 | throw new IllegalStateException("Call ZCfg.load(appName) first"); |
| 99 | var value = CACHE.getProperty(key); |
| 100 | if (value == null) |
| 101 | return List.of(); |
| 102 | return split(value); |
| 103 | } |
| 104 | |
| 105 | static List<String> split(String value) { |
| 106 | var values = value.split(","); |
| 107 | return Stream.of(values) |
| 108 | .map(String::trim) |
| 109 | .toList(); |
| 110 | } |
| 111 | } |
| 112 | ``` |
| 113 | |
| 114 | Replace `<base-package>` with the actual target project package. The import in consuming classes becomes `import <base-package>.zcfg.ZCfg;`. |
| 115 | |
| 116 | ## Configuration Loading Order |
| 117 | |
| 118 | `ZCfg.load("myapp")` merges properties from three sources, each overwriting the previous: |
| 119 | |
| 120 | 1. `$HOME/.myapp/app.properties` — global user configuration (`user.home` system property) |
| 121 | 2. `./app.properties` — local project configuration (current working directory) |
| 122 | 3. System properties via `-D` flags, e.g., |