ADR-033: JLine Interactive Prompt Enhancements
Status
Proposed
Date
2025-12-07
Deciders
- Norbert Bede
Context and Problem Statement
The idempiere-cli needs a modern, interactive terminal UI similar to tools like Cursor Agent, Claude Code, and other AI-powered CLIs. These tools provide rich input experiences including boxed input fields, slash command autocomplete, placeholder text, status lines, and thinking indicators.
The current JLinePrompt.java provides basic interactive prompts (select, checkbox, string input) but lacks the polished UI elements seen in modern AI-focused CLI tools.
Decision Drivers
- Modern UX: Users expect polished, intuitive terminal interfaces
- Discoverability: Slash commands need autocomplete for feature discovery
- Visual Feedback: Thinking/processing states should be clearly communicated
- Consistency: Follow established patterns from Cursor Agent, Claude Code
- Accessibility: Graceful fallback for non-interactive terminals
- clig.dev Compliance: Follow Command Line Interface Guidelines
Considered Options
- Enhance existing JLinePrompt with new methods
- Use Lanterna - full TUI library with windows/panels
- Use JLine3 LineReader with custom widgets
- Custom ANSI rendering from scratch
Decision Outcome
Chosen option: "Enhance existing JLinePrompt with new methods", because it builds on the existing JLine3 foundation, keeps dependencies minimal, and provides the specific features needed without over-engineering.
Confirmation
- Unit tests for each new prompt method
- Visual testing script demonstrating all new features
- Fallback behavior verified in non-TTY mode (piped input)
Pros and Cons of the Options
Option 1: Enhance existing JLinePrompt
Extend JLinePrompt.java with new methods for modern UI features.
- Good, because builds on existing, working code
- Good, because minimal new dependencies
- Good, because focused on specific needs
- Good, because maintains consistency with existing prompt methods
- Neutral, because requires manual ANSI escape handling
- Bad, because more complex features need careful implementation
Option 2: Use Lanterna
Full TUI library with windows, panels, text boxes.
- Good, because feature-rich out of the box
- Good, because handles complex layouts
- Bad, because heavyweight dependency (~2MB)
- Bad, because different paradigm from current prompts
- Bad, because overkill for our needs
Option 3: Use JLine3 LineReader with Widgets
Use JLine's built-in LineReader with custom AutopairWidgets.
- Good, because proper readline support (history, editing)
- Good, because built-in completion framework
- Neutral, because requires learning widget system
- Bad, because complex to customize visual appearance
- Bad, because less control over exact rendering
Option 4: Custom ANSI rendering
Build everything from raw ANSI escape sequences.
- Good, because total control
- Bad, because significant effort
- Bad, because cross-platform terminal issues
- Bad, because reinventing existing solutions
More Information
Feature Specifications
1. Boxed Input Field (promptWithBox)
┌─────────────────────────────────────────────────────────────────────┐
│ → Plan, search, build anything │
└─────────────────────────────────────────────────────────────────────┘
Features:
- Unicode box drawing characters from
CliTheme - Auto-size to terminal width
- Dim placeholder text that disappears on typing
- Cursor positioning within the box
- ESC to cancel, Enter to submit
2. Slash Command Autocomplete (promptWithAutocomplete)
When user types /:
→ /m█
/model <model> Set or list models
→ /mcp Start MCP server
/migrate Run migrations
Features:
- Triggered by
/prefix - Fuzzy matching of commands
- Arrow key navigation through suggestions
- Tab to complete selected
- Show command description alongside
3. Status Line (setStatusLine)
Persistent line at terminal bottom:
LangChain4j GPT-4 / for commands
Features:
- Fixed position at terminal bottom
- Left and right aligned sections
- Auto-update without disrupting main content
- Clear on exit
4. Thinking Indicator (showThinking)
● Thinking. 240 tokens ctrl+c to stop
Features:
- Animated dot/spinner using
CliTheme.SPINNER_* - Token counter (updated externally)
- Cancel hint
- Transition to result display
Implementation Architecture
JLinePrompt.java
├── Existing Methods (unchanged)
│ ├── promptString()
│ ├── promptBoolean()
│ ├── promptSelect()
│ └── promptCheckboxes()
│
├── New Box Drawing Methods
│ ├── promptWithBox(placeholder, width)
│ ├── drawBox(content, width)
│ └── updateBoxContent(content)
│
├── Autocomplete Methods
│ ├── promptWithAutocomplete(placeholder, completions)
│ ├── showCompletionDropdown(matches, selected)
│ └── filterCompletions(input, completions)
│
├── Status Line Methods
│ ├── setStatusLine(left, right)
│ ├── clearStatusLine()
│ └── saveAndRestorePosition()
│
└── Thinking Indicator Methods
├── showThinking(message)
├── updateThinking(message, tokens)
└── hideThinking()
Data Structures
/**
* Completion option for autocomplete prompts.
*/
public record CompletionOption(
String value, // The actual value to insert
String display, // Display text (may include formatting)
String description // Optional description shown beside
) {}
/**
* Status line configuration.
*/
public record StatusLine(
String leftContent, // Left-aligned text
String rightContent, // Right-aligned text
String separator // Optional middle separator
) {}
ANSI Escape Sequences Used
// Cursor positioning
"\033[s" // Save cursor position
"\033[u" // Restore cursor position
"\033[%d;%dH" // Move to row;col
"\033[%dA" // Move up N lines
"\033[%dB" // Move down N lines
"\033[2K" // Clear entire line
"\033[J" // Clear from cursor to end of screen
// Terminal queries
"\033[6n" // Query cursor position (response: ESC[row;colR)
"\033[18t" // Query terminal size (response: ESC[8;rows;colst)
Integration with Existing Components
- CliTheme: Box drawing characters, spinner frames, colors, ASCII logos
- ConsoleOutput: Print methods, spinner integration
- Spinner: Reuse for thinking indicator animation
- ProgressBar: Similar animation patterns
ASCII Logo / Banner (Implemented)
Added to CliTheme.java:
▗▄▄▄▖▗▄▄▄ ▗▄▄▄▖▗▖ ▗▖▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▄▄▖ ▗▄▄▄▖ ▗▄▄▖▗▖ ▗▄▄▄▖
█ ▐▌ █▐▌ ▐▛▚▞▜▌▐▌ ▐▌ █ ▐▌ ▐▌ ▐▌▐▌ ▐▌ ▐▌ █
█ ▐▌ █▐▛▀▀▘▐▌ ▐▌▐▛▀▘ █ ▐▛▀▀▘▐▛▀▚▖▐▛▀▀▘ ▐▌ ▐▌ █
▗▄█▄▖▐▙▄▄▀▐▙▄▄▖▐▌ ▐▌▐▌ ▗▄█▄▖▐▙▄▄▖▐▌ ▐▌▐▙▄▄▖ ▝▚▄▄▖▐▙▄▄▖▗▄█▄▖
Three logo variants:
- LOGO - Full block-letter logo (4 lines)
- LOGO_COMPACT - Double-line box drawing (3 lines)
- LOGO_MINI - Single line:
◆ iDempiere CLI
Methods in CliTheme:
| Method | Description |
|---|---|
printLogo() |
Full logo in primary color |
printLogo(version) |
Full logo with version |
printLogoCompact() |
Compact logo |
printLogoCompact(version) |
Compact logo with version |
printBanner(version, message) |
Logo + message + separator |
printBoxedBanner(version) |
Logo inside box frame |
Methods in JLinePrompt:
| Method | Description |
|---|---|
showBanner() |
Display full logo |
showBanner(version) |
Display logo with version |
showBannerCompact() |
Display compact logo |
showBoxedBanner(version) |
Display boxed banner |
showWelcome(version, desc) |
Welcome message with logo |
showLogoMini() |
Single-line logo |
Example Usage
@Inject
JLinePrompt prompt;
// Boxed input with placeholder
String input = prompt.promptWithBox(
"Plan, search, build anything",
80 // width, or -1 for terminal width
);
// Autocomplete for slash commands
List<CompletionOption> commands = List.of(
new CompletionOption("/help", "/help", "Show available commands"),
new CompletionOption("/model", "/model <name>", "Set AI model"),
new CompletionOption("/clear", "/clear", "Clear conversation")
);
String command = prompt.promptWithAutocomplete("Type / for commands", commands);
// Status line
prompt.setStatusLine("GPT-4", "/ for commands");
// ... do work ...
prompt.clearStatusLine();
// Thinking indicator
prompt.showThinking("Generating response");
for (int tokens = 0; tokens < 500; tokens += 10) {
prompt.updateThinking("Generating response", tokens);
Thread.sleep(50);
}
prompt.hideThinking();
Terminal Size Handling
private int getTerminalWidth() {
if (terminal != null) {
return terminal.getWidth();
}
// Fallback to standard 80 columns
return 80;
}
private int getTerminalHeight() {
if (terminal != null) {
return terminal.getHeight();
}
return 24;
}
Related ADRs
- ADR-028 - CLI Error Handling (uses ConsoleOutput)
- ADR-030 - CLI Design Guidelines (clig.dev)
- ADR-031 - CLI Usability
References
- JLine3 Documentation
- ANSI Escape Codes
- Command Line Interface Guidelines
- Cursor Agent CLI - UI inspiration
- Claude Code - UI inspiration
- Ink (React for CLIs) - Pattern reference
- Charm Libraries - Go CLI toolkit inspiration