ci: add GitHub Actions workflows with semantic-release auto-versioning

chore: fix all 702 clippy warnings across codebase
- auto-fix 475 via cargo clippy --fix
- fix remaining 227 manually: uninlined_format_args, redundant_closure, match_same_arms,
  underscore_binding, format_push_string, items_after_statements, needless_pass_by_value,
  clone_on_copy, case_sensitive_extension, single_match/let-else, write_with_newline,
  and other clippy lints
This commit is contained in:
asepharyana
2026-07-13 08:12:12 +07:00
parent be921d6836
commit 29a9fae3f6
79 changed files with 826 additions and 904 deletions
+40 -41
View File
@@ -7,6 +7,7 @@
//! bash exfiltration and destructive-pattern detection) so that subagents
//! are not a weaker link than the main agent.
use std::fmt::Write;
use tokio::sync::mpsc;
use crate::dto::chat::message::ChatMessage;
use crate::dto::provider::request::ToolDef;
@@ -143,8 +144,7 @@ fn gate_subagent_tool_call(
let reason = args.get("reason").and_then(|v| v.as_str()).unwrap_or("");
if reason.trim().len() < MIN_REASON_LEN {
return Some(format!(
"{} requires a non-trivial 'reason' (>= {} chars) explaining why",
tool_name, MIN_REASON_LEN,
"{tool_name} requires a non-trivial 'reason' (>= {MIN_REASON_LEN} chars) explaining why",
));
}
}
@@ -157,9 +157,7 @@ fn gate_subagent_tool_call(
let old = args.get("old").and_then(|v| v.as_str()).unwrap_or("");
let new = args.get("new").and_then(|v| v.as_str()).unwrap_or("");
// For edits, scanning old+new together catches stubs in both
return if contains_any(old, STUB_PATTERNS) {
Some("content contains stub/placeholder pattern; production code must be fully implemented".to_string())
} else if contains_any(new, STUB_PATTERNS) {
return if contains_any(old, STUB_PATTERNS) || contains_any(new, STUB_PATTERNS) {
Some("content contains stub/placeholder pattern; production code must be fully implemented".to_string())
} else if contains_any(new, DENIAL_PATTERNS) {
Some("content contains denial/punt pattern; implement properly instead of skipping".to_string())
@@ -202,13 +200,13 @@ fn gate_subagent_tool_call(
if !is_standard {
for pat in EXFIL_PATTERNS {
if cmd.contains(pat) {
return Some(format!("potential data-exfiltration command blocked (matched '{}')", pat));
return Some(format!("potential data-exfiltration command blocked (matched '{pat}')"));
}
}
}
for pat in SENSITIVE_PATH_PATTERNS {
if cmd.contains(pat) {
return Some(format!("refused to read/write sensitive path '{}'", pat));
return Some(format!("refused to read/write sensitive path '{pat}'"));
}
}
let dangerous = ["rm -rf /", "rm -rf --no-preserve-root", "rm -rf ~",
@@ -216,7 +214,7 @@ fn gate_subagent_tool_call(
"chmod -R 000 /", "shutdown ", "poweroff ", "reboot ", "halt "];
for pat in &dangerous {
if cmd.contains(pat) {
return Some(format!("destructive command pattern blocked: {}", pat));
return Some(format!("destructive command pattern blocked: {pat}"));
}
}
if contains_any(cmd, STUB_PATTERNS) {
@@ -251,7 +249,7 @@ fn generate_workspace_tree(roots: &[std::path::PathBuf]) -> String {
let mut out = String::new();
out.push_str("Current Workspace Directory Structure:\n");
for root in roots {
out.push_str(&format!("Root: {}\n", root.display()));
writeln!(out, "Root: {}", root.display()).unwrap();
let walker = ignore::WalkBuilder::new(root)
.hidden(true)
.git_ignore(true)
@@ -261,9 +259,9 @@ fn generate_workspace_tree(roots: &[std::path::PathBuf]) -> String {
let path = entry.path();
if let Ok(rel) = path.strip_prefix(root) {
if rel.as_os_str().is_empty() { continue; }
let is_dir = entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false);
let is_dir = entry.file_type().is_some_and(|ft| ft.is_dir());
let prefix = if is_dir { "[DIR] " } else { " " };
out.push_str(&format!(" {}{}\n", prefix, rel.display()));
writeln!(out, " {}{}", prefix, rel.display()).unwrap();
count += 1;
if count > 1000 {
out.push_str(" ... (truncated)\n");
@@ -291,7 +289,8 @@ fn generate_workspace_tree(roots: &[std::path::PathBuf]) -> String {
///
/// Return: the concatenated text output, or an `anyhow::Error` if the LLM
/// call fails at any step.
pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> anyhow::Result<String> {
#[allow(clippy::too_many_lines)]
pub fn run_subagent(ctx: &SubagentContext, tx: &mpsc::Sender<SubagentEvent>) -> anyhow::Result<String> {
let mut output = String::new();
let mut messages: Vec<ChatMessage> = Vec::new();
@@ -328,10 +327,10 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
// be cancelled from the parent (mirrors main agent behaviour).
if ctx.abort_flag.as_ref().is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst)) {
let _ = tx.blocking_send(SubagentEvent::StepFailed {
_step: step,
_error: "subagent aborted by parent".to_string(),
step,
error: "subagent aborted by parent".to_string(),
});
anyhow::bail!("subagent aborted by parent at step {}", step);
anyhow::bail!("subagent aborted by parent at step {step}");
}
// Use the structured tool-calling API so the LLM can request tools with
@@ -340,10 +339,10 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
Ok(result) => result,
Err(e) => {
let _ = tx.blocking_send(SubagentEvent::StepFailed {
_step: step,
_error: e.to_string(),
step,
error: e.to_string(),
});
anyhow::bail!("subagent call failed at step {}: {}", step, e);
anyhow::bail!("subagent call failed at step {step}: {e}");
}
};
@@ -361,10 +360,10 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
// Check abort flag before each tool execution
if ctx.abort_flag.as_ref().is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst)) {
let _ = tx.blocking_send(SubagentEvent::StepFailed {
_step: step,
_error: "subagent aborted by parent during tool execution".to_string(),
step,
error: "subagent aborted by parent during tool execution".to_string(),
});
anyhow::bail!("subagent aborted by parent during tool call at step {}", step);
anyhow::bail!("subagent aborted by parent during tool call at step {step}");
}
let tool_name = &tool_call.function.name;
@@ -373,28 +372,28 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
let generally_allowed = ctx.allowed_tools.is_empty() || explicitly_allowed;
let _ = tx.blocking_send(SubagentEvent::ToolCall {
_tool: tool_name.clone(),
_args: args.clone(),
tool: tool_name.clone(),
args: args.clone(),
});
// Level 1: allowlist check — is this tool even permitted?
if !generally_allowed {
let msg = format!("tool '{}' not allowed for this subagent", tool_name);
let msg = format!("tool '{tool_name}' not allowed for this subagent");
messages.push(ChatMessage::tool_result(tool_call.id.clone(), msg.clone()));
let _ = tx.blocking_send(SubagentEvent::ToolResult {
_tool: tool_name.clone(),
_output: msg,
tool: tool_name.clone(),
output: msg,
});
continue;
}
// Level 2: risky tool check — risky tools require explicit permission
if tool_is_risky(tool_name) && !explicitly_allowed {
let msg = format!("risky tool '{}' requires explicit permission; not allowed for this subagent", tool_name);
let msg = format!("risky tool '{tool_name}' requires explicit permission; not allowed for this subagent");
messages.push(ChatMessage::tool_result(tool_call.id.clone(), msg.clone()));
let _ = tx.blocking_send(SubagentEvent::ToolResult {
_tool: tool_name.clone(),
_output: msg,
tool: tool_name.clone(),
output: msg,
});
continue;
}
@@ -404,34 +403,34 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
// stub/denial/assumption scanning, bash exfiltration, destructive
// commands, sensitive path reads).
if let Some(block_reason) = gate_subagent_tool_call(tool_name, &args) {
let msg = format!("Blocked by subagent gate: {}", block_reason);
let msg = format!("Blocked by subagent gate: {block_reason}");
messages.push(ChatMessage::tool_result(tool_call.id.clone(), msg.clone()));
let _ = tx.blocking_send(SubagentEvent::ToolResult {
_tool: tool_name.clone(),
_output: msg,
tool: tool_name.clone(),
output: msg,
});
continue;
}
let result = match tools.iter().find(|t| t.name() == tool_name.as_str()) {
Some(tool) => tool.run(&tool_ctx, &args),
None => Err(anyhow::anyhow!("tool '{}' not found", tool_name)),
None => Err(anyhow::anyhow!("tool '{tool_name}' not found")),
};
match result {
Ok(output_text) => {
messages.push(ChatMessage::tool_result(tool_call.id.clone(), output_text.clone()));
let _ = tx.blocking_send(SubagentEvent::ToolResult {
_tool: tool_name.clone(),
_output: output_text,
tool: tool_name.clone(),
output: output_text,
});
}
Err(e) => {
let msg = format!("tool '{}' failed: {}", tool_name, e);
let msg = format!("tool '{tool_name}' failed: {e}");
messages.push(ChatMessage::tool_result(tool_call.id.clone(), msg.clone()));
let _ = tx.blocking_send(SubagentEvent::ToolResult {
_tool: tool_name.clone(),
_output: msg,
tool: tool_name.clone(),
output: msg,
});
}
}
@@ -443,8 +442,8 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
output.push('\n');
}
let _ = tx.blocking_send(SubagentEvent::StepCompleted {
_step: step,
_output: content.clone(),
step,
output: content.clone(),
});
// Break only when we got real content; empty means something went wrong
if !content.is_empty() {
@@ -453,6 +452,6 @@ pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> an
}
}
let _ = tx.blocking_send(SubagentEvent::Completed { _output: output.clone() });
let _ = tx.blocking_send(SubagentEvent::Completed { output: output.clone() });
Ok(output)
}