spring-shell

Spring Shell - Quick Reference

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "spring-shell" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-spring-shell

Spring Shell - Quick Reference

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: spring-shell for comprehensive documentation.

Dependencies

<dependency> <groupId>org.springframework.shell</groupId> <artifactId>spring-shell-starter</artifactId> </dependency>

Configuration

application.yml

spring: shell: interactive: enabled: true noninteractive: enabled: true history: enabled: true name: .myapp_history command: version: enabled: true

Basic Commands

Simple Command

@ShellComponent public class GreetingCommands {

@ShellMethod(value = "Say hello", key = "hello")
public String hello(
        @ShellOption(defaultValue = "World") String name) {
    return "Hello, " + name + "!";
}

@ShellMethod("Add two numbers")
public int add(int a, int b) {
    return a + b;
}

@ShellMethod("Show current date")
public String date() {
    return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}

}

Command Groups

@ShellComponent @ShellCommandGroup("User Management") public class UserCommands {

@ShellMethod("List all users")
public Table listUsers() {
    List&#x3C;User> users = userService.findAll();
    return buildUserTable(users);
}

@ShellMethod("Create a new user")
public String createUser(
        @ShellOption(help = "Username") String username,
        @ShellOption(help = "Email address") String email,
        @ShellOption(help = "User role", defaultValue = "USER") String role) {

    User user = userService.create(username, email, role);
    return "Created user: " + user.getId();
}

@ShellMethod("Delete a user")
public String deleteUser(@ShellOption(help = "User ID") Long id) {
    userService.delete(id);
    return "User deleted successfully";
}

}

Shell Options

Option Annotations

@ShellComponent public class AdvancedCommands {

@ShellMethod("Process file with options")
public String process(
        // Required option
        @ShellOption(help = "Input file path") String input,

        // Optional with default
        @ShellOption(defaultValue = "output.txt", help = "Output file") String output,

        // Boolean flag
        @ShellOption(defaultValue = "false", help = "Verbose mode") boolean verbose,

        // Array/List option
        @ShellOption(help = "Tags to apply") String[] tags,

        // Arity (number of values)
        @ShellOption(arity = 2, help = "Coordinates x y") int[] coords) {

    // Implementation
    return "Processing " + input;
}

// Named options with aliases
@ShellMethod("Export data")
public String export(
        @ShellOption(value = {"-f", "--format"}, defaultValue = "json") String format,
        @ShellOption(value = {"-o", "--output"}) String output,
        @ShellOption(value = {"-c", "--compress"}, defaultValue = "false") boolean compress) {

    return String.format("Exporting to %s in %s format (compressed: %s)",
        output, format, compress);
}

}

Availability and Validation

Command Availability

@ShellComponent public class AdminCommands {

private boolean authenticated = false;

@ShellMethod("Login to the system")
public String login(String username, String password) {
    if (authService.authenticate(username, password)) {
        authenticated = true;
        return "Login successful";
    }
    return "Login failed";
}

@ShellMethod("Perform admin action")
@ShellMethodAvailability("isAuthenticated")
public String adminAction() {
    return "Admin action performed";
}

public Availability isAuthenticated() {
    return authenticated
        ? Availability.available()
        : Availability.unavailable("You must login first");
}

// Apply to multiple methods
@ShellMethodAvailability({"adminAction", "deleteAll", "resetSystem"})
public Availability requiresAdmin() {
    return currentUser.isAdmin()
        ? Availability.available()
        : Availability.unavailable("Admin privileges required");
}

}

Input Validation

@ShellComponent public class ValidatedCommands {

@ShellMethod("Create user with validation")
public String createUser(
        @ShellOption @Size(min = 3, max = 20) String username,
        @ShellOption @Email String email,
        @ShellOption @Min(18) @Max(120) int age) {

    return "User created: " + username;
}

}

// Custom validator @Component public class FileExistsValidator implements Validator {

@Override
public boolean supports(Class&#x3C;?> clazz) {
    return String.class.isAssignableFrom(clazz);
}

@Override
public void validate(Object target, Errors errors) {
    String path = (String) target;
    if (!Files.exists(Path.of(path))) {
        errors.reject("file.notfound", "File does not exist: " + path);
    }
}

}

Output Formatting

Tables

@ShellComponent public class TableCommands {

@ShellMethod("Show users in table format")
public Table showUsers() {
    List&#x3C;User> users = userService.findAll();

    TableModel model = new BeanListTableModel&#x3C;>(users,
        new LinkedHashMap&#x3C;>() {{
            put("id", "ID");
            put("username", "Username");
            put("email", "Email");
            put("role", "Role");
            put("createdAt", "Created");
        }});

    TableBuilder tableBuilder = new TableBuilder(model);
    tableBuilder.addFullBorder(BorderStyle.fancy_light);
    return tableBuilder.build();
}

// Custom table
@ShellMethod("Show system info")
public Table systemInfo() {
    String[][] data = {
        {"OS", System.getProperty("os.name")},
        {"Java", System.getProperty("java.version")},
        {"Memory", Runtime.getRuntime().maxMemory() / 1024 / 1024 + " MB"},
        {"Processors", String.valueOf(Runtime.getRuntime().availableProcessors())}
    };

    TableModel model = new ArrayTableModel(data);
    TableBuilder builder = new TableBuilder(model);
    builder.addHeaderBorder(BorderStyle.fancy_double);
    return builder.build();
}

}

Colored Output

@ShellComponent public class ColoredCommands {

@ShellMethod("Show status with colors")
public AttributedString status() {
    AttributedStringBuilder builder = new AttributedStringBuilder();

    builder.append("Status: ");
    builder.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN));
    builder.append("RUNNING");
    builder.style(AttributedStyle.DEFAULT);
    builder.append("\n");

    builder.append("Errors: ");
    builder.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.RED).bold());
    builder.append("3");
    builder.style(AttributedStyle.DEFAULT);

    return builder.toAttributedString();
}

}

Interactive Input

User Prompts

@ShellComponent public class InteractiveCommands {

private final LineReader lineReader;

@ShellMethod("Interactive user creation")
public String createUserInteractive() {
    String username = lineReader.readLine("Enter username: ");
    String email = lineReader.readLine("Enter email: ");
    String password = lineReader.readLine("Enter password: ", '*');

    String confirm = lineReader.readLine("Create user? (y/n): ");
    if ("y".equalsIgnoreCase(confirm)) {
        userService.create(username, email, password);
        return "User created successfully";
    }
    return "Cancelled";
}

}

Progress Indicators

@ShellComponent public class ProgressCommands {

private final TerminalUI terminalUI;

@ShellMethod("Import data with progress")
public void importData(String file) throws Exception {
    List&#x3C;String> lines = Files.readAllLines(Path.of(file));
    int total = lines.size();

    try (ProgressView progress = terminalUI.progressView()
            .spinner(Spinner.SIMPLE)
            .text("Importing data...")
            .max(total)
            .start()) {

        for (int i = 0; i &#x3C; total; i++) {
            processLine(lines.get(i));
            progress.setValue(i + 1);
            progress.setText("Processing " + (i + 1) + "/" + total);
        }
    }
}

}

Non-Interactive Mode

// Run single command // java -jar myapp.jar hello --name John

// Run script // java -jar myapp.jar @commands.txt

@SpringBootApplication public class CliApplication implements CommandLineRunner {

@Autowired
private Shell shell;

@Override
public void run(String... args) throws Exception {
    if (args.length > 0 &#x26;&#x26; args[0].equals("--batch")) {
        // Run in non-interactive mode
        shell.evaluate(() -> "process-batch --file data.csv");
        shell.evaluate(() -> "export --format json --output result.json");
        System.exit(0);
    }
    // Otherwise, start interactive shell
}

}

Custom Prompt

@Component public class CustomPromptProvider implements PromptProvider {

private String currentContext = "default";

@Override
public AttributedString getPrompt() {
    return new AttributedString(
        String.format("myapp:%s> ", currentContext),
        AttributedStyle.DEFAULT.foreground(AttributedStyle.CYAN)
    );
}

public void setContext(String context) {
    this.currentContext = context;
}

}

Exception Handling

@Component public class CustomExceptionHandler implements CommandExceptionResolver {

@Override
public CommandHandlingResult resolve(Exception ex) {
    if (ex instanceof IllegalArgumentException) {
        return CommandHandlingResult.of(
            "Invalid argument: " + ex.getMessage() + "\n",
            1  // Exit code
        );
    }
    if (ex instanceof ResourceNotFoundException) {
        return CommandHandlingResult.of(
            "Error: " + ex.getMessage() + "\n",
            2
        );
    }
    // Let default handler process
    return null;
}

}

Testing

@SpringBootTest class CommandsTest {

@Autowired
private Shell shell;

@Test
void shouldSayHello() {
    Object result = shell.evaluate(() -> "hello --name John");
    assertThat(result).isEqualTo("Hello, John!");
}

@Test
void shouldAddNumbers() {
    Object result = shell.evaluate(() -> "add 5 3");
    assertThat(result).isEqualTo(8);
}

@Test
void shouldShowUnavailableForAdminCommands() {
    Object result = shell.evaluate(() -> "admin-action");
    assertThat(result.toString()).contains("must login first");
}

}

Best Practices

Do Don't

Group related commands Create flat command structure

Provide --help for all options Leave options undocumented

Use tables for structured data Dump raw data

Implement availability checks Let commands fail cryptically

Support both interactive and batch Force one mode only

Production Checklist

  • Commands grouped logically

  • All options documented (help)

  • Input validation implemented

  • Proper error handling

  • Progress indicators for long ops

  • Non-interactive mode supported

  • Exit codes defined

  • History enabled/configured

  • Security checks (authentication)

  • Tab completion working

When NOT to Use This Skill

  • Simple scripts - Use regular main method

  • Batch processing - Use spring-batch skill

  • Web APIs - Use spring-rest skill

  • GUI applications - Consider JavaFX

Anti-Patterns

Anti-Pattern Problem Solution

Flat command structure Hard to navigate Group related commands

Undocumented options Poor UX Add help to all options

No availability checks Cryptic failures Implement @ShellMethodAvailability

Raw data output Unreadable Use tables for structured data

Interactive-only mode Not scriptable Support batch mode

Quick Troubleshooting

Problem Diagnostic Fix

Command not found Check annotation Add @ShellComponent

Option not recognized Check naming Verify @ShellOption config

Validation not working Check annotations Add Bean Validation

Command unavailable Check availability Verify availability method

Tab completion missing Check setup Configure completion bean

Reference Documentation

  • Spring Shell Reference

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review