From fa30c58c5ebfe7f7c671a395167c1c80c35ab642 Mon Sep 17 00:00:00 2001 From: Daniel Bos Date: Tue, 4 Aug 2026 13:16:01 +0800 Subject: [PATCH] feat: improve help and error texts --- cmd/edit.go | 2 +- cmd/find.go | 2 +- cmd/list.go | 7 ++++++- cmd/show.go | 4 ++-- cmd/update.go | 2 +- cmd/version.go | 1 + go.mod | 1 + go.sum | 2 ++ internal/app/edit.go | 18 ++++++++++++++++-- internal/app/find.go | 2 +- internal/app/find_format_test.go | 6 ++++++ internal/app/list.go | 2 +- internal/app/list_test.go | 6 ++++++ internal/app/show.go | 2 +- internal/app/table.go | 3 ++- internal/app/update.go | 2 +- 16 files changed, 49 insertions(+), 13 deletions(-) diff --git a/cmd/edit.go b/cmd/edit.go index 34c87d9..f1278e1 100644 --- a/cmd/edit.go +++ b/cmd/edit.go @@ -25,7 +25,7 @@ not set, the command will fail.`, number, err := strconv.Atoi(args[0]) if err != nil { - log.Printf("invalid argument: %v", err) + log.Printf("invalid id %q: expected a number", args[0]) return } diff --git a/cmd/find.go b/cmd/find.go index 825c559..38e68b8 100644 --- a/cmd/find.go +++ b/cmd/find.go @@ -27,7 +27,7 @@ Use --text to also search frontmatter fields and the body.`, Run: func(_ *cobra.Command, args []string) { outputFormat, err := app.ParseFormat(format) if err != nil { - log.Printf("invalid format %q: %v", format, err) + log.Printf("invalid format %q: must be one of: md, raw, json", format) return } diff --git a/cmd/list.go b/cmd/list.go index d65c44e..11ccf83 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -15,10 +15,15 @@ func NewListCommand(conf *config.Config) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List all ADRs with their id, date and status", + Long: `List all ADRs in the configured directory. + +Prints a table with each ADR's number, date, status, and title, sorted by +number. Use --format to control output: md (rendered, default), raw (plain +markdown table), or json (machine-readable).`, Run: func(_ *cobra.Command, _ []string) { outputFormat, err := app.ParseFormat(format) if err != nil { - log.Printf("invalid format %q: %v", format, err) + log.Printf("invalid format %q: must be one of: md, raw, json", format) return } diff --git a/cmd/show.go b/cmd/show.go index fb18800..45428dd 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -25,14 +25,14 @@ rendered markdown body of the ADR.`, Run: func(_ *cobra.Command, args []string) { number, err := strconv.Atoi(args[0]) if err != nil { - log.Printf("invalid argument: %v", err) + log.Printf("invalid id %q: expected a number", args[0]) return } outputFormat, err := app.ParseFormat(format) if err != nil { - log.Printf("invalid format %q: %v", format, err) + log.Printf("invalid format %q: must be one of: md, raw, json", format) return } diff --git a/cmd/update.go b/cmd/update.go index 2869d9a..b54ebe7 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -24,7 +24,7 @@ The index (README.md) is regenerated automatically after the update.`, Run: func(_ *cobra.Command, args []string) { number, err := strconv.Atoi(args[0]) if err != nil { - log.Printf("invalid argument: %v", err) + log.Printf("invalid id %q: expected a number", args[0]) return } diff --git a/cmd/version.go b/cmd/version.go index 9e688a1..6c08d70 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -13,6 +13,7 @@ func NewVersionCommand() *cobra.Command { return &cobra.Command{ Use: "version", Short: "Show the version information", + Long: `Print the build version, commit hash, and build date of the adr binary.`, Run: func(_ *cobra.Command, _ []string) { if err := app.Version(os.Args[0]); err != nil { log.Printf("couldn't show version: %v", err) diff --git a/go.mod b/go.mod index ef84604..39498e3 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( golang.org/x/net v0.55.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.46.0 // indirect + golang.org/x/term v0.43.0 // indirect golang.org/x/text v0.37.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/ini.v1 v1.66.2 // indirect diff --git a/go.sum b/go.sum index b3c105c..889c7ce 100644 --- a/go.sum +++ b/go.sum @@ -621,6 +621,8 @@ golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/app/edit.go b/internal/app/edit.go index 331a5d5..6420e55 100644 --- a/internal/app/edit.go +++ b/internal/app/edit.go @@ -8,6 +8,8 @@ import ( "os/exec" "path/filepath" + "golang.org/x/term" + "github.com/corani/adr/config" "github.com/corani/adr/internal/adr" ) @@ -18,11 +20,23 @@ func Edit(ctx context.Context, conf *config.Config, number int) error { return fmt.Errorf("%w: edit: %w", ErrInternal, err) } + path := filepath.Join(conf.Project, conf.Root, found.Filename) + + if !term.IsTerminal(int(os.Stdout.Fd())) { + log.Printf("ADR saved at: %s", path) + + return nil + } + + editor := os.Getenv("VISUAL") + if editor == "" { + editor = os.Getenv("EDITOR") + } + log.Printf("editing ADR: %v", filepath.Join(conf.Root, found.Filename)) // #nosec G204,G702 // Command injection via environment variable - cmd := exec.CommandContext(ctx, os.Getenv("EDITOR"), - filepath.Join(conf.Project, conf.Root, found.Filename)) + cmd := exec.CommandContext(ctx, editor, path) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/internal/app/find.go b/internal/app/find.go index 3d796d2..41cfcd5 100644 --- a/internal/app/find.go +++ b/internal/app/find.go @@ -51,7 +51,7 @@ func Find(conf *config.Config, query string, fullText bool, format Format) error case FormatRaw, FormatMd: return renderMarkdownTable(os.Stdout, entries, format, "find") default: - return fmt.Errorf("%w: find: unknown format %q", ErrInternal, format) + return fmt.Errorf("%w: find: unknown format %q: must be one of: md, raw, json", ErrInternal, format) } } diff --git a/internal/app/find_format_test.go b/internal/app/find_format_test.go index 55b9f90..7c9260e 100644 --- a/internal/app/find_format_test.go +++ b/internal/app/find_format_test.go @@ -53,6 +53,12 @@ func TestFindMarkdownRaw(t *testing.T) { if !strings.Contains(out, wantRow) { t.Errorf("output missing %q\ngot: %s", wantRow, out) } + + const wantHint = "adr show " + + if !strings.Contains(out, wantHint) { + t.Errorf("output missing hint %q\ngot: %s", wantHint, out) + } } func TestMatchesFormat(t *testing.T) { diff --git a/internal/app/list.go b/internal/app/list.go index 6c6dfc8..e44ccd0 100644 --- a/internal/app/list.go +++ b/internal/app/list.go @@ -39,7 +39,7 @@ func List(conf *config.Config, format Format) error { case FormatRaw, FormatMd: return renderMarkdownTable(os.Stdout, entries, format, "list") default: - return fmt.Errorf("%w: list: unknown format %q", ErrInternal, format) + return fmt.Errorf("%w: list: unknown format %q: must be one of: md, raw, json", ErrInternal, format) } } diff --git a/internal/app/list_test.go b/internal/app/list_test.go index de5c5c2..ce87ad0 100644 --- a/internal/app/list_test.go +++ b/internal/app/list_test.go @@ -64,4 +64,10 @@ func TestListMarkdownRaw(t *testing.T) { if !strings.Contains(out, wantRow) { t.Errorf("output missing row %q\ngot: %s", wantRow, out) } + + const wantHint = "adr show " + + if !strings.Contains(out, wantHint) { + t.Errorf("output missing hint %q\ngot: %s", wantHint, out) + } } diff --git a/internal/app/show.go b/internal/app/show.go index fb95471..202200c 100644 --- a/internal/app/show.go +++ b/internal/app/show.go @@ -34,7 +34,7 @@ func Show(conf *config.Config, number int, format Format) error { case FormatRaw, FormatMd: return showMarkdown(os.Stdout, found, format) default: - return fmt.Errorf("%w: show: unknown format %q", ErrInternal, format) + return fmt.Errorf("%w: show: unknown format %q: must be one of: md, raw, json", ErrInternal, format) } } diff --git a/internal/app/table.go b/internal/app/table.go index 6c066ef..ed6edc9 100644 --- a/internal/app/table.go +++ b/internal/app/table.go @@ -16,7 +16,8 @@ func renderMarkdownTable(writer io.Writer, entries []adrListEntry, format Format rows[i] = fmt.Sprintf("| %04d | %s | %s | %s |", e.Number, e.Date, e.Status, e.Title) } - table := "| # | date | status | title |\n|---|------|--------|-------|\n" + strings.Join(rows, "\n") + "\n" + table := "| # | date | status | title |\n|---|------|--------|-------|\n" + strings.Join(rows, "\n") + "\n\n" + + "_Use `adr show ` to view a specific ADR._\n" if format == FormatRaw { if _, err := fmt.Fprint(writer, table); err != nil { diff --git a/internal/app/update.go b/internal/app/update.go index 110cab2..218f070 100644 --- a/internal/app/update.go +++ b/internal/app/update.go @@ -16,7 +16,7 @@ func Update(conf *config.Config, number int, status string) error { adr.StatusDeprecated, adr.StatusSuperseded: // okay default: - return fmt.Errorf("%w: %v", ErrInvalidStatus, status) + return fmt.Errorf("%w: %q: must be one of: proposed, accepted, deprecated, superseded", ErrInvalidStatus, status) } found, err := adr.ByID(conf, adr.Number(number))