You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Sub-issue of #138. Sibling of #136 (work-item list), #203 (work-item create), #238 (work-item show).
Command Description
Delete a single Azure Boards work item by ID. Without --destroy, the work item is moved to the Recycle Bin and can be restored via the Azure DevOps web UI. With --destroy, the work item is permanently removed and cannot be recovered. Mirrors az boards work-item delete.
The REST surface is Work Items - Delete (REST 7.1):
Parse the single positional with util.ParseTargetWithDefaultOrganization(ctx, args[0]). scope.Targets[0] is the ID string.
Accepts ID (default org) and ORG:ID forms.
5
No --project flag. The work item is identified solely by the org-scoped positional [ORG:]ID; DeleteWorkItemArgs.Project is never set and the SDK receives nil (org-level REST URL, no /project/ segment).
Overrides the earlier routing-flag design: scope is fully positional under the ORG: scope parser (#322); matches az boards work-item delete (no project parameter).
6
Destructive: requires confirmation unless --yes is provided. Prompt text uses the same wording as the Python: "Are you sure you want to delete this work item?". When --destroy is set, the prompt text changes to "Are you sure you want to permanently destroy this work item? This cannot be undone.".
Mirrors the AzDO Extension's delete_work_item confirmation. On cancellation, return util.ErrCancel.
7
--destroy is an optional bool flag (default false). When set, DeleteWorkItemArgs.Destroy = &true and the REST query param is ?destroy=true.
Matches Python --destroy. Permanent deletion is the explicit-opt-in escalation.
8
Plain output: print Deleted work item to stdout. When --destroy is set, print Permanently deleted work item .
Mirrors the Python's print('Deleted work item {}'.format(id)).
9
JSON output: pass the raw *workitemtracking.WorkItemDelete returned by DeleteWorkItem directly to opts.exporter.Write. No view struct.
The SDK type is the API surface. Symmetric with #203 (create).
10
No table output. Delete returns a single status (the WorkItemDelete envelope), not a work item. The plain text message is the right human-readable surface; --json covers structured needs.
No pre-check that the work item exists. The REST 404 is surfaced via util.FlagErrorWrap.
Saves a round-trip. REST error is already clear.
14
ID must parse as a positive integer. If strconv.Atoi(scope.Targets[0]) fails or returns <= 0, return util.FlagErrorf("work item ID must be a positive integer; got %q", scope.Targets[0]) and do not call the SDK.
Defends against typo'd IDs without a wasted round-trip.
15
No go mod tidy / go mod vendor / scripts/generate_mocks.sh work.
Command Signature
vardeleteCmd=&cobra.Command{
Use: "delete [ORG:]ID",
Aliases: []string{"d", "del", "rm"},
Short: "Deleteaworkitem.",
Long: heredoc.Doc(` Delete a work item by ID. By default the work item is moved to the Recycle Bin and can be restored via the Azure DevOps web UI. Use --destroy to permanently remove the work item; this cannot be undone. `),
Args: cobra.ExactArgs(1),
RunE: func(cmd*cobra.Command, args []string) error {
returnrunDelete(cmd.Context(), opts, args[0])
},
}
Flags
Flag
Type
Default
Required
Description
--yes
bool
false
no
Skip the confirmation prompt.
--destroy
bool
false
no
Permanently delete the work item (bypasses Recycle Bin).
Also add util.AddJSONFlags(cmd, &opts.exporter, []string{"id", "code", "deletedBy", "deletedDate", "message", "name", "project", "type", "url", "resource"}) (every JSON-tagged field of *workitemtracking.WorkItemDelete).
ID Validation
After positional parsing, before the confirmation prompt:
The vendored SDK enforces args.Id != nil — will return ArgumentNilError if missing. No extra validation needed in our wrapper beyond the positive-integer check (Decision 14).
Reference Existing Patterns
internal/cmd/pipelines/variablegroup/delete/delete.go:30-170 — primary reference. Mirrors the Use/Aliases, the confirmation prompt lifecycle, the ios.StopProgressIndicator/ios.StartProgressIndicator dance, the util.ErrCancel return path, and the JSON output split.
Azure DevOps REST API: Work Items - Delete (DELETE, route id 1e4b52db-afe4-4d8b-b1b5-95aa0e13a3b0, API version 7.1-preview.3). Optional destroy query param.
Sub-issue of #138. Sibling of #136 (
work-item list), #203 (work-item create), #238 (work-item show).Command Description
Delete a single Azure Boards work item by ID. Without
--destroy, the work item is moved to the Recycle Bin and can be restored via the Azure DevOps web UI. With--destroy, the work item is permanently removed and cannot be recovered. Mirrorsaz boards work-item delete.The REST surface is Work Items - Delete (REST 7.1):
Response is a
WorkItemDeleteobject (id,code,deletedBy,deletedDate,message,name,project,type,url,resource).Locked Decisions
Use: "delete [ORG:]ID". The work item ID is unique within the organization; a project segment is not required.az boards work-item delete(no project in positional) andinternal/cmd/boards/workitem/show(org-scoped).Aliases: []string{"d", "del", "rm"}.cobra.ExactArgs(1).util.ParseTargetWithDefaultOrganization(ctx, args[0]).scope.Targets[0]is the ID string.ID(default org) andORG:IDforms.--projectflag. The work item is identified solely by the org-scoped positional[ORG:]ID;DeleteWorkItemArgs.Projectis never set and the SDK receivesnil(org-level REST URL, no/project/segment).az boards work-item delete(no project parameter).--yesis provided. Prompt text uses the same wording as the Python:"Are you sure you want to delete this work item?". When--destroyis set, the prompt text changes to"Are you sure you want to permanently destroy this work item? This cannot be undone.".delete_work_itemconfirmation. On cancellation, returnutil.ErrCancel.--destroyis an optional bool flag (defaultfalse). When set,DeleteWorkItemArgs.Destroy = &trueand the REST query param is?destroy=true.--destroy. Permanent deletion is the explicit-opt-in escalation.Deleted work itemto stdout. When--destroyis set, printPermanently deleted work item.print('Deleted work item {}'.format(id)).*workitemtracking.WorkItemDeletereturned byDeleteWorkItemdirectly toopts.exporter.Write. No view struct.#203 (create).WorkItemDeleteenvelope), not a work item. The plain text message is the right human-readable surface;--jsoncovers structured needs.workitemtracking.Client.DeleteWorkItem.vendor/.../v7/workitemtracking/client.go:865(impl) and:61(interface).internal/mocks/workitemtracking_client_mock.go:281-294already mocksDeleteWorkItem.404is surfaced viautil.FlagErrorWrap.strconv.Atoi(scope.Targets[0])fails or returns<= 0, returnutil.FlagErrorf("work item ID must be a positive integer; got %q", scope.Targets[0])and do not call the SDK.go mod tidy/go mod vendor/scripts/generate_mocks.shwork.Command Signature
Flags
--yesboolfalse--destroyboolfalseAlso add
util.AddJSONFlags(cmd, &opts.exporter, []string{"id", "code", "deletedBy", "deletedDate", "message", "name", "project", "type", "url", "resource"})(every JSON-tagged field of*workitemtracking.WorkItemDelete).ID Validation
After positional parsing, before the confirmation prompt:
Confirmation Prompt
When
--yesis not set:JSON Output Contract
Pass the raw
*workitemtracking.WorkItemDeletereturned byDeleteWorkItemtoopts.exporter.Write. No view struct.JSON fields exposed (matching SDK struct tags):
id,code,deletedBy,deletedDate,message,name,project,type,url,resource.Table Output Contract
N/A. On success, print one line to stdout:
When
--destroyis set:Command Wiring
internal/cmd/boards/workitem/delete/delete.gowithpackage delete, factoryfunc NewCmd(ctx util.CmdContext) *cobra.Command.import "github.com/tmeckel/azdo-cli/internal/cmd/boards/workitem/delete"tointernal/cmd/boards/workitem/workitem.go.workitem.gowithcmd.AddCommand(delete.NewCmd(ctx))and extend the group'sExampleblock.API Surface
Vendored SDK call (from
vendor/.../v7/workitemtracking/client.go:865):The vendored SDK enforces
args.Id != nil— will returnArgumentNilErrorif missing. No extra validation needed in our wrapper beyond the positive-integer check (Decision 14).Reference Existing Patterns
internal/cmd/pipelines/variablegroup/delete/delete.go:30-170— primary reference. Mirrors theUse/Aliases, the confirmation prompt lifecycle, theios.StopProgressIndicator/ios.StartProgressIndicatordance, theutil.ErrCancelreturn path, and the JSON output split.internal/cmd/pipelines/folder/delete/delete.go(issue feat: Implementazdo pipelines folder deletecommand #264) — sibling for the simpler destructive shape (no project-references or--all).internal/cmd/boards/workitem/show— sibling for the org-scopedParseTargetWithDefaultOrganizationusage and scope parsing.internal/cmd/boards/workitem/create(feat: Implementazdo boards work-item createcommand #203) — sibling for the JSON Patch output conventions (Decision 9: raw SDK type toopts.exporter.Write).TDD Test Plan
TestNewCmd_deleteUse == "delete [ORG:]ID",Aliases == ["d", "del", "rm"],Args == cobra.ExactArgs(1).Test_runDelete_success_withYes--yes, mockDeleteWorkItemreturns&workitemtracking.WorkItemDelete{Id: ptr(1234), DeletedDate: ptr("2026-06-05T...")}.Deleted work item 1234to stdout;args.Project == nilis passed to the SDK (org-scoped).Test_runDelete_success_destroy--yes --destroy, mock returns same.Permanently deleted work item 1234.args.Destroy == &trueis passed to the SDK.Test_runDelete_success_confirmedtrue.Deleted work item 1234.Test_runDelete_cancelledfalse.util.ErrCancel. The SDK is not called.Test_runDelete_notInteractiveios.CanPrompt() == false, no--yes.util.FlagErrorf("--yes required when not running interactively"). SDK is not called.Test_runDelete_invalidID"abc".util.FlagErrorfwith"work item ID must be a positive integer; got \"abc\"". SDK is not called.Test_runDelete_zeroID"0".util.FlagErrorfwith the same message. SDK is not called.Test_runDelete_negativeID"-5".util.FlagErrorf. SDK is not called.Test_runDelete_orgScopeOnly"1234"(no org). MockedcmdCtx.Config()returns a default org.args.Id == ptr(1234), scope.Organization is the default.Test_runDelete_explicitOrg"myorg/1234".args.Id == ptr(1234), scope.Organization is"myorg".Test_runDelete_APIErrorerrors.New("boom").--yes.Test_runDelete_success_JSON--jsonflag set, mock returns*WorkItemDelete.*WorkItemDelete(assertres.Id == ptr(1234)).Test_runDelete_destroyPromptText--destroy, prompter stubbed to returntrue. Capture the prompt message."permanently destroy"and"cannot be undone".Test_runDelete_missingIDazdo boards work-item deletewith no args.References
vendor/.../v7/workitemtracking/client.go:865(DeleteWorkItem),:898(DeleteWorkItemArgs).vendor/.../v7/workitemtracking/models.go:1096(WorkItemDelete).internal/mocks/workitemtracking_client_mock.go:281-294(DeleteWorkItem).internal/azdo/factory.go:149—ClientFactory().WorkItemTracking(ctx, organization).1e4b52db-afe4-4d8b-b1b5-95aa0e13a3b0, API version7.1-preview.3). Optionaldestroyquery param.azdo boards work-itemcommand group #138.azdo boards work-item listcommand #136 (list), feat: Implementazdo boards work-item createcommand #203 (create), feat: Implementazdo boards work-item showcommand #238 (show).internal/cmd/pipelines/variablegroup/delete/delete.go(destructive confirmation, JSON split, success message).