From 950080bb73db476ebea1180fb29d30b8f310ff1c Mon Sep 17 00:00:00 2001 From: Heng Liu Date: Tue, 11 Aug 2026 08:12:25 +0000 Subject: [PATCH 1/3] Add Kiro CLI agent support --- app/assets/bundled/svg/kiro.svg | 5 ++ app/src/integration_testing/input/step.rs | 59 ++++++++++++++++++- app/src/server/telemetry/events.rs | 1 + app/src/terminal/cli_agent.rs | 20 ++++++- .../cli_agent_sessions/listener/mod.rs | 1 + .../cli_agent_sessions/plugin_manager/mod.rs | 1 + app/src/terminal/cli_agent_tests.rs | 31 ++++++++++ app/src/terminal/view/use_agent_footer/mod.rs | 1 + app/src/terminal/view_tests.rs | 1 + crates/ai/src/skills/skill_provider.rs | 4 +- crates/integration/src/bin/integration.rs | 1 + crates/integration/src/test.rs | 2 + crates/integration/src/test/kiro_cli.rs | 34 +++++++++++ .../integration/tests/integration/ui_tests.rs | 1 + crates/warp_core/src/ui/icons.rs | 2 + 15 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 app/assets/bundled/svg/kiro.svg create mode 100644 crates/integration/src/test/kiro_cli.rs diff --git a/app/assets/bundled/svg/kiro.svg b/app/assets/bundled/svg/kiro.svg new file mode 100644 index 00000000000..6dc7c7a2942 --- /dev/null +++ b/app/assets/bundled/svg/kiro.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/integration_testing/input/step.rs b/app/src/integration_testing/input/step.rs index 417ecba6971..5013c0659dc 100644 --- a/app/src/integration_testing/input/step.rs +++ b/app/src/integration_testing/input/step.rs @@ -21,6 +21,11 @@ use crate::terminal::view::TerminalAction; /// Opens the CLI-agent Rich Input for the terminal view at `tab_index`. pub fn open_cli_agent_rich_input(tab_index: usize) -> TestStep { + open_cli_agent_rich_input_for_agent(tab_index, CLIAgent::Claude) +} + +/// Opens the CLI-agent Rich Input for `agent` in the terminal view at `tab_index`. +pub fn open_cli_agent_rich_input_for_agent(tab_index: usize, agent: CLIAgent) -> TestStep { new_step_with_default_assertions("Open CLI Agent Rich Input").with_action( move |app, window_id, _step_data| { let terminal_view = single_terminal_view_for_tab(app, window_id, tab_index); @@ -31,7 +36,7 @@ pub fn open_cli_agent_rich_input(tab_index: usize) -> TestStep { sessions.set_session( view_id, CLIAgentSession { - agent: CLIAgent::Claude, + agent, status: CLIAgentSessionStatus::InProgress, session_context: CLIAgentSessionContext::default(), input_state: CLIAgentInputState::Closed, @@ -65,6 +70,58 @@ pub fn open_cli_agent_rich_input(tab_index: usize) -> TestStep { ) } +/// Asserts that the CLI-agent Rich Input is open for `tab_index`. +pub fn cli_agent_rich_input_is_open(tab_index: usize) -> warpui::integration::AssertionCallback { + Box::new(move |app, window_id| { + let terminal_view = single_terminal_view_for_tab(app, window_id, tab_index); + terminal_view.read(app, |view, ctx| { + let is_open = CLIAgentSessionsModel::as_ref(ctx) + .session(view.view_id()) + .is_some_and(|session| { + matches!(session.input_state, CLIAgentInputState::Open { .. }) + }); + warpui::async_assert!( + is_open, + "Expected CLI-agent Rich Input to be open for tab {tab_index}" + ) + }) + }) +} + +/// Asserts that the active CLI agent for `tab_index` matches `expected_agent`. +pub fn active_cli_agent_is( + tab_index: usize, + expected_agent: CLIAgent, +) -> warpui::integration::AssertionCallback { + Box::new(move |app, window_id| { + let terminal_view = single_terminal_view_for_tab(app, window_id, tab_index); + terminal_view.read(app, |view, ctx| { + let active_agent = CLIAgentSessionsModel::as_ref(ctx) + .session(view.view_id()) + .map(|session| session.agent); + warpui::async_assert!( + active_agent == Some(expected_agent), + "Expected active CLI agent {:?}; got {:?}", + expected_agent, + active_agent + ) + }) + }) +} + +/// Asserts that the Rich Input editor for `tab_index` has placeholder text configured. +pub fn rich_input_placeholder_exists(tab_index: usize) -> warpui::integration::AssertionCallback { + Box::new(move |app, window_id| { + let input_view = single_input_view_for_tab(app, window_id, tab_index); + input_view.read(app, |view, ctx| { + warpui::async_assert!( + view.editor().as_ref(ctx).placeholder_text_exists(), + "Expected Rich Input placeholder text to exist for tab {tab_index}" + ) + }) + }) +} + /// Asserts that the Rich Input buffer text for `tab_index` is empty. pub fn rich_input_buffer_text_is_empty(tab_index: usize) -> warpui::integration::AssertionCallback { Box::new(move |app, window_id| { diff --git a/app/src/server/telemetry/events.rs b/app/src/server/telemetry/events.rs index 5d0826fb51a..03a572a4783 100644 --- a/app/src/server/telemetry/events.rs +++ b/app/src/server/telemetry/events.rs @@ -454,6 +454,7 @@ pub enum CLIAgentType { Claude, Gemini, Codex, + Kiro, Amp, Droid, OpenCode, diff --git a/app/src/terminal/cli_agent.rs b/app/src/terminal/cli_agent.rs index ee2e6dd4bb2..fa1fcef5020 100644 --- a/app/src/terminal/cli_agent.rs +++ b/app/src/terminal/cli_agent.rs @@ -1,7 +1,7 @@ //! CLI agent detection and configuration. //! //! This module provides types for detecting and working with CLI-based AI agents -//! like Claude Code, Gemini CLI, Codex, Amp, and Droid. +//! like Claude Code, Gemini CLI, Codex, Kiro, Amp, and Droid. use std::borrow::Cow; use std::collections::HashMap; @@ -47,6 +47,14 @@ pub(crate) const OPENAI_COLOR: ColorU = ColorU { a: 255, }; +/// Kiro brand purple (from the official Kiro app icon gradient) +const KIRO_PURPLE: ColorU = ColorU { + r: 192, + g: 156, + b: 255, + a: 255, +}; + /// Amp brand color (#F34E3F) const AMP_COLOR: ColorU = ColorU { r: 243, @@ -135,12 +143,13 @@ const MISTRAL_ORANGE: ColorU = ColorU { a: 255, }; -/// Represents a CLI agent (e.g., Claude Code, Gemini CLI, Codex, Amp, Droid, OpenCode, Copilot, Pi, Auggie, Cursor, Goose, Hermes, Mistral Vibe) +/// Represents a CLI agent (e.g., Claude Code, Gemini CLI, Codex, Kiro, Amp, Droid, OpenCode, Copilot, Pi, Auggie, Cursor, Goose, Hermes, Mistral Vibe) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Sequence, Serialize, Deserialize)] pub enum CLIAgent { Claude, Gemini, Codex, + Kiro, Amp, Droid, OpenCode, @@ -166,6 +175,7 @@ impl CLIAgent { CLIAgent::Claude => &["claude"], CLIAgent::Gemini => &["gemini"], CLIAgent::Codex => &["codex"], + CLIAgent::Kiro => &["kiro-cli", "kiro", "kiro-cli-chat", "kiro-cli-term"], CLIAgent::Amp => &["amp"], CLIAgent::Droid => &["droid"], CLIAgent::OpenCode => &["opencode"], @@ -229,6 +239,7 @@ impl CLIAgent { CLIAgent::Claude => "Claude Code", CLIAgent::Gemini => "Gemini", CLIAgent::Codex => "Codex", + CLIAgent::Kiro => "Kiro", CLIAgent::Amp => "Amp", CLIAgent::Droid => "Droid", CLIAgent::OpenCode => "OpenCode", @@ -252,6 +263,7 @@ impl CLIAgent { CLIAgent::Claude => Some(Icon::ClaudeLogo), CLIAgent::Gemini => Some(Icon::GeminiLogo), CLIAgent::Codex => Some(Icon::OpenAILogo), + CLIAgent::Kiro => Some(Icon::KiroLogo), CLIAgent::Amp => Some(Icon::AmpLogo), CLIAgent::Droid => Some(Icon::DroidLogo), CLIAgent::OpenCode => Some(Icon::OpenCodeLogo), @@ -283,6 +295,7 @@ impl CLIAgent { SkillProvider::Claude, SkillProvider::Codex, ], + CLIAgent::Kiro => &[SkillProvider::Kiro], CLIAgent::OpenCode => &[ SkillProvider::OpenCode, SkillProvider::Agents, @@ -338,6 +351,7 @@ impl CLIAgent { CLIAgent::Claude => Some(CLAUDE_ORANGE), CLIAgent::Gemini => Some(GEMINI_BLUE), CLIAgent::Codex => Some(OPENAI_COLOR), + CLIAgent::Kiro => Some(KIRO_PURPLE), CLIAgent::Amp => Some(AMP_COLOR), CLIAgent::Droid => Some(DROID_COLOR), CLIAgent::OpenCode => Some(OPENCODE_COLOR), @@ -362,6 +376,7 @@ impl CLIAgent { CLIAgent::Pi | CLIAgent::OhMyPi | CLIAgent::Auggie + | CLIAgent::Kiro | CLIAgent::Droid | CLIAgent::Antigravity => ColorU::new(0, 0, 0, 255), _ => ColorU::white(), @@ -614,6 +629,7 @@ impl From for CLIAgentType { CLIAgent::Claude => CLIAgentType::Claude, CLIAgent::Gemini => CLIAgentType::Gemini, CLIAgent::Codex => CLIAgentType::Codex, + CLIAgent::Kiro => CLIAgentType::Kiro, CLIAgent::Amp => CLIAgentType::Amp, CLIAgent::Droid => CLIAgentType::Droid, CLIAgent::OpenCode => CLIAgentType::OpenCode, diff --git a/app/src/terminal/cli_agent_sessions/listener/mod.rs b/app/src/terminal/cli_agent_sessions/listener/mod.rs index a07211893b8..a72d72b6ebf 100644 --- a/app/src/terminal/cli_agent_sessions/listener/mod.rs +++ b/app/src/terminal/cli_agent_sessions/listener/mod.rs @@ -71,6 +71,7 @@ fn create_handler(agent: &CLIAgent) -> Option> { | CLIAgent::WarpTui => Some(Box::new(DefaultSessionListener)), CLIAgent::Codex => Some(Box::new(CodexSessionHandler)), CLIAgent::Hermes + | CLIAgent::Kiro | CLIAgent::Amp | CLIAgent::Copilot | CLIAgent::CursorCli diff --git a/app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs b/app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs index 7b67ad814dd..bfa447f6272 100644 --- a/app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs +++ b/app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs @@ -288,6 +288,7 @@ pub(crate) fn plugin_manager_for_with_shell( CLIAgent::OpenCode | CLIAgent::Codex | CLIAgent::Gemini + | CLIAgent::Kiro | CLIAgent::Amp | CLIAgent::Droid | CLIAgent::Copilot diff --git a/app/src/terminal/cli_agent_tests.rs b/app/src/terminal/cli_agent_tests.rs index 9bf95dac5e6..32925d4ce9d 100644 --- a/app/src/terminal/cli_agent_tests.rs +++ b/app/src/terminal/cli_agent_tests.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::sync::Arc; +use ai::skills::SkillProvider; use chrono::Local; use pathfinder_color::ColorU; use smol_str::SmolStr; @@ -259,6 +260,10 @@ fn test_detect_known_agents() { ("claude", CLIAgent::Claude), ("gemini", CLIAgent::Gemini), ("codex", CLIAgent::Codex), + ("kiro", CLIAgent::Kiro), + ("kiro-cli", CLIAgent::Kiro), + ("kiro-cli-chat", CLIAgent::Kiro), + ("kiro-cli-term", CLIAgent::Kiro), ("amp", CLIAgent::Amp), ("droid", CLIAgent::Droid), ("opencode", CLIAgent::OpenCode), @@ -580,6 +585,32 @@ fn test_oh_my_pi_supports_bash_mode() { assert!(CLIAgent::OhMyPi.supports_bash_mode()); } +#[test] +fn test_kiro_variant_properties() { + assert_eq!(CLIAgent::Kiro.command_prefix(), "kiro-cli"); + assert_eq!( + CLIAgent::Kiro.command_prefixes(), + &["kiro-cli", "kiro", "kiro-cli-chat", "kiro-cli-term"] + ); + assert_eq!(CLIAgent::Kiro.display_name(), "Kiro"); + assert_eq!( + CLIAgent::Kiro.brand_color(), + Some(ColorU::new(192, 156, 255, 255)) + ); + assert_eq!(CLIAgent::Kiro.icon(), Some(Icon::KiroLogo)); + assert_eq!(CLIAgent::Kiro.brand_icon_color(), ColorU::black()); + assert_eq!( + CLIAgent::Kiro.supported_skill_providers(), + &[SkillProvider::Kiro] + ); + assert!(!CLIAgent::Kiro.supports_bash_mode()); + assert!(CLIAgent::Kiro.supports_cli_agent_footer()); + assert!(matches!( + crate::server::telemetry::CLIAgentType::from(CLIAgent::Kiro), + crate::server::telemetry::CLIAgentType::Kiro + )); +} + #[test] fn test_warp_tui_matches_binaries_and_launchers() { // Direct binary names. diff --git a/app/src/terminal/view/use_agent_footer/mod.rs b/app/src/terminal/view/use_agent_footer/mod.rs index 6259b2db9bb..775f4fe4948 100644 --- a/app/src/terminal/view/use_agent_footer/mod.rs +++ b/app/src/terminal/view/use_agent_footer/mod.rs @@ -126,6 +126,7 @@ fn rich_input_submit_strategy(agent: CLIAgent) -> RichInputSubmitStrategy { CLIAgent::OhMyPi => RichInputSubmitStrategy::BracketedPaste, CLIAgent::Copilot => RichInputSubmitStrategy::BracketedPasteDelayedEnter, CLIAgent::Claude + | CLIAgent::Kiro | CLIAgent::OpenCode | CLIAgent::Gemini | CLIAgent::Auggie diff --git a/app/src/terminal/view_tests.rs b/app/src/terminal/view_tests.rs index 50a44575b6f..4f5239d3199 100644 --- a/app/src/terminal/view_tests.rs +++ b/app/src/terminal/view_tests.rs @@ -6869,6 +6869,7 @@ fn cli_agent_rich_input_hint_text_mentions_active_cli_agent() { (CLIAgent::Claude, "Enter prompt for Claude Code..."), (CLIAgent::Gemini, "Enter prompt for Gemini..."), (CLIAgent::Codex, "Enter prompt for Codex..."), + (CLIAgent::Kiro, "Enter prompt for Kiro..."), (CLIAgent::Unknown, "Tell the agent what to build..."), ] { let terminal = open_cli_agent_rich_input_for_agent(&mut app, agent); diff --git a/crates/ai/src/skills/skill_provider.rs b/crates/ai/src/skills/skill_provider.rs index 8a49d3e6a5c..23e7f4f47da 100644 --- a/crates/ai/src/skills/skill_provider.rs +++ b/crates/ai/src/skills/skill_provider.rs @@ -83,12 +83,12 @@ impl SkillProvider { SkillProvider::Gemini => Icon::GeminiLogo, SkillProvider::Droid => Icon::DroidLogo, SkillProvider::OpenCode => Icon::OpenCodeLogo, + SkillProvider::Kiro => Icon::KiroLogo, SkillProvider::Warp | SkillProvider::Agents | SkillProvider::Cursor | SkillProvider::Copilot - | SkillProvider::Github - | SkillProvider::Kiro => Icon::WarpLogoLight, + | SkillProvider::Github => Icon::WarpLogoLight, } } diff --git a/crates/integration/src/bin/integration.rs b/crates/integration/src/bin/integration.rs index 8e1e4ee7673..cf87286f822 100644 --- a/crates/integration/src/bin/integration.rs +++ b/crates/integration/src/bin/integration.rs @@ -164,6 +164,7 @@ fn register_tests() -> HashMap<&'static str, BoxedBuilderFn> { register_test!(test_typeahead); register_test!(test_input_reporting_posix_shells); register_test!(test_input_reporting_powershell); + register_test!(test_kiro_cli_rich_input_shows_kiro_branding); register_test!(test_background_output); register_test!(test_home_key_should_not_appear_in_input); register_test!(test_change_font_size); diff --git a/crates/integration/src/test.rs b/crates/integration/src/test.rs index 54b294f26dc..6c28653f55f 100644 --- a/crates/integration/src/test.rs +++ b/crates/integration/src/test.rs @@ -15,6 +15,7 @@ mod goto_line; mod history; mod input; mod keyboard_protocol; +mod kiro_cli; mod launch_configs; mod notebooks; mod osc8_hyperlinks; @@ -61,6 +62,7 @@ pub use goto_line::*; pub use history::*; pub use input::*; pub use keyboard_protocol::*; +pub use kiro_cli::*; pub use launch_configs::*; pub use notebooks::*; pub use osc8_hyperlinks::*; diff --git a/crates/integration/src/test/kiro_cli.rs b/crates/integration/src/test/kiro_cli.rs new file mode 100644 index 00000000000..b4cc0f467e7 --- /dev/null +++ b/crates/integration/src/test/kiro_cli.rs @@ -0,0 +1,34 @@ +use std::time::Duration; + +use warp::features::FeatureFlag; +use warp::integration_testing::input::{ + active_cli_agent_is, cli_agent_rich_input_is_open, open_cli_agent_rich_input_for_agent, + rich_input_placeholder_exists, +}; +use warp::integration_testing::terminal::{ + clear_blocklist_to_remove_bootstrapped_blocks, wait_until_bootstrapped_single_pane_for_tab, +}; +use warp::terminal::CLIAgent; +use warpui_core::integration::TestStep; + +use super::new_builder; +use crate::Builder; + +pub fn test_kiro_cli_rich_input_shows_kiro_branding() -> Builder { + FeatureFlag::CLIAgentRichInput.set_enabled(true); + + new_builder() + .with_real_display() + .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) + .with_step(clear_blocklist_to_remove_bootstrapped_blocks()) + .with_step(open_cli_agent_rich_input_for_agent(0, CLIAgent::Kiro)) + .with_step( + TestStep::new("Assert Kiro Rich Input state and capture screenshot") + .set_timeout(Duration::from_secs(20)) + .set_post_step_pause(Duration::from_secs(2)) + .with_take_screenshot("kiro_cli_rich_input.png") + .add_assertion(cli_agent_rich_input_is_open(0)) + .add_assertion(active_cli_agent_is(0, CLIAgent::Kiro)) + .add_assertion(rich_input_placeholder_exists(0)), + ) +} diff --git a/crates/integration/tests/integration/ui_tests.rs b/crates/integration/tests/integration/ui_tests.rs index 104b312f93a..9bc954bcd70 100644 --- a/crates/integration/tests/integration/ui_tests.rs +++ b/crates/integration/tests/integration/ui_tests.rs @@ -15,6 +15,7 @@ integration_tests! { test_execute_multiple_cursor_command, test_home_key_should_not_appear_in_input, test_change_font_size, + test_kiro_cli_rich_input_shows_kiro_branding, test_long_running_block_height_updated, test_instant_prompt_bootstrap, test_unescaped_prompt_bootstraps, diff --git a/crates/warp_core/src/ui/icons.rs b/crates/warp_core/src/ui/icons.rs index 7954b6380e1..e98e147694d 100644 --- a/crates/warp_core/src/ui/icons.rs +++ b/crates/warp_core/src/ui/icons.rs @@ -278,6 +278,7 @@ pub enum Icon { GrokLogo, OpenAILogo, XLogo, + KiroLogo, AmpLogo, DroidLogo, OpenCodeLogo, @@ -627,6 +628,7 @@ impl From for &'static str { Icon::GrokLogo => "bundled/svg/grok.svg", Icon::OpenAILogo => "bundled/svg/openai.svg", Icon::XLogo => "bundled/svg/x-logo.svg", + Icon::KiroLogo => "bundled/svg/kiro.svg", Icon::AmpLogo => "bundled/svg/amp.svg", Icon::DroidLogo => "bundled/svg/droid.svg", Icon::OpenCodeLogo => "bundled/svg/opencode.svg", From 4f0f59d78cdd5e60f6a28d9cb7114fe592288a22 Mon Sep 17 00:00:00 2001 From: Heng Liu Date: Tue, 11 Aug 2026 15:39:50 +0000 Subject: [PATCH 2/3] Add Kiro vertical tabs e2e screenshot --- crates/integration/src/test/kiro_cli.rs | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/crates/integration/src/test/kiro_cli.rs b/crates/integration/src/test/kiro_cli.rs index b4cc0f467e7..f7b2d2f5010 100644 --- a/crates/integration/src/test/kiro_cli.rs +++ b/crates/integration/src/test/kiro_cli.rs @@ -1,21 +1,28 @@ use std::time::Duration; +use settings::Setting as _; use warp::features::FeatureFlag; use warp::integration_testing::input::{ active_cli_agent_is, cli_agent_rich_input_is_open, open_cli_agent_rich_input_for_agent, rich_input_placeholder_exists, }; +use warp::integration_testing::step::new_step_with_default_assertions; use warp::integration_testing::terminal::{ clear_blocklist_to_remove_bootstrapped_blocks, wait_until_bootstrapped_single_pane_for_tab, }; +use warp::integration_testing::view_getters::workspace_view; use warp::terminal::CLIAgent; +use warp::workspace::WorkspaceAction; +use warp::workspace::tab_settings::{TabSettings, VerticalTabsDisplayGranularity}; use warpui_core::integration::TestStep; +use warpui_core::{SingletonEntity, TypedActionView}; use super::new_builder; use crate::Builder; pub fn test_kiro_cli_rich_input_shows_kiro_branding() -> Builder { FeatureFlag::CLIAgentRichInput.set_enabled(true); + FeatureFlag::VerticalTabs.set_enabled(true); new_builder() .with_real_display() @@ -31,4 +38,45 @@ pub fn test_kiro_cli_rich_input_shows_kiro_branding() -> Builder { .add_assertion(active_cli_agent_is(0, CLIAgent::Kiro)) .add_assertion(rich_input_placeholder_exists(0)), ) + .with_step( + new_step_with_default_assertions("Enable and open vertical tabs").with_action( + |app, window_id, _| { + TabSettings::handle(app).update(app, |settings, ctx| { + settings + .use_vertical_tabs + .set_value(true, ctx) + .expect("vertical tabs setting should update"); + settings + .vertical_tabs_display_granularity + .set_value(VerticalTabsDisplayGranularity::Panes, ctx) + .expect("vertical tabs display granularity should update"); + }); + + let workspace = workspace_view(app, window_id); + workspace.update(app, |workspace, ctx| { + workspace.handle_action(&WorkspaceAction::OpenVerticalTabsPanel, ctx); + }); + }, + ), + ) + .with_step( + TestStep::new("Assert Kiro vertical tab branding and capture screenshot") + .set_timeout(Duration::from_secs(20)) + .set_post_step_pause(Duration::from_secs(2)) + .with_take_screenshot("kiro_cli_vertical_tab.png") + .add_assertion(|app, window_id| { + let presenter = app.presenter(window_id).expect("presenter should exist"); + let panel_is_rendered = presenter + .borrow() + .position_cache() + .get_position("workspace_view:vertical_tabs_panel") + .is_some(); + warpui_core::async_assert!( + panel_is_rendered, + "Expected vertical tabs panel to be rendered" + ) + }) + .add_assertion(cli_agent_rich_input_is_open(0)) + .add_assertion(active_cli_agent_is(0, CLIAgent::Kiro)), + ) } From 8acf7064377ee4c676980cb862a1f7dc0015fbef Mon Sep 17 00:00:00 2001 From: Heng Liu Date: Tue, 11 Aug 2026 16:14:36 +0000 Subject: [PATCH 3/3] Add Kiro product and technical specs Define the supported Kiro CLI integration scope for GH9066 and document why plugin status support remains out of scope. --- specs/GH9066/product.md | 88 +++++++++++++++++ specs/GH9066/tech.md | 202 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 specs/GH9066/product.md create mode 100644 specs/GH9066/tech.md diff --git a/specs/GH9066/product.md b/specs/GH9066/product.md new file mode 100644 index 00000000000..7b1c35bf82d --- /dev/null +++ b/specs/GH9066/product.md @@ -0,0 +1,88 @@ +# Product Spec: Support Kiro CLI agent integration + +**Issue:** [warpdotdev/warp#9066](https://github.com/warpdotdev/warp/issues/9066) + +**Prior spec:** [warpdotdev/warp#12387](https://github.com/warpdotdev/warp/pull/12387) + +**Figma:** none provided + +## Summary + +Warp recognizes Kiro CLI as a first-class CLI agent. An active Kiro session uses +Kiro branding in the CLI-agent footer, Rich Input, vertical tabs, settings, and +telemetry. + +Warp also shows Kiro skills from `.kiro/skills` in the Rich Input slash menu. This +initial integration does not install a plugin or claim real-time Kiro status +support. + +## Problem + +Warp currently treats Kiro CLI commands as unknown terminal commands. Users do not +get the branded footer, Rich Input workflow, Kiro skill filtering, or Kiro +telemetry that supported CLI agents receive. + +## Goals + +- Detect the supported Kiro CLI executable and launcher names. +- Show consistent Kiro branding across CLI-agent surfaces. +- Enable Rich Input and Kiro skills for an active Kiro session. +- Preserve Kiro identity in telemetry and shared sessions. +- Add automated coverage for the user-visible flow. + +## Non-goals + +- Installing or updating Kiro CLI. +- Installing a Warp plugin into Kiro CLI. +- Adding Kiro-specific session event or status tracking. +- Adding Kiro support for Rich Input bash mode. +- Changing Kiro CLI behavior or its skill-file format. +- Changing the behavior of another CLI agent. + +## Behavior invariants + +1. Warp recognizes `kiro-cli`, `kiro`, `kiro-cli-chat`, and `kiro-cli-term` as + Kiro CLI executable names. + +2. Detection uses the existing CLI-agent command rules. It supports executable + paths, arguments, shell aliases, and leading environment assignments where the + existing parser supports them. + +3. A recognized Kiro command maps to the first-class Kiro agent identity. It does + not use the generic unknown-agent identity. + +4. An active Kiro session shows the Kiro name, logo, and brand colors in the + CLI-agent footer and other agent-identity surfaces. + +5. The vertical-tabs panel uses the Kiro identity for a pane that has an active + Kiro session. + +6. A user can open Rich Input for an active Kiro session. The editor shows + `Enter prompt for Kiro...` when the input is empty. + +7. The Rich Input slash menu shows skills from the Kiro skill provider. This + provider reads the existing home and project `.kiro/skills` directories. + +8. Rich Input sends prompt text to the active Kiro process through the terminal + PTY. It sends Enter after the existing short delay used by compatible agents. + +9. Kiro does not enable Rich Input bash mode. A leading `!` does not receive the + Kiro-specific shell-mode treatment. + +10. CLI-agent telemetry identifies Kiro with a distinct Kiro value. Shared-session + serialization preserves the Kiro identity with the name `Kiro`. + +11. Kiro is available in settings surfaces that list known CLI agents, including + the command-to-agent mapping selector. + +12. Warp does not show Kiro-specific plugin installation or update instructions. + Warp does not claim Kiro status updates until a compatible event integration + exists. + +13. Existing CLI-agent detection, branding, Rich Input, settings, telemetry, and + shared-session behavior remain unchanged. + +## Open questions + +- Does the initial release require a feature flag for staged rollout? +- Are all four executable names part of the supported Kiro CLI distribution? diff --git a/specs/GH9066/tech.md b/specs/GH9066/tech.md new file mode 100644 index 00000000000..2bd2233ace2 --- /dev/null +++ b/specs/GH9066/tech.md @@ -0,0 +1,202 @@ +# Tech Spec: Support Kiro CLI agent integration + +**Issue:** [warpdotdev/warp#9066](https://github.com/warpdotdev/warp/issues/9066) + +**Product spec:** [`specs/GH9066/product.md`](product.md) + +**Prior spec:** [warpdotdev/warp#12387](https://github.com/warpdotdev/warp/pull/12387) + +## Context + +Warp models each recognized terminal agent with `CLIAgent`. This enum supplies the +agent name, executable names, icon, colors, skill providers, serialization, and +telemetry conversion. + +The existing footer, Rich Input, settings, and shared-session systems consume this +identity. Kiro support therefore needs a new enum variant and exhaustive updates +to these consumers. It does not need a separate UI implementation. + +Relevant current code: + +- `app/src/terminal/cli_agent.rs:146-450` defines agent identities, command + detection, display properties, skill providers, and input capabilities. +- `app/src/terminal/cli_agent.rs:626-648` converts an agent identity to its + telemetry value. +- `app/src/server/telemetry/events.rs:453-473` defines `CLIAgentType`. +- `crates/warp_core/src/ui/icons.rs:260-300,610-650` defines icon identities and + bundled asset paths. +- `crates/ai/src/skills/skill_provider.rs:31-43,77-103,105-154` defines Kiro as a + skill provider and maps `.kiro/skills` paths. +- `app/src/terminal/view/use_agent_footer/mod.rs:101-144` selects the PTY + submission strategy for each agent. +- `app/src/terminal/cli_agent_sessions/listener/mod.rs:60-90` selects session event + handlers. +- `app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs:250-305` selects + plugin managers. +- `app/src/settings_view/ai_page.rs:3584-3657` builds the known-agent selector for + custom command mappings. +- `app/src/terminal/local_tty/terminal_view_adaptor.rs` and + `app/src/terminal/shared_session/` serialize CLI-agent identities for shared + sessions. +- `app/src/integration_testing/input/step.rs` provides Rich Input test steps. +- `crates/integration/src/test/kiro_cli.rs` covers the Kiro Rich Input and + vertical-tabs flow. + +The closed spec PR #12387 proposed a Kiro plugin manager, status events, install +instructions, and a rollout flag. Kiro does not currently expose the compatible +plugin integration that proposal requires. This specification limits the initial +change to supported behavior. + +## Proposed changes + +### 1. Add the Kiro agent identity + +Add `CLIAgent::Kiro` to `app/src/terminal/cli_agent.rs`. Define these properties: + +| Property | Value | +| --- | --- | +| Canonical executable | `kiro-cli` | +| Executable names | `kiro-cli`, `kiro`, `kiro-cli-chat`, `kiro-cli-term` | +| Display name | `Kiro` | +| Icon | `Icon::KiroLogo` | +| Brand color | `ColorU::new(192, 156, 255, 255)` | +| Brand icon color | black | +| Skill providers | `SkillProvider::Kiro` | +| Skill command prefix | `/` | +| Rich Input bash mode | disabled | +| CLI-agent footer | enabled | + +The existing `CLIAgent::detect` iteration then recognizes Kiro through +`matches_command`. Existing parsing continues to handle executable basenames, +paths, aliases, arguments, and shell environment assignments. + +The enum derives Serde traits and `Sequence`. As a result, the existing +serialization functions use `Kiro`, and known-agent selectors include the new +variant without separate settings code. + +### 2. Add Kiro branding + +Add `Icon::KiroLogo` to `crates/warp_core/src/ui/icons.rs`. Map it to +`app/assets/bundled/svg/kiro.svg`. + +Use the Kiro logo for both `CLIAgent::Kiro` and `SkillProvider::Kiro`. Use a black +foreground on the light Kiro brand color to preserve icon contrast. + +The asset must remain a monochrome SVG because callers control its fill color. +The source and license of the asset must be acceptable for repository use. + +### 3. Connect Kiro skills and Rich Input + +Return only `SkillProvider::Kiro` from +`CLIAgent::Kiro.supported_skill_providers()`. The existing skill index reads Kiro +skills from these paths: + +- `~/.kiro/skills` +- `/.kiro/skills` + +Use `RichInputSubmitStrategy::DelayedEnter` for Kiro. This strategy writes the +prompt text to the PTY and writes carriage return after the standard short delay. +It avoids an input submission race without changing Kiro CLI. + +Do not add Kiro to `supports_bash_mode`. Kiro bash-mode compatibility is not +established. + +### 4. Preserve telemetry and shared-session identity + +Add `CLIAgentType::Kiro` to the telemetry enum. Map `CLIAgent::Kiro` to this value +in the exhaustive conversion. + +The derived Serde representation supplies `Kiro` to existing shared-session +writers. Existing readers reconstruct `CLIAgent::Kiro` through +`from_serialized_name`. + +### 5. Keep plugin behavior explicit + +Add Kiro to the no-manager arm in `plugin_manager_for_with_shell`. Add Kiro to the +no-listener arm in `create_handler`. + +These exhaustive arms make the current limitation explicit. They prevent the code +from implying that Kiro has plugin installation, update, or event-status support. +A later contribution can add those features after Kiro exposes a compatible +integration. + +The generic version-1 event parser can recognize a Kiro executable name because it +iterates all agent identities. Without a Kiro session handler, these events do not +enable a Kiro-specific status integration. + +### 6. Add focused automated coverage + +Extend `app/src/terminal/cli_agent_tests.rs` with: + +- Detection cases for all supported executable names. +- Assertions for the Kiro name, icon, colors, skill provider, footer, input mode, + and telemetry conversion. +- Existing all-variant serialization coverage for the Kiro identity. + +Extend `app/src/terminal/view_tests.rs` to assert the Kiro Rich Input placeholder. + +Generalize the integration helper in +`app/src/integration_testing/input/step.rs` so a test can open Rich Input for a +specified agent. Add assertions for the active agent, open input state, and +configured placeholder. + +Add `crates/integration/src/test/kiro_cli.rs`. The real-display test opens Kiro +Rich Input, captures its branding, opens vertical tabs, and captures the pane +identity there. + +## End-to-end flow + +1. The user starts a supported Kiro executable in a terminal pane. +2. `CLIAgent::detect` returns `CLIAgent::Kiro`. +3. The CLI-agent session model stores the Kiro identity for that pane. +4. The footer and vertical-tabs UI read the Kiro name, icon, and colors. +5. Rich Input filters slash commands to Kiro skills. +6. Rich Input writes the prompt and delayed Enter to the Kiro PTY. +7. Telemetry and shared-session code serialize the Kiro identity. +8. The session ends through the existing command and session lifecycle. + +## Testing and validation + +| Product invariants | Verification | +| --- | --- | +| 1-3 | Unit tests call `CLIAgent::detect` for each supported Kiro executable name. | +| 4 | Unit property tests and a real-display screenshot cover the name, icon, and colors. | +| 5 | The real-display integration test opens vertical tabs and asserts that the panel exists for the active Kiro session. | +| 6 | A view test checks `Enter prompt for Kiro...`. The integration test asserts that Rich Input is open. | +| 7 | A unit property test checks `SkillProvider::Kiro`. Existing provider tests cover `.kiro/skills` discovery. | +| 8-9 | Manual testing covers prompt submission. A unit property test makes sure that bash mode remains disabled. | +| 10 | Unit tests check telemetry conversion. The existing all-variant round-trip test covers serialization. | +| 11 | The enum-driven settings selector includes every known agent except `Unknown`. Focused manual testing covers Kiro. | +| 12 | Exhaustive plugin-manager and listener matches return no Kiro integration. | +| 13 | Repository formatting, Clippy, unit tests, integration tests, and presubmit protect existing behavior. | + +Run these required checks: + +```bash +./script/presubmit +``` + +Manually run Warp with `./script/run`. Start Kiro CLI, open Rich Input, submit a +prompt, and inspect the vertical-tabs panel. + +Capture before and after screenshots for the footer, Rich Input, and vertical-tabs +panel. + +## Risks and mitigations + +- Kiro can change its executable names. Keep detection names covered by tests and + remove unsupported aliases after maintainer review. +- An incorrect or unlicensed logo can block the visual change. Record the asset + source and replace it if the maintainers request another official asset. +- Kiro input behavior can differ across releases. Manual prompt submission and the + real-display test reduce this risk. +- Adding an enum variant exposes Kiro in all known-agent iterations. Exhaustive + matches and serialization tests make these surfaces visible during review. +- Kiro status can appear static without event support. The initial integration + does not claim plugin status support. + +## Open questions + +- Does the initial release require a feature flag for staged rollout? +- Which Kiro source supplies the approved logo and exact brand color? +- Are `kiro`, `kiro-cli-chat`, and `kiro-cli-term` supported public launchers?