Skip to content

Add CLI delete with must-be-empty semantics#22

Merged
StuartMeeks merged 3 commits into
masterfrom
feat/cli-delete
May 30, 2026
Merged

Add CLI delete with must-be-empty semantics#22
StuartMeeks merged 3 commits into
masterfrom
feat/cli-delete

Conversation

@StuartMeeks

Copy link
Copy Markdown
Owner

What

Adds the ability to delete a CLI, settling the cascade question flagged in CLAUDE.md / TODO with must-be-empty semantics.

Behaviour

  • A "Delete CLI" action on the CLI view.
  • Must-be-empty: if the CLI still has visible (non-trashed) snips, the delete is refused with an explanatory notice — the user must clear those snips first.
  • Once empty, deletes after a confirmation dialog.
  • Cleans up the CLI's icon asset and removes any leftover trashed snips that belonged to it (which would otherwise be orphaned, referencing a CLI that no longer exists).
  • Selection falls back to Home after deletion.

Notable bits

  • New IShellInteractions.NotifyAsync (single-button notice) for the "can't delete" message; implemented as a ContentDialog in the App head.
  • Icon cleanup happens after the store save (best-effort), so a failed save can't leave the store referencing a CLI whose icon is already gone.
  • Hardened IconAssetStorage path resolution to stay within <data>/icons/: an untrusted/hand-edited store could previously carry an absolute or ../ IconRef that, via File.Delete, deleted files outside the data directory. Resolution now canonicalises and rejects escaping paths (protects both display and deletion).

Tests

  • ShellViewModelCommandsTests: blocked-when-active, requires-confirmation, removes-CLI-and-trashed-snips + falls-back-to-Home, deletes-icon, no-op-on-Home.
  • New IconAssetStorageTests: save/delete round-trip, .. traversal rejected, absolute path rejected, ResolveAbsolutePath containment.
  • All 120 Core tests pass.

Review

Independent codex exec review across three passes: two findings raised and fixed (icon-delete ordering; icon path traversal), final pass clean.

Verification still owed (Windows)

WinUI head: confirm the "Delete CLI" button shows on the CLI view, the "can't delete — N snips" notice appears when snips exist, and confirmation deletes an empty CLI and returns to Home.

🤖 Generated with Claude Code

StuartMeeks and others added 3 commits May 30, 2026 04:58
A CLI can now be deleted from its view via a "Delete CLI" action. The
rule is must-be-empty: if the CLI still has visible (non-trashed) snips
the delete is refused with an explanatory notice. Once empty it deletes
after confirmation, cleaning up the CLI's icon asset and removing any
leftover trashed snips (which would otherwise be orphaned).

Adds IShellInteractions.NotifyAsync (single-button notice) for the
"can't delete" message, implemented as a ContentDialog in the App head.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses a codex review finding: deleting the icon asset before
SaveAsync meant a failed save would leave the persisted store still
referencing the CLI while its icon was already gone. Persist the removal
first, then clean up the icon as a best-effort post-save side effect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses a codex review finding: IconAssetStorage.ResolveAbsolutePath
combined an untrusted IconRef from the store directly with the base
directory. An absolute path (Path.Combine silently drops the base) or a
`..` traversal could therefore escape icon storage — and since the path
feeds DeleteIconAsync -> File.Delete, a malformed/hand-edited store could
delete arbitrary user files.

Resolution now canonicalises the path and returns null unless it stays
under <data>/icons/, so both display and deletion are confined to
app-managed assets. Adds IconAssetStorageTests covering traversal and
absolute-path rejection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@StuartMeeks StuartMeeks merged commit b2305df into master May 30, 2026
4 checks passed
@StuartMeeks StuartMeeks deleted the feat/cli-delete branch May 30, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant