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..f7b2d2f5010
--- /dev/null
+++ b/crates/integration/src/test/kiro_cli.rs
@@ -0,0 +1,82 @@
+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()
+ .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)),
+ )
+ .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)),
+ )
+}
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",
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?