java/zcfg/SKILL.md
Integrate 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", "application configuration with zcfg", or requests to use the zcfg library for configuration management.
npx skillsauth add adambien/airails zcfgInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Integrate zcfg into a Java application using $ARGUMENTS. Apply all rules below.
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.
zcfg is integrated by copying the source file directly into the target project — no Maven dependency required.
Source location: https://github.com/AdamBien/zcfg/blob/main/src/main/java/airhacks/zcfg/ZCfg.java
For zb projects: Download the file preserving its package structure:
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
The package airhacks.zcfg; declaration stays as-is. Import with import airhacks.zcfg.*;.
For other projects:
com.example.myapp)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)package declaration adjusted to match the target location:package <base-package>.zcfg;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Properties;
import java.util.stream.Stream;
public class ZCfg {
static final String PROPERTIES_FILE = "app.properties";
static Properties CACHE;
public static void load(String appName) {
CACHE = loadProperties(appName);
}
static Properties loadProperties(String appName) {
var properties = new Properties();
var userHome = System.getProperty("user.home");
var globalConfig = Path.of(userHome, "." + appName, PROPERTIES_FILE);
if (Files.exists(globalConfig)) {
loadFromFile(globalConfig, properties);
}
var localConfig = Path.of(PROPERTIES_FILE);
if (Files.exists(localConfig)) {
loadFromFile(localConfig, properties);
}
properties.putAll(System.getProperties());
return properties;
}
static void loadFromFile(Path file, Properties properties) {
try (var is = Files.newBufferedReader(file)) {
properties.load(is);
} catch (IOException e) {
throw new IllegalStateException("Cannot load properties from: " + file, e);
}
}
public static String string(String key) {
if (CACHE == null)
throw new IllegalStateException("Call ZCfg.load(appName) first");
return CACHE.getProperty(key);
}
public static String string(String key, String defaultValue) {
if (CACHE == null)
throw new IllegalStateException("Call ZCfg.load(appName) first");
return CACHE.getProperty(key, defaultValue);
}
public static int integer(String key, int defaultValue) {
if (CACHE == null)
throw new IllegalStateException("Call ZCfg.load(appName) first");
var value = CACHE.getProperty(key);
return value != null ? Integer.parseInt(value) : defaultValue;
}
public static boolean bool(String key, boolean defaultValue) {
if (CACHE == null)
throw new IllegalStateException("Call ZCfg.load(appName) first");
var value = CACHE.getProperty(key);
return value != null ? Boolean.parseBoolean(value) : defaultValue;
}
public static List<String> strings(String key) {
if (CACHE == null)
throw new IllegalStateException("Call ZCfg.load(appName) first");
var value = CACHE.getProperty(key);
if (value == null)
return List.of();
return split(value);
}
static List<String> split(String value) {
var values = value.split(",");
return Stream.of(values)
.map(String::trim)
.toList();
}
}
Replace <base-package> with the actual target project package. The import in consuming classes becomes import <base-package>.zcfg.ZCfg;.
ZCfg.load("myapp") merges properties from three sources, each overwriting the previous:
$HOME/.myapp/app.properties — global user configuration (user.home system property)./app.properties — local project configuration (current working directory)-D flags, e.g., -Dserver.port=9090The app name passed to load() determines the global directory name: load("myapp") reads from $HOME/.myapp/.
Standard Java properties format:
server.port=8080
db.url=localhost:5432
db.timeout=30
debug.enabled=true
tags=web,api,backend
Call once at application startup before accessing any values:
ZCfg.load("myapp");
| Method | Returns | Behavior |
|--------|---------|----------|
| ZCfg.string(key) | String | Returns value or null |
| ZCfg.string(key, default) | String | Returns value or default |
| ZCfg.integer(key, default) | int | Parses with Integer.parseInt |
| ZCfg.bool(key, default) | boolean | Parses with Boolean.parseBoolean |
| ZCfg.strings(key) | List<String> | Splits comma-separated values, trims whitespace |
All methods throw IllegalStateException if called before load().
public class App {
public static void main(String[] args) {
ZCfg.load("myapp");
var port = ZCfg.integer("server.port", 8080);
var dbUrl = ZCfg.string("db.url", "localhost:5432");
var debug = ZCfg.bool("debug.enabled", false);
var tags = ZCfg.strings("tags");
// use configuration values
}
}
Override at runtime without changing files:
java -Dserver.port=9090 -Ddb.url=prod-db:5432 -jar myapp.jar
ZCfg.java into a zcfg sub-package of the target project's base package — never add it as a Maven dependencypackage declaration to match the target locationZCfg.load() exactly once, early in the application lifecycle (main method, @Initialized(ApplicationScoped.class) observer, or servlet context listener)string(key) without a default unless the key is mandatory"myapp", "order-service")tags=web,api,backendload() multiple times — the configuration is cached staticallytools
Generic, composable Java 25 code conventions — modern syntax, code style, naming, visibility, structure, methods, streams, exceptions, and documentation rules that apply across all Java contexts (single-file scripts, CLI apps, MicroProfile/Jakarta EE servers, libraries). Technology-neutral within the Java world; meant to be composed with context-specific skills (e.g. `java-cli-script`, `java-cli-app`, `microprofile-server`, `bce`). Use when writing, generating, or reviewing Java code anywhere the composed skill does not already specify style. Triggers on "Java conventions", "Java style", "Java code style", "modern Java", "Java 25", "idiomatic Java", or any request to write or review Java code where context-specific skills do not already cover style.
tools
Create zero-dependency, single-file executable Java scripts for system-wide use via PATH. Use when asked to create a single-file Java shell script, system utility, PATH-installed Java tool, or shebang-launched Java program without the .java extension. Triggers on "Java script", "Java utility", "PATH script", "system script", or requests for single-file Java programs installed in /usr/local/bin or similar PATH directories. Not for multi-file Java applications — use java-cli-app for those.
development
Architecture and coding rules for long-running Java MicroProfile / Jakarta EE server applications — BCE layering, business components (BC), JAX-RS resources, CDI, JSON-P, testing (unit/integration/system), and Maven project structure. Use when creating, generating, scaffolding, writing, or reviewing code, resources, entities, boundaries, or business components in MicroProfile server projects. Not for serverless deployments.
tools
Create and maintain multi-file Java 25 CLI applications packaged as executable JARs with zb (Zero Dependencies Builder). Use when asked to create a Java CLI application, a CLI project with multiple source files, or an executable JAR. Triggers on "Java CLI app", "CLI application", "multi-file Java", "executable JAR", "zb build", or requests for Java programs that need multiple source files or JAR packaging. Not for single-file scripts — use java-cli-script for those.