core/tools+cli/display: name the trash in the delete confirm

The box asked "delete path 'x'", which reads as final even now that it is not.
It now says "move path 'x' to the system trash" — and "delete path 'x'
permanently" when the path really is destroyed: an explicit `permanent=true`, a
host that turned `[trash] enabled` off, or a scratch path under the OS temporary
directory. The classifier resolves the path to answer that, and falls back to
the trash wording when path policy will refuse the call anyway.

The pretty line follows: a delete that landed in the trash prints `(to trash)`
instead of `(done)`, so a session's transcript still shows which deletes could
be undone.
This commit is contained in:
2026-10-21 20:23:54 +08:00
parent 17813281d8
commit 2369592473
4 changed files with 46 additions and 3 deletions
+19 -1
View File
@@ -4,6 +4,7 @@ from pathlib import Path
from plyngent.agent import DangerReason
from plyngent.tools.danger import classify_danger
from plyngent.tools.workspace import set_trash_enabled
def _reason(name: str, args: dict[str, object]) -> str | None:
@@ -22,11 +23,28 @@ def _danger(name: str, args: dict[str, object]) -> DangerReason:
def test_classify_delete_and_move() -> None:
assert _reason("delete_path", {"path": "a.txt"}) == "delete path 'a.txt'"
# No bound workspace here, so the confirm falls back to the default wording.
assert _reason("delete_path", {"path": "a.txt"}) == "move path 'a.txt' to the system trash"
assert "recursively" in (_reason("delete_path", {"path": "d", "recursive": True}) or "")
assert "move" in (_reason("move_path", {"src": "a", "dst": "b"}) or "")
def test_classify_permanent_delete() -> None:
assert _reason("delete_path", {"path": "a.txt", "permanent": True}) == "delete path 'a.txt' permanently"
def test_a_scratch_path_is_announced_as_permanent(workspace: object) -> None:
"""The confirm says what will happen: the temp workspace is never trashed."""
del workspace
assert _reason("delete_path", {"path": "a.txt"}) == "delete path 'a.txt' permanently"
def test_the_host_switch_is_announced_as_permanent(workspace: object) -> None:
del workspace
set_trash_enabled(enabled=False)
assert _reason("delete_path", {"path": "a.txt"}) == "delete path 'a.txt' permanently"
def test_classify_copy() -> None:
# Without overwrite flag, copy is not soft-confirmed.
assert _reason("copy_path", {"src": "a", "dst": "b"}) is None