From 7f627636cf12d9cea51b4b3aab3e44800f4bc985 Mon Sep 17 00:00:00 2001 From: Dov Benyomin Sohacheski Date: Thu, 2 Jul 2026 04:24:56 +0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Annotate=20commands=20with=20versio?= =?UTF-8?q?n=20metadata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Deprecated field to the docs serializer beside Since (both from cobra Annotations, omitempty) and backfill a since annotation on every command so ws-docs renders per-command version badges, matching env vars. Released groups floor to 0.2.0; the unreleased seed and editor groups use next, which the workspace release codemod resolves to the cut version. Regenerate commands.yaml and add a guard test asserting every command carries a since annotation. --- cmd/clip/clip.go | 7 ++-- cmd/clip/paste.go | 7 ++-- cmd/commands_test.go | 20 ++++++++++++ cmd/editor/diagnostics.go | 7 ++-- cmd/editor/editor.go | 7 ++-- cmd/editor/list.go | 7 ++-- cmd/editor/open.go | 9 +++--- cmd/editor/selection.go | 7 ++-- cmd/feature/feature.go | 7 ++-- cmd/feature/info.go | 9 +++--- cmd/feature/install.go | 9 +++--- cmd/feature/list.go | 9 +++--- cmd/feature/new.go | 5 +-- cmd/feature/store.go | 7 ++-- cmd/info/env.go | 7 ++-- cmd/info/extensions.go | 7 ++-- cmd/info/info.go | 14 ++++---- cmd/info/metrics.go | 7 ++-- cmd/info/uptime.go | 7 ++-- cmd/log/log.go | 27 +++++++++------- cmd/logs/logs.go | 11 ++++--- cmd/secrets/decrypt.go | 9 +++--- cmd/secrets/encrypt.go | 9 +++--- cmd/secrets/generate.go | 7 ++-- cmd/secrets/login.go | 9 +++--- cmd/secrets/master.go | 9 +++--- cmd/secrets/secrets.go | 7 ++-- cmd/seed/apply.go | 1 + cmd/seed/ls.go | 9 +++--- cmd/seed/rotate.go | 1 + cmd/seed/seed.go | 7 ++-- cmd/serve/current.go | 7 ++-- cmd/serve/font.go | 7 ++-- cmd/serve/metrics.go | 5 +-- cmd/serve/serve.go | 7 ++-- cmd/show/env.go | 9 +++--- cmd/show/ip.go | 14 ++++---- cmd/show/path.go | 21 ++++++------ cmd/show/show.go | 7 ++-- cmd/template/apply.go | 1 + cmd/template/list.go | 11 ++++--- cmd/template/show.go | 5 +-- cmd/template/template.go | 7 ++-- commands.yaml | 54 +++++++++++++++++++++++++++++++ internals/docs/serialize.go | 8 +++++ internals/docs/serialize_test.go | 55 ++++++++++++++++++++++++++++++++ 46 files changed, 334 insertions(+), 149 deletions(-) diff --git a/cmd/clip/clip.go b/cmd/clip/clip.go index cbc1df6..fa66bc2 100644 --- a/cmd/clip/clip.go +++ b/cmd/clip/clip.go @@ -5,7 +5,8 @@ import ( ) var ClipCmd = &cobra.Command{ - Use: "clip", - Short: "Interact with the native clipboard", - Long: "Reach the browser clipboard from the terminal over the workspace IPC socket.", + Use: "clip", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Interact with the native clipboard", + Long: "Reach the browser clipboard from the terminal over the workspace IPC socket.", } diff --git a/cmd/clip/paste.go b/cmd/clip/paste.go index 6cf1d9e..c1e5a86 100644 --- a/cmd/clip/paste.go +++ b/cmd/clip/paste.go @@ -6,9 +6,10 @@ import ( ) var pasteCmd = &cobra.Command{ - Use: "paste", - Short: "Paste clipboard content", - Long: "Read the browser clipboard over the workspace IPC socket and write it to stdout — redirect it to a file or pipe it onward. Pairs with the pbcopy/xclip/xsel shims for terminal clipboard access.", + Use: "paste", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Paste clipboard content", + Long: "Read the browser clipboard over the workspace IPC socket and write it to stdout — redirect it to a file or pipe it onward. Pairs with the pbcopy/xclip/xsel shims for terminal clipboard access.", RunE: func(cmd *cobra.Command, args []string) error { return clipboard.Paste(cmd.OutOrStdout()) }, diff --git a/cmd/commands_test.go b/cmd/commands_test.go index 9010611..16a03b8 100644 --- a/cmd/commands_test.go +++ b/cmd/commands_test.go @@ -7,6 +7,7 @@ import ( "github.com/kloudkit/ws-cli/cmd" "github.com/kloudkit/ws-cli/internals/docs" + "gopkg.in/yaml.v3" "gotest.tools/v3/assert" ) @@ -20,6 +21,25 @@ func TestCommandsManifestMatchesCommittedFile(t *testing.T) { assert.Equal(t, string(got), string(want)) } +func TestEveryCommandCarriesVersion(t *testing.T) { + out, err := docs.Serialize(cmd.RootCmd()) + assert.NilError(t, err) + + var root docs.Command + assert.NilError(t, yaml.Unmarshal(out, &root)) + + var check func(docs.Command) + check = func(c docs.Command) { + for _, child := range c.Commands { + assert.Assert(t, child.Since != "" || child.Deprecated != "", + "command %q must carry a since or deprecated annotation", child.Name) + check(child) + } + } + + check(root) +} + func TestSerializeIsDeterministic(t *testing.T) { first, err := docs.Serialize(cmd.RootCmd()) assert.NilError(t, err) diff --git a/cmd/editor/diagnostics.go b/cmd/editor/diagnostics.go index 4a46b73..e4f66c7 100644 --- a/cmd/editor/diagnostics.go +++ b/cmd/editor/diagnostics.go @@ -11,9 +11,10 @@ import ( ) var diagnosticsCmd = &cobra.Command{ - Use: "diagnostics", - Short: "Show language diagnostics for the workspace (or a single file)", - Long: "Pull language-server diagnostics (errors, warnings) from the editor over the IPC socket, across the whole workspace or a single file with --uri. Styled table by default, --raw for the JSON.", + Use: "diagnostics", + Annotations: map[string]string{"since": "next"}, + Short: "Show language diagnostics for the workspace (or a single file)", + Long: "Pull language-server diagnostics (errors, warnings) from the editor over the IPC socket, across the whole workspace or a single file with --uri. Styled table by default, --raw for the JSON.", RunE: func(cmd *cobra.Command, args []string) error { uri, _ := cmd.Flags().GetString("uri") diff --git a/cmd/editor/editor.go b/cmd/editor/editor.go index fa15623..976076d 100644 --- a/cmd/editor/editor.go +++ b/cmd/editor/editor.go @@ -16,9 +16,10 @@ var errNoEditorOverSSH = errors.New( ) var EditorCmd = &cobra.Command{ - Use: "editor", - Short: "Inspect and drive the active editor session", - Long: "Query and control the running VS Code / code-server window over the workspace IPC socket — list open tabs, read diagnostics and the current selection, or open a file. Blocked over SSH, where there is no browser editor to reach.", + Use: "editor", + Annotations: map[string]string{"since": "next"}, + Short: "Inspect and drive the active editor session", + Long: "Query and control the running VS Code / code-server window over the workspace IPC socket — list open tabs, read diagnostics and the current selection, or open a file. Blocked over SSH, where there is no browser editor to reach.", PersistentPreRunE: func(cmd *cobra.Command, args []string) error { if env.IsSSHSession() { return errNoEditorOverSSH diff --git a/cmd/editor/list.go b/cmd/editor/list.go index f1b0f31..741a08a 100644 --- a/cmd/editor/list.go +++ b/cmd/editor/list.go @@ -11,9 +11,10 @@ import ( ) var listCmd = &cobra.Command{ - Use: "list", - Short: "List the currently open editor tabs", - Long: "List the editor's open tabs over the IPC socket, with each tab's path, language, and active or dirty state.", + Use: "list", + Annotations: map[string]string{"since": "next"}, + Short: "List the currently open editor tabs", + Long: "List the editor's open tabs over the IPC socket, with each tab's path, language, and active or dirty state.", RunE: func(cmd *cobra.Command, args []string) error { body, err := editoripc.FetchEditors() if err != nil { diff --git a/cmd/editor/open.go b/cmd/editor/open.go index b80f31c..8d7280c 100644 --- a/cmd/editor/open.go +++ b/cmd/editor/open.go @@ -10,10 +10,11 @@ import ( ) var openCmd = &cobra.Command{ - Use: "open ", - Short: "Open a file in the editor", - Long: "Open a file in the running editor window over the workspace IPC socket — a tab in the current window by default, a separate one with --new-window, jumping to a range with --selection. Fails fast over SSH, where there is no browser editor to open into.", - Args: cobra.ExactArgs(1), + Use: "open ", + Annotations: map[string]string{"since": "next"}, + Short: "Open a file in the editor", + Long: "Open a file in the running editor window over the workspace IPC socket — a tab in the current window by default, a separate one with --new-window, jumping to a range with --selection. Fails fast over SSH, where there is no browser editor to open into.", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { newWindow, _ := cmd.Flags().GetBool("new-window") preview, _ := cmd.Flags().GetBool("preview") diff --git a/cmd/editor/selection.go b/cmd/editor/selection.go index be2bced..a6e29e3 100644 --- a/cmd/editor/selection.go +++ b/cmd/editor/selection.go @@ -11,9 +11,10 @@ import ( ) var selectionCmd = &cobra.Command{ - Use: "selection", - Short: "Show the active editor's current selection", - Long: "Report the active editor's current selection — file, range, and selected text — over the IPC socket. Empty when nothing is selected.", + Use: "selection", + Annotations: map[string]string{"since": "next"}, + Short: "Show the active editor's current selection", + Long: "Report the active editor's current selection — file, range, and selected text — over the IPC socket. Empty when nothing is selected.", RunE: func(cmd *cobra.Command, args []string) error { body, err := editoripc.FetchSelection() if err != nil { diff --git a/cmd/feature/feature.go b/cmd/feature/feature.go index 4afd7e2..f7d2090 100644 --- a/cmd/feature/feature.go +++ b/cmd/feature/feature.go @@ -7,9 +7,10 @@ import ( ) var FeatureCmd = &cobra.Command{ - Use: "feature", - Short: "Install additional pre-configured features", - Long: "Install and inspect optional workspace features — Ansible playbooks that add tools on top of the base image. Ships a curated set; --root points at your own under ~/.ws/features.d.", + Use: "feature", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Install additional pre-configured features", + Long: "Install and inspect optional workspace features — Ansible playbooks that add tools on top of the base image. Ships a curated set; --root points at your own under ~/.ws/features.d.", } func featureDirs(cmd *cobra.Command) []string { diff --git a/cmd/feature/info.go b/cmd/feature/info.go index 7ca7d2d..69ed319 100644 --- a/cmd/feature/info.go +++ b/cmd/feature/info.go @@ -9,10 +9,11 @@ import ( ) var infoCmd = &cobra.Command{ - Use: "info ", - Short: "Show detailed information about a feature", - Long: "Show a feature's description and the variables it accepts, so you know what --opt values install will take.", - Args: cobra.ExactArgs(1), + Use: "info ", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Show detailed information about a feature", + Long: "Show a feature's description and the variables it accepts, so you know what --opt values install will take.", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { featureName := args[0] diff --git a/cmd/feature/install.go b/cmd/feature/install.go index 002a4fc..12c0864 100644 --- a/cmd/feature/install.go +++ b/cmd/feature/install.go @@ -23,10 +23,11 @@ var skippableSections = []struct { } var installCmd = &cobra.Command{ - Use: "install", - Short: "Install additional pre-configured features", - Long: "Run a feature's playbook to install it. Pass variables with --opt KEY=VAL, and skip parts you do not want with --skip-extensions, --skip-completion, or --skip-repository.", - Args: cobra.ExactArgs(1), + Use: "install", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Install additional pre-configured features", + Long: "Run a feature's playbook to install it. Pass variables with --opt KEY=VAL, and skip parts you do not want with --skip-extensions, --skip-completion, or --skip-repository.", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { featureName := args[0] diff --git a/cmd/feature/list.go b/cmd/feature/list.go index 4ac4144..65c9c0e 100644 --- a/cmd/feature/list.go +++ b/cmd/feature/list.go @@ -9,10 +9,11 @@ import ( ) var listCmd = &cobra.Command{ - Use: "list", - Short: "List available features that can be installed", - Long: "List the features you can install, marking where each comes from — the shipped set, a workspace override, or your own ~/.ws/features.d.", - Aliases: []string{"ls"}, + Use: "list", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "List available features that can be installed", + Long: "List the features you can install, marking where each comes from — the shipped set, a workspace override, or your own ~/.ws/features.d.", + Aliases: []string{"ls"}, RunE: func(cmd *cobra.Command, args []string) error { result, err := features.ListFeatures(featureDirs(cmd)) if err != nil { diff --git a/cmd/feature/new.go b/cmd/feature/new.go index a36d655..2c3806f 100644 --- a/cmd/feature/new.go +++ b/cmd/feature/new.go @@ -18,8 +18,9 @@ const scaffoldTemplate = `--- ` var newCmd = &cobra.Command{ - Use: "new [name]", - Short: "Print boilerplate for a custom feature playbook", + Use: "new [name]", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Print boilerplate for a custom feature playbook", Long: `Print a starter feature playbook to stdout. Redirect it into ~/.ws/features.d/.yaml, then extend it and install diff --git a/cmd/feature/store.go b/cmd/feature/store.go index ed615a1..b67b3dc 100644 --- a/cmd/feature/store.go +++ b/cmd/feature/store.go @@ -10,9 +10,10 @@ import ( ) var storeCmd = &cobra.Command{ - Use: "store", - Short: "List packages available in the feature store", - Long: "List the artifacts published to the feature store (WS_FEATURES_STORE_URL) — the offline mirror features install from when the network is locked down.", + Use: "store", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "List packages available in the feature store", + Long: "List the artifacts published to the feature store (WS_FEATURES_STORE_URL) — the offline mirror features install from when the network is locked down.", RunE: func(cmd *cobra.Command, args []string) error { storeURL, _ := config.Resolve("features", "store_url") if storeURL == "" { diff --git a/cmd/info/env.go b/cmd/info/env.go index 689f686..4e97992 100644 --- a/cmd/info/env.go +++ b/cmd/info/env.go @@ -31,9 +31,10 @@ func showEnvironment(writer io.Writer) { } var envCmd = &cobra.Command{ - Use: "env", - Short: "Display effective workspace environment variables", - Long: "Print every WS_* variable in effect, sorted — the resolved environment the workspace booted with.", + Use: "env", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display effective workspace environment variables", + Long: "Print every WS_* variable in effect, sorted — the resolved environment the workspace booted with.", RunE: func(cmd *cobra.Command, args []string) error { showEnvironment(cmd.OutOrStdout()) return nil diff --git a/cmd/info/extensions.go b/cmd/info/extensions.go index 769d1d7..5efd977 100644 --- a/cmd/info/extensions.go +++ b/cmd/info/extensions.go @@ -10,9 +10,10 @@ import ( ) var extensionsCmd = &cobra.Command{ - Use: "extensions", - Short: "Display installed extensions", - Long: "List the installed VS Code extensions with their versions.", + Use: "extensions", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display installed extensions", + Long: "List the installed VS Code extensions with their versions.", RunE: func(cmd *cobra.Command, args []string) error { extensions, _ := config.GetExtensions() diff --git a/cmd/info/info.go b/cmd/info/info.go index fe462bf..d3660b2 100644 --- a/cmd/info/info.go +++ b/cmd/info/info.go @@ -34,15 +34,17 @@ func showVersion(writer io.Writer) { } var InfoCmd = &cobra.Command{ - Use: "info", - Short: "Display workspace information", - Long: "Report facts about the running workspace — version, effective environment, installed extensions, live resource metrics, and uptime.", + Use: "info", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display workspace information", + Long: "Report facts about the running workspace — version, effective environment, installed extensions, live resource metrics, and uptime.", } var showVersionCmd = &cobra.Command{ - Use: "version", - Short: "Display installed workspace version", - Long: "Print the workspace version. --all expands to the full table — workspace, ws-cli, and VS Code.", + Use: "version", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display installed workspace version", + Long: "Print the workspace version. --all expands to the full table — workspace, ws-cli, and VS Code.", Run: func(cmd *cobra.Command, args []string) { if all, _ := cmd.Flags().GetBool("all"); all { showVersion(cmd.OutOrStdout()) diff --git a/cmd/info/metrics.go b/cmd/info/metrics.go index 3179a7b..e795686 100644 --- a/cmd/info/metrics.go +++ b/cmd/info/metrics.go @@ -10,9 +10,10 @@ import ( ) var metricsCmd = &cobra.Command{ - Use: "metrics", - Short: "Display workspace metrics", - Long: "Show live resource usage — CPU, memory, disk, and file descriptors, plus GPU with --gpu.", + Use: "metrics", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display workspace metrics", + Long: "Show live resource usage — CPU, memory, disk, and file descriptors, plus GPU with --gpu.", RunE: func(cmd *cobra.Command, args []string) error { includeGPU, _ := cmd.Flags().GetBool("gpu") diff --git a/cmd/info/uptime.go b/cmd/info/uptime.go index 7b5d913..a0d73ca 100644 --- a/cmd/info/uptime.go +++ b/cmd/info/uptime.go @@ -10,9 +10,10 @@ import ( ) var uptimeCmd = &cobra.Command{ - Use: "uptime", - Short: "Display the workspace uptime", - Long: "Show when the workspace session started and how long it has been running.", + Use: "uptime", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display the workspace uptime", + Long: "Show when the workspace session started and how long it has been running.", RunE: func(cmd *cobra.Command, args []string) error { started, running, err := config.GetSessionInfo() diff --git a/cmd/log/log.go b/cmd/log/log.go index 83aac41..6063775 100644 --- a/cmd/log/log.go +++ b/cmd/log/log.go @@ -8,9 +8,10 @@ import ( ) var LogCmd = &cobra.Command{ - Use: "log", - Short: "Log messages", - Long: "Emit styled, level-tagged log lines — the same formatting the startup scripts use. --pipe runs each line of piped input through the logger.", + Use: "log", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Log messages", + Long: "Emit styled, level-tagged log lines — the same formatting the startup scripts use. --pipe runs each line of piped input through the logger.", } var debugCmd = createCommand("debug", "debugging") @@ -18,10 +19,11 @@ var errorCmd = createCommand("error", "error") var infoCmd = createCommand("info", "information") var warnCmd = createCommand("warn", "warning") var stampCmd = &cobra.Command{ - Use: "stamp", - Short: "Log the current timestamp", - Long: "Print just the current timestamp in the workspace log style — handy for marking phases in a startup log.", - Args: cobra.NoArgs, + Use: "stamp", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Log the current timestamp", + Long: "Print just the current timestamp in the workspace log style — handy for marking phases in a startup log.", + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { withPipe, _ := cmd.Flags().GetBool("pipe") @@ -36,11 +38,12 @@ var stampCmd = &cobra.Command{ func createCommand(short, long string) *cobra.Command { cmd := &cobra.Command{ - Use: fmt.Sprintf("%s message", short), - Short: fmt.Sprintf("Log %s messages", long), - Long: fmt.Sprintf("Emit a log line at %s level in the workspace style — the same formatting the startup scripts use. --indent nests it under a preceding line, --stamp prefixes a timestamp.", short), - Args: validate, - RunE: execute(short), + Use: fmt.Sprintf("%s message", short), + Annotations: map[string]string{"since": "0.2.0"}, + Short: fmt.Sprintf("Log %s messages", long), + Long: fmt.Sprintf("Emit a log line at %s level in the workspace style — the same formatting the startup scripts use. --indent nests it under a preceding line, --stamp prefixes a timestamp.", short), + Args: validate, + RunE: execute(short), } cmd.Flags().IntP("indent", "i", 0, "Desired prefixed indentation") diff --git a/cmd/logs/logs.go b/cmd/logs/logs.go index bb6dd25..532b697 100644 --- a/cmd/logs/logs.go +++ b/cmd/logs/logs.go @@ -15,11 +15,12 @@ var validLogLevels = []string{"debug", "info", "warn", "error"} var validLogTargets = []string{"main", "metrics", "docker", "auth_proxy", "cloudflared"} var LogsCmd = &cobra.Command{ - Use: "logs", - Short: "Retrieve workspace logs", - Long: "Read a workspace daemon's log — the main log by default, or --target metrics|docker|auth_proxy|cloudflared. Filter by --level, limit with --tail, or stream live with --follow.", - Args: cobra.NoArgs, - RunE: execute, + Use: "logs", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Retrieve workspace logs", + Long: "Read a workspace daemon's log — the main log by default, or --target metrics|docker|auth_proxy|cloudflared. Filter by --level, limit with --tail, or stream live with --follow.", + Args: cobra.NoArgs, + RunE: execute, } func execute(cmd *cobra.Command, args []string) error { diff --git a/cmd/secrets/decrypt.go b/cmd/secrets/decrypt.go index 08e8753..f1ff2d9 100644 --- a/cmd/secrets/decrypt.go +++ b/cmd/secrets/decrypt.go @@ -7,10 +7,11 @@ import ( ) var decryptCmd = &cobra.Command{ - Use: "decrypt ", - Short: "Decrypt an encrypted value", - Long: "Decrypt a value produced by encrypt, under the master key. Reads from the argument or stdin (-); writes the plaintext to stdout, or a file with --output.", - Args: cobra.ExactArgs(1), + Use: "decrypt ", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Decrypt an encrypted value", + Long: "Decrypt a value produced by encrypt, under the master key. Reads from the argument or stdin (-); writes the plaintext to stdout, or a file with --output.", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cfg := getOutputConfig(cmd) masterKeyFlag, _ := cmd.Flags().GetString("master") diff --git a/cmd/secrets/encrypt.go b/cmd/secrets/encrypt.go index 4a5b0f7..49ea7a9 100644 --- a/cmd/secrets/encrypt.go +++ b/cmd/secrets/encrypt.go @@ -10,10 +10,11 @@ import ( ) var encryptCmd = &cobra.Command{ - Use: "encrypt ", - Short: "Encrypt a plaintext value", - Long: "Encrypt a value under the master key. Reads the plaintext from the argument or stdin (-); writes the ciphertext to stdout, or a file with --output.", - Args: cobra.ExactArgs(1), + Use: "encrypt ", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Encrypt a plaintext value", + Long: "Encrypt a value under the master key. Reads the plaintext from the argument or stdin (-); writes the ciphertext to stdout, or a file with --output.", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cfg := getOutputConfig(cmd) masterKeyFlag, _ := cmd.Flags().GetString("master") diff --git a/cmd/secrets/generate.go b/cmd/secrets/generate.go index 7ba92f0..0008865 100644 --- a/cmd/secrets/generate.go +++ b/cmd/secrets/generate.go @@ -3,9 +3,10 @@ package secrets import "github.com/spf13/cobra" var generateCmd = &cobra.Command{ - Use: "generate", - Short: "Generate master keys or login password hashes", - Long: "Generate the credentials the workspace needs — a master key for secrets, or a login password hash for the server.", + Use: "generate", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Generate master keys or login password hashes", + Long: "Generate the credentials the workspace needs — a master key for secrets, or a login password hash for the server.", } func init() { diff --git a/cmd/secrets/login.go b/cmd/secrets/login.go index e635c75..9da8fad 100644 --- a/cmd/secrets/login.go +++ b/cmd/secrets/login.go @@ -11,10 +11,11 @@ import ( ) var loginCmd = &cobra.Command{ - Use: "login", - Short: "Generate a workspace password hash for authentication", - Long: "Prompt for a password and print its hash for the workspace server login (WS_AUTH_PASSWORD_HASHED). Store the hash, never the password.", - Args: cobra.NoArgs, + Use: "login", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Generate a workspace password hash for authentication", + Long: "Prompt for a password and print its hash for the workspace server login (WS_AUTH_PASSWORD_HASHED). Store the hash, never the password.", + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { cfg := getOutputConfig(cmd) return generateLoginHash(cmd, cfg) diff --git a/cmd/secrets/master.go b/cmd/secrets/master.go index 4d2f3f2..36bab88 100644 --- a/cmd/secrets/master.go +++ b/cmd/secrets/master.go @@ -12,10 +12,11 @@ import ( ) var masterCmd = &cobra.Command{ - Use: "master", - Short: "Generate a cryptographically secure master key", - Long: "Generate a random master key, printed base64-encoded — the key encrypt, decrypt, and the seed engine use. --length sets the byte size (default 32).", - Args: cobra.NoArgs, + Use: "master", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Generate a cryptographically secure master key", + Long: "Generate a random master key, printed base64-encoded — the key encrypt, decrypt, and the seed engine use. --length sets the byte size (default 32).", + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { cfg := getOutputConfig(cmd) keyLength, _ := cmd.Flags().GetInt("length") diff --git a/cmd/secrets/secrets.go b/cmd/secrets/secrets.go index 5acfcb8..75ae75c 100644 --- a/cmd/secrets/secrets.go +++ b/cmd/secrets/secrets.go @@ -5,9 +5,10 @@ import ( ) var SecretsCmd = &cobra.Command{ - Use: "secrets", - Short: "Manage encryption and decryption of secrets", - Long: "Encrypt and decrypt values under a master key, and generate the keys themselves. Encrypted values are what the seed engine's secrets: map stores and decrypts at boot.", + Use: "secrets", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Manage encryption and decryption of secrets", + Long: "Encrypt and decrypt values under a master key, and generate the keys themselves. Encrypted values are what the seed engine's secrets: map stores and decrypts at boot.", } func init() { diff --git a/cmd/seed/apply.go b/cmd/seed/apply.go index 6648503..9c02c9a 100644 --- a/cmd/seed/apply.go +++ b/cmd/seed/apply.go @@ -14,6 +14,7 @@ var applyCmd = &cobra.Command{ Short: "Project seed content onto the filesystem", Long: "Apply the seed source to the filesystem — mirror bare files and run the .seed.yaml operations. Writes only where the destination is missing unless --force; pass destinations to limit the run to those paths.", SilenceUsage: true, + Annotations: map[string]string{"since": "next"}, RunE: runApply, } diff --git a/cmd/seed/ls.go b/cmd/seed/ls.go index 169ff34..fe1fa79 100644 --- a/cmd/seed/ls.go +++ b/cmd/seed/ls.go @@ -9,10 +9,11 @@ import ( ) var lsCmd = &cobra.Command{ - Use: "ls", - Short: "List seed destinations and their behaviors", - Long: "List what apply would write — each destination with its operation and whether it carries a secret or a template — without touching the filesystem.", - RunE: runLs, + Use: "ls", + Short: "List seed destinations and their behaviors", + Long: "List what apply would write — each destination with its operation and whether it carries a secret or a template — without touching the filesystem.", + Annotations: map[string]string{"since": "next"}, + RunE: runLs, } func runLs(cmd *cobra.Command, args []string) error { diff --git a/cmd/seed/rotate.go b/cmd/seed/rotate.go index 0c9ad4a..9debd3e 100644 --- a/cmd/seed/rotate.go +++ b/cmd/seed/rotate.go @@ -10,6 +10,7 @@ var rotateCmd = &cobra.Command{ Short: "Re-encrypt managed secrets under a new master key", Long: "Re-encrypt every managed secret from the old master key (--master) to a new one (--new-master), in place. All-or-nothing: it verifies every secret decrypts before writing anything.", SilenceUsage: true, + Annotations: map[string]string{"since": "next"}, RunE: runRotate, } diff --git a/cmd/seed/seed.go b/cmd/seed/seed.go index 9cc88f9..ed38e4f 100644 --- a/cmd/seed/seed.go +++ b/cmd/seed/seed.go @@ -6,9 +6,10 @@ import ( ) var SeedCmd = &cobra.Command{ - Use: "seed", - Short: "Project declarative content onto the filesystem", - Long: "Copy files and apply small edits from a seed source onto the filesystem at boot. Bare files mirror verbatim; a .seed.yaml manifest overlays behavior — copy, merge, append — and decrypts secrets under the master key. Point --source at a mounted volume to seed a container from durable storage.", + Use: "seed", + Short: "Project declarative content onto the filesystem", + Long: "Copy files and apply small edits from a seed source onto the filesystem at boot. Bare files mirror verbatim; a .seed.yaml manifest overlays behavior — copy, merge, append — and decrypts secrets under the master key. Point --source at a mounted volume to seed a container from durable storage.", + Annotations: map[string]string{"since": "next"}, } func init() { diff --git a/cmd/serve/current.go b/cmd/serve/current.go index 0ab9e21..ff1fb2f 100644 --- a/cmd/serve/current.go +++ b/cmd/serve/current.go @@ -10,9 +10,10 @@ import ( ) var currentCmd = &cobra.Command{ - Use: "current", - Short: "Serve current directory as a static site", - Long: "Serve the current directory over HTTP as a static site — a quick way to preview built files.", + Use: "current", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Serve current directory as a static site", + Long: "Serve the current directory over HTTP as a static site — a quick way to preview built files.", RunE: func(cmd *cobra.Command, args []string) error { port, _ := cmd.Flags().GetInt("port") bind, _ := cmd.Flags().GetString("bind") diff --git a/cmd/serve/font.go b/cmd/serve/font.go index d8d1ef9..9ace5ef 100644 --- a/cmd/serve/font.go +++ b/cmd/serve/font.go @@ -9,9 +9,10 @@ import ( ) var fontCmd = &cobra.Command{ - Use: "font", - Short: "Serve fonts for local download", - Long: "Serve the installed fonts over HTTP so a browser can fetch them for local install.", + Use: "font", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Serve fonts for local download", + Long: "Serve the installed fonts over HTTP so a browser can fetch them for local install.", RunE: func(cmd *cobra.Command, args []string) error { port, _ := cmd.Flags().GetInt("port") bind, _ := cmd.Flags().GetString("bind") diff --git a/cmd/serve/metrics.go b/cmd/serve/metrics.go index 5638e4d..f96afe1 100644 --- a/cmd/serve/metrics.go +++ b/cmd/serve/metrics.go @@ -11,8 +11,9 @@ import ( ) var metricsCmd = &cobra.Command{ - Use: "metrics", - Short: "Start the Prometheus metrics server", + Use: "metrics", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Start the Prometheus metrics server", RunE: func(cmd *cobra.Command, args []string) error { port, _ := cmd.Flags().GetInt("port") collectors, _ := cmd.Flags().GetStringSlice("collectors") diff --git a/cmd/serve/serve.go b/cmd/serve/serve.go index ae62bc3..acde043 100644 --- a/cmd/serve/serve.go +++ b/cmd/serve/serve.go @@ -3,9 +3,10 @@ package serve import "github.com/spf13/cobra" var ServeCmd = &cobra.Command{ - Use: "serve", - Short: "Serve internal assets", - Long: "Run a small HTTP server for local assets — fonts or the current directory — on --port (default 38080).", + Use: "serve", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Serve internal assets", + Long: "Run a small HTTP server for local assets — fonts or the current directory — on --port (default 38080).", } func init() { diff --git a/cmd/show/env.go b/cmd/show/env.go index 41c4ae4..4db93cf 100644 --- a/cmd/show/env.go +++ b/cmd/show/env.go @@ -15,10 +15,11 @@ import ( var osExit = os.Exit var envCmd = &cobra.Command{ - Use: "env ", - Short: "Display the resolved value of a workspace environment variable", - Long: "Resolve a setting by its dotted key (server.port) and print it with its source and description. --value emits just the value for scripts, --as bool|int|list validates the shape, --check tests whether it is set so a startup script can guard on it.", - Args: cobra.ExactArgs(1), + Use: "env ", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display the resolved value of a workspace environment variable", + Long: "Resolve a setting by its dotted key (server.port) and print it with its source and description. --value emits just the value for scripts, --as bool|int|list validates the shape, --check tests whether it is set so a startup script can guard on it.", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { dotted := args[0] diff --git a/cmd/show/ip.go b/cmd/show/ip.go index e52b904..d0f07b3 100644 --- a/cmd/show/ip.go +++ b/cmd/show/ip.go @@ -7,16 +7,18 @@ import ( ) var ipCmd = &cobra.Command{ - Use: "ip", - Short: "Display IP addresses", - Long: "Print the workspace's IP addresses — the internal container address or the node it runs on.", + Use: "ip", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display IP addresses", + Long: "Print the workspace's IP addresses — the internal container address or the node it runs on.", } func makeIPCmd(use, short, long, title string, getter func() (string, error)) *cobra.Command { return &cobra.Command{ - Use: use, - Short: short, - Long: long, + Use: use, + Annotations: map[string]string{"since": "0.2.0"}, + Short: short, + Long: long, RunE: func(cmd *cobra.Command, args []string) error { ip, err := getter() if err != nil { diff --git a/cmd/show/path.go b/cmd/show/path.go index 837b462..856eb40 100644 --- a/cmd/show/path.go +++ b/cmd/show/path.go @@ -8,15 +8,17 @@ import ( ) var pathCmd = &cobra.Command{ - Use: "path", - Short: "Display various paths", - Long: "Print well-known workspace paths — the home root or the VS Code settings file.", + Use: "path", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display various paths", + Long: "Print well-known workspace paths — the home root or the VS Code settings file.", } var pathHomeCmd = &cobra.Command{ - Use: "home", - Short: "Display the workspace home path", - Long: "Print the workspace home (server root) path.", + Use: "home", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display the workspace home path", + Long: "Print the workspace home (server root) path.", RunE: func(cmd *cobra.Command, args []string) error { homePath := config.MustResolve("server", "root") @@ -33,9 +35,10 @@ var pathHomeCmd = &cobra.Command{ } var pathVscodeCmd = &cobra.Command{ - Use: "vscode-settings", - Short: "Display the VS Code settings path", - Long: "Print the path to the VS Code settings file — the user file by default, or the folder's with --workspace.", + Use: "vscode-settings", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display the VS Code settings path", + Long: "Print the path to the VS Code settings file — the user file by default, or the folder's with --workspace.", RunE: func(cmd *cobra.Command, args []string) error { useWorkspace, _ := cmd.Flags().GetBool("workspace") diff --git a/cmd/show/show.go b/cmd/show/show.go index 8167cca..0576ae7 100644 --- a/cmd/show/show.go +++ b/cmd/show/show.go @@ -5,9 +5,10 @@ import ( ) var ShowCmd = &cobra.Command{ - Use: "show", - Short: "Display information about the current workspace instance", - Long: "Resolve and print facts about this workspace instance — settings, IP addresses, and paths. --raw drops the styling for use in scripts.", + Use: "show", + Annotations: map[string]string{"since": "0.2.0"}, + Short: "Display information about the current workspace instance", + Long: "Resolve and print facts about this workspace instance — settings, IP addresses, and paths. --raw drops the styling for use in scripts.", } func init() { diff --git a/cmd/template/apply.go b/cmd/template/apply.go index b0f943e..23e0c04 100644 --- a/cmd/template/apply.go +++ b/cmd/template/apply.go @@ -10,6 +10,7 @@ import ( var applyCmd = &cobra.Command{ Use: "apply