Skip to content

refactor: use services for commands - #1434

Draft
mrexox wants to merge 6 commits into
evilmartians:masterfrom
mrexox:refactor/use-services-for-commands
Draft

refactor: use services for commands#1434
mrexox wants to merge 6 commits into
evilmartians:masterfrom
mrexox:refactor/use-services-for-commands

Conversation

@mrexox

@mrexox mrexox commented May 30, 2026

Copy link
Copy Markdown
Member

Closes # (issue)

Context

Changes

Confidence Score: 1/5

Not safe to merge — the build does not compile and two core commands are no-ops.

Three distinct compile errors will prevent any binary from being produced. Even if those were patched, lefthook run and lefthook uninstall both return nil without doing anything, silently breaking the two most common user workflows. Additionally, loadConfig dereferences a potentially nil pointer and ForEachRemote can crash the process via panic on bad config.

internal/commands/run.go, internal/commands/uninstall.go, and internal/app/hooks_service.go have compile errors. internal/app/config_service.go needs attention for the nil-deref, panic, and unsafe type assertions.

Comments Outside Diff (1)

  1. internal/app/config_service.go, line 625-633 (link)

    P1 Unchecked type assertions in setColors can panic

    In the map[string]any branch, each color is extracted with colorsTyped["cyan"].(string) and similar. If a key is absent from the map the assertion is on nil, and if the value is not actually a string both cases produce a runtime panic. The config is user-supplied, so a missing or wrong-typed color name is entirely plausible. Use the two-value assertion form (v, ok := colorsTyped["cyan"].(string)) and skip or fall back gracefully when !ok.

Reviews (1): Last reviewed commit: "chore: rebase the branch [skip ci]" | Re-trigger Greptile

Greptile also left 8 inline comments on this PR.

@mrexox

mrexox commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

@greptileai review

Comment thread internal/commands/run.go
@@ -0,0 +1,31 @@
package app

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Wrong package declaration — compile error

run.go and uninstall.go both live in internal/commands/ but declare package app. In Go every file in a directory must share the same package name; the other four files in this directory (add.go, check_install.go, dump.go, install.go) all declare package commands. The build will fail until these two files are corrected to package commands.

@@ -0,0 +1,16 @@
package app

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Wrong package declaration — compile error

Same issue as run.go: this file declares package app but belongs in internal/commands/ alongside files that declare package commands. The mismatch causes a build failure. Change the declaration to package commands.

Comment on lines +329 to +330
return errMsg.String()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 formatHooksPathError returns a string, not an error — compile error

errMsg.String() has type string, but the declared return type is error. strings.Builder does not implement the error interface (Error() string), so the compiler rejects this. Wrap it in errors.New or fmt.Errorf.

Suggested change
return errMsg.String()
}
return errors.New(errMsg.String())
}

Comment on lines +228 to +235
func (s *ConfigService) loadConfig() (*config.Config, error) {
cfg, err := config.Load(s.repo)

// Reset loaded colors
s.setColors(cfg.Colors)

return cfg, err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Nil-pointer dereference when config.Load returns an error

config.Load(s.repo) can return (nil, err). The very next line unconditionally reads cfg.Colors, which panics on a nil cfg. The color reset should only run when cfg is non-nil.

Suggested change
func (s *ConfigService) loadConfig() (*config.Config, error) {
cfg, err := config.Load(s.repo)
// Reset loaded colors
s.setColors(cfg.Colors)
return cfg, err
}
func (s *ConfigService) loadConfig() (*config.Config, error) {
cfg, err := config.Load(s.repo)
if err != nil {
return nil, err
}
// Reset loaded colors
s.setColors(cfg.Colors)
return cfg, err
}

Comment on lines +199 to +203
func (s *ConfigService) ForEachRemote(f func(remote *config.Remote)) {
cfg, err := s.Load()
if err != nil {
panic(err.Error())
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 ForEachRemote panics on load error — violates project rule

AGENTS.md explicitly forbids panic in production paths. If config loading fails here the process crashes with a stack trace instead of a clean error. The function signature should return error so callers can handle it, or at minimum the panic should be replaced with a logged warning.

Suggested change
func (s *ConfigService) ForEachRemote(f func(remote *config.Remote)) {
cfg, err := s.Load()
if err != nil {
panic(err.Error())
}
func (s *ConfigService) ForEachRemote(f func(remote *config.Remote)) error {
cfg, err := s.Load()
if err != nil {
return err
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread internal/commands/run.go
Comment on lines +27 to +29
}

func Run(ctx context.Context, app *app.App, args RunArgs) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Run is an unimplemented stub

The function body is return nil, meaning any invocation of lefthook run will silently succeed without executing any hooks. If this is intentional work in progress the PR description should say so; otherwise the missing implementation is a regression.

Comment on lines +14 to +16
func Uninstall(ctx context.Context, app *app.App, args UninstallArgs) error {
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Uninstall is an unimplemented stub

The function always returns nil, so lefthook uninstall will silently do nothing. If left merged as-is, installed hooks will never be removed when users run the uninstall command.

Comment thread cmd/add.go
Comment on lines 41 to +46
Action: func(ctx context.Context, cmd *cli.Command) error {
l, err := command.NewLefthook(verbose, "auto")
// l, err := command.NewLefthook(verbose, "auto")
// if err != nil {
// return err
// }
app, err := newApp(verbose, "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Dead commented-out code should be removed

The old command.NewLefthook call was left behind as a multi-line comment. It serves no documentation purpose and adds noise; remove it before merging.

Suggested change
Action: func(ctx context.Context, cmd *cli.Command) error {
l, err := command.NewLefthook(verbose, "auto")
// l, err := command.NewLefthook(verbose, "auto")
// if err != nil {
// return err
// }
app, err := newApp(verbose, "")
Action: func(ctx context.Context, cmd *cli.Command) error {
app, err := newApp(verbose, "")

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant