Skip to content

Commit 1af95e8

Browse files
committed
Replace bs version subcommand with --version flag showing client and server versions
bs --version (-v) now contacts the server and prints both client and server versions as plain text, or 'server: unavailable' if the server cannot be reached. This makes it a quick way to check server presence. The separate 'bs version' subcommand is removed.
1 parent cb90fbc commit 1af95e8

5 files changed

Lines changed: 61 additions & 65 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ The client also reads `BS_TOKEN`, `BS_USER`, and `BS_URL` from a `.env` file in
125125

126126
| Command | Description |
127127
|---------|-------------|
128+
| `bs -v` / `bs --version` | Show client version and server version (or `server: unavailable` if unreachable) |
128129
| `bs whoami` | Print current agent identity (local, no server contact) |
129130
| `bs add "title"` | Create a bead (`--type`, `--priority`, `--description`, `--tags`, `--parent <id>`, `--status open\|not_ready`) |
130131
| `bs show <id>` | Show full bead details |
@@ -144,7 +145,7 @@ The client also reads `BS_TOKEN`, `BS_USER`, and `BS_URL` from a `.env` file in
144145
| `bs move <id> --into <epic-id>` | Move a bead into an epic (set parent) |
145146
| `bs move <id> --out` | Detach a bead from its parent epic |
146147

147-
All output is pretty-printed JSON. IDs are short by default (`bd-` + 4 chars) and must be specified exactly and in full.
148+
All command output is pretty-printed JSON except `--version`, which outputs plain text. IDs are short by default (`bd-` + 4 chars) and must be specified exactly and in full.
148149

149150
Hidden aliases (not shown in `bs --help`): `bs create` = `bs add`, `bs resolve` = `bs close`.
150151

internal/cli/commands_query.go

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,13 @@
11
package cli
22

33
import (
4-
"encoding/json"
54
"fmt"
65
"net/url"
76
"strconv"
87

98
"github.com/spf13/cobra"
109
)
1110

12-
func newVersionCmd() *cobra.Command {
13-
return &cobra.Command{
14-
Use: "version",
15-
Short: "Show client and server versions",
16-
RunE: func(cmd *cobra.Command, args []string) error {
17-
c, err := NewClientFromEnv()
18-
if err != nil {
19-
return err
20-
}
21-
22-
data, err := c.Do("GET", "/api/v1/version", nil)
23-
if err != nil {
24-
return err
25-
}
26-
27-
var serverResp struct {
28-
Version string `json:"version"`
29-
}
30-
if err := json.Unmarshal(data, &serverResp); err != nil {
31-
return fmt.Errorf("parsing server response: %w", err)
32-
}
33-
34-
combined := struct {
35-
Client string `json:"client"`
36-
Server string `json:"server"`
37-
}{Client: version, Server: serverResp.Version}
38-
b, err := json.Marshal(combined)
39-
if err != nil {
40-
return err
41-
}
42-
out, err := prettyJSON(b)
43-
if err != nil {
44-
return err
45-
}
46-
fmt.Fprintln(cmd.OutOrStdout(), out)
47-
return nil
48-
},
49-
}
50-
}
51-
5211
func newListCmd() *cobra.Command {
5312
var all bool
5413
var ready bool

internal/cli/commands_query_test.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,6 @@ import (
99
"github.com/yourorg/beads_server/internal/store"
1010
)
1111

12-
func TestVersionCmd(t *testing.T) {
13-
ts := startTestServerWithVersion(t, "9.8.7")
14-
setClientEnv(t, ts.URL)
15-
16-
out := runCmd(t, "version")
17-
18-
var result map[string]string
19-
if err := json.Unmarshal([]byte(out), &result); err != nil {
20-
t.Fatalf("parse output: %v\noutput: %s", err, out)
21-
}
22-
if result["server"] != "9.8.7" {
23-
t.Errorf("server = %q, want 9.8.7", result["server"])
24-
}
25-
if result["client"] != version {
26-
t.Errorf("client = %q, want %q", result["client"], version)
27-
}
28-
}
29-
3012
func TestList_Default(t *testing.T) {
3113
ts := startTestServer(t)
3214
setClientEnv(t, ts.URL)

internal/cli/commands_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,38 @@ func TestRedirectCmd_BlockHelp(t *testing.T) {
606606
}
607607
}
608608

609+
func TestVersionFlag(t *testing.T) {
610+
ts := startTestServerWithVersion(t, "9.8.7")
611+
setClientEnv(t, ts.URL)
612+
613+
out := runCmd(t, "--version")
614+
615+
if !strings.Contains(out, "client: "+version) {
616+
t.Errorf("expected client version in output, got: %s", out)
617+
}
618+
if !strings.Contains(out, "server: 9.8.7") {
619+
t.Errorf("expected server: 9.8.7 in output, got: %s", out)
620+
}
621+
}
622+
623+
func TestVersionFlagServerUnavailable(t *testing.T) {
624+
os.Setenv("BS_URL", "http://localhost:59999")
625+
os.Setenv("BS_TOKEN", testToken)
626+
t.Cleanup(func() {
627+
os.Unsetenv("BS_URL")
628+
os.Unsetenv("BS_TOKEN")
629+
})
630+
631+
out := runCmd(t, "--version")
632+
633+
if !strings.Contains(out, "client: "+version) {
634+
t.Errorf("expected client version in output, got: %s", out)
635+
}
636+
if !strings.Contains(out, "server: unavailable") {
637+
t.Errorf("expected server: unavailable in output, got: %s", out)
638+
}
639+
}
640+
609641
func TestRedirectCmdsHiddenFromHelp(t *testing.T) {
610642
out := runCmd(t, "--help")
611643
if strings.Contains(out, "\n depend") {

internal/cli/root.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package cli
22

33
import (
4+
"encoding/json"
5+
"fmt"
6+
47
"github.com/spf13/cobra"
58
)
69

@@ -9,12 +12,32 @@ var version = "dev"
912
// NewRootCmd creates the root cobra command for the bs CLI.
1013
func NewRootCmd() *cobra.Command {
1114
root := &cobra.Command{
12-
Use: "bs",
13-
Short: "Beads server CLI",
14-
Long: "Beads server CLI — a tool for managing beads (issues/tasks).\n\nClient commands require BS_TOKEN and optionally BS_URL (default http://localhost:9999).",
15-
Version: version,
15+
Use: "bs",
16+
Short: "Beads server CLI",
17+
Long: "Beads server CLI — a tool for managing beads (issues/tasks).\n\nClient commands require BS_TOKEN and optionally BS_URL (default http://localhost:9999).",
18+
RunE: func(cmd *cobra.Command, args []string) error {
19+
showVersion, _ := cmd.Flags().GetBool("version")
20+
if !showVersion {
21+
return cmd.Help()
22+
}
23+
fmt.Fprintf(cmd.OutOrStdout(), "client: %s\n", version)
24+
serverVersion := "unavailable"
25+
if c, err := NewClientFromEnv(); err == nil {
26+
if data, err := c.Do("GET", "/api/v1/version", nil); err == nil {
27+
var resp struct {
28+
Version string `json:"version"`
29+
}
30+
if json.Unmarshal(data, &resp) == nil && resp.Version != "" {
31+
serverVersion = resp.Version
32+
}
33+
}
34+
}
35+
fmt.Fprintf(cmd.OutOrStdout(), "server: %s\n", serverVersion)
36+
return nil
37+
},
1638
}
1739

40+
root.Flags().BoolP("version", "v", false, "show version information")
1841
root.CompletionOptions.DisableDefaultCmd = true
1942
root.AddGroup(
2043
&cobra.Group{ID: "server", Title: "Server Commands:"},
@@ -26,7 +49,6 @@ func NewRootCmd() *cobra.Command {
2649
root.AddCommand(serveCmd)
2750

2851
for _, cmd := range []*cobra.Command{
29-
newVersionCmd(),
3052
newWhoamiCmd(),
3153
newAddCmd(),
3254
newShowCmd(),

0 commit comments

Comments
 (0)