# tool

`software.visionary:tool:2026.10.13` · https://codeberg.org/javanese/tool

Visionary Software Solutions Tool: command-line tools written once and run by name

Write a command-line tool by stating its name, its arguments and its work; a module providing it as a <code>FromCommandLine</code> launches as that tool.

## Rationale

**Why.** A <code>ToolProvider</code> leaves reading arguments, printing help and refusing to each tool. Aenima does them once, so every tool does them alike.

## Key Concepts

### Aenima

**Role.** The <code>ToolProvider</code> and <code>FromCommandLine</code> a tool extends, stating <code>name()</code>, <code>arguments(String...)</code> and <code>answer(Invocation)</code>.

**Pattern.** Template Method

### ToolProviderToCommand

**Role.** An ADAPTER from a <code>ToolProvider</code> and its arguments to a commander <code>Command</code>, which throws when the tool exits other than 0, quoting what it said.

## Lane

**Purpose.** Writing a command-line tool, and launching it or running it from Java.

**Alternative.** The JDK's <code>ToolProvider.findFirst</code>, when the tool's exact name is known and its status is read by hand.

## Usage

Write a tool: its arguments, its name, its work

```java
public record Root(List<Path> value) implements Required<List<Path>> {
    public static Root create(final String said) {
        return new Root(Stream.of(said.split("\u001f", -1)).map(Path::of).toList());
    }
}

public final class Arguments extends software.visionary.argumentative.Arguments {
    public Arguments(final String... line) {
        super(line, accepted -> accepted.option(Root.class, Root::create));
    }
}

public final class FindShape extends Aenima {
    @Override
    protected Arguments arguments(final String... line) {
        return new Arguments(line);
    }

    @Override
    public String name() {
        return "find-shape";
    }

    @Override
    protected String answer(final Invocation invocation) {
        final List<Path> roots = invocation.required(Root.class);
        roots.forEach(root -> invocation.emitProgress("censusing " + root));
        return census(roots);
    }
}

```

Give it help: a bundle beside the class, its package opened to tool

```java
# src/main/resources/com/example/shape/FindShape.properties
help=Censuses source roots for one structural shape.\n\
Example: find-shape --root src/main/java

// module-info.java
opens com.example.shape to software.visionary.tool;

# find-shape --help
#   Censuses source roots for one structural shape.
#   Example: find-shape --root src/main/java

```

Run it from Java: run it, throw if it fails

```java
new ToolProviderToCommand(new FindShape(), List.of("--root", "src/main/java")).execute();

```

Launch it: its module provides it as a FromCommandLine, and the whole line is its arguments

```java
// module-info.java
provides software.visionary.cthulu.FromCommandLine with com.example.shape.FindShape;

ServiceLoader.load(FromCommandLine.class).findFirst().orElseThrow()
        .apply(new String[]{"--root", "src/main/java"})
        .execute();

```

## Dependencies

- `software.visionary:cthulu:2026.10.13` at Compile
- `software.visionary.contrax:processor:2026.10.13` at Processor
- `software.visionary:junit-closure:2026.09.17` at Testing

## Caveats

- Progress goes to err as the work runs; the answer goes to out once, at the end, so a refusal leaves no half answer on out.
- The bundle's <code>help</code> is plain text, not a MessageFormat, and is the tool's whole description.
- A tool whose non-zero status is still an answer calls <code>complain()</code> and returns the answer; throwing discards it.
- What <code>arguments(String...)</code> throws escapes <code>run</code> and <code>apply</code>, before any work; a refusal from the work is exit 1, its message on err.

