feat: update_milestone tool and workspace milestone refresh
- Add update_milestone LLM tool: accepts project + milestone title to look up, then applies any of title/description/status changes - WorkspaceView SSE watcher now triggers taskPanelRef.reload() on create_milestone and update_milestone success, so milestone groups appear immediately without a manual refresh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,7 +80,7 @@ watch(
|
||||
activeNoteId.value = tc.result.data.id as number;
|
||||
}
|
||||
if (
|
||||
["create_task", "update_task"].includes(tc.function) &&
|
||||
["create_task", "update_task", "create_milestone", "update_milestone"].includes(tc.function) &&
|
||||
tc.status === "success"
|
||||
) {
|
||||
taskPanelRef.value?.reload();
|
||||
|
||||
@@ -436,6 +436,24 @@ _CORE_TOOLS = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "update_milestone",
|
||||
"description": "Update the title, description, or status of an existing milestone.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {"type": "string", "description": "Project title the milestone belongs to"},
|
||||
"milestone": {"type": "string", "description": "Current milestone title to look up"},
|
||||
"title": {"type": "string", "description": "New title (omit to keep current)"},
|
||||
"description": {"type": "string", "description": "New description (omit to keep current)"},
|
||||
"status": {"type": "string", "enum": ["active", "completed", "cancelled"], "description": "New status (omit to keep current)"},
|
||||
},
|
||||
"required": ["project", "milestone"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
@@ -1426,6 +1444,35 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict:
|
||||
)
|
||||
return {"success": True, "type": "milestone", "data": ms.to_dict()}
|
||||
|
||||
elif tool_name == "update_milestone":
|
||||
from fabledassistant.services.projects import get_project_by_title as _gpbt, list_projects as _lp
|
||||
from fabledassistant.services.milestones import get_milestone_by_title as _gmbt, update_milestone as _um
|
||||
project_name = arguments.get("project", "")
|
||||
milestone_name = arguments.get("milestone", "")
|
||||
if not project_name or not milestone_name:
|
||||
return {"success": False, "error": "Both project and milestone are required"}
|
||||
proj = await _gpbt(user_id, project_name)
|
||||
if proj is None:
|
||||
all_p = await _lp(user_id)
|
||||
matches = [p for p in all_p if project_name.lower() in p.title.lower()]
|
||||
proj = matches[0] if matches else None
|
||||
if proj is None:
|
||||
return {"success": False, "error": f"Project '{project_name}' not found"}
|
||||
ms = await _gmbt(user_id, proj.id, milestone_name)
|
||||
if ms is None:
|
||||
return {"success": False, "error": f"Milestone '{milestone_name}' not found in project '{proj.title}'. Use list_milestones to see available milestones."}
|
||||
fields: dict[str, object] = {}
|
||||
if "title" in arguments:
|
||||
fields["title"] = arguments["title"]
|
||||
if "description" in arguments:
|
||||
fields["description"] = arguments["description"]
|
||||
if "status" in arguments:
|
||||
fields["status"] = arguments["status"]
|
||||
if not fields:
|
||||
return {"success": False, "error": "No fields to update — provide at least one of: title, description, status"}
|
||||
updated = await _um(user_id, ms.id, **fields)
|
||||
return {"success": True, "type": "milestone", "data": updated.to_dict()}
|
||||
|
||||
elif tool_name == "list_milestones":
|
||||
from fabledassistant.services.projects import get_project_by_title as _gpbt, list_projects as _lp
|
||||
from fabledassistant.services.milestones import get_project_milestone_summary
|
||||
|
||||
Reference in New Issue
Block a user