Merge pull request #2181 from Hmbown/fix/search-excludes-and-version-hint
fix: grep_files skips large dirs; add version-update footer notification
This commit is contained in:
@@ -115,17 +115,28 @@ impl ToolSpec for GrepFilesTool {
|
||||
let exclude_patterns: Vec<String> =
|
||||
input.get("exclude").and_then(|v| v.as_array()).map_or_else(
|
||||
|| {
|
||||
// Default exclusions for common non-code directories
|
||||
// Default exclusions for common non-code directories.
|
||||
// Bare directory names skip the directory traversal entirely;
|
||||
// `dir/*` filters files inside if the directory is already
|
||||
// being walked (belt-and-suspenders — see #2200).
|
||||
vec![
|
||||
"node_modules".to_string(),
|
||||
"node_modules/*".to_string(),
|
||||
".git".to_string(),
|
||||
".git/*".to_string(),
|
||||
"target".to_string(),
|
||||
"target/*".to_string(),
|
||||
"*.min.js".to_string(),
|
||||
"*.min.css".to_string(),
|
||||
"dist".to_string(),
|
||||
"dist/*".to_string(),
|
||||
"build".to_string(),
|
||||
"build/*".to_string(),
|
||||
"__pycache__".to_string(),
|
||||
"__pycache__/*".to_string(),
|
||||
".venv".to_string(),
|
||||
".venv/*".to_string(),
|
||||
"venv".to_string(),
|
||||
"venv/*".to_string(),
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1076,6 +1076,10 @@ pub struct App {
|
||||
pub status_toasts: VecDeque<StatusToast>,
|
||||
/// Sticky status toast used for important warnings/errors.
|
||||
pub sticky_status: Option<StatusToast>,
|
||||
/// Version-update hint shown in the footer when a newer release
|
||||
/// is available. Set by a background GitHub API check after app
|
||||
/// startup; `None` until the check completes or if up-to-date.
|
||||
pub version_hint: Option<String>,
|
||||
/// Last status text already promoted from `status_message` into toast state.
|
||||
pub last_status_message_seen: Option<String>,
|
||||
pub model: String,
|
||||
@@ -1801,6 +1805,7 @@ impl App {
|
||||
status_message: None,
|
||||
status_toasts: VecDeque::new(),
|
||||
sticky_status: None,
|
||||
version_hint: None,
|
||||
last_status_message_seen: None,
|
||||
model,
|
||||
auto_model,
|
||||
|
||||
@@ -44,6 +44,13 @@ pub(crate) fn render_footer(f: &mut Frame, area: Rect, app: &mut App) {
|
||||
None
|
||||
};
|
||||
let toast = quit_prompt.or_else(|| {
|
||||
// Version-update hint takes precedence over ephemeral status toasts
|
||||
// so the user sees it even when status traffic would hide it.
|
||||
app.version_hint.as_ref().map(|hint| FooterToast {
|
||||
text: hint.clone(),
|
||||
color: palette::STATUS_INFO,
|
||||
})
|
||||
}).or_else(|| {
|
||||
app.active_status_toast().map(|toast| FooterToast {
|
||||
text: toast.text,
|
||||
color: status_color(toast.level),
|
||||
|
||||
@@ -893,7 +893,59 @@ async fn run_event_loop(
|
||||
.checked_sub(Duration::from_secs(60))
|
||||
.unwrap_or_else(Instant::now);
|
||||
|
||||
// Fire-and-forget version check — runs once per session in the
|
||||
// background. On success, `app.version_hint` is set and the footer
|
||||
// renders the update recommendation on the next frame.
|
||||
let mut version_check: Option<tokio::task::JoinHandle<Option<String>>> = Some({
|
||||
let current = env!("CARGO_PKG_VERSION").to_string();
|
||||
tokio::spawn(async move {
|
||||
let client = match reqwest::Client::builder()
|
||||
.user_agent("codewhale-version-check")
|
||||
.timeout(std::time::Duration::from_secs(5))
|
||||
.build()
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(_) => return None,
|
||||
};
|
||||
let resp = client
|
||||
.get("https://api.github.com/repos/Hmbown/CodeWhale/releases/latest")
|
||||
.header("Accept", "application/vnd.github+json")
|
||||
.send()
|
||||
.await
|
||||
.ok()?;
|
||||
let json: serde_json::Value = resp.json().await.ok()?;
|
||||
let tag = json["tag_name"].as_str()?;
|
||||
let latest = tag.trim_start_matches('v');
|
||||
// Compare semver so dev builds (e.g. "0.8.46-pre") don't
|
||||
// trigger false hints. Falls back to string compare on
|
||||
// unparseable versions.
|
||||
let newer = match (parse_semver(latest), parse_semver(¤t)) {
|
||||
(Some(l), Some(c)) => l > c,
|
||||
_ => latest != current,
|
||||
};
|
||||
if newer {
|
||||
Some(format!(
|
||||
"v{latest} available — run `codewhale update` and restart"
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
loop {
|
||||
// Drain the version-check handle once; re-assign None so we
|
||||
// don't poll it again.
|
||||
let mut done = false;
|
||||
if let Some(ref handle) = version_check {
|
||||
done = handle.is_finished();
|
||||
}
|
||||
if done {
|
||||
if let Ok(Some(hint)) = version_check.take().unwrap().await {
|
||||
app.version_hint = Some(hint);
|
||||
}
|
||||
}
|
||||
|
||||
if !drain_web_config_events(&mut web_config_session, app, config, &engine_handle).await {
|
||||
web_config_session = None;
|
||||
}
|
||||
@@ -7939,5 +7991,15 @@ fn extract_reasoning_header(text: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a `major.minor.patch` version string into a comparable tuple.
|
||||
/// Returns `None` on any parse failure (non-semver, dev suffixes, etc.).
|
||||
fn parse_semver(v: &str) -> Option<(u32, u32, u32)> {
|
||||
let mut parts = v.splitn(3, '.');
|
||||
let major = parts.next()?.parse::<u32>().ok()?;
|
||||
let minor = parts.next()?.parse::<u32>().ok()?;
|
||||
let patch = parts.next().unwrap_or("0").parse::<u32>().ok()?;
|
||||
Some((major, minor, patch))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
Reference in New Issue
Block a user