# Java CLI Script

> 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.

- Skill: `adambien/java-cli-script` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add adambien/java-cli-script`
- Raw SKILL.md: https://api.skillmd.com/api/skills/adambien/java-cli-script/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: AdamBien (https://skillmd.com/u/adambien)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/adambien/java-cli-script

---


Create or maintain a zero-dependency, single-file executable Java script using $ARGUMENTS. Apply all rules below strictly.

## Purpose

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.

## Composition

- Follow `/java-conventions` for all generic Java 25 style, syntax, naming, methods, streams, exceptions, and comments
- The rules below specialize `/java-conventions` for single-file source-mode scripts; when this skill is silent on a topic, `/java-conventions` applies

## Single-File Constraint

- 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-app` skill instead
- No multi-file source-code programs — this skill is strictly for single-file scripts

## Build

- Use Java 25 source-file mode — no build tool, no compilation step
- Never use the `.java` extension — scripts are executable files with a lowercase filename and a shebang
- Never use `--enable-preview` in 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/` or `ln -s $(pwd)/scriptname /usr/local/bin/scriptname`

## Script Structure

A minimal script:

```
#!/usr/bin/env -S java --source 25

void main(String... args) throws Exception {
    // ...
}
```

## Naming Convention

- 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

## Version Management

- 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 `-version` flag to print the version — only for scripts that accept additional arguments

## Main Method Conventions

- Use `void main(String... args) throws Exception` — PATH-installed scripts always accept arguments; declaring `throws Exception` keeps the code clean by avoiding try-catch boilerplate for checked exceptions like `IOException`
- Parse arguments manually for simple scripts with few flags
- For scripts with multiple options or complex argument handling, use the `/zargs` skill to generate an enum-based argument parser — it remains zero-dependency and single-file

## Argument Handling

- Scripts with no additional arguments: do not introduce `-help` or `-version` flags — the script should just do its work
- Scripts with additional arguments: support both `-help` and `-version` flags
- 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;
    }
    // ...
}
```

## Convenience Scripts for an Existing java-cli-app or JAR

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](#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 — calling `main` directly 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-app` to 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

## Zero Dependencies

- Only use the `java.base` module and standard JDK modules
- Never add external JARs or classpath entries to the shebang
- Use `java.net.http.HttpClient` for HTTP, `javax.crypto` for crypto, `java.nio.file` for file I/O
- If a task genuinely requires external libraries or multiple files, suggest using the `java-cli-app` skill with zb instead

## Code Style

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 (or `IO::println` as method reference) — never `System.out.println()` and never `System.Logger` (scripts use stdout directly)
- For multi-line output, use a single `IO.println` with a text block — never multiple `IO.println` calls for consecutive lines

## stdin / stdout / stderr

- 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 `/zcl` skill — it adds ANSI color support with zero dependencies
- Support both piped input and file arguments where applicable

## Configuration (Optional)

- For scripts that need external configuration (properties files, environment overrides), use the `/zcfg` skill to add zero-dependency configuration loading
- Most scripts should prefer command-line arguments over configuration files — only use `/zcfg` when persistent settings are genuinely needed

## Companion Libraries

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 `/zargs` skill generates the code directly)
- **zcfg** — configuration loading: `git clone https://github.com/AdamBien/zcfg`

## Installation Guidance

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
```

