fix: resolve lint errors blocking CI
- Add explicit error checking for fmt.Fprintf/Fprintln in claude.go - Add gosec nolint for safe exec.CommandContext calls in sync_git.go - Remove unused error return from findTownRoutes in routes.go 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -71,21 +71,21 @@ func installClaude(env claudeEnv, project bool, stealth bool) error {
|
|||||||
var settingsPath string
|
var settingsPath string
|
||||||
if project {
|
if project {
|
||||||
settingsPath = projectSettingsPath(env.projectDir)
|
settingsPath = projectSettingsPath(env.projectDir)
|
||||||
fmt.Fprintln(env.stdout, "Installing Claude hooks for this project...")
|
_, _ = fmt.Fprintln(env.stdout, "Installing Claude hooks for this project...")
|
||||||
} else {
|
} else {
|
||||||
settingsPath = globalSettingsPath(env.homeDir)
|
settingsPath = globalSettingsPath(env.homeDir)
|
||||||
fmt.Fprintln(env.stdout, "Installing Claude hooks globally...")
|
_, _ = fmt.Fprintln(env.stdout, "Installing Claude hooks globally...")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := env.ensureDir(filepath.Dir(settingsPath), 0o755); err != nil {
|
if err := env.ensureDir(filepath.Dir(settingsPath), 0o755); err != nil {
|
||||||
fmt.Fprintf(env.stderr, "Error: %v\n", err)
|
_, _ = fmt.Fprintf(env.stderr, "Error: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings := make(map[string]interface{})
|
settings := make(map[string]interface{})
|
||||||
if data, err := env.readFile(settingsPath); err == nil {
|
if data, err := env.readFile(settingsPath); err == nil {
|
||||||
if err := json.Unmarshal(data, &settings); err != nil {
|
if err := json.Unmarshal(data, &settings); err != nil {
|
||||||
fmt.Fprintf(env.stderr, "Error: failed to parse settings.json: %v\n", err)
|
_, _ = fmt.Fprintf(env.stderr, "Error: failed to parse settings.json: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,26 +102,26 @@ func installClaude(env claudeEnv, project bool, stealth bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if addHookCommand(hooks, "SessionStart", command) {
|
if addHookCommand(hooks, "SessionStart", command) {
|
||||||
fmt.Fprintln(env.stdout, "✓ Registered SessionStart hook")
|
_, _ = fmt.Fprintln(env.stdout, "✓ Registered SessionStart hook")
|
||||||
}
|
}
|
||||||
if addHookCommand(hooks, "PreCompact", command) {
|
if addHookCommand(hooks, "PreCompact", command) {
|
||||||
fmt.Fprintln(env.stdout, "✓ Registered PreCompact hook")
|
_, _ = fmt.Fprintln(env.stdout, "✓ Registered PreCompact hook")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.MarshalIndent(settings, "", " ")
|
data, err := json.MarshalIndent(settings, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(env.stderr, "Error: marshal settings: %v\n", err)
|
_, _ = fmt.Fprintf(env.stderr, "Error: marshal settings: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := env.writeFile(settingsPath, data); err != nil {
|
if err := env.writeFile(settingsPath, data); err != nil {
|
||||||
fmt.Fprintf(env.stderr, "Error: write settings: %v\n", err)
|
_, _ = fmt.Fprintf(env.stderr, "Error: write settings: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(env.stdout, "\n✓ Claude Code integration installed")
|
_, _ = fmt.Fprintln(env.stdout, "\n✓ Claude Code integration installed")
|
||||||
fmt.Fprintf(env.stdout, " Settings: %s\n", settingsPath)
|
_, _ = fmt.Fprintf(env.stdout, " Settings: %s\n", settingsPath)
|
||||||
fmt.Fprintln(env.stdout, "\nRestart Claude Code for changes to take effect.")
|
_, _ = fmt.Fprintln(env.stdout, "\nRestart Claude Code for changes to take effect.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,14 +144,14 @@ func checkClaude(env claudeEnv) error {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case hasBeadsHooks(globalSettings):
|
case hasBeadsHooks(globalSettings):
|
||||||
fmt.Fprintf(env.stdout, "✓ Global hooks installed: %s\n", globalSettings)
|
_, _ = fmt.Fprintf(env.stdout, "✓ Global hooks installed: %s\n", globalSettings)
|
||||||
return nil
|
return nil
|
||||||
case hasBeadsHooks(projectSettings):
|
case hasBeadsHooks(projectSettings):
|
||||||
fmt.Fprintf(env.stdout, "✓ Project hooks installed: %s\n", projectSettings)
|
_, _ = fmt.Fprintf(env.stdout, "✓ Project hooks installed: %s\n", projectSettings)
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
fmt.Fprintln(env.stdout, "✗ No hooks installed")
|
_, _ = fmt.Fprintln(env.stdout, "✗ No hooks installed")
|
||||||
fmt.Fprintln(env.stdout, " Run: bd setup claude")
|
_, _ = fmt.Fprintln(env.stdout, " Run: bd setup claude")
|
||||||
return errClaudeHooksMissing
|
return errClaudeHooksMissing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,27 +173,27 @@ func removeClaude(env claudeEnv, project bool) error {
|
|||||||
var settingsPath string
|
var settingsPath string
|
||||||
if project {
|
if project {
|
||||||
settingsPath = projectSettingsPath(env.projectDir)
|
settingsPath = projectSettingsPath(env.projectDir)
|
||||||
fmt.Fprintln(env.stdout, "Removing Claude hooks from project...")
|
_, _ = fmt.Fprintln(env.stdout, "Removing Claude hooks from project...")
|
||||||
} else {
|
} else {
|
||||||
settingsPath = globalSettingsPath(env.homeDir)
|
settingsPath = globalSettingsPath(env.homeDir)
|
||||||
fmt.Fprintln(env.stdout, "Removing Claude hooks globally...")
|
_, _ = fmt.Fprintln(env.stdout, "Removing Claude hooks globally...")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := env.readFile(settingsPath)
|
data, err := env.readFile(settingsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(env.stdout, "No settings file found")
|
_, _ = fmt.Fprintln(env.stdout, "No settings file found")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings map[string]interface{}
|
var settings map[string]interface{}
|
||||||
if err := json.Unmarshal(data, &settings); err != nil {
|
if err := json.Unmarshal(data, &settings); err != nil {
|
||||||
fmt.Fprintf(env.stderr, "Error: failed to parse settings.json: %v\n", err)
|
_, _ = fmt.Fprintf(env.stderr, "Error: failed to parse settings.json: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks, ok := settings["hooks"].(map[string]interface{})
|
hooks, ok := settings["hooks"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Fprintln(env.stdout, "No hooks found")
|
_, _ = fmt.Fprintln(env.stdout, "No hooks found")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,16 +204,16 @@ func removeClaude(env claudeEnv, project bool) error {
|
|||||||
|
|
||||||
data, err = json.MarshalIndent(settings, "", " ")
|
data, err = json.MarshalIndent(settings, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(env.stderr, "Error: marshal settings: %v\n", err)
|
_, _ = fmt.Fprintf(env.stderr, "Error: marshal settings: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := env.writeFile(settingsPath, data); err != nil {
|
if err := env.writeFile(settingsPath, data); err != nil {
|
||||||
fmt.Fprintf(env.stderr, "Error: write settings: %v\n", err)
|
_, _ = fmt.Fprintf(env.stderr, "Error: write settings: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(env.stdout, "✓ Claude hooks removed")
|
_, _ = fmt.Fprintln(env.stdout, "✓ Claude hooks removed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ func gitPull(ctx context.Context) error {
|
|||||||
remote := strings.TrimSpace(string(remoteOutput))
|
remote := strings.TrimSpace(string(remoteOutput))
|
||||||
|
|
||||||
// Pull with explicit remote and branch
|
// Pull with explicit remote and branch
|
||||||
cmd := exec.CommandContext(ctx, "git", "pull", remote, branch)
|
cmd := exec.CommandContext(ctx, "git", "pull", remote, branch) //nolint:gosec // G204: remote/branch from git config, not user input
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git pull failed: %w\n%s", err, output)
|
return fmt.Errorf("git pull failed: %w\n%s", err, output)
|
||||||
@@ -422,7 +422,7 @@ func restoreBeadsDirFromBranch(ctx context.Context) error {
|
|||||||
|
|
||||||
// Restore .beads/ from HEAD (current branch's committed state)
|
// Restore .beads/ from HEAD (current branch's committed state)
|
||||||
// Using -- to ensure .beads/ is treated as a path, not a branch name
|
// Using -- to ensure .beads/ is treated as a path, not a branch name
|
||||||
cmd := exec.CommandContext(ctx, "git", "checkout", "HEAD", "--", beadsDir)
|
cmd := exec.CommandContext(ctx, "git", "checkout", "HEAD", "--", beadsDir) //nolint:gosec // G204: beadsDir from FindBeadsDir(), not user input
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git checkout failed: %w\n%s", err, output)
|
return fmt.Errorf("git checkout failed: %w\n%s", err, output)
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func LookupRigForgiving(input, beadsDir string) (Route, bool) {
|
|||||||
// lookupRigForgivingWithTown finds a route with flexible matching and returns the town root.
|
// lookupRigForgivingWithTown finds a route with flexible matching and returns the town root.
|
||||||
// Returns (route, townRoot, found).
|
// Returns (route, townRoot, found).
|
||||||
func lookupRigForgivingWithTown(input, beadsDir string) (Route, string, bool) {
|
func lookupRigForgivingWithTown(input, beadsDir string) (Route, string, bool) {
|
||||||
routes, townRoot, _ := findTownRoutes(beadsDir)
|
routes, townRoot := findTownRoutes(beadsDir)
|
||||||
if len(routes) == 0 {
|
if len(routes) == 0 {
|
||||||
return Route{}, "", false
|
return Route{}, "", false
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ func ResolveToExternalRef(id, beadsDir string) string {
|
|||||||
func ResolveBeadsDirForID(ctx context.Context, id, currentBeadsDir string) (string, bool, error) {
|
func ResolveBeadsDirForID(ctx context.Context, id, currentBeadsDir string) (string, bool, error) {
|
||||||
// Step 1: Check for routes.jsonl based on ID prefix
|
// Step 1: Check for routes.jsonl based on ID prefix
|
||||||
// First try local, then walk up to find town-level routes
|
// First try local, then walk up to find town-level routes
|
||||||
routes, townRoot, _ := findTownRoutes(currentBeadsDir)
|
routes, townRoot := findTownRoutes(currentBeadsDir)
|
||||||
if len(routes) > 0 {
|
if len(routes) > 0 {
|
||||||
prefix := ExtractPrefix(id)
|
prefix := ExtractPrefix(id)
|
||||||
if prefix != "" {
|
if prefix != "" {
|
||||||
@@ -294,29 +294,29 @@ func findTownRoot(startDir string) string {
|
|||||||
// findTownRoutes searches for routes.jsonl at the town level.
|
// findTownRoutes searches for routes.jsonl at the town level.
|
||||||
// It walks up from currentBeadsDir to find the town root, then loads routes
|
// It walks up from currentBeadsDir to find the town root, then loads routes
|
||||||
// from <townRoot>/.beads/routes.jsonl.
|
// from <townRoot>/.beads/routes.jsonl.
|
||||||
// Returns (routes, townRoot, error).
|
// Returns (routes, townRoot). Returns nil routes if not in a Gas Town or no routes found.
|
||||||
func findTownRoutes(currentBeadsDir string) ([]Route, string, error) {
|
func findTownRoutes(currentBeadsDir string) ([]Route, string) {
|
||||||
// First try the current beads dir (works if we're already at town level)
|
// First try the current beads dir (works if we're already at town level)
|
||||||
routes, err := LoadRoutes(currentBeadsDir)
|
routes, err := LoadRoutes(currentBeadsDir)
|
||||||
if err == nil && len(routes) > 0 {
|
if err == nil && len(routes) > 0 {
|
||||||
// Return the parent of the beads dir as "town root" for path resolution
|
// Return the parent of the beads dir as "town root" for path resolution
|
||||||
return routes, filepath.Dir(currentBeadsDir), nil
|
return routes, filepath.Dir(currentBeadsDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk up to find town root
|
// Walk up to find town root
|
||||||
townRoot := findTownRoot(currentBeadsDir)
|
townRoot := findTownRoot(currentBeadsDir)
|
||||||
if townRoot == "" {
|
if townRoot == "" {
|
||||||
return nil, "", nil // Not in a Gas Town
|
return nil, "" // Not in a Gas Town
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load routes from town beads
|
// Load routes from town beads
|
||||||
townBeadsDir := filepath.Join(townRoot, ".beads")
|
townBeadsDir := filepath.Join(townRoot, ".beads")
|
||||||
routes, err = LoadRoutes(townBeadsDir)
|
routes, err = LoadRoutes(townBeadsDir)
|
||||||
if err != nil || len(routes) == 0 {
|
if err != nil || len(routes) == 0 {
|
||||||
return nil, "", nil // No town routes
|
return nil, "" // No town routes
|
||||||
}
|
}
|
||||||
|
|
||||||
return routes, townRoot, nil
|
return routes, townRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveRedirect checks for a redirect file in the beads directory
|
// resolveRedirect checks for a redirect file in the beads directory
|
||||||
|
|||||||
Reference in New Issue
Block a user