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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ dispatch
7. Press `s` to cycle sort fields, `S` to flip direction
8. Press `,` to open settings — change theme, launch mode, model, and more

### Shell Completion

Print completion scripts for supported shells:

```sh
dispatch completion bash
dispatch completion zsh
dispatch completion powershell
```

### Diagnostics

Run `dispatch doctor` to print setup checks for the config file, session store, session-state directory, and Copilot CLI binary.
Expand Down
87 changes: 87 additions & 0 deletions cmd/dispatch/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ func handleArgs(args []string, origStderr io.Writer, updateCh <-chan *update.Upd
}
return true, cleanup, nil

case "completion":
if len(args) < 2 {
err := errors.New("completion requires a shell: bash, zsh, or powershell")
fmt.Fprintf(os.Stderr, "completion: %v\n", err)
return true, cleanup, err
}
if cErr := runCompletion(os.Stdout, args[1]); cErr != nil {
fmt.Fprintf(os.Stderr, "completion: %v\n", cErr)
return true, cleanup, cErr
}
return true, cleanup, nil

case "doctor":
runDoctor(os.Stdout)
showUpdateNotification(origStderr, updateCh)
Expand Down Expand Up @@ -106,6 +118,81 @@ func handleArgs(args []string, origStderr io.Writer, updateCh <-chan *update.Upd
return false, cleanup, nil
}

func runCompletion(w io.Writer, shell string) error {
if w == nil {
w = io.Discard
}
switch strings.ToLower(shell) {
case "bash":
fmt.Fprint(w, bashCompletionScript)
case "zsh":
fmt.Fprint(w, zshCompletionScript)
case "powershell", "pwsh":
fmt.Fprint(w, powershellCompletionScript)
default:
return fmt.Errorf("unsupported shell %q (want bash, zsh, or powershell)", shell)
}
return nil
}

const bashCompletionScript = `# bash completion for dispatch
_dispatch_completion() {
local cur="${COMP_WORDS[COMP_CWORD]}"
local commands="help version update completion"
local flags="-h --help -v --version --demo --clear-cache --reindex"

if [[ "${COMP_CWORD}" -eq 1 ]]; then
COMPREPLY=( $(compgen -W "${commands} ${flags}" -- "${cur}") )
return 0
fi

if [[ "${COMP_WORDS[1]}" == "completion" ]]; then
COMPREPLY=( $(compgen -W "bash zsh powershell" -- "${cur}") )
return 0
fi
}
complete -F _dispatch_completion dispatch disp
`

const zshCompletionScript = `#compdef dispatch disp
_dispatch_completion() {
local -a commands shells flags
commands=(help version update completion)
shells=(bash zsh powershell)
flags=(-h --help -v --version --demo --clear-cache --reindex)

if (( CURRENT == 2 )); then
_describe -t commands 'dispatch command' commands || _describe -t flags 'dispatch flag' flags
return
fi

if [[ ${words[2]} == completion ]]; then
_describe -t shells 'shell' shells
return
fi
}
_dispatch_completion "$@"
`

const powershellCompletionScript = `# PowerShell completion for dispatch
$script:DispatchCommands = @('help', 'version', 'update', 'completion')
$script:DispatchFlags = @('-h', '--help', '-v', '--version', '--demo', '--clear-cache', '--reindex')
$script:DispatchShells = @('bash', 'zsh', 'powershell')

Register-ArgumentCompleter -Native -CommandName dispatch, disp -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$tokens = @($commandAst.CommandElements | ForEach-Object { $_.ToString() })
$values = if ($tokens.Count -ge 2 -and $tokens[1] -eq 'completion') {
$script:DispatchShells
} else {
$script:DispatchCommands + $script:DispatchFlags
}
$values |
Where-Object { $_ -like "$wordToComplete*" } |
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
}
`

func runDoctor(w io.Writer) {
if w == nil {
w = io.Discard
Expand Down
60 changes: 60 additions & 0 deletions cmd/dispatch/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,66 @@ func TestHandleArgs_VersionCommand(t *testing.T) {
}
}

func TestRunCompletion_SupportedShells(t *testing.T) {
for _, tc := range []struct {
shell string
want string
}{
{"bash", "complete -F _dispatch_completion dispatch disp"},
{"zsh", "#compdef dispatch disp"},
{"powershell", "Register-ArgumentCompleter"},
{"pwsh", "Register-ArgumentCompleter"},
} {
t.Run(tc.shell, func(t *testing.T) {
var buf bytes.Buffer
if err := runCompletion(&buf, tc.shell); err != nil {
t.Fatalf("runCompletion: %v", err)
}
if !strings.Contains(buf.String(), tc.want) {
t.Errorf("completion output missing %q:\n%s", tc.want, buf.String())
}
})
}
}

func TestRunCompletion_UnsupportedShell(t *testing.T) {
var buf bytes.Buffer
err := runCompletion(&buf, "fish")
if err == nil {
t.Fatal("expected error for unsupported shell")
}
if !strings.Contains(err.Error(), "unsupported shell") {
t.Errorf("error = %v", err)
}
}

func TestHandleArgs_Completion(t *testing.T) {
ch := make(chan *update.UpdateInfo, 1)

done, cleanup, err := handleArgs([]string{"completion", "bash"}, io.Discard, ch)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !done {
t.Error("expected done=true for completion")
}
if cleanup != nil {
t.Error("expected cleanup=nil for completion")
}
}

func TestHandleArgs_CompletionMissingShell(t *testing.T) {
ch := make(chan *update.UpdateInfo, 1)

done, _, err := handleArgs([]string{"completion"}, io.Discard, ch)
if err == nil {
t.Fatal("expected error for missing shell")
}
if !done {
t.Error("expected done=true for completion error")
}
}

func TestRunDoctor_PrintsDiagnostics(t *testing.T) {
db := filepath.Join(t.TempDir(), "session-store.db")
if err := os.WriteFile(db, []byte("sqlite"), 0o600); err != nil {
Expand Down
1 change: 1 addition & 0 deletions cmd/dispatch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Usage:
Commands:
help Show this help message
version Print the version
completion <shell> Print shell completion (bash, zsh, powershell)
doctor Print environment diagnostics
update Update dispatch to the latest release

Expand Down