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
14 changes: 14 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@

The examples assume `jevwise` is installed on PATH. For a source build, use `bin/jevwise` instead.

## Shell completion

Enable completion in your current Zsh session (after installing/upgrading Jevwise):

```sh
autoload -Uz compinit
compinit
source <(jevwise completion zsh)
```

- For Bash, use `source <(jevwise completion bash)` instead. Fish and PowerShell scripts are available through `jevwise completion fish` and `jevwise completion powershell`.
- `jevwise mcp install claude --scope <Tab>` suggests `user`, `local`, `project`; Codex suggests only `user`. Prefixes and `--scope=prefix` also work, without filename suggestions.
- Completion reads no decision configuration or credentials and never invokes a client installer. Use a binary containing the new completion callback; source/worktree changes do not update an installed binary automatically.

## Decisions

```sh
Expand Down
40 changes: 40 additions & 0 deletions internal/cli/mcp_completion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cli

import (
"context"
"strings"
"testing"
)

func TestMCPInstallScopeCompletionIsClientSpecificAndLazy(t *testing.T) {
decisionEnvironment(t)
for _, tt := range []struct {
args []string
want string
}{
{[]string{"mcp", "install", "claude", "--scope", ""}, "user\nlocal\nproject\n:4\n"},
{[]string{"mcp", "install", "codex", "--scope", ""}, "user\n:4\n"},
{[]string{"mcp", "install", "--scope", ""}, "user\nlocal\nproject\n:4\n"},
{[]string{"mcp", "install", "claude", "--scope", "l"}, "local\n:4\n"},
{[]string{"mcp", "install", "claude", "--scope=pr"}, "project\n:4\n"},
{[]string{"mcp", "install", "codex", "--scope=u"}, "user\n:4\n"},
{[]string{"mcp", "install", "codex", "--scope", "l"}, ":4\n"},
{[]string{"mcp", "install", "claude", "--scope", "private-prefix"}, ":4\n"},
} {
for _, hidden := range []string{"__complete", "__completeNoDesc"} {
root := installerRoot(t, func(context.Context, string, ...string) ([]byte, error) {
t.Fatal("completion invoked a client")
return nil, nil
})
// Completion must remain available with unusable runtime settings.
args := []string{hidden, "--debug", "--config", "/missing", "--timeout", "invalid", "--api-key", "private-key"}
out, stderr, err := execute(root, append(args, tt.args...)...)
if err != nil || out != tt.want || !strings.Contains(stderr, "ShellCompDirectiveNoFileComp") {
t.Fatal("scope completion candidates or no-file directive changed", tt.args, err)
}
if strings.Contains(stderr, "mcp.install.register") || strings.Contains(out+stderr, "private") {
t.Fatal("completion ran installation or exposed input")
}
}
}
}
14 changes: 14 additions & 0 deletions internal/cli/mcp_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"io"
"strings"
"time"

"github.com/bitbrew-dev/jevwise/internal/debuglog"
Expand All @@ -24,6 +25,19 @@ func newMCPInstall(run agentCommand) *cobra.Command {
RunE: func(cmd *cobra.Command, _ []string) error { return cmd.Help() }}
cmd.PersistentFlags().StringVar(&address, "listen", "127.0.0.1:8080", "Server's literal loopback IP:port (same as mcp --listen)")
cmd.PersistentFlags().StringVar(&scope, "scope", "user", "Client settings scope: user; Claude also supports local or project")
_ = cmd.RegisterFlagCompletionFunc("scope", func(cmd *cobra.Command, _ []string, prefix string) ([]string, cobra.ShellCompDirective) {
choices := []string{"user"}
if cmd.Name() != "codex" {
choices = append(choices, "local", "project")
}
var matches []string
for _, choice := range choices {
if strings.HasPrefix(choice, prefix) {
matches = append(matches, choice)
}
}
return matches, cobra.ShellCompDirectiveNoFileComp
})
for _, client := range []string{"codex", "claude"} {
cmd.AddCommand(&cobra.Command{Use: client, Short: "Register Jevwise with " + client, Args: noArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
Expand Down
Loading