feat: add reminder edit and delete commands
This commit is contained in:
@@ -209,6 +209,20 @@ def gitea_repo(name: str, json_output: bool = typer.Option(False, "--json")):
|
||||
fail(str(exc), json_output)
|
||||
|
||||
|
||||
def run_remindctl(args: List[str]) -> Any:
|
||||
"""Execute the supported Reminders CLI without invoking a shell."""
|
||||
result = subprocess.run(["remindctl", *args], capture_output=True, text=True, check=False)
|
||||
if result.returncode:
|
||||
raise RuntimeError(result.stderr.strip() or result.stdout.strip() or "remindctl command failed")
|
||||
output = result.stdout.strip()
|
||||
if not output:
|
||||
return {}
|
||||
try:
|
||||
return json.loads(output)
|
||||
except json.JSONDecodeError:
|
||||
return {"output": output}
|
||||
|
||||
|
||||
def get_required_device(name: str, json_output: bool = False) -> Device:
|
||||
device = get_device(name)
|
||||
if not device:
|
||||
@@ -2139,6 +2153,56 @@ def macmini_reminders_create(
|
||||
fail(str(exc), json_output)
|
||||
|
||||
|
||||
@macmini_reminders_app.command("edit")
|
||||
def macmini_reminders_edit(
|
||||
reminder_id: str,
|
||||
title: Optional[str] = typer.Option(None, "--title"),
|
||||
list_name: Optional[str] = typer.Option(None, "--list"),
|
||||
due: Optional[str] = typer.Option(None, "--due"),
|
||||
notes: Optional[str] = typer.Option(None, "--notes"),
|
||||
priority: Optional[str] = typer.Option(None, "--priority"),
|
||||
clear_due: bool = typer.Option(False, "--clear-due"),
|
||||
complete: bool = typer.Option(False, "--complete"),
|
||||
incomplete: bool = typer.Option(False, "--incomplete"),
|
||||
json_output: bool = typer.Option(False, "--json"),
|
||||
):
|
||||
"""Edit a reminder by its ID or ID prefix returned by ``reminders list``."""
|
||||
if complete and incomplete:
|
||||
fail("Use only one of --complete or --incomplete", json_output)
|
||||
if not any((title is not None, list_name is not None, due is not None, notes is not None, priority is not None, clear_due, complete, incomplete)):
|
||||
fail("Specify at least one field to edit", json_output)
|
||||
args = ["edit", reminder_id]
|
||||
for flag, value in (("--title", title), ("--list", list_name), ("--due", due), ("--notes", notes), ("--priority", priority)):
|
||||
if value is not None:
|
||||
args.extend([flag, value])
|
||||
if clear_due:
|
||||
args.append("--clear-due")
|
||||
if complete:
|
||||
args.append("--complete")
|
||||
if incomplete:
|
||||
args.append("--incomplete")
|
||||
args.extend(["--json", "--no-input"])
|
||||
try:
|
||||
emit({"ok": True, "source": "remindctl", "result": run_remindctl(args)}, json_output)
|
||||
except Exception as exc:
|
||||
fail(str(exc), json_output)
|
||||
|
||||
|
||||
@macmini_reminders_app.command("delete")
|
||||
def macmini_reminders_delete(
|
||||
reminder_ids: List[str] = typer.Argument(..., help="Reminder IDs or ID prefixes returned by reminders list."),
|
||||
force: bool = typer.Option(False, "--force", help="Required: permanently delete without an interactive prompt."),
|
||||
json_output: bool = typer.Option(False, "--json"),
|
||||
):
|
||||
"""Permanently delete one or more reminders; requires explicit --force."""
|
||||
if not force:
|
||||
fail("Deletion requires --force", json_output)
|
||||
try:
|
||||
emit({"ok": True, "source": "remindctl", "result": run_remindctl(["delete", *reminder_ids, "--force", "--json", "--no-input"])}, json_output)
|
||||
except Exception as exc:
|
||||
fail(str(exc), json_output)
|
||||
|
||||
|
||||
def emit_deco_result(tool: str, result: Dict[str, Any], json_output: bool = False) -> None:
|
||||
emit({"ok": True, "source": "direct", "tool": tool, "result": result}, json_output)
|
||||
|
||||
|
||||
@@ -248,6 +248,42 @@ def test_cli_reminders_no_mcp_tool_call_remaining():
|
||||
assert 'call_macmini_tool("reminders_create"' not in src
|
||||
|
||||
|
||||
def test_cli_reminders_edit_uses_remindctl(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_run(args):
|
||||
captured["args"] = args
|
||||
return {"updated": {"id": "r1", "title": "New title"}}
|
||||
|
||||
monkeypatch.setattr("reyna_cli.cli.run_remindctl", fake_run)
|
||||
res = runner.invoke(app, ["macmini", "reminders", "edit", "r1", "--title", "New title", "--due", "tomorrow", "--json"])
|
||||
|
||||
assert res.exit_code == 0
|
||||
assert captured["args"] == ["edit", "r1", "--title", "New title", "--due", "tomorrow", "--json", "--no-input"]
|
||||
assert json.loads(res.stdout)["source"] == "remindctl"
|
||||
|
||||
|
||||
def test_cli_reminders_delete_requires_force(monkeypatch):
|
||||
res = runner.invoke(app, ["macmini", "reminders", "delete", "r1", "--json"])
|
||||
|
||||
assert res.exit_code == 1
|
||||
assert "--force" in res.stdout
|
||||
|
||||
|
||||
def test_cli_reminders_delete_uses_remindctl_with_force(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
def fake_run(args):
|
||||
captured["args"] = args
|
||||
return {"deleted": ["r1"]}
|
||||
|
||||
monkeypatch.setattr("reyna_cli.cli.run_remindctl", fake_run)
|
||||
res = runner.invoke(app, ["macmini", "reminders", "delete", "r1", "--force", "--json"])
|
||||
|
||||
assert res.exit_code == 0
|
||||
assert captured["args"] == ["delete", "r1", "--force", "--json", "--no-input"]
|
||||
|
||||
|
||||
def test_privacy_host_cli_has_reminders_authorize():
|
||||
res = runner.invoke(app, ["privacy-host", "--help"])
|
||||
assert res.exit_code == 0
|
||||
|
||||
Reference in New Issue
Block a user