use super::{get_bool, get_string, make_tool_with_prompts};
use crate::config::{AutoAdvanceConfig, DependenciesConfig, PhasesConfig, Prompts, StatesConfig};
use crate::db::Database;
use crate::error::ToolError;
use crate::prompts::PromptContext;
use anyhow::Result;
use rmcp::model::Tool;
use serde_json::{Value, json};
pub fn get_tools(prompts: &Prompts, _states_config: &StatesConfig) -> Vec<Tool> {
vec![make_tool_with_prompts(
"claim",
"Commit to working on a task (like adding to a changelist). Fails if: already claimed, deps unsatisfied, or worker lacks required tags. Sets status to timed (working) status.",
json!({
"worker_id": {
"type": "string",
"description": "Worker ID claiming the task"
},
"task": {
"type": "string",
"description": "Task ID to claim"
},
"force": {
"type": "boolean",
"description": "Force claim even if owned by another agent (default: false)"
}
}),
vec!["worker_id", "task"],
prompts,
)]
}
pub fn claim(
db: &Database,
states_config: &StatesConfig,
phases_config: &PhasesConfig,
deps_config: &DependenciesConfig,
auto_advance: &AutoAdvanceConfig,
workflows: &crate::config::workflows::WorkflowsConfig,
args: Value,
) -> Result<Value> {
let worker_id =
get_string(&args, "worker_id").ok_or_else(|| ToolError::missing_field("worker_id"))?;
let task_id = get_string(&args, "task").ok_or_else(|| ToolError::missing_field("task"))?;
let force = get_bool(&args, "force").unwrap_or(false);
let claim_status = states_config
.definitions
.iter()
.find(|(_, def)| def.timed)
.map(|(name, _)| name.clone())
.unwrap_or_else(|| "working".to_string());
let (task, _unblocked, _auto_advanced) = db.update_task_unified(
&task_id,
&worker_id,
None, None, None, Some(claim_status), None, None, None, None, None, None, None, None, force,
states_config,
deps_config,
auto_advance,
)?;
let transition_prompt_list: Vec<String> = {
match db.update_worker_state(&worker_id, Some(&task.status), task.phase.as_deref()) {
Ok((old_status, old_phase)) => {
let ctx = PromptContext::new(
&task.status,
task.phase.as_deref(),
states_config,
phases_config,
);
crate::prompts::get_transition_prompts_with_context(
old_status.as_deref().unwrap_or(""),
old_phase.as_deref(),
&task.status,
task.phase.as_deref(),
workflows,
&ctx,
)
}
Err(_) => vec![],
}
};
let mut response = json!({
"success": true,
"task": {
"id": &task.id,
"title": task.title,
"status": task.status,
"worker_id": task.worker_id,
"claimed_at": task.claimed_at
}
});
if !transition_prompt_list.is_empty() {
if let Value::Object(ref mut map) = response {
map.insert("prompts".to_string(), json!(transition_prompt_list));
}
}
Ok(response)
}