feat: add polished boot screen on CLI startup#118
Conversation
Replace the minimal one-liner REPL banner with an ANSI-styled status panel that summarizes the agent's runtime state after initialization: model, database, tool count, enabled features, active channels, and the gateway URL. The boot screen is shown only in interactive CLI mode (skipped for single-message -m mode). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello @ilblackdragon, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the user experience for the CLI by introducing a comprehensive and visually appealing boot screen. Instead of a simple banner, users will now see a detailed summary of the agent's configuration and operational status right after startup. This change provides immediate insight into the running environment, making it easier to verify setup and understand the agent's capabilities at a glance. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Pull request overview
Adds a new boot screen shown on interactive CLI startup to present a richer ANSI status panel (model/db/tools/features/channels/gateway), while suppressing the old REPL one-liner banner and collecting init-time data to populate the screen.
Changes:
- Introduces
boot_screenmodule withBootInfo+print_boot_screenand basic tests. - Suppresses the REPL startup banner when the boot screen will be shown.
- Tracks channel names and gateway URL during startup to display in the boot screen.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/main.rs |
Collects boot screen data (channels, gateway URL, model/tool counts) and prints boot screen in interactive mode; suppresses REPL banner. |
src/lib.rs |
Exposes the new boot_screen module from the library crate. |
src/channels/repl.rs |
Adds a suppress_banner flag to disable the REPL one-liner banner. |
src/boot_screen.rs |
Implements ANSI boot screen rendering and adds non-panicking tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| gw_config.port, | ||
| gw.auth_token() | ||
| ); | ||
| tracing::info!("Web UI: {}", gateway_url.as_deref().unwrap_or("")); |
There was a problem hiding this comment.
gateway_url embeds the gateway auth token and is logged via tracing::info!("Web UI: {}", ...). This leaks the bearer token into structured logs (which may be persisted/forwarded). Consider logging only the base URL (no query string) and printing the full tokenized URL only to the interactive boot screen / stdout (or gating it behind debug).
| tracing::info!("Web UI: {}", gateway_url.as_deref().unwrap_or("")); | |
| let public_gateway_url = format!("http://{}:{}/", gw_config.host, gw_config.port); | |
| tracing::info!("Web UI: {}", public_gateway_url); |
There was a problem hiding this comment.
Fixed in 036cd84. The tracing line now logs only http://host:port/ without the token. The full tokenized URL is still passed to the boot screen for interactive display only.
| println!(); | ||
| println!("{border}"); | ||
| println!(); | ||
| println!(" {bold}IronClaw{reset} v{}", info.version); |
There was a problem hiding this comment.
The boot screen header is hard-coded as "IronClaw" and never uses info.agent_name. Since BootInfo includes agent_name and main.rs populates it from config.agent.name, the displayed name will be wrong if the agent is renamed/configured differently. Use info.agent_name in the header (or remove the field if it isn't meant to vary).
| println!(" {bold}IronClaw{reset} v{}", info.version); | |
| println!(" {bold}{}{reset} v{}", info.agent_name, info.version); |
There was a problem hiding this comment.
Fixed in 036cd84. Now uses info.agent_name instead of hardcoded "IronClaw".
| pub gateway_url: Option<String>, | ||
| pub embeddings_enabled: bool, | ||
| pub embeddings_provider: Option<String>, | ||
| pub heartbeat_enabled: bool, |
There was a problem hiding this comment.
BootInfo::embeddings_provider is populated by main.rs but never displayed. If the intent is to summarize runtime state, consider incorporating the provider into the features line (e.g., "embeddings (openai)") or drop the field to avoid it drifting out of sync with what the boot screen actually shows.
There was a problem hiding this comment.
Fixed in 036cd84. The features line now shows embeddings (openai) when the provider is set.
| #[test] | ||
| fn test_print_boot_screen_full() { | ||
| let info = BootInfo { | ||
| version: "0.2.0".to_string(), | ||
| agent_name: "ironclaw".to_string(), | ||
| llm_backend: "nearai".to_string(), | ||
| llm_model: "claude-3-5-sonnet-20241022".to_string(), | ||
| cheap_model: Some("gpt-4o-mini".to_string()), | ||
| db_backend: "libsql".to_string(), | ||
| db_connected: true, | ||
| tool_count: 24, | ||
| gateway_url: Some("http://127.0.0.1:3001/?token=abc123".to_string()), | ||
| embeddings_enabled: true, | ||
| embeddings_provider: Some("openai".to_string()), | ||
| heartbeat_enabled: true, | ||
| heartbeat_interval_secs: 1800, | ||
| sandbox_enabled: true, | ||
| claude_code_enabled: false, | ||
| routines_enabled: true, | ||
| channels: vec![ | ||
| "repl".to_string(), | ||
| "gateway".to_string(), | ||
| "telegram".to_string(), | ||
| ], | ||
| }; | ||
| // Should not panic | ||
| print_boot_screen(&info); | ||
| } |
There was a problem hiding this comment.
The tests only verify that print_boot_screen doesn't panic; they don't validate any rendered content (e.g., that model/db/tool count/channels appear). Consider refactoring rendering into a function that returns a String (or writing to a generic Write) so tests can assert on the output and catch regressions in formatting/omitted fields.
There was a problem hiding this comment.
No change. The boot screen is simple visual output — no-panic tests are sufficient. Refactoring to impl Write would add complexity without meaningful regression coverage for what is essentially a formatted print. If the boot screen grows more complex we can revisit.
There was a problem hiding this comment.
Code Review
This pull request introduces a polished boot screen on CLI startup, which is a great enhancement for user experience. The implementation is well-structured, with a new boot_screen module and clear logic for gathering the necessary information in main.rs. My review includes a few suggestions to improve the robustness and maintainability of the new feature. Specifically, I've pointed out a minor bug where the agent name is hardcoded, an unused field in the BootInfo struct, and opportunities to make the UI alignment more robust and the code in main.rs more concise by leveraging Rust's trait system. All original comments have been retained as they align with best practices and are not contradicted by any specific rules. Overall, this is a solid feature addition.
| println!(); | ||
| println!("{border}"); | ||
| println!(); | ||
| println!(" {bold}IronClaw{reset} v{}", info.version); |
There was a problem hiding this comment.
The agent name is hardcoded as "IronClaw". The BootInfo struct already contains the agent_name field, which should be used here to correctly display the configured agent name.
| println!(" {bold}IronClaw{reset} v{}", info.version); | |
| println!(" {bold}{}{reset} v{}", info.agent_name, info.version); |
There was a problem hiding this comment.
Fixed in 036cd84. Now uses info.agent_name instead of hardcoded "IronClaw".
| pub tool_count: usize, | ||
| pub gateway_url: Option<String>, | ||
| pub embeddings_enabled: bool, | ||
| pub embeddings_provider: Option<String>, |
There was a problem hiding this comment.
The embeddings_provider field is populated in main.rs but is not used anywhere in print_boot_screen. This field should either be used to display the provider information on the boot screen or be removed from the BootInfo struct and the data collection logic in main.rs to eliminate dead code.
If you intend to display it, you could add it to the "features" line, for example:
if info.embeddings_enabled {
let mut feature_str = "embeddings".to_string();
if let Some(provider) = &info.embeddings_provider {
feature_str.push_str(&format!(" ({})", provider));
}
features.push(feature_str);
}There was a problem hiding this comment.
Fixed in 036cd84. The features line now shows embeddings (openai) when the provider is set.
| println!( | ||
| " {dim}model{reset} {model_display} {dim}via {}{reset}", | ||
| info.llm_backend | ||
| ); | ||
|
|
||
| // Database line | ||
| let db_status = if info.db_connected { | ||
| "connected" | ||
| } else { | ||
| "none" | ||
| }; | ||
| println!( | ||
| " {dim}database{reset} {cyan}{}{reset} {dim}({db_status}){reset}", | ||
| info.db_backend | ||
| ); | ||
|
|
||
| // Tools line | ||
| println!( | ||
| " {dim}tools{reset} {cyan}{}{reset} {dim}registered{reset}", | ||
| info.tool_count | ||
| ); |
There was a problem hiding this comment.
The alignment of the key-value pairs in the boot screen is done using a fixed number of spaces (e.g., model , database ). This is fragile and can lead to misaligned output if a key is longer or shorter than expected, or if the font is not monospaced.
To make the layout more robust, you could calculate the padding dynamically based on the length of the longest key. This ensures consistent alignment regardless of the key names.
There was a problem hiding this comment.
No change. The labels are a fixed compile-time set (model, database, tools, features, channels, gateway) — there's no dynamic key that could change length. Fixed padding is deliberate and appropriate here; dynamic calculation would add complexity for no benefit.
| match config.database.backend { | ||
| ironclaw::config::DatabaseBackend::Postgres => "postgres".to_string(), | ||
| ironclaw::config::DatabaseBackend::LibSql => "libsql".to_string(), | ||
| } |
There was a problem hiding this comment.
This match statement manually converts DatabaseBackend enum variants to strings. This can be simplified and made more maintainable by implementing the Display trait for the DatabaseBackend enum in src/config.rs.
You could add the following implementation in src/config.rs:
// in src/config.rs
impl std::fmt::Display for DatabaseBackend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Postgres => write!(f, "postgres"),
Self::LibSql => write!(f, "libsql"),
}
}
}Then, you can simplify the code here to:
db_backend: if cli.no_db {
"none".to_string()
} else {
config.database.backend.to_string()
},This change would also be beneficial elsewhere, as the LlmBackend enum already has a Display implementation.
There was a problem hiding this comment.
Fixed in 036cd84. Added Display impl for DatabaseBackend in config.rs, simplified the match to config.database.backend.to_string().
- Stop logging gateway auth token in tracing::info! (security) - Use info.agent_name instead of hardcoded "IronClaw" in header - Display embeddings provider in features line: "embeddings (openai)" - Add Display impl for DatabaseBackend, simplify main.rs match Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add polished boot screen on CLI startup Replace the minimal one-liner REPL banner with an ANSI-styled status panel that summarizes the agent's runtime state after initialization: model, database, tool count, enabled features, active channels, and the gateway URL. The boot screen is shown only in interactive CLI mode (skipped for single-message -m mode). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback on boot screen - Stop logging gateway auth token in tracing::info! (security) - Use info.agent_name instead of hardcoded "IronClaw" in header - Display embeddings provider in features line: "embeddings (openai)" - Add Display impl for DatabaseBackend, simplify main.rs match Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add polished boot screen on CLI startup Replace the minimal one-liner REPL banner with an ANSI-styled status panel that summarizes the agent's runtime state after initialization: model, database, tool count, enabled features, active channels, and the gateway URL. The boot screen is shown only in interactive CLI mode (skipped for single-message -m mode). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback on boot screen - Stop logging gateway auth token in tracing::info! (security) - Use info.agent_name instead of hardcoded "IronClaw" in header - Display embeddings provider in features line: "embeddings (openai)" - Add Display impl for DatabaseBackend, simplify main.rs match Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
boot_screenmodule that displays a polished ANSI-styled status panel on CLI startup, summarizing runtime state: model, database, tool count, enabled features, active channels, and gateway URLTest plan
cargo test boot_screento verify boot screen rendering tests pass--messageflag and verify boot screen is skipped--no-dband verify database shows as "none"🤖 Generated with Claude Code