Publish harness and TUI open-source

initial sync from the monorepo
This commit is contained in:
grokkybara[bot] 2026-07-16 06:46:02 +01:00
commit c68e39f604
2734 changed files with 1437016 additions and 0 deletions

View file

@ -0,0 +1,132 @@
//! `/find` -- open an incremental search over the conversation scrollback.
//!
//! In simple mode a bare `/` goes to the prompt, so simple-mode users can't
//! reach the vim `/` scrollback search. `/find` focuses the scrollback pane
//! and opens the same search from either mode.
use crate::app::actions::Action;
use crate::slash::command::{CommandExecCtx, CommandResult, SlashCommand};
/// Open scrollback search via `/find`.
pub struct FindCommand;
impl SlashCommand for FindCommand {
fn name(&self) -> &str {
"find"
}
fn description(&self) -> &str {
"Search the conversation scrollback"
}
fn session_scoped(&self) -> bool {
true
}
fn usage(&self) -> &str {
"/find [text]"
}
fn takes_args(&self) -> bool {
true
}
fn arg_placeholder(&self) -> Option<&str> {
Some("[text]")
}
/// Minimal mode has no interactive scrollback pane to search — the
/// terminal's own search covers it (K7/§6.13). Gated off with a message.
fn available_in_minimal(&self) -> bool {
false
}
fn run(&self, _ctx: &mut CommandExecCtx, args: &str) -> CommandResult {
// whitespace-only args open a blank search.
let initial = args.trim();
let query = (!initial.is_empty()).then(|| initial.to_string());
CommandResult::Action(Action::OpenScrollbackSearch(query))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::acp::model_state::ModelState;
static DEFAULT_BUNDLE_STATE: crate::app::bundle::BundleState =
crate::app::bundle::BundleState {
has_cache: false,
version: String::new(),
personas: Vec::new(),
roles: Vec::new(),
agents: Vec::new(),
skills: Vec::new(),
persona_details: Vec::new(),
role_details: Vec::new(),
};
fn make_ctx(models: &ModelState) -> CommandExecCtx<'_> {
CommandExecCtx {
models,
session_id: None,
bundle_state: &DEFAULT_BUNDLE_STATE,
screen_mode: crate::app::ScreenMode::Inline,
pager_state: crate::settings::PagerLocalSnapshot::default(),
}
}
#[test]
fn find_returns_open_scrollback_search_action() {
let models = ModelState::default();
let mut ctx = make_ctx(&models);
let cmd = FindCommand;
assert!(matches!(
cmd.run(&mut ctx, ""),
CommandResult::Action(Action::OpenScrollbackSearch(None))
));
}
#[test]
fn find_with_word_carries_it_as_initial_query() {
let models = ModelState::default();
let mut ctx = make_ctx(&models);
let cmd = FindCommand;
let CommandResult::Action(Action::OpenScrollbackSearch(query)) = cmd.run(&mut ctx, "foo")
else {
panic!("/find foo must open scrollback search");
};
assert_eq!(query.as_deref(), Some("foo"));
}
#[test]
fn find_with_blank_args_carries_no_initial_query() {
let models = ModelState::default();
let mut ctx = make_ctx(&models);
let cmd = FindCommand;
// Both a bare `/find` and whitespace-only args open a blank search.
for args in ["", " "] {
assert!(matches!(
cmd.run(&mut ctx, args),
CommandResult::Action(Action::OpenScrollbackSearch(None))
));
}
}
#[test]
fn find_advertises_optional_text_arg() {
// Pins the slash-arg contract so completion-accept appends a trailing
// space and the `[text]` placeholder shows while typing; bare `/find`
// stays valid (args not required).
let cmd = FindCommand;
assert!(cmd.takes_args());
assert!(!cmd.args_required());
assert_eq!(cmd.arg_placeholder(), Some("[text]"));
}
#[test]
fn not_available_in_minimal() {
// Native terminal search replaces in-app scrollback search in minimal.
assert!(!FindCommand.available_in_minimal());
}
}