| name | java-cli-script |
|---|---|
| description | 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. |
Create or maintain a zero-dependency, single-file executable Java script using $ARGUMENTS. Apply all rules below strictly.
These are self-contained, single-file Java scripts installed in a PATH directory (e.g., /usr/local/bin) and invoked like any shell command — no project structure, no build tool, no .java extension.
- Follow
/java-conventionsfor all generic Java 25 style, syntax, naming, methods, streams, exceptions, and comments - The rules below specialize
/java-conventionsfor single-file source-mode scripts; when this skill is silent on a topic,/java-conventionsapplies
- Everything lives in one file — all logic, records, enums, sealed types, and helper methods
- If the script grows beyond what fits comfortably in a single file, suggest switching to the
java-cli-appskill instead - No multi-file source-code programs — this skill is strictly for single-file scripts
- Use Java 25 source-file mode — no build tool, no compilation step
- Never use the
.javaextension — scripts are executable files with a lowercase filename and a shebang - Never use
--enable-previewin the shebang — Java 25 is a GA release, all features used here are standard - The shebang line is always:
#!/usr/bin/env -S java --source 25
- Save with a short, descriptive, lowercase command name (e.g.,
jsonformat,portcheck,sysinfo) - Never use dashes in filenames (e.g.,
hello-world) — Java source-file mode does not support them. Use camelCase instead (e.g.,helloWorld) - Mark executable:
chmod +x scriptname - Install by copying or symlinking to a PATH directory:
cp scriptname /usr/local/bin/orln -s $(pwd)/scriptname /usr/local/bin/scriptname
A minimal script:
#!/usr/bin/env -S java --source 25
void main(String... args) throws Exception {
// ...
}
- The script's filename is its application name — use it consistently in all output: version strings, help text, usage lines, and error messages
- Derive the application name dynamically at startup using
MethodHandles.lookup().lookupClass().getName()— in source-file mode, the class name matches the filename, so there is nothing to hardcode or keep in sync
- Suggest maintaining a
String version = "YYYY-MM-DD.N";instance variable (e.g.,String version = "2026-02-20.1";) - On every change, update the date to the current date and increase the last number
- Print the script name and version on startup as the first line of output (e.g.,
IO.println(name + " " + version);) - Support
-versionflag to print the version — only for scripts that accept additional arguments
- Use
void main(String... args) throws Exception— PATH-installed scripts always accept arguments; declaringthrows Exceptionkeeps the code clean by avoiding try-catch boilerplate for checked exceptions likeIOException - Parse arguments manually for simple scripts with few flags
- For scripts with multiple options or complex argument handling, use the
/zargsskill to generate an enum-based argument parser — it remains zero-dependency and single-file
- Scripts with no additional arguments: do not introduce
-helpor-versionflags — the script should just do its work - Scripts with additional arguments: support both
-helpand-versionflags - Use clear, descriptive error messages for invalid input
- Exit with code 0 on success, non-zero on failure
Script with additional arguments — include -help and -version:
String name = MethodHandles.lookup().lookupClass().getName();
String version = "2026-02-20.1";
void main(String... args) throws Exception {
IO.println(name + " " + version);
if (args.length == 0 || args[0].equals("-help")) {
IO.println("""
Usage: %s <input> [options]
-help Show this help
-version Show version
""".formatted(name));
return;
}
if (args[0].equals("-version")) {
return;
}
// ...
}
When asked for an executable convenience script for a /java-cli-app project or an existing JAR, do not replicate the application's logic in the script. The script stays a single-file source script whose main calls into the JAR's classes. Instead:
- Keep source-file mode and add the JAR to the classpath in the same shebang — both are required so the source file compiles and can resolve the app's classes:
#!/usr/bin/env -S java --source 25 -cp /path/to/app.jar
- This is the one sanctioned exception to the "never add classpath entries to the shebang" rule under Zero Dependencies — it applies only when wrapping an existing app, never for standalone scripts
- Delegate to the existing functionality by calling the app's public API. Only fall back to invoking its
main(String[])when no other entry point exists — callingmaindirectly re-runs the app's startup banner and argument parsing - If the existing code is not callable from a single-file launcher (e.g., no accessible entry point, tightly coupled logic), recommend refactoring the
java-cli-appto expose a public API or executable entry point rather than duplicating its behavior in the script - Never copy or paraphrase the application's logic into the convenience script
- Only use the
java.basemodule and standard JDK modules - Never add external JARs or classpath entries to the shebang
- Use
java.net.http.HttpClientfor HTTP,javax.cryptofor crypto,java.nio.filefor file I/O - If a task genuinely requires external libraries or multiple files, suggest using the
java-cli-appskill with zb instead
Generic Java style comes from /java-conventions. The rules below are script-specific specializations.
- Do not create classes or interfaces — use unnamed classes with top-level methods, records, enums, and sealed types only
- No package declaration
- Use
IO.println()for printing (orIO::printlnas method reference) — neverSystem.out.println()and neverSystem.Logger(scripts use stdout directly) - For multi-line output, use a single
IO.printlnwith a text block — never multipleIO.printlncalls for consecutive lines
- Read from stdin when no file argument is given — support piping (e.g.,
cat file | scriptname) - Write normal output to stdout
- Write errors and diagnostics to stderr using
System.err.println() - For colored terminal output (status messages, errors, warnings), use the
/zclskill — it adds ANSI color support with zero dependencies - Support both piped input and file arguments where applicable
- For scripts that need external configuration (properties files, environment overrides), use the
/zcfgskill to add zero-dependency configuration loading - Most scripts should prefer command-line arguments over configuration files — only use
/zcfgwhen persistent settings are genuinely needed
The following zero-dependency libraries can be embedded directly into single-file scripts. They may need to be cloned first:
- zcl — colored terminal output:
git clone https://github.com/AdamBien/zcl - zargs — enum-based argument parsing pattern: https://github.com/AdamBien/zeeds/blob/main/zargs (no repo to clone — the
/zargsskill generates the code directly) - zcfg — configuration loading:
git clone https://github.com/AdamBien/zcfg
When presenting the finished script, always include installation instructions:
# Make executable
chmod +x scriptname
# Install system-wide (pick one)
sudo cp scriptname /usr/local/bin/
# or symlink for development
sudo ln -s $(pwd)/scriptname /usr/local/bin/scriptname