feat: enhance strictness of Rust compiler settings and improve code quality by treating warnings as errors
This commit is contained in:
@@ -398,13 +398,6 @@ impl Harness {
|
||||
}
|
||||
}
|
||||
|
||||
fn classify(cmd: &str) -> Verdict {
|
||||
match cmd {
|
||||
"bash" | "write" | "edit" | "delete" | "git_operator" => Verdict::Allow,
|
||||
_ => Verdict::Allow,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Default for Harness {
|
||||
|
||||
@@ -404,14 +404,15 @@ impl McpManager {
|
||||
self.servers.iter().flat_map(|server| {
|
||||
let handle = server.child_handle.clone();
|
||||
server.tools.iter().map(move |info| {
|
||||
Box::new(McpToolAdapter {
|
||||
let adapter: Box<dyn crate::tool::Tool> = Box::new(McpToolAdapter {
|
||||
tool_name: info.name.clone(),
|
||||
server_name: server.name.clone(),
|
||||
transport: server.transport.clone(),
|
||||
description: info.description.clone(),
|
||||
parameters: info.input_schema.clone(),
|
||||
child_handle: handle.clone(),
|
||||
}) as Box<dyn crate::tool::Tool>
|
||||
});
|
||||
adapter
|
||||
})
|
||||
}).collect()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ pub mod editor;
|
||||
pub mod effort;
|
||||
pub mod key_input;
|
||||
pub mod mcp;
|
||||
pub mod workflow;
|
||||
|
||||
pub mod quit_confirm;
|
||||
pub mod rewind;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
//! Workflow-mode helper logic for the TUI workflow overlay.
|
||||
//!
|
||||
//! Flow: exposes a dismiss handler invoked by a keybinding to close the
|
||||
//! workflow overlay, and a status query used elsewhere to check whether
|
||||
//! it is currently showing.
|
||||
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use crate::app::state::types::Overlay;
|
||||
|
||||
/// Close the workflow overlay if it is the currently active overlay.
|
||||
///
|
||||
/// Flow: check `state.misc.overlay == Overlay::Workflow`, reset to `Overlay::None`
|
||||
/// if so, then mark state dirty regardless.
|
||||
///
|
||||
/// Why: no-ops safely if another overlay is showing, so it can be called
|
||||
/// unconditionally from a dismiss keybinding.
|
||||
///
|
||||
/// Return: nothing; mutates `state.misc.overlay` and `state.dirty` in place.
|
||||
pub fn handle_workflow_dismiss(state: &mut AppStateRest) {
|
||||
if state.misc.overlay == Overlay::Workflow {
|
||||
state.misc.overlay = Overlay::None;
|
||||
}
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
/// Report whether the workflow overlay is currently displayed.
|
||||
///
|
||||
/// Flow: compare `state.misc.overlay` against `Overlay::Workflow`.
|
||||
///
|
||||
/// Return: `"active"` if the workflow overlay is shown, `"idle"` otherwise.
|
||||
pub fn workflow_status(state: &AppStateRest) -> &str {
|
||||
if state.misc.overlay == Overlay::Workflow {
|
||||
"active"
|
||||
} else {
|
||||
"idle"
|
||||
}
|
||||
}
|
||||
@@ -405,15 +405,7 @@ pub fn apply_action(state: &mut AppStateRest, action: Action) {
|
||||
lifetime_ms: 8000,
|
||||
});
|
||||
state.dirty = true;
|
||||
} else if kind == "bg-arch-review" {
|
||||
state.push_toast(Toast {
|
||||
kind: ToastKind::Info,
|
||||
message: message.clone(),
|
||||
created_at: chrono::Utc::now().timestamp_millis(),
|
||||
lifetime_ms: 10000,
|
||||
});
|
||||
state.dirty = true;
|
||||
} else if kind == "bg-security-review" {
|
||||
} else if kind == "bg-arch-review" || kind == "bg-security-review" {
|
||||
state.push_toast(Toast {
|
||||
kind: ToastKind::Info,
|
||||
message: message.clone(),
|
||||
@@ -1789,7 +1781,7 @@ fn rand_bytes(n: usize) -> Vec<u8> {
|
||||
.unwrap_or_default()
|
||||
.as_nanos() as u64;
|
||||
let base = seed ^ counter;
|
||||
(0..n).map(|i| ((base >> ((i as u64 % 8) * 8)) ^ (i as u64 * 2654435761)) as u8).collect()
|
||||
(0..n).map(|i| ((base >> ((i as u64 % 8) * 8)) ^ (i as u64 * 2_654_435_761)) as u8).collect()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ pub struct AppStateRest {
|
||||
pub session_id: String,
|
||||
pub session_dir: PathBuf,
|
||||
pub memory_dir: PathBuf,
|
||||
pub download_dir: PathBuf,
|
||||
pub worktrees_dir: PathBuf,
|
||||
pub dir_cache: Arc<RwLock<DirCache>>,
|
||||
pub edit_log: EditLog,
|
||||
@@ -88,10 +87,6 @@ impl AppStateRest {
|
||||
pub fn new(workspace_roots: Vec<PathBuf>, session_dir: PathBuf, memory_dir: PathBuf) -> Self {
|
||||
let settings = Settings::load();
|
||||
let app_config = AppConfig::load();
|
||||
let download_dir = memory_dir.parent().unwrap_or_else(|| {
|
||||
tracing::warn!("[state] memory_dir '{}' has no parent, using it for downloads", memory_dir.display());
|
||||
&memory_dir
|
||||
}).join("downloads");
|
||||
let worktrees_dir = memory_dir.parent().unwrap_or_else(|| {
|
||||
tracing::warn!("[state] memory_dir '{}' has no parent, using it for worktrees", memory_dir.display());
|
||||
&memory_dir
|
||||
@@ -112,7 +107,6 @@ impl AppStateRest {
|
||||
session_id,
|
||||
session_dir: session_dir.clone(),
|
||||
memory_dir,
|
||||
download_dir,
|
||||
worktrees_dir,
|
||||
turn_events: Arc::new(Mutex::new(VecDeque::new())),
|
||||
turn_in_flight: Arc::new(Mutex::new(false)),
|
||||
@@ -290,7 +284,6 @@ impl AppStateRest {
|
||||
workspaces: self.workspace_roots.clone(),
|
||||
session_dir: self.session_dir.clone(),
|
||||
memory_dir: self.memory_dir.clone(),
|
||||
_download_dir: self.download_dir.clone(),
|
||||
worktrees_dir: self.worktrees_dir.clone(),
|
||||
dir_cache: self.dir_cache.clone(),
|
||||
origin,
|
||||
|
||||
@@ -44,7 +44,7 @@ const QUICK_REVIEW_MAX_STEPS: usize = 2;
|
||||
const BG_SUBAGENT_MAX_STEPS: usize = 8;
|
||||
|
||||
/// ─── Helpers ───
|
||||
|
||||
///
|
||||
/// Check whether a file path is worth auto-reviewing (not config/lock/data).
|
||||
pub fn is_reviewable_path(path: &str) -> bool {
|
||||
let lower = path.to_lowercase();
|
||||
@@ -81,7 +81,7 @@ fn is_production_code(path: &str) -> bool {
|
||||
}
|
||||
|
||||
/// ─── Inline Quick Review (synchronous, feeds back to LLM) ───
|
||||
|
||||
///
|
||||
/// Spawn a lightweight inline code review subagent for the given file.
|
||||
///
|
||||
/// The subagent reads the file (read-only), checks for common issues,
|
||||
@@ -143,7 +143,7 @@ pub fn spawn_quick_review(
|
||||
}
|
||||
|
||||
/// ─── Background Subagent Spawners (async, report via SystemNote) ───
|
||||
|
||||
///
|
||||
/// Spawn a background subagent that generates tests for modified files.
|
||||
///
|
||||
/// Uses the test-generator prompt and has read-write access so it can
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! including the default read-only tool set for reviewer agents.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex, atomic::{AtomicBool, Ordering}};
|
||||
use std::sync::{Arc, Mutex, atomic::AtomicBool};
|
||||
use super::spawn::AgentDefinition;
|
||||
|
||||
/// Default read-only tool names granted to `role == "reviewer"` agents.
|
||||
|
||||
@@ -32,7 +32,7 @@ pub mod roles {
|
||||
}
|
||||
|
||||
/// ─── Division Agent Definitions ───
|
||||
|
||||
///
|
||||
/// Build the Strategy Division agent — chief architect and planner.
|
||||
///
|
||||
/// Tools: read-only (read, grep, glob, search, lsp, plan, seqthink, recall)
|
||||
@@ -172,16 +172,17 @@ pub fn documentation_division() -> AgentDefinition {
|
||||
}
|
||||
|
||||
/// ─── Division Registry ───
|
||||
|
||||
///
|
||||
/// A named division with its agent definition and display metadata.
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Division {
|
||||
/// Display name for the division (e.g. "Strategy", "Engineering").
|
||||
pub name: &'static str,
|
||||
/// Role tag used for pipeline routing (matches `roles::*` constants).
|
||||
#[allow(dead_code)]
|
||||
pub role: &'static str,
|
||||
/// One-line description of what this division does.
|
||||
#[allow(dead_code)]
|
||||
pub description: &'static str,
|
||||
/// Agent definition with tools, prompt, and step budget.
|
||||
pub agent_def: AgentDefinition,
|
||||
|
||||
@@ -14,14 +14,6 @@ use crate::tool::{all_tools, tool_defs, tool_is_risky};
|
||||
use super::context::SubagentContext;
|
||||
use super::event::SubagentEvent;
|
||||
|
||||
/// Upper bound on agent loop steps; effectively unbounded (`usize::MAX`).
|
||||
#[allow(dead_code)]
|
||||
pub const MAX_AGENT_STEPS: usize = usize::MAX;
|
||||
|
||||
/// Maximum time a single tool call may block inside a subagent before
|
||||
/// being abandoned. Prevents a stuck tool from hanging the subagent loop.
|
||||
const SUBAGENT_TOOL_TIMEOUT_MS: u64 = 120_000;
|
||||
|
||||
/// Maps a subagent's allowed tool names to concrete Tool trait objects and
|
||||
/// OpenAI-style tool definitions.
|
||||
///
|
||||
|
||||
@@ -151,7 +151,7 @@ fn detect_claude_settings_provider() -> Option<ProviderConfig> {
|
||||
let settings: ClaudeSettings = serde_json::from_str(&content).ok()?;
|
||||
let env = settings.env?;
|
||||
let base_url = env.anthropic_base_url?;
|
||||
let _api_key = env.anthropic_api_key?; // presence check — stored as env var, not in config.
|
||||
let _ = env.anthropic_api_key?; // presence check — stored as env var, not in config.
|
||||
Some(ProviderConfig {
|
||||
api_base: base_url,
|
||||
api_key_env: Some("ANTHROPIC_API_KEY".to_string()),
|
||||
|
||||
@@ -27,8 +27,6 @@ const MAX_MEMORY_ENTRIES: usize = 10_000;
|
||||
pub struct EditLog {
|
||||
pub entries: Vec<EditLogEntry>,
|
||||
pub path: std::path::PathBuf,
|
||||
/// Total entries on disk (may exceed `entries.len()` if truncated).
|
||||
pub total_on_disk: usize,
|
||||
}
|
||||
|
||||
impl EditLog {
|
||||
@@ -37,28 +35,26 @@ impl EditLog {
|
||||
/// `MAX_MEMORY_ENTRIES` to prevent OOM).
|
||||
pub fn new(session_dir: &std::path::Path) -> Self {
|
||||
let path = session_dir.join("edits.jsonl");
|
||||
let (entries, total_on_disk) = Self::load_from_disk(&path);
|
||||
EditLog { entries, path, total_on_disk }
|
||||
let entries = Self::load_from_disk(&path);
|
||||
EditLog { entries, path }
|
||||
}
|
||||
|
||||
/// Reads lines of edits.jsonl into memory, keeping only the most recent
|
||||
/// `MAX_MEMORY_ENTRIES` entries. The full history is preserved on disk
|
||||
/// regardless of the in-memory limit.
|
||||
fn load_from_disk(path: &std::path::Path) -> (Vec<EditLogEntry>, usize) {
|
||||
fn load_from_disk(path: &std::path::Path) -> Vec<EditLogEntry> {
|
||||
let file = match std::fs::File::open(path) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return (Vec::new(), 0),
|
||||
Err(_) => return Vec::new(),
|
||||
};
|
||||
use std::io::{BufRead, BufReader};
|
||||
let reader = BufReader::new(file);
|
||||
let mut entries: Vec<EditLogEntry> = Vec::new();
|
||||
let mut total = 0usize;
|
||||
for line in reader.lines() {
|
||||
let line = match line {
|
||||
Ok(l) => l,
|
||||
Err(_) => continue,
|
||||
};
|
||||
total += 1;
|
||||
if let Ok(entry) = serde_json::from_str::<EditLogEntry>(&line) {
|
||||
// Keep only the most recent entries in memory
|
||||
if entries.len() >= MAX_MEMORY_ENTRIES {
|
||||
@@ -68,7 +64,7 @@ impl EditLog {
|
||||
entries.push(entry);
|
||||
}
|
||||
}
|
||||
(entries, total)
|
||||
entries
|
||||
}
|
||||
|
||||
/// Append one entry to `edits.jsonl` on disk and to the in-memory log,
|
||||
|
||||
@@ -226,6 +226,7 @@ pub fn slug_path(memory_dir: &Path, raw: &str) -> PathBuf {
|
||||
///
|
||||
/// Return: `Ok(())` on success, or an `io::Error` from serialization or
|
||||
/// the write.
|
||||
#[cfg(test)]
|
||||
pub fn export_lessons(memory_dir: &Path, output: &Path) -> std::io::Result<()> {
|
||||
let names = Memory::list(memory_dir);
|
||||
let lessons: Vec<Memory> = names.iter()
|
||||
@@ -255,6 +256,7 @@ pub fn export_lessons(memory_dir: &Path, output: &Path) -> std::io::Result<()> {
|
||||
/// import on the same file won't overwrite or duplicate existing memories.
|
||||
///
|
||||
/// Return: the number of memories actually imported (skips existing ones).
|
||||
#[cfg(test)]
|
||||
pub fn import_lessons(memory_dir: &Path, input: &Path) -> std::io::Result<usize> {
|
||||
let data = std::fs::read_to_string(input)?;
|
||||
let lessons: Vec<Memory> = serde_json::from_str(&data)
|
||||
|
||||
@@ -44,7 +44,6 @@ pub struct ToolCtx {
|
||||
pub workspaces: Vec<PathBuf>,
|
||||
pub session_dir: PathBuf,
|
||||
pub memory_dir: PathBuf,
|
||||
pub _download_dir: PathBuf,
|
||||
pub worktrees_dir: PathBuf,
|
||||
pub dir_cache: std::sync::Arc<tokio::sync::RwLock<super::app::state::misc::DirCache>>,
|
||||
pub origin: crate::app::state::types::Origin,
|
||||
@@ -87,7 +86,6 @@ pub struct ToolCtxBuilder {
|
||||
pub workspaces: Vec<PathBuf>,
|
||||
pub session_dir: PathBuf,
|
||||
pub memory_dir: PathBuf,
|
||||
pub download_dir: PathBuf,
|
||||
pub worktrees_dir: PathBuf,
|
||||
pub dir_cache: std::sync::Arc<tokio::sync::RwLock<super::app::state::misc::DirCache>>,
|
||||
pub origin: crate::app::state::types::Origin,
|
||||
@@ -103,7 +101,6 @@ impl Default for ToolCtxBuilder {
|
||||
workspaces: Vec::new(),
|
||||
session_dir: PathBuf::new(),
|
||||
memory_dir: PathBuf::new(),
|
||||
download_dir: PathBuf::new(),
|
||||
worktrees_dir: PathBuf::new(),
|
||||
dir_cache: std::sync::Arc::new(tokio::sync::RwLock::new(super::app::state::misc::DirCache::new())),
|
||||
origin: crate::app::state::types::Origin::Main,
|
||||
@@ -122,9 +119,6 @@ impl ToolCtxBuilder {
|
||||
pub fn workspaces(mut self, v: Vec<PathBuf>) -> Self { self.workspaces = v; self }
|
||||
/// Set the origin (main process vs. daemon-attached).
|
||||
pub fn origin(mut self, v: crate::app::state::types::Origin) -> Self { self.origin = v; self }
|
||||
/// Set the lsp_manager.
|
||||
#[allow(dead_code)]
|
||||
pub fn lsp_manager(mut self, v: Arc<Mutex<crate::app::lsp::LspManager>>) -> Self { self.lsp_manager = v; self }
|
||||
/// Set the workflow-level findings sharing Arc (for subagent-to-subagent
|
||||
/// communication within a workflow run).
|
||||
pub fn workflow_findings(mut self, v: Option<Arc<Mutex<Vec<String>>>>) -> Self { self.workflow_findings = v; self }
|
||||
@@ -134,7 +128,6 @@ impl ToolCtxBuilder {
|
||||
workspaces: self.workspaces,
|
||||
session_dir: self.session_dir,
|
||||
memory_dir: self.memory_dir,
|
||||
_download_dir: self.download_dir,
|
||||
worktrees_dir: self.worktrees_dir,
|
||||
dir_cache: self.dir_cache,
|
||||
origin: self.origin,
|
||||
@@ -228,7 +221,6 @@ pub fn tool_defs(tools: &[Box<dyn Tool>]) -> Vec<crate::dto::provider::request::
|
||||
/// Return: the canonical absolute path, or an error if the workspace index is invalid
|
||||
/// or the resolved path falls outside all workspace roots.
|
||||
pub fn resolve_path(workspaces: &[PathBuf], rel: &str) -> Result<PathBuf> {
|
||||
let _parts: Vec<&str> = rel.splitn(2, '/').collect();
|
||||
let (ws_idx, path) = if rel.starts_with('[') {
|
||||
let close = rel.find(']').ok_or_else(|| anyhow::anyhow!("invalid workspace prefix"))?;
|
||||
let idx: usize = rel[1..close].parse().map_err(|_| anyhow::anyhow!("invalid workspace index"))?;
|
||||
|
||||
+3
-3
@@ -39,10 +39,10 @@ impl Tool for PlanEnter {
|
||||
///
|
||||
/// Return: fixed acknowledgement string on success; error if either arg is missing.
|
||||
fn run(&self, _ctx: &ToolCtx, args: &Value) -> Result<String> {
|
||||
let _plan = args.get("plan")
|
||||
let _ = args.get("plan")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow!("missing required argument: plan"))?;
|
||||
let _sign_off = args.get("sign_off")
|
||||
let _ = args.get("sign_off")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow!("missing required argument: sign_off"))?;
|
||||
Ok("plan recorded".to_string())
|
||||
@@ -78,7 +78,7 @@ impl Tool for PlanReady {
|
||||
///
|
||||
/// Return: fixed "ready to execute" string on success; error if `confirmation` is missing.
|
||||
fn run(&self, _ctx: &ToolCtx, args: &Value) -> Result<String> {
|
||||
let _confirmation = args.get("confirmation")
|
||||
let _ = args.get("confirmation")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow!("missing required argument: confirmation"))?;
|
||||
Ok("ready to execute".to_string())
|
||||
|
||||
+2
-4
@@ -63,8 +63,7 @@ impl Tool for Bash {
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow!("missing required argument: command"))?
|
||||
.to_string();
|
||||
let _description = args.get("description").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let timeout_ms = args.get("timeout").and_then(|v| v.as_u64()).unwrap_or(120000).min(600000);
|
||||
let timeout_ms = args.get("timeout").and_then(|v| v.as_u64()).unwrap_or(120_000).min(600_000);
|
||||
// Only gate destructive git operations; credential reads are allowed
|
||||
// locally since the AI needs access, and the real threat is committing
|
||||
// secrets to a public repo (handled by git pre-commit hooks / user).
|
||||
@@ -100,9 +99,8 @@ impl Tool for Bash {
|
||||
} else {
|
||||
format!("{}\n\nExit code: 0 ({:.2}s)", trimmed, elapsed)
|
||||
});
|
||||
} else {
|
||||
return Ok(format!("{}\n\nExit code: {} ({:.2}s)", trimmed, status.code().unwrap_or(-1), elapsed));
|
||||
}
|
||||
return Ok(format!("{}\n\nExit code: {} ({:.2}s)", trimmed, status.code().unwrap_or(-1), elapsed));
|
||||
}
|
||||
Ok(None) => {
|
||||
if start.elapsed() > timeout {
|
||||
|
||||
+2
-2
@@ -656,8 +656,8 @@ fn render_overlay(
|
||||
})
|
||||
.unwrap_or((0, 0, 0, 0));
|
||||
let elapsed_ms = chrono::Utc::now().timestamp_millis().saturating_sub(session_start);
|
||||
let hours = elapsed_ms / 3600000;
|
||||
let minutes = (elapsed_ms % 3600000) / 60000;
|
||||
let hours = elapsed_ms / 3_600_000;
|
||||
let minutes = (elapsed_ms % 3_600_000) / 60_000;
|
||||
let seconds = (elapsed_ms % 60000) / 1000;
|
||||
let total_tokens = tokens_in.saturating_add(tokens_out);
|
||||
let self_learning_total = review_tokens;
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ pub fn draw_status_bar(frame: &mut Frame, area: Rect, state: &crate::app::state:
|
||||
let spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
|
||||
// ── Agent status badge ────────────────────────────────────────────────
|
||||
let (status_text, status_bg, status_fg) = if state.turn_in_flight() {
|
||||
let (status_text, badge_bg, status_fg) = if state.turn_in_flight() {
|
||||
let f = spinner_frames[(state.misc.tick_count as usize / 2) % spinner_frames.len()];
|
||||
(format!(" {} PROG ", f), Theme::MODE_YOLO, Theme::BG)
|
||||
} else if state.misc.api_connected {
|
||||
@@ -38,7 +38,7 @@ pub fn draw_status_bar(frame: &mut Frame, area: Rect, state: &crate::app::state:
|
||||
status_text,
|
||||
Style::default()
|
||||
.fg(status_fg)
|
||||
.bg(status_bg)
|
||||
.bg(badge_bg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user