From 7fc2aaa0d950c48f32d180abc2b49ffc3f7d3bdd Mon Sep 17 00:00:00 2001
From: Charlie Moog <moogcharlie@gmail.com>
Date: Sat, 7 Nov 2020 19:09:58 -0600
Subject: [PATCH 1/6] Add new image tags commands

---
 coder-sdk/tags.go         |  68 +++++++++++++++
 docs/coder.md             |   1 +
 docs/coder_tags.md        |  23 ++++++
 docs/coder_tags_create.md |  33 ++++++++
 docs/coder_tags_ls.md     |  33 ++++++++
 docs/coder_tags_rm.md     |  32 ++++++++
 internal/cmd/cmd.go       |   1 +
 internal/cmd/tags.go      | 168 ++++++++++++++++++++++++++++++++++++++
 8 files changed, 359 insertions(+)
 create mode 100644 coder-sdk/tags.go
 create mode 100644 docs/coder_tags.md
 create mode 100644 docs/coder_tags_create.md
 create mode 100644 docs/coder_tags_ls.md
 create mode 100644 docs/coder_tags_rm.md
 create mode 100644 internal/cmd/tags.go

diff --git a/coder-sdk/tags.go b/coder-sdk/tags.go
new file mode 100644
index 00000000..8c7282a9
--- /dev/null
+++ b/coder-sdk/tags.go
@@ -0,0 +1,68 @@
+package coder
+
+import (
+	"context"
+	"net/http"
+	"time"
+)
+
+// ImageTag is a Docker image tag.
+type ImageTag struct {
+	ImageID           string         `json:"image_id"             table:"-"`
+	Tag               string         `json:"tag"                  table:"Tag"`
+	LatestHash        string         `json:"latest_hash"          table:"-"`
+	HashLastUpdatedAt time.Time      `json:"hash_last_updated_at" table:"-"`
+	OSRelease         *OSRelease     `json:"os_release"           table:"OS"`
+	Environments      []*Environment `json:"environments"         table:"-"`
+	UpdatedAt         time.Time      `json:"updated_at"           table:"UpdatedAt"`
+	CreatedAt         time.Time      `json:"created_at"           table:"-"`
+}
+
+// OSRelease is the marshalled /etc/os-release file.
+type OSRelease struct {
+	ID         string `json:"id"`
+	PrettyName string `json:"pretty_name"`
+	HomeURL    string `json:"home_url"`
+}
+
+func (o OSRelease) String() string {
+	return o.PrettyName
+}
+
+// CreateImageTagReq defines the request parameters for creating a new image tag.
+type CreateImageTagReq struct {
+	Tag     string `json:"tag"`
+	Default bool   `json:"default"`
+}
+
+// CreateImageTag creates a new image tag resource.
+func (c Client) CreateImageTag(ctx context.Context, imageID string, req CreateImageTagReq) (*ImageTag, error) {
+	var tag ImageTag
+	if err := c.requestBody(ctx, http.MethodPost, "/api/images/"+imageID+"/tags", req, tag); err != nil {
+		return nil, err
+	}
+	return &tag, nil
+}
+
+// DeleteImageTag deletes an image tag resource.
+func (c Client) DeleteImageTag(ctx context.Context, imageID, tag string) error {
+	return c.requestBody(ctx, http.MethodDelete, "/api/images/"+imageID+"/tags/"+tag, nil, nil)
+}
+
+// ImageTags fetch all image tags.
+func (c Client) ImageTags(ctx context.Context, imageID string) ([]ImageTag, error) {
+	var tags []ImageTag
+	if err := c.requestBody(ctx, http.MethodGet, "/api/images/"+imageID+"/tags", nil, &tags); err != nil {
+		return nil, err
+	}
+	return tags, nil
+}
+
+// ImageTagByID fetch an image tag by ID.
+func (c Client) ImageTagByID(ctx context.Context, imageID, tagID string) (*ImageTag, error) {
+	var tag ImageTag
+	if err := c.requestBody(ctx, http.MethodGet, "/api/images/"+imageID+"/tags/"+tagID, nil, &tag); err != nil {
+		return nil, err
+	}
+	return &tag, nil
+}
diff --git a/docs/coder.md b/docs/coder.md
index e268d312..f9099719 100644
--- a/docs/coder.md
+++ b/docs/coder.md
@@ -18,6 +18,7 @@ coder provides a CLI for working with an existing Coder Enterprise installation
 * [coder logout](coder_logout.md)	 - Remove local authentication credentials if any exist
 * [coder sh](coder_sh.md)	 - Open a shell and execute commands in a Coder environment
 * [coder sync](coder_sync.md)	 - Establish a one way directory sync to a Coder environment
+* [coder tags](coder_tags.md)	 - operate on Coder image tags
 * [coder urls](coder_urls.md)	 - Interact with environment DevURLs
 * [coder users](coder_users.md)	 - Interact with Coder user accounts
 
diff --git a/docs/coder_tags.md b/docs/coder_tags.md
new file mode 100644
index 00000000..5cf8ca00
--- /dev/null
+++ b/docs/coder_tags.md
@@ -0,0 +1,23 @@
+## coder tags
+
+operate on Coder image tags
+
+### Options
+
+```
+  -h, --help   help for tags
+```
+
+### Options inherited from parent commands
+
+```
+  -v, --verbose   show verbose output
+```
+
+### SEE ALSO
+
+* [coder](coder.md)	 - coder provides a CLI for working with an existing Coder Enterprise installation
+* [coder tags create](coder_tags_create.md)	 - 
+* [coder tags ls](coder_tags_ls.md)	 - 
+* [coder tags rm](coder_tags_rm.md)	 - 
+
diff --git a/docs/coder_tags_create.md b/docs/coder_tags_create.md
new file mode 100644
index 00000000..cc8ce253
--- /dev/null
+++ b/docs/coder_tags_create.md
@@ -0,0 +1,33 @@
+## coder tags create
+
+
+
+```
+coder tags create [tag] [flags]
+```
+
+### Examples
+
+```
+coder tags create latest --image ubuntu --org default
+```
+
+### Options
+
+```
+      --default        make this tag the default for its image
+  -h, --help           help for create
+      --image string   image name
+      --org string     organization name
+```
+
+### Options inherited from parent commands
+
+```
+  -v, --verbose   show verbose output
+```
+
+### SEE ALSO
+
+* [coder tags](coder_tags.md)	 - operate on Coder image tags
+
diff --git a/docs/coder_tags_ls.md b/docs/coder_tags_ls.md
new file mode 100644
index 00000000..423b136b
--- /dev/null
+++ b/docs/coder_tags_ls.md
@@ -0,0 +1,33 @@
+## coder tags ls
+
+
+
+```
+coder tags ls [flags]
+```
+
+### Examples
+
+```
+coder tags ls --image ubuntu --org default --output json
+```
+
+### Options
+
+```
+  -h, --help            help for ls
+  -i, --image string    image by name
+      --org string      organization by name
+      --output string   output format (human|json) (default "human")
+```
+
+### Options inherited from parent commands
+
+```
+  -v, --verbose   show verbose output
+```
+
+### SEE ALSO
+
+* [coder tags](coder_tags.md)	 - operate on Coder image tags
+
diff --git a/docs/coder_tags_rm.md b/docs/coder_tags_rm.md
new file mode 100644
index 00000000..9d0b6ac6
--- /dev/null
+++ b/docs/coder_tags_rm.md
@@ -0,0 +1,32 @@
+## coder tags rm
+
+
+
+```
+coder tags rm [tag] [flags]
+```
+
+### Examples
+
+```
+coder tags rm latest --image ubuntu --org default
+```
+
+### Options
+
+```
+  -h, --help           help for rm
+  -i, --image string   image by name
+  -o, --org string     organization by name
+```
+
+### Options inherited from parent commands
+
+```
+  -v, --verbose   show verbose output
+```
+
+### SEE ALSO
+
+* [coder tags](coder_tags.md)	 - operate on Coder image tags
+
diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index 937c0558..e7b8aa63 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -26,6 +26,7 @@ func Make() *cobra.Command {
 		logoutCmd(),
 		shCmd(),
 		usersCmd(),
+		tagsCmd(),
 		configSSHCmd(),
 		secretsCmd(),
 		envsCmd(),
diff --git a/internal/cmd/tags.go b/internal/cmd/tags.go
new file mode 100644
index 00000000..0317ba46
--- /dev/null
+++ b/internal/cmd/tags.go
@@ -0,0 +1,168 @@
+package cmd
+
+import (
+	"encoding/json"
+	"os"
+
+	"cdr.dev/coder-cli/coder-sdk"
+	"cdr.dev/coder-cli/pkg/clog"
+	"cdr.dev/coder-cli/pkg/tablewriter"
+	"github.com/spf13/cobra"
+	"golang.org/x/xerrors"
+)
+
+func tagsCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "tags",
+		Short: "operate on Coder image tags",
+	}
+
+	cmd.AddCommand(
+		tagsLsCmd(),
+		tagsCreateCmd(),
+		tagsRmCmd(),
+	)
+	return cmd
+}
+
+func tagsCreateCmd() *cobra.Command {
+	var (
+		orgName    string
+		imageName  string
+		defaultTag bool
+	)
+	cmd := &cobra.Command{
+		Use:     "create [tag]",
+		Example: `coder tags create latest --image ubuntu --org default`,
+		Args:    cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := cmd.Context()
+			client, err := newClient(ctx)
+			if err != nil {
+				return err
+			}
+			img, err := findImg(ctx, client, findImgConf{
+				orgName: orgName,
+				imgName: imageName,
+				email:   coder.Me,
+			})
+			if err != nil {
+				return xerrors.Errorf("find image: %w", err)
+			}
+
+			_, err = client.CreateImageTag(ctx, img.ID, coder.CreateImageTagReq{
+				Tag:     args[0],
+				Default: defaultTag,
+			})
+			if err != nil {
+				return xerrors.Errorf("create image tag: %w", err)
+			}
+			clog.LogSuccess("created new tag")
+
+			return nil
+		},
+	}
+
+	cmd.Flags().StringVar(&imageName, "image", "", "image name")
+	cmd.Flags().StringVar(&orgName, "org", "", "organization name")
+	cmd.Flags().BoolVar(&defaultTag, "default", false, "make this tag the default for its image")
+	_ = cmd.MarkFlagRequired("org")
+	_ = cmd.MarkFlagRequired("image")
+	return cmd
+}
+
+func tagsLsCmd() *cobra.Command {
+	var (
+		orgName   string
+		imageName string
+		outputFmt string
+	)
+	cmd := &cobra.Command{
+		Use:     "ls",
+		Example: `coder tags ls --image ubuntu --org default --output json`,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := cmd.Context()
+			client, err := newClient(ctx)
+			if err != nil {
+				return err
+			}
+
+			img, err := findImg(ctx, client, findImgConf{
+				email:   coder.Me,
+				orgName: orgName,
+				imgName: imageName,
+			})
+			if err != nil {
+				return err
+			}
+
+			tags, err := client.ImageTags(ctx, img.ID)
+			if err != nil {
+				return err
+			}
+
+			switch outputFmt {
+			case humanOutput:
+				err = tablewriter.WriteTable(len(tags), func(i int) interface{} { return tags[i] })
+				if err != nil {
+					return err
+				}
+			case jsonOutput:
+				err := json.NewEncoder(os.Stdout).Encode(tags)
+				if err != nil {
+					return err
+				}
+			default:
+				return clog.Error("unknown --output value")
+			}
+
+			return nil
+		},
+	}
+	cmd.Flags().StringVar(&orgName, "org", "", "organization by name")
+	cmd.Flags().StringVarP(&imageName, "image", "i", "", "image by name")
+	cmd.Flags().StringVar(&outputFmt, "output", humanOutput, "output format (human|json)")
+	_ = cmd.MarkFlagRequired("image")
+	_ = cmd.MarkFlagRequired("org")
+	return cmd
+}
+
+func tagsRmCmd() *cobra.Command {
+	var (
+		imageName string
+		orgName   string
+	)
+	cmd := &cobra.Command{
+		Use:     "rm [tag]",
+		Example: `coder tags rm latest --image ubuntu --org default`,
+		Args:    cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := cmd.Context()
+			client, err := newClient(ctx)
+			if err != nil {
+				return err
+			}
+
+			img, err := findImg(ctx, client, findImgConf{
+				email:   coder.Me,
+				imgName: imageName,
+				orgName: orgName,
+			})
+			if err != nil {
+				return err
+			}
+
+			if err = client.DeleteImageTag(ctx, img.ID, args[0]); err != nil {
+				return err
+			}
+			clog.LogSuccess("removed tag")
+
+			return nil
+		},
+	}
+	cmd.Flags().StringVarP(&orgName, "org", "o", "", "organization by name")
+	cmd.Flags().StringVarP(&imageName, "image", "i", "", "image by name")
+	_ = cmd.MarkFlagRequired("image")
+	_ = cmd.MarkFlagRequired("org")
+	return cmd
+}

From 10eb0cca0ea1046b488e9c0489e80f445435c165 Mon Sep 17 00:00:00 2001
From: Charlie Moog <moogcharlie@gmail.com>
Date: Sat, 7 Nov 2020 19:13:49 -0600
Subject: [PATCH 2/6] fixup! Add new image tags commands

---
 docs/coder_tags_create.md | 4 ++--
 internal/cmd/tags.go      | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/coder_tags_create.md b/docs/coder_tags_create.md
index cc8ce253..e35ecf02 100644
--- a/docs/coder_tags_create.md
+++ b/docs/coder_tags_create.md
@@ -17,8 +17,8 @@ coder tags create latest --image ubuntu --org default
 ```
       --default        make this tag the default for its image
   -h, --help           help for create
-      --image string   image name
-      --org string     organization name
+  -i, --image string   image name
+  -o, --org string     organization name
 ```
 
 ### Options inherited from parent commands
diff --git a/internal/cmd/tags.go b/internal/cmd/tags.go
index 0317ba46..9b2325eb 100644
--- a/internal/cmd/tags.go
+++ b/internal/cmd/tags.go
@@ -63,8 +63,8 @@ func tagsCreateCmd() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&imageName, "image", "", "image name")
-	cmd.Flags().StringVar(&orgName, "org", "", "organization name")
+	cmd.Flags().StringVarP(&imageName, "image", "i", "", "image name")
+	cmd.Flags().StringVarP(&orgName, "org", "o", "", "organization name")
 	cmd.Flags().BoolVar(&defaultTag, "default", false, "make this tag the default for its image")
 	_ = cmd.MarkFlagRequired("org")
 	_ = cmd.MarkFlagRequired("image")

From 0caca7b698616c6f51ba6b54d09af2b94ae6d96d Mon Sep 17 00:00:00 2001
From: Charlie Moog <moogcharlie@gmail.com>
Date: Sat, 7 Nov 2020 19:25:45 -0600
Subject: [PATCH 3/6] fixup! Add new image tags commands

---
 docs/coder_tags.md        | 4 ++--
 docs/coder_tags_create.md | 4 ++++
 docs/coder_tags_rm.md     | 2 +-
 internal/cmd/tags.go      | 3 +++
 4 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/docs/coder_tags.md b/docs/coder_tags.md
index 5cf8ca00..663920e5 100644
--- a/docs/coder_tags.md
+++ b/docs/coder_tags.md
@@ -17,7 +17,7 @@ operate on Coder image tags
 ### SEE ALSO
 
 * [coder](coder.md)	 - coder provides a CLI for working with an existing Coder Enterprise installation
-* [coder tags create](coder_tags_create.md)	 - 
+* [coder tags create](coder_tags_create.md)	 - add an image tag
 * [coder tags ls](coder_tags_ls.md)	 - 
-* [coder tags rm](coder_tags_rm.md)	 - 
+* [coder tags rm](coder_tags_rm.md)	 - remove an image tag
 
diff --git a/docs/coder_tags_create.md b/docs/coder_tags_create.md
index e35ecf02..a4089d97 100644
--- a/docs/coder_tags_create.md
+++ b/docs/coder_tags_create.md
@@ -1,6 +1,10 @@
 ## coder tags create
 
+add an image tag
 
+### Synopsis
+
+allow users to create environments with this image tag
 
 ```
 coder tags create [tag] [flags]
diff --git a/docs/coder_tags_rm.md b/docs/coder_tags_rm.md
index 9d0b6ac6..c909f8df 100644
--- a/docs/coder_tags_rm.md
+++ b/docs/coder_tags_rm.md
@@ -1,6 +1,6 @@
 ## coder tags rm
 
-
+remove an image tag
 
 ```
 coder tags rm [tag] [flags]
diff --git a/internal/cmd/tags.go b/internal/cmd/tags.go
index 9b2325eb..7c9bbe52 100644
--- a/internal/cmd/tags.go
+++ b/internal/cmd/tags.go
@@ -33,6 +33,8 @@ func tagsCreateCmd() *cobra.Command {
 	)
 	cmd := &cobra.Command{
 		Use:     "create [tag]",
+		Short:   "add an image tag",
+		Long:    "allow users to create environments with this image tag",
 		Example: `coder tags create latest --image ubuntu --org default`,
 		Args:    cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
@@ -134,6 +136,7 @@ func tagsRmCmd() *cobra.Command {
 	)
 	cmd := &cobra.Command{
 		Use:     "rm [tag]",
+		Short:   "remove an image tag",
 		Example: `coder tags rm latest --image ubuntu --org default`,
 		Args:    cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {

From 1115494d774ffe387a7dd9decdbb15c47a4d5236 Mon Sep 17 00:00:00 2001
From: Charlie Moog <moogcharlie@gmail.com>
Date: Sat, 7 Nov 2020 19:47:03 -0600
Subject: [PATCH 4/6] Add ls integration test

---
 ci/integration/tags_test.go | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 ci/integration/tags_test.go

diff --git a/ci/integration/tags_test.go b/ci/integration/tags_test.go
new file mode 100644
index 00000000..5f7c9aed
--- /dev/null
+++ b/ci/integration/tags_test.go
@@ -0,0 +1,35 @@
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"cdr.dev/coder-cli/coder-sdk"
+	"cdr.dev/coder-cli/pkg/tcli"
+	"cdr.dev/slog/sloggers/slogtest/assert"
+)
+
+func TestTags(t *testing.T) {
+	t.Parallel()
+	run(t, "tags-cli-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) {
+		headlessLogin(ctx, t, c)
+		client := cleanupClient(ctx, t)
+
+		ensureImageImported(ctx, t, client, "ubuntu")
+
+		c.Run(ctx, "coder tags ls").Assert(t,
+			tcli.Error(),
+		)
+		c.Run(ctx, "coder tags ls --image ubuntu --org default").Assert(t,
+			tcli.Success(),
+		)
+		var tags []coder.ImageTag
+		c.Run(ctx, "coder tags ls --image ubuntu --org default --output json").Assert(t,
+			tcli.Success(),
+			tcli.StdoutJSONUnmarshal(&tags),
+		)
+		assert.True(t, "> 0 tags", len(tags) > 0)
+
+		// TODO(@cmoog) add create and rm integration tests
+	})
+}

From 0484bd3839716e6432daaa3b1de1cb4e8725a7eb Mon Sep 17 00:00:00 2001
From: Charlie Moog <moogcharlie@gmail.com>
Date: Sat, 7 Nov 2020 20:02:12 -0600
Subject: [PATCH 5/6] fixup! Add ls integration test

---
 ci/integration/tags_test.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/ci/integration/tags_test.go b/ci/integration/tags_test.go
index 5f7c9aed..2fb7dfe0 100644
--- a/ci/integration/tags_test.go
+++ b/ci/integration/tags_test.go
@@ -11,6 +11,7 @@ import (
 
 func TestTags(t *testing.T) {
 	t.Parallel()
+	t.Skip("wait for dedicated test cluster so we can create an org")
 	run(t, "tags-cli-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) {
 		headlessLogin(ctx, t, c)
 		client := cleanupClient(ctx, t)

From e0228262ebb8ca9a3f8ed2b1cfefd1397624ff20 Mon Sep 17 00:00:00 2001
From: Charlie Moog <moogcharlie@gmail.com>
Date: Sat, 7 Nov 2020 20:30:07 -0600
Subject: [PATCH 6/6] Hide tags cmd

---
 docs/coder.md             |  1 -
 docs/coder_tags.md        | 23 -----------------------
 docs/coder_tags_create.md | 37 -------------------------------------
 docs/coder_tags_ls.md     | 33 ---------------------------------
 docs/coder_tags_rm.md     | 32 --------------------------------
 internal/cmd/tags.go      |  5 +++--
 6 files changed, 3 insertions(+), 128 deletions(-)
 delete mode 100644 docs/coder_tags.md
 delete mode 100644 docs/coder_tags_create.md
 delete mode 100644 docs/coder_tags_ls.md
 delete mode 100644 docs/coder_tags_rm.md

diff --git a/docs/coder.md b/docs/coder.md
index f9099719..e268d312 100644
--- a/docs/coder.md
+++ b/docs/coder.md
@@ -18,7 +18,6 @@ coder provides a CLI for working with an existing Coder Enterprise installation
 * [coder logout](coder_logout.md)	 - Remove local authentication credentials if any exist
 * [coder sh](coder_sh.md)	 - Open a shell and execute commands in a Coder environment
 * [coder sync](coder_sync.md)	 - Establish a one way directory sync to a Coder environment
-* [coder tags](coder_tags.md)	 - operate on Coder image tags
 * [coder urls](coder_urls.md)	 - Interact with environment DevURLs
 * [coder users](coder_users.md)	 - Interact with Coder user accounts
 
diff --git a/docs/coder_tags.md b/docs/coder_tags.md
deleted file mode 100644
index 663920e5..00000000
--- a/docs/coder_tags.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## coder tags
-
-operate on Coder image tags
-
-### Options
-
-```
-  -h, --help   help for tags
-```
-
-### Options inherited from parent commands
-
-```
-  -v, --verbose   show verbose output
-```
-
-### SEE ALSO
-
-* [coder](coder.md)	 - coder provides a CLI for working with an existing Coder Enterprise installation
-* [coder tags create](coder_tags_create.md)	 - add an image tag
-* [coder tags ls](coder_tags_ls.md)	 - 
-* [coder tags rm](coder_tags_rm.md)	 - remove an image tag
-
diff --git a/docs/coder_tags_create.md b/docs/coder_tags_create.md
deleted file mode 100644
index a4089d97..00000000
--- a/docs/coder_tags_create.md
+++ /dev/null
@@ -1,37 +0,0 @@
-## coder tags create
-
-add an image tag
-
-### Synopsis
-
-allow users to create environments with this image tag
-
-```
-coder tags create [tag] [flags]
-```
-
-### Examples
-
-```
-coder tags create latest --image ubuntu --org default
-```
-
-### Options
-
-```
-      --default        make this tag the default for its image
-  -h, --help           help for create
-  -i, --image string   image name
-  -o, --org string     organization name
-```
-
-### Options inherited from parent commands
-
-```
-  -v, --verbose   show verbose output
-```
-
-### SEE ALSO
-
-* [coder tags](coder_tags.md)	 - operate on Coder image tags
-
diff --git a/docs/coder_tags_ls.md b/docs/coder_tags_ls.md
deleted file mode 100644
index 423b136b..00000000
--- a/docs/coder_tags_ls.md
+++ /dev/null
@@ -1,33 +0,0 @@
-## coder tags ls
-
-
-
-```
-coder tags ls [flags]
-```
-
-### Examples
-
-```
-coder tags ls --image ubuntu --org default --output json
-```
-
-### Options
-
-```
-  -h, --help            help for ls
-  -i, --image string    image by name
-      --org string      organization by name
-      --output string   output format (human|json) (default "human")
-```
-
-### Options inherited from parent commands
-
-```
-  -v, --verbose   show verbose output
-```
-
-### SEE ALSO
-
-* [coder tags](coder_tags.md)	 - operate on Coder image tags
-
diff --git a/docs/coder_tags_rm.md b/docs/coder_tags_rm.md
deleted file mode 100644
index c909f8df..00000000
--- a/docs/coder_tags_rm.md
+++ /dev/null
@@ -1,32 +0,0 @@
-## coder tags rm
-
-remove an image tag
-
-```
-coder tags rm [tag] [flags]
-```
-
-### Examples
-
-```
-coder tags rm latest --image ubuntu --org default
-```
-
-### Options
-
-```
-  -h, --help           help for rm
-  -i, --image string   image by name
-  -o, --org string     organization by name
-```
-
-### Options inherited from parent commands
-
-```
-  -v, --verbose   show verbose output
-```
-
-### SEE ALSO
-
-* [coder tags](coder_tags.md)	 - operate on Coder image tags
-
diff --git a/internal/cmd/tags.go b/internal/cmd/tags.go
index 7c9bbe52..ad77932d 100644
--- a/internal/cmd/tags.go
+++ b/internal/cmd/tags.go
@@ -13,8 +13,9 @@ import (
 
 func tagsCmd() *cobra.Command {
 	cmd := &cobra.Command{
-		Use:   "tags",
-		Short: "operate on Coder image tags",
+		Use:    "tags",
+		Hidden: true,
+		Short:  "operate on Coder image tags",
 	}
 
 	cmd.AddCommand(
