Add update_task fields, list_tasks, and update_todo tools
- update_note: extend with status/priority/due_date fields so task attributes can be changed via chat (mark done, set priority, move due date). body is now optional — task field updates work without touching content. - list_tasks: new core tool with status/priority/due_before/due_after/limit filters backed by list_notes(is_task=True). Enables queries like "overdue tasks", "high priority tasks", "what's in progress". - update_todo: new CalDAV tool to modify VTODO summary, due date, description, and priority — follows update_event pattern (modify component, rebuild ical, save). Completes the CalDAV todo CRUD suite. - tools.py: add update_todo import + execute case (type: todo_updated) - llm.py: add list_tasks and update_todo to available actions + guidance - intent.py: routing rules for mark-done/priority/due-date → update_note, overdue/in-progress/high-priority queries → list_tasks, CalDAV todo updates → update_todo - ToolCallCard.vue: tasks list block (linked titles + due + priority badges), todo_updated label, tool-task-priority CSS classes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,10 @@ const label = computed(() => {
|
||||
return "Created note";
|
||||
case "note_updated":
|
||||
return "Updated note";
|
||||
case "tasks":
|
||||
return "Tasks";
|
||||
case "todo_updated":
|
||||
return "Updated todo";
|
||||
case "search":
|
||||
return "Searched notes";
|
||||
case "event":
|
||||
@@ -95,7 +99,7 @@ const calendarList = computed(() => {
|
||||
const todoData = computed(() => {
|
||||
const data = props.toolCall.result.data;
|
||||
const type = props.toolCall.result.type;
|
||||
if (!data || (type !== "todo" && type !== "todo_completed" && type !== "todo_deleted")) return null;
|
||||
if (!data || (type !== "todo" && type !== "todo_completed" && type !== "todo_deleted" && type !== "todo_updated")) return null;
|
||||
return data as { summary: string; due?: string; status?: string };
|
||||
});
|
||||
|
||||
@@ -141,6 +145,19 @@ function formatEventTime(iso: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
const taskList = computed(() => {
|
||||
const data = props.toolCall.result.data;
|
||||
if (!data || props.toolCall.result.type !== "tasks") return null;
|
||||
const results = data.results as Array<{ id: number; title: string; status: string; priority: string; due_date?: string }> | undefined;
|
||||
return results ?? [];
|
||||
});
|
||||
|
||||
const taskCount = computed(() => {
|
||||
const data = props.toolCall.result.data;
|
||||
if (!data || props.toolCall.result.type !== "tasks") return 0;
|
||||
return (data.total as number) ?? 0;
|
||||
});
|
||||
|
||||
const searchResults = computed(() => {
|
||||
const data = props.toolCall.result.data;
|
||||
if (!data || props.toolCall.result.type !== "search") return null;
|
||||
@@ -215,6 +232,17 @@ async function applyTag(tag: string) {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="taskList !== null">
|
||||
<span class="tool-search-info">{{ taskCount }} found</span>
|
||||
<div v-if="taskList.length > 0" class="tool-event-list">
|
||||
<div v-for="(t, i) in taskList.slice(0, 5)" :key="i" class="tool-event-item">
|
||||
<router-link :to="`/tasks/${t.id}`" class="tool-event-item-title">{{ t.title }}</router-link>
|
||||
<span v-if="t.due_date" class="tool-event-item-time">{{ t.due_date }}</span>
|
||||
<span v-if="t.priority && t.priority !== 'none'" class="tool-task-priority" :class="`priority-${t.priority}`">{{ t.priority }}</span>
|
||||
</div>
|
||||
<div v-if="taskList.length > 5" class="tool-event-more">+{{ taskList.length - 5 }} more</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="eventList !== null">
|
||||
<span class="tool-search-info">{{ eventCount }} found</span>
|
||||
<div v-if="eventList.length > 0" class="tool-event-list">
|
||||
@@ -341,6 +369,17 @@ async function applyTag(tag: string) {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.tool-task-priority {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.priority-high { color: var(--color-danger, #e74c3c); }
|
||||
.priority-medium { color: var(--color-warning, #f59e0b); }
|
||||
.priority-low { color: var(--color-text-muted); }
|
||||
.tool-completed {
|
||||
text-decoration: line-through;
|
||||
color: var(--color-success, #2ecc71);
|
||||
|
||||
Reference in New Issue
Block a user