Skip to content

oysh spring - Commands


Writing a command

An SSH command is a Picocli @Command class annotated with @SshCommand. The annotation is meta-annotated with @Component, so the class is picked up by component scanning and auto-registered as a shell subcommand.

@SshCommand
@Command(name = "hello", description = "Says hello.")
public class HelloCommand implements Runnable {
    @Override
    public void run() {
        SshSession.current().writeLine("Hello, world!");
    }
}

Every command has access to SshSession.current() for writing output, checking roles, and reading the principal name.


Role-gated commands

Pass roles to @SshCommand to restrict visibility:

@SshCommand(roles = "ADMIN")
@Command(name = "top-secret", description = "Admin-only operation.")
public class TopSecretCommand implements Runnable {
    @Override
    public void run() {
        SshSession.current().writeLine("Super secret data.");
    }
}

Sessions that don't hold the required roles won't see the command in help output and can't invoke it.


Picocli features

The starter uses Picocli with the JLine3 shell integration. You get the full Picocli feature set:

@SshCommand
@Command(name = "user", description = "Manage users.",
         mixinStandardHelpOptions = true,
         subcommands = {HelpCommand.class})
public class UserCommand implements Runnable {

    @Option(names = {"-n", "--name"}, description = "User name.", required = true)
    private String name;

    @Option(names = "--admin", description = "Grant admin role.")
    private boolean admin;

    @Override
    public void run() {
        SshSession session = SshSession.current();
        session.writeLine("User: " + name + ", admin: " + admin);
    }
}

Autocompletion, --help flags, subcommands, positional parameters, type conversion — all work out of the box.


Dependency injection

Commands are Spring beans. Inject any bean from the context via constructor injection:

@SshCommand
@Command(name = "health", description = "Show application health.")
public class HealthCommand implements Runnable {

    private final HealthEndpoint healthEndpoint;

    public HealthCommand(HealthEndpoint healthEndpoint) {
        this.healthEndpoint = healthEndpoint;
    }

    @Override
    public void run() {
        var health = healthEndpoint.health();
        SshSession.current().writeLine(health.getStatus().toString());
    }
}

The SpringIFactory resolves command classes from the Spring context and falls back to Picocli's default factory with autowiring for non-bean types — so nested command classes and mixins also work.


Built-in commands

The command tree always includes:

  • help — lists all visible commands (respects role gates).
  • cls — clears the terminal screen.

These come from Picocli's HelpCommand and ClearScreen classes, wired into the RootCommand.