feat: add --plain output and fix auth stderr#5
feat: add --plain output and fix auth stderr#5builtbyrobben wants to merge 2 commits intofeat/initial-templatefrom
Conversation
- Add WritePlain function to outfmt package for TSV output - Add --plain branches to all commands alongside existing --json - Fix auth status to write data to stdout (keep hints on stderr) Co-Authored-By: Claude Opus 4.6 <[email protected]>
| func WritePlain(w io.Writer, headers []string, rows [][]string) error { | ||
| if len(headers) > 0 { | ||
| fmt.Fprintln(w, strings.Join(headers, "\t")) | ||
| } | ||
|
|
||
| for _, row := range rows { | ||
| fmt.Fprintln(w, strings.Join(row, "\t")) | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
TSV output breaks on embedded tabs/newlines
WritePlain joins cell values with \t and writes rows with Fprintln, but does not sanitize the values. API responses for fields like Answer and Summary can contain tab and newline characters, which will corrupt the TSV structure — a newline inside a cell will be interpreted as a row boundary, and a tab will be interpreted as a column boundary.
Consider replacing or stripping \t and \n from cell values before joining. For example:
// WritePlain writes tab-separated values to the writer.
func WritePlain(w io.Writer, headers []string, rows [][]string) error {
if len(headers) > 0 {
fmt.Fprintln(w, strings.Join(headers, "\t"))
}
replacer := strings.NewReplacer("\t", " ", "\n", " ", "\r", "")
for _, row := range rows {
cleaned := make([]string, len(row))
for i, cell := range row {
cleaned[i] = replacer.Replace(cell)
}
fmt.Fprintln(w, strings.Join(cleaned, "\t"))
}
return nil
}Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/outfmt/outfmt.go
Line: 68:77
Comment:
**TSV output breaks on embedded tabs/newlines**
`WritePlain` joins cell values with `\t` and writes rows with `Fprintln`, but does not sanitize the values. API responses for fields like `Answer` and `Summary` can contain tab and newline characters, which will corrupt the TSV structure — a newline inside a cell will be interpreted as a row boundary, and a tab will be interpreted as a column boundary.
Consider replacing or stripping `\t` and `\n` from cell values before joining. For example:
```go
// WritePlain writes tab-separated values to the writer.
func WritePlain(w io.Writer, headers []string, rows [][]string) error {
if len(headers) > 0 {
fmt.Fprintln(w, strings.Join(headers, "\t"))
}
replacer := strings.NewReplacer("\t", " ", "\n", " ", "\r", "")
for _, row := range rows {
cleaned := make([]string, len(row))
for i, cell := range row {
cleaned[i] = replacer.Replace(cell)
}
fmt.Fprintln(w, strings.Join(cleaned, "\t"))
}
return nil
}
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Fixed — WritePlain now sanitizes tabs/newlines with strings.NewReplacer.
| func WritePlain(w io.Writer, headers []string, rows [][]string) error { | ||
| if len(headers) > 0 { | ||
| fmt.Fprintln(w, strings.Join(headers, "\t")) | ||
| } | ||
|
|
||
| for _, row := range rows { | ||
| fmt.Fprintln(w, strings.Join(row, "\t")) | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
Write errors silently discarded
WritePlain returns error but never returns a non-nil one — fmt.Fprintln write errors are silently ignored. This is minor since stdout write failures are typically unrecoverable, but for consistency with WriteJSON (which wraps encode errors), consider checking at least one write:
| func WritePlain(w io.Writer, headers []string, rows [][]string) error { | |
| if len(headers) > 0 { | |
| fmt.Fprintln(w, strings.Join(headers, "\t")) | |
| } | |
| for _, row := range rows { | |
| fmt.Fprintln(w, strings.Join(row, "\t")) | |
| } | |
| return nil | |
| // WritePlain writes tab-separated values to the writer. | |
| func WritePlain(w io.Writer, headers []string, rows [][]string) error { | |
| if len(headers) > 0 { | |
| if _, err := fmt.Fprintln(w, strings.Join(headers, "\t")); err != nil { | |
| return fmt.Errorf("write plain headers: %w", err) | |
| } | |
| } | |
| for _, row := range rows { | |
| if _, err := fmt.Fprintln(w, strings.Join(row, "\t")); err != nil { | |
| return fmt.Errorf("write plain row: %w", err) | |
| } | |
| } | |
| return nil | |
| } |
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!
Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/outfmt/outfmt.go
Line: 68:77
Comment:
**Write errors silently discarded**
`WritePlain` returns `error` but never returns a non-nil one — `fmt.Fprintln` write errors are silently ignored. This is minor since stdout write failures are typically unrecoverable, but for consistency with `WriteJSON` (which wraps encode errors), consider checking at least one write:
```suggestion
// WritePlain writes tab-separated values to the writer.
func WritePlain(w io.Writer, headers []string, rows [][]string) error {
if len(headers) > 0 {
if _, err := fmt.Fprintln(w, strings.Join(headers, "\t")); err != nil {
return fmt.Errorf("write plain headers: %w", err)
}
}
for _, row := range rows {
if _, err := fmt.Fprintln(w, strings.Join(row, "\t")); err != nil {
return fmt.Errorf("write plain row: %w", err)
}
}
return nil
}
```
<sub>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!</sub>
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Fixed — WritePlain now propagates fmt.Fprintln errors.
Address Greptile review feedback: - Sanitize tabs, newlines, and carriage returns in plain output fields - Propagate fmt.Fprintln write errors for consistency with WriteJSON Co-Authored-By: Claude Opus 4.6 <[email protected]>
Summary
WritePlainto outfmt package for TSV (tab-separated) output--plainbranches to all list/get/status commandsTest plan
make cipassesexa-cli version --plainoutputs TSV rowexa-cli auth status 2>/dev/nulloutputs data to stdoutexa-cli auth status --plainoutputs TSV🤖 Generated with Claude Code
Greptile Summary
Adds TSV-formatted
--plainoutput mode to all commands and fixesauth statusto write data to stdout instead of stderr, aligning with the project's output conventions for parseable formats.WritePlainfunction inoutfmtpackage with proper field sanitization (tabs, newlines, carriage returns) and error propagation--plainbranches to all list/get/status commands (version,answer,search,find-similar,contents,auth)auth statushuman-readable output to write data to stdout (lines 134-147 ininternal/cmd/auth.go), following the pattern: data to stdout, hints/errors to stderrConfidence Score: 5/5
--plainoutput mode, and the auth status stdout fix correctly aligns with the project's output conventions.Important Files Changed
WritePlainfunction with proper TSV sanitization and error handling--plainoutput to auth commands and fixed status output from stderr to stdoutFlowchart
flowchart TD A[Command Run] --> B{Check Output Mode} B -->|IsJSON| C[WriteJSON to stdout] B -->|IsPlain| D[WritePlain TSV to stdout] B -->|Default| E[Human-readable to stdout] D --> F[Sanitize Fields] F --> G[Replace tabs with spaces] F --> H[Replace newlines with spaces] F --> I[Replace carriage returns] G --> J[Join with tabs] H --> J I --> J J --> K[Write headers] K --> L[Write rows] L --> M[Propagate errors] E --> N{Command Type} N -->|auth status| O[Write to stdout instead of stderr] N -->|auth set-key/remove| P[Write to stderr] N -->|Other commands| Q[Data to stdout, hints to stderr]Last reviewed commit: 95082a8