fix: add --all to cargo fmt in script/test and autoformat codebase
cargo fmt without --all fails with "Failed to find targets" in workspace repos. This was blocking every story's gates. Also ran cargo fmt --all to fix all existing formatting issues. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+115
-109
@@ -5,7 +5,7 @@ use crate::http::context::AppContext;
|
||||
use crate::http::settings::get_editor_command_from_store;
|
||||
use crate::slog_warn;
|
||||
use crate::worktree;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub(super) async fn tool_start_agent(args: &Value, ctx: &AppContext) -> Result<String, String> {
|
||||
let story_id = args
|
||||
@@ -72,28 +72,32 @@ pub(super) async fn tool_stop_agent(args: &Value, ctx: &AppContext) -> Result<St
|
||||
.stop_agent(&project_root, story_id, agent_name)
|
||||
.await?;
|
||||
|
||||
Ok(format!("Agent '{agent_name}' for story '{story_id}' stopped."))
|
||||
Ok(format!(
|
||||
"Agent '{agent_name}' for story '{story_id}' stopped."
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) fn tool_list_agents(ctx: &AppContext) -> Result<String, String> {
|
||||
let project_root = ctx.agents.get_project_root(&ctx.state).ok();
|
||||
let agents = ctx.agents.list_agents()?;
|
||||
serde_json::to_string_pretty(&json!(agents
|
||||
.iter()
|
||||
.filter(|a| {
|
||||
project_root
|
||||
.as_deref()
|
||||
.map(|root| !crate::http::agents::story_is_archived(root, &a.story_id))
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.map(|a| json!({
|
||||
"story_id": a.story_id,
|
||||
"agent_name": a.agent_name,
|
||||
"status": a.status.to_string(),
|
||||
"session_id": a.session_id,
|
||||
"worktree_path": a.worktree_path,
|
||||
}))
|
||||
.collect::<Vec<_>>()))
|
||||
serde_json::to_string_pretty(&json!(
|
||||
agents
|
||||
.iter()
|
||||
.filter(|a| {
|
||||
project_root
|
||||
.as_deref()
|
||||
.map(|root| !crate::http::agents::story_is_archived(root, &a.story_id))
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.map(|a| json!({
|
||||
"story_id": a.story_id,
|
||||
"agent_name": a.agent_name,
|
||||
"status": a.status.to_string(),
|
||||
"session_id": a.session_id,
|
||||
"worktree_path": a.worktree_path,
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
))
|
||||
.map_err(|e| format!("Serialization error: {e}"))
|
||||
}
|
||||
|
||||
@@ -124,16 +128,12 @@ pub(super) async fn tool_get_agent_output(
|
||||
let project_root = ctx.agents.get_project_root(&ctx.state)?;
|
||||
|
||||
// Collect all matching log files, oldest first.
|
||||
let log_files =
|
||||
agent_log::list_story_log_files(&project_root, story_id, agent_name_filter);
|
||||
let log_files = agent_log::list_story_log_files(&project_root, story_id, agent_name_filter);
|
||||
|
||||
let mut all_lines: Vec<String> = Vec::new();
|
||||
|
||||
for path in &log_files {
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("?");
|
||||
let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("?");
|
||||
all_lines.push(format!("=== {} ===", file_name.trim_end_matches(".log")));
|
||||
match agent_log::read_log_as_readable_lines(path) {
|
||||
Ok(lines) => all_lines.extend(lines),
|
||||
@@ -156,8 +156,7 @@ pub(super) async fn tool_get_agent_output(
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
for event in &live_events {
|
||||
if let Ok(event_value) = serde_json::to_value(event)
|
||||
&& let Some(line) =
|
||||
agent_log::format_log_entry_as_text(&now, &event_value)
|
||||
&& let Some(line) = agent_log::format_log_entry_as_text(&now, &event_value)
|
||||
{
|
||||
all_lines.push(line);
|
||||
}
|
||||
@@ -201,8 +200,7 @@ pub(super) fn tool_get_agent_config(ctx: &AppContext) -> Result<String, String>
|
||||
|
||||
// Collect available (idle) agent names across all stages so the caller can
|
||||
// see at a glance which agents are free to start (story 190).
|
||||
let mut available_names: std::collections::HashSet<String> =
|
||||
std::collections::HashSet::new();
|
||||
let mut available_names: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
for stage in &[
|
||||
PipelineStage::Coder,
|
||||
PipelineStage::Qa,
|
||||
@@ -214,19 +212,21 @@ pub(super) fn tool_get_agent_config(ctx: &AppContext) -> Result<String, String>
|
||||
}
|
||||
}
|
||||
|
||||
serde_json::to_string_pretty(&json!(config
|
||||
.agent
|
||||
.iter()
|
||||
.map(|a| json!({
|
||||
"name": a.name,
|
||||
"role": a.role,
|
||||
"model": a.model,
|
||||
"allowed_tools": a.allowed_tools,
|
||||
"max_turns": a.max_turns,
|
||||
"max_budget_usd": a.max_budget_usd,
|
||||
"available": available_names.contains(&a.name),
|
||||
}))
|
||||
.collect::<Vec<_>>()))
|
||||
serde_json::to_string_pretty(&json!(
|
||||
config
|
||||
.agent
|
||||
.iter()
|
||||
.map(|a| json!({
|
||||
"name": a.name,
|
||||
"role": a.role,
|
||||
"model": a.model,
|
||||
"allowed_tools": a.allowed_tools,
|
||||
"max_turns": a.max_turns,
|
||||
"max_budget_usd": a.max_budget_usd,
|
||||
"available": available_names.contains(&a.name),
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
))
|
||||
.map_err(|e| format!("Serialization error: {e}"))
|
||||
}
|
||||
|
||||
@@ -254,11 +254,13 @@ pub(super) async fn tool_wait_for_agent(args: &Value, ctx: &AppContext) -> Resul
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let completion = info.completion.as_ref().map(|r| json!({
|
||||
"summary": r.summary,
|
||||
"gates_passed": r.gates_passed,
|
||||
"gate_output": r.gate_output,
|
||||
}));
|
||||
let completion = info.completion.as_ref().map(|r| {
|
||||
json!({
|
||||
"summary": r.summary,
|
||||
"gates_passed": r.gates_passed,
|
||||
"gate_output": r.gate_output,
|
||||
})
|
||||
});
|
||||
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"story_id": info.story_id,
|
||||
@@ -295,13 +297,15 @@ pub(super) fn tool_list_worktrees(ctx: &AppContext) -> Result<String, String> {
|
||||
let project_root = ctx.agents.get_project_root(&ctx.state)?;
|
||||
let entries = worktree::list_worktrees(&project_root)?;
|
||||
|
||||
serde_json::to_string_pretty(&json!(entries
|
||||
.iter()
|
||||
.map(|e| json!({
|
||||
"story_id": e.story_id,
|
||||
"path": e.path.to_string_lossy(),
|
||||
}))
|
||||
.collect::<Vec<_>>()))
|
||||
serde_json::to_string_pretty(&json!(
|
||||
entries
|
||||
.iter()
|
||||
.map(|e| json!({
|
||||
"story_id": e.story_id,
|
||||
"path": e.path.to_string_lossy(),
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
))
|
||||
.map_err(|e| format!("Serialization error: {e}"))
|
||||
}
|
||||
|
||||
@@ -332,7 +336,10 @@ pub(super) fn tool_get_editor_command(args: &Value, ctx: &AppContext) -> Result<
|
||||
|
||||
/// Run `git log <base>..HEAD --oneline` in the worktree and return the commit
|
||||
/// summaries, or `None` if git is unavailable or there are no new commits.
|
||||
pub(super) async fn get_worktree_commits(worktree_path: &str, base_branch: &str) -> Option<Vec<String>> {
|
||||
pub(super) async fn get_worktree_commits(
|
||||
worktree_path: &str,
|
||||
base_branch: &str,
|
||||
) -> Option<Vec<String>> {
|
||||
let wt = worktree_path.to_string();
|
||||
let base = base_branch.to_string();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
@@ -382,7 +389,11 @@ mod tests {
|
||||
let result = tool_get_agent_config(&ctx).unwrap();
|
||||
let parsed: Vec<Value> = serde_json::from_str(&result).unwrap();
|
||||
// Default config contains one agent entry with default values
|
||||
assert_eq!(parsed.len(), 1, "default config should have one fallback agent");
|
||||
assert_eq!(
|
||||
parsed.len(),
|
||||
1,
|
||||
"default config should have one fallback agent"
|
||||
);
|
||||
assert!(parsed[0].get("name").is_some());
|
||||
assert!(parsed[0].get("role").is_some());
|
||||
}
|
||||
@@ -401,12 +412,10 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
// No agent registered, no log file → returns "no log files found" message
|
||||
let result = tool_get_agent_output(
|
||||
&json!({"story_id": "99_nope", "agent_name": "bot"}),
|
||||
&ctx,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let result =
|
||||
tool_get_agent_output(&json!({"story_id": "99_nope", "agent_name": "bot"}), &ctx)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.contains("No log files found"),
|
||||
"expected 'No log files found' message: {result}"
|
||||
@@ -418,12 +427,9 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
// No agent_name provided — should succeed (no error)
|
||||
let result = tool_get_agent_output(
|
||||
&json!({"story_id": "99_nope"}),
|
||||
&ctx,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let result = tool_get_agent_output(&json!({"story_id": "99_nope"}), &ctx)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(result.contains("No log files found"));
|
||||
}
|
||||
|
||||
@@ -440,13 +446,8 @@ mod tests {
|
||||
.set("project_root", json!(tmp.path().to_string_lossy().as_ref()));
|
||||
|
||||
// Write a log file
|
||||
let mut writer = AgentLogWriter::new(
|
||||
tmp.path(),
|
||||
"42_story_foo",
|
||||
"coder-1",
|
||||
"sess-test",
|
||||
)
|
||||
.unwrap();
|
||||
let mut writer =
|
||||
AgentLogWriter::new(tmp.path(), "42_story_foo", "coder-1", "sess-test").unwrap();
|
||||
writer
|
||||
.write_event(&AgentEvent::Output {
|
||||
story_id: "42_story_foo".to_string(),
|
||||
@@ -488,13 +489,8 @@ mod tests {
|
||||
ctx.store
|
||||
.set("project_root", json!(tmp.path().to_string_lossy().as_ref()));
|
||||
|
||||
let mut writer = AgentLogWriter::new(
|
||||
tmp.path(),
|
||||
"42_story_bar",
|
||||
"coder-1",
|
||||
"sess-tail",
|
||||
)
|
||||
.unwrap();
|
||||
let mut writer =
|
||||
AgentLogWriter::new(tmp.path(), "42_story_bar", "coder-1", "sess-tail").unwrap();
|
||||
for i in 0..10 {
|
||||
writer
|
||||
.write_event(&AgentEvent::Output {
|
||||
@@ -514,8 +510,14 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// Should contain "line 7", "line 8", "line 9" but NOT "line 0"
|
||||
assert!(result.contains("line 9"), "should contain last line: {result}");
|
||||
assert!(!result.contains("line 0"), "should not contain early lines: {result}");
|
||||
assert!(
|
||||
result.contains("line 9"),
|
||||
"should contain last line: {result}"
|
||||
);
|
||||
assert!(
|
||||
!result.contains("line 0"),
|
||||
"should not contain early lines: {result}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -529,13 +531,8 @@ mod tests {
|
||||
ctx.store
|
||||
.set("project_root", json!(tmp.path().to_string_lossy().as_ref()));
|
||||
|
||||
let mut writer = AgentLogWriter::new(
|
||||
tmp.path(),
|
||||
"42_story_baz",
|
||||
"coder-1",
|
||||
"sess-filter",
|
||||
)
|
||||
.unwrap();
|
||||
let mut writer =
|
||||
AgentLogWriter::new(tmp.path(), "42_story_baz", "coder-1", "sess-filter").unwrap();
|
||||
writer
|
||||
.write_event(&AgentEvent::Output {
|
||||
story_id: "42_story_baz".to_string(),
|
||||
@@ -559,8 +556,14 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(result.contains("needle"), "filter should keep matching lines: {result}");
|
||||
assert!(!result.contains("haystack"), "filter should remove non-matching lines: {result}");
|
||||
assert!(
|
||||
result.contains("needle"),
|
||||
"filter should keep matching lines: {result}"
|
||||
);
|
||||
assert!(
|
||||
!result.contains("haystack"),
|
||||
"filter should remove non-matching lines: {result}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -697,10 +700,7 @@ stage = "coder"
|
||||
fn tool_get_editor_command_no_editor_configured() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
let result = tool_get_editor_command(
|
||||
&json!({"worktree_path": "/some/path"}),
|
||||
&ctx,
|
||||
);
|
||||
let result = tool_get_editor_command(&json!({"worktree_path": "/some/path"}), &ctx);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("No editor configured"));
|
||||
}
|
||||
@@ -725,17 +725,14 @@ stage = "coder"
|
||||
let ctx = test_ctx(tmp.path());
|
||||
ctx.store.set("editor_command", json!("code"));
|
||||
|
||||
let result = tool_get_editor_command(
|
||||
&json!({"worktree_path": "/path/to/worktree"}),
|
||||
&ctx,
|
||||
)
|
||||
.unwrap();
|
||||
let result =
|
||||
tool_get_editor_command(&json!({"worktree_path": "/path/to/worktree"}), &ctx).unwrap();
|
||||
assert_eq!(result, "code /path/to/worktree");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_editor_command_in_tools_list() {
|
||||
use super::super::{handle_tools_list};
|
||||
use super::super::handle_tools_list;
|
||||
let resp = handle_tools_list(Some(json!(1)));
|
||||
let tools = resp.result.unwrap()["tools"].as_array().unwrap().clone();
|
||||
let tool = tools.iter().find(|t| t["name"] == "get_editor_command");
|
||||
@@ -769,9 +766,11 @@ stage = "coder"
|
||||
async fn wait_for_agent_tool_nonexistent_agent_returns_error() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
let result =
|
||||
tool_wait_for_agent(&json!({"story_id": "99_nope", "agent_name": "bot", "timeout_ms": 50}), &ctx)
|
||||
.await;
|
||||
let result = tool_wait_for_agent(
|
||||
&json!({"story_id": "99_nope", "agent_name": "bot", "timeout_ms": 50}),
|
||||
&ctx,
|
||||
)
|
||||
.await;
|
||||
// No agent registered — should error
|
||||
assert!(result.is_err());
|
||||
}
|
||||
@@ -802,13 +801,19 @@ stage = "coder"
|
||||
|
||||
#[test]
|
||||
fn wait_for_agent_tool_in_list() {
|
||||
use super::super::{handle_tools_list};
|
||||
use super::super::handle_tools_list;
|
||||
let resp = handle_tools_list(Some(json!(1)));
|
||||
let tools = resp.result.unwrap()["tools"].as_array().unwrap().clone();
|
||||
let wait_tool = tools.iter().find(|t| t["name"] == "wait_for_agent");
|
||||
assert!(wait_tool.is_some(), "wait_for_agent missing from tools list");
|
||||
assert!(
|
||||
wait_tool.is_some(),
|
||||
"wait_for_agent missing from tools list"
|
||||
);
|
||||
let t = wait_tool.unwrap();
|
||||
assert!(t["description"].as_str().unwrap().contains("block") || t["description"].as_str().unwrap().contains("Block"));
|
||||
assert!(
|
||||
t["description"].as_str().unwrap().contains("block")
|
||||
|| t["description"].as_str().unwrap().contains("Block")
|
||||
);
|
||||
let required = t["inputSchema"]["required"].as_array().unwrap();
|
||||
let req_names: Vec<&str> = required.iter().map(|v| v.as_str().unwrap()).collect();
|
||||
assert!(req_names.contains(&"story_id"));
|
||||
@@ -821,7 +826,8 @@ stage = "coder"
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let cov_dir = tmp.path().join(".huskies/coverage");
|
||||
fs::create_dir_all(&cov_dir).unwrap();
|
||||
let json_content = r#"{"data":[{"totals":{"lines":{"count":100,"covered":78,"percent":78.0}}}]}"#;
|
||||
let json_content =
|
||||
r#"{"data":[{"totals":{"lines":{"count":100,"covered":78,"percent":78.0}}}]}"#;
|
||||
fs::write(cov_dir.join("server.json"), json_content).unwrap();
|
||||
|
||||
let pct = read_coverage_percent_from_json(tmp.path());
|
||||
|
||||
Reference in New Issue
Block a user