Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ Add custom color schemes using Windows Terminal JSON format in the `schemes` arr
| Flag | Description |
|---|---|
| `--help`, `-h`, `help` | Show usage information |
| `--version`, `-v`, `version` | Print the version and exit |
| `--version`, `-v`, `version` | Print the version and exit. Add `--json` for script output |
| `update` | Update dispatch to the latest release |
| `--demo` | Load a demo database with synthetic sessions |
| `--reindex` | Full chronicle reindex via Copilot CLI (falls back to FTS5 rebuild) |
Expand Down
38 changes: 37 additions & 1 deletion cmd/dispatch/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,39 @@ var (
doctorSessionCountFn = defaultSessionCount
)

type versionOutput struct {
Version string `json:"version"`
}

func runVersion(w io.Writer, args []string) error {
if w == nil {
w = io.Discard
}

jsonOut := false
rest := args
if len(rest) > 0 {
rest = rest[1:]
}
for _, arg := range rest {
switch arg {
case "--json":
jsonOut = true
default:
if strings.HasPrefix(arg, "-") {
return fmt.Errorf("unknown flag: %s", arg)
}
return fmt.Errorf("version does not take positional arguments, got %q", arg)
}
}

if jsonOut {
return json.NewEncoder(w).Encode(versionOutput{Version: version.Version})
}
_, err := fmt.Fprintln(w, version.Version)
return err
}

func handleArgs(args []string, origStderr io.Writer, updateCh <-chan *update.UpdateInfo) (done bool, cleanup func(), startup startupOptions, err error) {
var flags startupFlags
for i := 0; i < len(args); i++ {
Expand All @@ -57,7 +90,10 @@ func handleArgs(args []string, origStderr io.Writer, updateCh <-chan *update.Upd
return true, cleanup, startupOptions{}, nil

case "--version", "-v", "version":
fmt.Println(version.Version)
if vErr := runVersion(os.Stdout, args[i:]); vErr != nil {
fmt.Fprintf(os.Stderr, "version: %v\n", vErr)
return true, cleanup, startupOptions{}, vErr
}
showUpdateNotification(origStderr, updateCh)
return true, cleanup, startupOptions{}, nil

Expand Down
41 changes: 41 additions & 0 deletions cmd/dispatch/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,47 @@ func TestHandleArgs_VersionCommand(t *testing.T) {
}
}

func TestRunVersionJSON(t *testing.T) {
for _, args := range [][]string{
{"version", "--json"},
{"--version", "--json"},
} {
var buf bytes.Buffer
if err := runVersion(&buf, args); err != nil {
t.Fatalf("runVersion(%v): %v", args, err)
}
var out versionOutput
if err := json.Unmarshal(buf.Bytes(), &out); err != nil {
t.Fatalf("output is not valid JSON: %v\n%s", err, buf.String())
}
if out.Version == "" {
t.Errorf("version should be set for args %v", args)
}
}
}

func TestRunVersionPlainText(t *testing.T) {
var buf bytes.Buffer
if err := runVersion(&buf, []string{"version"}); err != nil {
t.Fatalf("runVersion: %v", err)
}
if got := strings.TrimSpace(buf.String()); got == "" || strings.HasPrefix(got, "{") {
t.Errorf("plain version output = %q", got)
}
}

func TestRunVersionRejectsExtraArgs(t *testing.T) {
cases := [][]string{
{"version", "--yaml"},
{"version", "extra"},
}
for _, args := range cases {
if err := runVersion(io.Discard, args); err == nil {
t.Errorf("runVersion(%v) returned nil error, want error", args)
}
}
}

func TestRunCompletion_SupportedShells(t *testing.T) {
for _, tc := range []struct {
shell string
Expand Down
2 changes: 1 addition & 1 deletion cmd/dispatch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Usage:

Commands:
help Show this help message
version Print the version
version [--json] Print the version
open <id> [--mode M] Resume a session by ID (M: inplace, tab, window, pane)
--print writes the resume command instead of launching
open --last [--mode M] Resume the most recently active session
Expand Down
11 changes: 8 additions & 3 deletions cmd/dispatch/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,13 +457,18 @@ func TestPrintUsage_Output(t *testing.T) {
origStdout := os.Stdout
os.Stdout = w

var buf bytes.Buffer
readDone := make(chan struct{})
go func() {
_, _ = io.Copy(&buf, r)
close(readDone)
}()

printUsage()

w.Close()
os.Stdout = origStdout

var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
<-readDone

output := buf.String()
for _, want := range []string{"dispatch", "help", "version", "update", "--demo"} {
Expand Down