Interactive Spring Shell-based command-line interface for the Embabel Agent platform, providing terminal interaction, chat sessions, and agent management commands.
Learn how to customize the Embabel Agent Shell appearance and behavior.
Select a personality theme in configuration:
embabel:
agent:
logging:
personality: starwars # or severance, hitchhiker, colossus, montypythonAvailable personalities:
| Personality | Prompt | Color | Theme |
|---|---|---|---|
starwars | starwars> | Gold yellow (0xFFD700) | Star Wars universe |
severance | Random dept | Sky blue (0x87CEEB) | Lumon Industries |
hitchhiker | Random entry | Lime green (0x32CD32) | Hitchhiker's Guide |
colossus | Colossus> | Orange-red (0xFF4500) | Colossus computer |
montypython | pythons> | Pure red (0xFF0000) | Monty Python |
import com.embabel.agent.shell.personality.starwars.StarWarsPromptProvider
// Configuration
embabel.agent.logging.personality=starwars
// Prompt: "starwars> " in gold yellow
// Messages: Random Star Wars quotes from logging/starwars.txtimport com.embabel.agent.shell.personality.severance.SeverancePromptProvider
// Configuration
embabel.agent.logging.personality=severance
// Prompt: Random department name in sky blue
// Departments: MDR, Lumon, Choreography and Merriment, etc.
// Messages: Random Severance quotes from logging/severance.txtimport com.embabel.agent.shell.personality.hitchhiker.HitchhikerPromptProvider
// Configuration
embabel.agent.logging.personality=hitchhiker
// Prompt: Random Guide entry in lime green
// Entries: DON'T PANIC, Mostly Harmless, 42, etc.
// Messages: Random Hitchhiker quotes from logging/hitchhiker.txtimport com.embabel.agent.shell.personality.colossus.ColossusPromptProvider
// Configuration
embabel.agent.logging.personality=colossus
// Prompt: "Colossus> " in orange-red
// Messages: Random Colossus quotes from logging/colossus.txtimport com.embabel.agent.shell.personality.montypython.MontyPythonPromptProvider
// Configuration
embabel.agent.logging.personality=montypython
// Prompt: "pythons> " in pure red
// Messages: Random Monty Python quotes from logging/montypython.txtCreate a simple prompt without dynamic messages:
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.shell.jline.PromptProvider
import org.jline.utils.AttributedString
import org.jline.utils.AttributedStyle
@Configuration
class CustomPromptConfiguration {
@Bean
fun customPromptProvider(): PromptProvider {
return PromptProvider {
AttributedString(
"custom> ",
AttributedStyle.DEFAULT.foreground(AttributedStyle.MAGENTA)
)
}
}
}Result: custom> prompt in magenta
Create a prompt with dynamic messages:
import com.embabel.agent.shell.MessageGeneratorPromptProvider
import com.embabel.common.util.RandomFromFileMessageGenerator
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Component
@Component
@ConditionalOnProperty(name = ["embabel.agent.logging.personality"], havingValue = "custom")
class CustomPromptProvider : MessageGeneratorPromptProvider(
prompt = "custom",
color = 0xFF00FF, // Magenta
messageGenerator = RandomFromFileMessageGenerator(
url = "logging/custom.txt"
)
)Configuration:
embabel:
agent:
logging:
personality: customMessage File (src/main/resources/logging/custom.txt):
Character: Welcome message
Character: Another message
Character: Yet another messageMessages with ":" are split into character and text parts, formatted with personality styling.
Implement your own message generator:
import com.embabel.common.util.MessageGenerator
class CustomMessageGenerator : MessageGenerator {
private val messages = listOf(
"Ready for action",
"Standing by",
"Awaiting orders"
)
override fun generate(): String {
return messages.random()
}
}
@Component
@ConditionalOnProperty(name = ["embabel.agent.logging.personality"], havingValue = "custom")
class CustomPromptProvider : MessageGeneratorPromptProvider(
prompt = "bot",
color = 0x00FF00,
messageGenerator = CustomMessageGenerator()
)Each personality has an associated color palette:
import com.embabel.agent.shell.personality.starwars.StarWarsColorPalette
import com.embabel.agent.shell.personality.severance.LumonColorPalette
import com.embabel.agent.shell.personality.hitchhiker.HitchhikerColorPalette
import com.embabel.agent.shell.personality.colossus.ColossusColorPalette
import com.embabel.agent.shell.personality.montypython.MontyPythonColorPalette
// Use in output formatting
val palette = StarWarsColorPalette()Implement the ColorPalette interface:
import com.embabel.agent.spi.logging.ColorPalette
import org.springframework.stereotype.Component
@Component
class MyCustomColorPalette : ColorPalette {
// Define your color scheme methods
// Implementation depends on ColorPalette interface
}Use your custom palette:
@Component
class MyService(
private val customPalette: MyCustomColorPalette
) {
fun formatOutput(result: AgentProcessExecution) {
val formatted = formatProcessOutput(
result = result,
colorPalette = customPalette,
objectMapper = objectMapper,
lineLength = 140
)
}
}Customize shell behavior:
embabel:
agent:
shell:
# Maximum line length for text wrapping
lineLength: 140
# Redirect logs to file during chat
redirectLogToFile: falseControls text wrapping in:
Default: 140 Range: Any positive integer (typically 80-200)
embabel:
agent:
shell:
lineLength: 120 # More compactControls whether logs are redirected to file during chat sessions:
Default: false Values: true | false
embabel:
agent:
shell:
redirectLogToFile: true # Cleaner chat UIWhen enabled:
logs/chat-session.logInject ShellProperties to access configuration:
import com.embabel.agent.shell.config.ShellProperties
import org.springframework.stereotype.Component
@Component
class MyComponent(
private val shellProperties: ShellProperties
) {
fun processText(text: String) {
val maxLength = shellProperties.lineLength
val shouldRedirect = shellProperties.redirectLogToFile
// Use configuration values
}
}Add your own shell commands:
import org.springframework.shell.standard.ShellComponent
import org.springframework.shell.standard.ShellMethod
import org.springframework.shell.standard.ShellOption
@ShellComponent
class MyCustomCommands {
@ShellMethod("My custom command")
fun myCommand(
@ShellOption(help = "Input parameter") input: String
): String {
return "Processed: $input"
}
}Usage:
embabel> myCommand "test"
Processed: testUse aliases for convenience:
@ShellMethod(
value = "Execute task",
key = ["execute", "x", "run"] // Multiple aliases
)
fun execute(@ShellOption(help = "task") task: String): String {
// Implementation
}Usage:
embabel> execute "task"
embabel> x "task"
embabel> run "task"Configure Spring Shell behavior:
spring:
shell:
interactive:
enabled: true
noninteractive:
enabled: false
command:
version:
enabled: false # Disable version commandThe terminal services can be customized through dependency injection:
import com.embabel.agent.shell.TerminalServices
import com.embabel.agent.shell.config.ShellProperties
import org.jline.terminal.Terminal
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
@Configuration
class TerminalCustomization {
@Bean
@Primary
fun customTerminalServices(
terminal: Terminal,
shellProperties: ShellProperties
): TerminalServices {
// Create custom terminal services
return TerminalServices(terminal, shellProperties)
}
}Customize the JLine terminal:
import org.jline.terminal.Terminal
import org.jline.terminal.TerminalBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class TerminalConfiguration {
@Bean
fun terminal(): Terminal {
return TerminalBuilder.builder()
.system(true)
.encoding(Charsets.UTF_8)
.build()
}
}Customize the autonomy service:
import com.embabel.agent.api.common.autonomy.Autonomy
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class AutonomyCustomization {
@Bean
fun customAutonomy(): Autonomy {
// Create and configure custom autonomy implementation
}
}Provide custom LLM models:
import com.embabel.agent.model.ModelProvider
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class ModelConfiguration {
@Bean
fun customModelProvider(): ModelProvider {
// Create and configure custom model provider
}
}Implement a custom chatbot:
import com.embabel.chat.Chatbot
import com.embabel.chat.ChatSession
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class ChatbotConfiguration {
@Bean
fun customChatbot(): Chatbot {
return object : Chatbot {
override fun createSession(): ChatSession {
// Return custom chat session implementation
}
}
}
}Create custom message files in src/main/resources/logging/:
Character: Message text
Character: Another message
Single line message without character
Character: Multi-word message text hereMessages can:
logging/starwars.txt:
Yoda: Do or do not, there is no try
Obi-Wan: May the Force be with you
Vader: I find your lack of faith disturbinglogging/custom.txt:
Bot: Ready to assist
Bot: Processing your request
Bot: Standing by for commands
System: All systems operationalMessages are randomly selected on each prompt display using RandomFromFileMessageGenerator:
import com.embabel.common.util.RandomFromFileMessageGenerator
val generator = RandomFromFileMessageGenerator(
url = "logging/custom.txt"
)
val message = generator.generate() // Random message from file@ConditionalOnPropertyPersonality providers use conditional registration:
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
@Component
@ConditionalOnProperty(
name = ["embabel.agent.logging.personality"],
havingValue = "custom"
)
class CustomPromptProvider : MessageGeneratorPromptProvider(...)Only loads when:
embabel:
agent:
logging:
personality: custom@ConditionalOnMissingBeanDefault providers use missing bean condition:
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
@Bean
@ConditionalOnMissingBean(PromptProvider::class)
fun defaultPromptProvider(): PromptProvider {
return DefaultPromptProvider()
}Only loads when no other PromptProvider bean exists.
Spring loads prompt providers in this order:
Property-based personality (if configured):
embabel.agent.logging.personality: starwars→ Loads StarWarsPromptProvider
Custom PromptProvider bean (if defined):
@Bean
fun customPromptProvider(): PromptProvider { ... }→ Uses your custom provider
Default provider (fallback):
→ Loads DefaultPromptProvider ("embabel> " in yellow)
Only one prompt provider is active at a time.
Putting it all together:
// 1. Custom message generator
class RobotMessageGenerator : MessageGenerator {
override fun generate(): String {
return listOf(
"Robot: Beep boop",
"Robot: Analyzing data",
"Robot: Ready to serve"
).random()
}
}
// 2. Custom prompt provider
@Component
@ConditionalOnProperty(name = ["embabel.agent.logging.personality"], havingValue = "robot")
class RobotPromptProvider : MessageGeneratorPromptProvider(
prompt = "robot",
color = 0x00FFFF, // Cyan
messageGenerator = RobotMessageGenerator()
)
// 3. Custom color palette
@Component
class RobotColorPalette : ColorPalette {
// Implement color palette methods
}
// 4. Configurationapplication.yaml:
embabel:
agent:
shell:
lineLength: 120
redirectLogToFile: true
logging:
personality: robotResult: Cyan "robot>" prompt with random robot messages
tessl i tessl/maven-com-embabel-agent--embabel-agent-shell@0.3.0