feat(items): expose item restore via CLI + MCP (TASK-1828) - #734
Merged
Conversation
Adds the agent-facing restore surface so an archived item discovered via
`pad item list --all` can be recovered without dropping to the web UI. The
server already had restore end-to-end (Store.RestoreItem + handleRestoreItem
at POST /items/{ref}/restore, used by the web UI and bulk ops); this wires
the two missing surfaces:
- CLI: `pad item restore <ref>` (cli.Client.RestoreItem → the existing
endpoint, which resolves the ref include-deleted server-side). Mirrors
`pad item delete`'s structured JSON envelope: {ref, title, restored: true}.
- MCP: pad_item action=restore via passThrough(["item","restore"]). Restore
is non-destructive, so it's safe to expose. The action auto-joins the
schema's action enum (derived from the Actions map) and is documented in
the tool description.
Conflict case (slug/invocation_slug reclaimed while archived) is already
handled by handleRestoreItem (409) and surfaced by the client's
handleResponse.
Tests: restore endpoint already covered (handlers_items_test.go); restore
added to the MCP catalog<->cmdhelp bijection + dispatch tests. Child of
BUG-1791 (TASK-1827 shipped in #733).
xarmian
added a commit
that referenced
this pull request
Jun 15, 2026
…#735) With GET now returning soft-deleted items read-only (deleted_at populated, shipped in #733), the detail route can show an archived item instead of a hard 404. Adds the recovery UI: - isArchived derived from item.deleted_at; folded into the existing canEdit derived so every edit affordance disables while archived. - A read-only "Archived" banner at the top of the item view (date via the file's relativeTime helper) with a Restore button -> api.items.restore, then re-fetches the item so the banner clears and editing re-enables. Success/error via the existing toastStore; a 409 reclaimed-slug conflict surfaces verbatim. - restoring in-flight flag; handleRestore is a standalone async function (not an effect) per CONVE-1688 / CONVE-606. No API/client/server change — GET is already ungated (#733) and api.items.restore already existed. Child of BUG-1791 (TASK-1827 in #733, TASK-1828 in #734).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes TASK-1828 (child of BUG-1791). Adds the agent-facing restore surface so an archived item discovered via
pad item list --allcan be recovered without dropping to the web UI.The server already had restore end-to-end —
Store.RestoreItem+handleRestoreItematPOST /items/{ref}/restore, used by the web UI and bulk ops. This wires the two missing surfaces:pad item restore <ref>(internal/cli/client.goRestoreItem→ the existing endpoint, which resolves the ref with include-deleted semantics server-side — the normal resolver 404s on archived items). Mirrorspad item delete's structured JSON envelope:{ref, title, restored: true}.pad_item action=restorefor both transports —passThrough(["item","restore"])for stdio, plus arouteTableentry (POST .../items/{ref}/restore) for the HTTP transport so cloud/HTTP MCP clients work too. Restore is non-destructive (it un-deletes), so it's safe to expose — unlike a hard delete. The action auto-joins the schema's action enum (derived from the Actions map), is documented in the tool description, andrefis listed as required for it.Conflict case (slug /
invocation_slugreclaimed while the item was archived) is already handled byhandleRestoreItem(409) and surfaced cleanly by the CLI client'shandleResponse.Tests
handlers_items_test.go).restoreadded to the catalog ⇄ cmdhelp bijection + dispatch tests (catalog_readonly_test.go).make checkgreen.Part of the BUG-1791 follow-through: TASK-1827 shipped in #733; TASK-1829 (web direct-access recovery) next.