Skip to content

Commit e5afb4a

Browse files
FelixIsaacclaude
andcommitted
Add URL validation + fix path display for Git Bash
- Validate repo URL format before cloning - Check remote accessibility with git ls-remote - Use forward slashes in path output (Git Bash compat) - Quote paths in instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a190eae commit e5afb4a

2 files changed

Lines changed: 53 additions & 2 deletions

File tree

internal/cmd/init.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"fmt"
55
"os"
6+
"strings"
67

78
"github.com/fatih/color"
89
"github.com/felixisaac/claude-code-sync/internal/config"
@@ -12,6 +13,11 @@ import (
1213
"github.com/spf13/cobra"
1314
)
1415

16+
// toUnixPath converts Windows backslashes to forward slashes for display
17+
func toUnixPath(path string) string {
18+
return strings.ReplaceAll(path, "\\", "/")
19+
}
20+
1521
var initCmd = &cobra.Command{
1622
Use: "init [repo-url]",
1723
Short: "Initialize sync (generate keys, clone/create repo)",
@@ -95,8 +101,19 @@ func runInit(cmd *cobra.Command, args []string) error {
95101
g := git.New(paths.RepoDir)
96102

97103
if repoURL != "" {
104+
// Validate URL format
105+
if !git.IsValidRepoURL(repoURL) {
106+
return fmt.Errorf("invalid repo URL: %s\nExpected format: https://github.com/user/repo or git@github.com:user/repo.git", repoURL)
107+
}
108+
109+
// Check if URL is reachable
110+
logInfo("Verifying repo URL...")
111+
if err := git.CheckRemote(repoURL); err != nil {
112+
return fmt.Errorf("cannot access repo: %w\nCheck the URL and your permissions", err)
113+
}
114+
98115
if g.IsRepo() {
99-
logWarn(fmt.Sprintf("Repo already exists at %s", paths.RepoDir))
116+
logWarn(fmt.Sprintf("Repo already exists at %s", toUnixPath(paths.RepoDir)))
100117
} else {
101118
logInfo("Cloning repo...")
102119
if err := git.Clone(repoURL, paths.RepoDir); err != nil {
@@ -115,7 +132,7 @@ func runInit(cmd *cobra.Command, args []string) error {
115132
}
116133
fmt.Println()
117134
logInfo("No repo URL provided. To add a remote later:")
118-
fmt.Printf(" git -C %s remote add origin <your-repo-url>\n", paths.RepoDir)
135+
fmt.Printf(" git -C \"%s\" remote add origin <your-repo-url>\n", toUnixPath(paths.RepoDir))
119136
fmt.Println(" claude-code-sync push")
120137
}
121138

internal/git/git.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,40 @@ func IsInstalled() bool {
145145
return err == nil
146146
}
147147

148+
// IsValidRepoURL checks if a string looks like a valid git repo URL
149+
func IsValidRepoURL(url string) bool {
150+
// HTTPS URLs
151+
if strings.HasPrefix(url, "https://") || strings.HasPrefix(url, "http://") {
152+
return strings.Contains(url, "/")
153+
}
154+
// SSH URLs (git@host:user/repo)
155+
if strings.HasPrefix(url, "git@") {
156+
return strings.Contains(url, ":")
157+
}
158+
// SSH URLs (ssh://git@host/user/repo)
159+
if strings.HasPrefix(url, "ssh://") {
160+
return strings.Contains(url, "/")
161+
}
162+
return false
163+
}
164+
165+
// CheckRemote verifies a remote URL is accessible
166+
func CheckRemote(url string) error {
167+
cmd := exec.Command("git", "ls-remote", "--exit-code", url)
168+
var stderr bytes.Buffer
169+
cmd.Stderr = &stderr
170+
171+
err := cmd.Run()
172+
if err != nil {
173+
errMsg := strings.TrimSpace(stderr.String())
174+
if errMsg != "" {
175+
return fmt.Errorf("%s", errMsg)
176+
}
177+
return fmt.Errorf("repository not found or not accessible")
178+
}
179+
return nil
180+
}
181+
148182
// CreateInitialCommit creates a README and initial commit
149183
func (g *Git) CreateInitialCommit() error {
150184
readme := filepath.Join(g.repoDir, "README.md")

0 commit comments

Comments
 (0)