From 6580d1694265489d616d4de2904cf1eaf313f1bd Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 2 Mar 2026 21:33:13 -0500 Subject: [PATCH] Improve tools reliability and add milestone management to ProjectView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tools.py: - Default tag_mode changed from 'replace' to 'add' — existing tags are preserved unless the user explicitly requests replacement - create_task, create_note, update_note, create_milestone: project and milestone lookup now uses get_by_title with fuzzy fallback; returns a clear error (with a hint to use list_projects/list_milestones) instead of silently creating duplicate projects or milestones via get_or_create - create_task: project/milestone resolved before note creation so a bad project name fails fast without leaving an orphaned task behind - update_note return value now includes item_type, tags, project_id, and an 'updated' summary string so the LLM can confirm what was modified - research_topic stub now logs an error and returns failure instead of silently returning success when reached via wrong code path ProjectView.vue: - Milestone headers now show edit (✎) and delete (✕) action buttons on hover - Edit triggers inline rename input; Enter/blur commits, Escape cancels - Delete opens a confirmation modal clarifying tasks are unlinked not deleted Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/views/ProjectView.vue | 134 +++++++++++++++++++++++++- src/fabledassistant/services/tools.py | 101 +++++++++++++------ 2 files changed, 199 insertions(+), 36 deletions(-) diff --git a/frontend/src/views/ProjectView.vue b/frontend/src/views/ProjectView.vue index 7787ec2..34509a2 100644 --- a/frontend/src/views/ProjectView.vue +++ b/frontend/src/views/ProjectView.vue @@ -65,6 +65,11 @@ const showNewMilestone = ref(false); const newMilestoneTitle = ref(""); const creatingMilestone = ref(false); +// Milestone edit/delete state +const renamingMilestoneId = ref(null); +const renamingMilestoneTitle = ref(""); +const deletingMilestone = ref(null); + // Edit state const editTitle = ref(""); const editDescription = ref(""); @@ -156,6 +161,38 @@ async function createMilestone() { } } +function startRenameMilestone(ms: Milestone) { + renamingMilestoneId.value = ms.id; + renamingMilestoneTitle.value = ms.title; +} + +async function commitRenameMilestone(ms: Milestone) { + const newTitle = renamingMilestoneTitle.value.trim(); + renamingMilestoneId.value = null; + if (!newTitle || newTitle === ms.title) return; + try { + await apiPatch(`/api/projects/${projectId.value}/milestones/${ms.id}`, { title: newTitle }); + await loadMilestones(); + toast.show("Milestone renamed"); + } catch { + toast.show("Failed to rename milestone", "error"); + } +} + +async function confirmDeleteMilestone() { + const ms = deletingMilestone.value; + if (!ms) return; + deletingMilestone.value = null; + try { + await apiDelete(`/api/projects/${projectId.value}/milestones/${ms.id}`); + await loadMilestones(); + await loadTasks(); + toast.show("Milestone deleted"); + } catch { + toast.show("Failed to delete milestone", "error"); + } +} + async function loadTasks() { tasksLoading.value = true; try { @@ -384,13 +421,25 @@ function formatDate(dateStr?: string | null): string {
{{ collapsedMilestones.has(group.milestone.id) ? '▶' : '▼' }} - {{ group.milestone?.title ?? "No Milestone" }} + + + {{ group.milestone?.title ?? "No Milestone" }} {{ group.tasks.length }}
@@ -497,7 +559,24 @@ function formatDate(dateStr?: string | null): string { - + + + + + +