Skip to content
This repository was archived by the owner on Feb 15, 2026. It is now read-only.

Commit 66c75ae

Browse files
feat: smart default command with onboarding and action menu
Running `keyway` without arguments now: - Non-interactive: shows help - Not logged in: runs full onboarding (login → init → push) - Logged in: shows action menu (pull/push/sync/dashboard) This makes `npx @keywaysh/cli` useful immediately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ace4ff5 commit 66c75ae

1 file changed

Lines changed: 96 additions & 2 deletions

File tree

internal/cmd/root.go

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ package cmd
22

33
import (
44
"fmt"
5+
"os"
56

67
"github.com/fatih/color"
8+
"github.com/keywaysh/cli/internal/auth"
9+
"github.com/keywaysh/cli/internal/git"
10+
"github.com/keywaysh/cli/internal/ui"
11+
"github.com/pkg/browser"
712
"github.com/spf13/cobra"
813
)
914

@@ -18,9 +23,98 @@ var rootCmd = &cobra.Command{
1823
Short: "Sync secrets with your team and infra",
1924
SilenceUsage: true,
2025
SilenceErrors: true,
21-
Run: func(cmd *cobra.Command, args []string) {
26+
RunE: runRoot,
27+
}
28+
29+
func runRoot(cmd *cobra.Command, args []string) error {
30+
// Check if running in non-interactive mode
31+
if !ui.IsInteractive() {
2232
printCustomHelp(cmd)
23-
},
33+
return nil
34+
}
35+
36+
// Check if user is logged in
37+
store := auth.NewStore()
38+
storedAuth, err := store.GetAuth()
39+
isLoggedIn := err == nil && storedAuth != nil && storedAuth.KeywayToken != ""
40+
41+
// Also check env var
42+
if os.Getenv("KEYWAY_TOKEN") != "" {
43+
isLoggedIn = true
44+
}
45+
46+
if !isLoggedIn {
47+
// Not logged in: run full onboarding flow
48+
return runOnboarding(cmd)
49+
}
50+
51+
// Logged in: show action menu
52+
return runActionMenu(cmd)
53+
}
54+
55+
func runOnboarding(cmd *cobra.Command) error {
56+
ui.Intro("welcome")
57+
58+
ui.Message("Let's set up Keyway for this project.")
59+
ui.Message("")
60+
61+
// Check if we're in a git repo
62+
repo, err := git.DetectRepo()
63+
if err != nil {
64+
ui.Error("Not in a git repository with GitHub remote")
65+
ui.Message(ui.Dim("Navigate to your project folder and try again."))
66+
return err
67+
}
68+
69+
ui.Step(fmt.Sprintf("Repository: %s", ui.Value(repo)))
70+
71+
// Run init (which handles login, GitHub App, vault creation, and push)
72+
return runInit(initCmd, nil)
73+
}
74+
75+
func runActionMenu(cmd *cobra.Command) error {
76+
fmt.Println()
77+
78+
// Show current repo if available
79+
repo, _ := git.DetectRepo()
80+
if repo != "" {
81+
ui.Step(fmt.Sprintf("Repository: %s", ui.Value(repo)))
82+
}
83+
84+
options := []string{
85+
"Pull secrets from vault",
86+
"Push secrets to vault",
87+
"Sync with Vercel/Railway/Netlify",
88+
"Open dashboard",
89+
"Show help",
90+
}
91+
92+
selected, err := ui.Select("What would you like to do?", options)
93+
if err != nil {
94+
return err
95+
}
96+
97+
switch selected {
98+
case "Pull secrets from vault":
99+
return runPull(pullCmd, nil)
100+
case "Push secrets to vault":
101+
return runPush(pushCmd, nil)
102+
case "Sync with Vercel/Railway/Netlify":
103+
return runSync(syncCmd, nil)
104+
case "Open dashboard":
105+
url := "https://www.keyway.sh/dashboard"
106+
if repo != "" {
107+
url = fmt.Sprintf("https://www.keyway.sh/dashboard/vaults/%s", repo)
108+
}
109+
ui.Success(fmt.Sprintf("Opening %s", ui.Link(url)))
110+
_ = browser.OpenURL(url)
111+
return nil
112+
case "Show help":
113+
printCustomHelp(cmd)
114+
return nil
115+
}
116+
117+
return nil
24118
}
25119

26120
func printCustomHelp(cmd *cobra.Command) {

0 commit comments

Comments
 (0)