feat(trash): MCP delete tools soft-delete via trash; add delete_task/project/milestone
This commit is contained in:
@@ -21,6 +21,7 @@ from datetime import datetime, timedelta, timezone
|
|||||||
|
|
||||||
from fabledassistant.mcp._context import current_user_id
|
from fabledassistant.mcp._context import current_user_id
|
||||||
from fabledassistant.services import events as events_svc
|
from fabledassistant.services import events as events_svc
|
||||||
|
from fabledassistant.services import trash as trash_svc
|
||||||
|
|
||||||
|
|
||||||
def _combine(start_date: str, start_time: str) -> datetime:
|
def _combine(start_date: str, start_time: str) -> datetime:
|
||||||
@@ -149,14 +150,14 @@ async def update_event(
|
|||||||
return event.to_dict()
|
return event.to_dict()
|
||||||
|
|
||||||
|
|
||||||
async def delete_event(event_id: int) -> str:
|
async def delete_event(event_id: int) -> dict:
|
||||||
"""Permanently delete a calendar event by ID. Cannot be undone."""
|
"""Move a calendar event to the trash (recoverable). Restore via restore(batch_id)."""
|
||||||
uid = current_user_id()
|
uid = current_user_id()
|
||||||
existing = await events_svc.get_event(uid, event_id)
|
batch = await trash_svc.delete(uid, "event", event_id)
|
||||||
if existing is None:
|
if batch is None:
|
||||||
raise ValueError(f"event {event_id} not found")
|
raise ValueError(f"event {event_id} not found")
|
||||||
await events_svc.delete_event(uid, event_id)
|
return {"deleted_batch_id": batch,
|
||||||
return f"Event {event_id} deleted."
|
"message": f"Event {event_id} moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
def register(mcp) -> None:
|
def register(mcp) -> None:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from fabledassistant.mcp._context import current_user_id
|
from fabledassistant.mcp._context import current_user_id
|
||||||
from fabledassistant.services import milestones as milestones_svc
|
from fabledassistant.services import milestones as milestones_svc
|
||||||
|
from fabledassistant.services import trash as trash_svc
|
||||||
|
|
||||||
|
|
||||||
async def list_milestones(project_id: int) -> dict:
|
async def list_milestones(project_id: int) -> dict:
|
||||||
@@ -86,10 +87,22 @@ async def update_milestone(
|
|||||||
return milestone.to_dict()
|
return milestone.to_dict()
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_milestone(milestone_id: int) -> dict:
|
||||||
|
"""Move a milestone to the trash (recoverable). Its tasks go with it as one batch.
|
||||||
|
Restore via restore(batch_id)."""
|
||||||
|
uid = current_user_id()
|
||||||
|
batch = await trash_svc.delete(uid, "milestone", milestone_id)
|
||||||
|
if batch is None:
|
||||||
|
raise ValueError(f"milestone {milestone_id} not found")
|
||||||
|
return {"deleted_batch_id": batch,
|
||||||
|
"message": f"Milestone {milestone_id} + its tasks moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
def register(mcp) -> None:
|
def register(mcp) -> None:
|
||||||
for fn in (
|
for fn in (
|
||||||
list_milestones,
|
list_milestones,
|
||||||
create_milestone,
|
create_milestone,
|
||||||
update_milestone,
|
update_milestone,
|
||||||
|
delete_milestone,
|
||||||
):
|
):
|
||||||
mcp.tool(name=fn.__name__)(fn)
|
mcp.tool(name=fn.__name__)(fn)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from fabledassistant.mcp._context import current_user_id
|
from fabledassistant.mcp._context import current_user_id
|
||||||
from fabledassistant.services import notes as notes_svc
|
from fabledassistant.services import notes as notes_svc
|
||||||
|
from fabledassistant.services import trash as trash_svc
|
||||||
|
|
||||||
|
|
||||||
async def list_notes(
|
async def list_notes(
|
||||||
@@ -113,13 +114,14 @@ async def update_note(
|
|||||||
return note.to_dict()
|
return note.to_dict()
|
||||||
|
|
||||||
|
|
||||||
async def delete_note(note_id: int) -> str:
|
async def delete_note(note_id: int) -> dict:
|
||||||
"""Permanently delete a Scribe note by ID. This cannot be undone."""
|
"""Move a Scribe note to the trash (recoverable). Restore via restore(batch_id)."""
|
||||||
uid = current_user_id()
|
uid = current_user_id()
|
||||||
deleted = await notes_svc.delete_note(uid, note_id)
|
batch = await trash_svc.delete(uid, "note", note_id)
|
||||||
if not deleted:
|
if batch is None:
|
||||||
raise ValueError(f"note {note_id} not found")
|
raise ValueError(f"note {note_id} not found")
|
||||||
return f"Note {note_id} deleted."
|
return {"deleted_batch_id": batch,
|
||||||
|
"message": f"Note {note_id} moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
def register(mcp) -> None:
|
def register(mcp) -> None:
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from fabledassistant.mcp._context import current_user_id
|
|||||||
from fabledassistant.services import milestones as milestones_svc
|
from fabledassistant.services import milestones as milestones_svc
|
||||||
from fabledassistant.services import projects as projects_svc
|
from fabledassistant.services import projects as projects_svc
|
||||||
from fabledassistant.services import rulebooks as rulebooks_svc
|
from fabledassistant.services import rulebooks as rulebooks_svc
|
||||||
|
from fabledassistant.services import trash as trash_svc
|
||||||
|
|
||||||
|
|
||||||
async def list_projects() -> dict:
|
async def list_projects() -> dict:
|
||||||
@@ -121,11 +122,23 @@ async def update_project(
|
|||||||
return project.to_dict()
|
return project.to_dict()
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_project(project_id: int) -> dict:
|
||||||
|
"""Move a project to the trash (recoverable). Its milestones, tasks, and notes
|
||||||
|
go with it as one batch. Restore via restore(batch_id)."""
|
||||||
|
uid = current_user_id()
|
||||||
|
batch = await trash_svc.delete(uid, "project", project_id)
|
||||||
|
if batch is None:
|
||||||
|
raise ValueError(f"project {project_id} not found")
|
||||||
|
return {"deleted_batch_id": batch,
|
||||||
|
"message": f"Project {project_id} + its contents moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
def register(mcp) -> None:
|
def register(mcp) -> None:
|
||||||
for fn in (
|
for fn in (
|
||||||
list_projects,
|
list_projects,
|
||||||
get_project,
|
get_project,
|
||||||
create_project,
|
create_project,
|
||||||
update_project,
|
update_project,
|
||||||
|
delete_project,
|
||||||
):
|
):
|
||||||
mcp.tool(name=fn.__name__)(fn)
|
mcp.tool(name=fn.__name__)(fn)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from fabledassistant.mcp._context import current_user_id
|
from fabledassistant.mcp._context import current_user_id
|
||||||
from fabledassistant.services import rulebooks as rulebooks_svc
|
from fabledassistant.services import rulebooks as rulebooks_svc
|
||||||
|
from fabledassistant.services import trash as trash_svc
|
||||||
|
|
||||||
|
|
||||||
# ── Rulebook CRUD ───────────────────────────────────────────────────────
|
# ── Rulebook CRUD ───────────────────────────────────────────────────────
|
||||||
@@ -90,8 +91,9 @@ async def delete_rulebook(rulebook_id: int, confirmed: bool = False) -> dict:
|
|||||||
),
|
),
|
||||||
"confirmed_required": True,
|
"confirmed_required": True,
|
||||||
}
|
}
|
||||||
await rulebooks_svc.delete_rulebook(rulebook_id, uid)
|
batch = await trash_svc.delete(uid, "rulebook", rulebook_id)
|
||||||
return {"deleted": rulebook_id}
|
return {"deleted": rulebook_id, "deleted_batch_id": batch,
|
||||||
|
"message": f"Moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
# ── Topic CRUD ─────────────────────────────────────────────────────────
|
# ── Topic CRUD ─────────────────────────────────────────────────────────
|
||||||
@@ -159,8 +161,9 @@ async def delete_topic(topic_id: int, confirmed: bool = False) -> dict:
|
|||||||
),
|
),
|
||||||
"confirmed_required": True,
|
"confirmed_required": True,
|
||||||
}
|
}
|
||||||
await rulebooks_svc.delete_topic(topic_id, uid)
|
batch = await trash_svc.delete(uid, "topic", topic_id)
|
||||||
return {"deleted": topic_id}
|
return {"deleted": topic_id, "deleted_batch_id": batch,
|
||||||
|
"message": f"Moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
# ── Rule CRUD ──────────────────────────────────────────────────────────
|
# ── Rule CRUD ──────────────────────────────────────────────────────────
|
||||||
@@ -266,8 +269,9 @@ async def delete_rule(rule_id: int, confirmed: bool = False) -> dict:
|
|||||||
),
|
),
|
||||||
"confirmed_required": True,
|
"confirmed_required": True,
|
||||||
}
|
}
|
||||||
await rulebooks_svc.delete_rule(rule_id, uid)
|
batch = await trash_svc.delete(uid, "rule", rule_id)
|
||||||
return {"deleted": rule_id}
|
return {"deleted": rule_id, "deleted_batch_id": batch,
|
||||||
|
"message": f"Moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
# ── Subscriptions ──────────────────────────────────────────────────────
|
# ── Subscriptions ──────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from fabledassistant.services import notes as notes_svc
|
|||||||
from fabledassistant.services import planning as planning_svc
|
from fabledassistant.services import planning as planning_svc
|
||||||
from fabledassistant.services import rulebooks as rulebooks_svc
|
from fabledassistant.services import rulebooks as rulebooks_svc
|
||||||
from fabledassistant.services import task_logs as task_logs_svc
|
from fabledassistant.services import task_logs as task_logs_svc
|
||||||
|
from fabledassistant.services import trash as trash_svc
|
||||||
|
|
||||||
|
|
||||||
async def list_tasks(
|
async def list_tasks(
|
||||||
@@ -197,6 +198,17 @@ async def start_planning(project_id: int, title: str) -> dict:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_task(task_id: int) -> dict:
|
||||||
|
"""Move a Scribe task (or plan) to the trash (recoverable). Sub-tasks go with it.
|
||||||
|
Restore via restore(batch_id)."""
|
||||||
|
uid = current_user_id()
|
||||||
|
batch = await trash_svc.delete(uid, "task", task_id)
|
||||||
|
if batch is None:
|
||||||
|
raise ValueError(f"task {task_id} not found")
|
||||||
|
return {"deleted_batch_id": batch,
|
||||||
|
"message": f"Task {task_id} moved to trash. Restore with restore('{batch}')."}
|
||||||
|
|
||||||
|
|
||||||
def register(mcp) -> None:
|
def register(mcp) -> None:
|
||||||
for fn in (
|
for fn in (
|
||||||
list_tasks,
|
list_tasks,
|
||||||
@@ -205,5 +217,6 @@ def register(mcp) -> None:
|
|||||||
update_task,
|
update_task,
|
||||||
add_task_log,
|
add_task_log,
|
||||||
start_planning,
|
start_planning,
|
||||||
|
delete_task,
|
||||||
):
|
):
|
||||||
mcp.tool(name=fn.__name__)(fn)
|
mcp.tool(name=fn.__name__)(fn)
|
||||||
|
|||||||
Reference in New Issue
Block a user