Description
When using -o json, several commands write human-readable status messages to stdout alongside the JSON payload. This makes stdout invalid JSON, breaking jq, json.load(), and any pipeline that expects clean machine-readable output.
Examples
notes create -o json prepends a success banner:
✓ Note created with ID: 98765
{
"id": "98765",
...
}
contacts search -o json prepends a count line:
Found 3 contact(s)
{
"results": [...]
}
notes list -o json appends pagination text after the JSON:
{
"results": [...]
}
More results available. Use --after abc123 to get the next page.
Impact
Any scripting that pipes hspt output through jq or parses it programmatically fails without preprocessing (e.g. tail -n +2 | jq). This defeats the purpose of --output json.
Root cause
In internal/view/view.go, the Success(), Info(), Print(), and Println() methods all write to v.Out (stdout). The JSON() method also writes to v.Out, so status messages and JSON data are interleaved on the same stream.
Suggested fix
Route Success(), Info(), Print(), and Println() to v.Err (stderr):
func (v *View) Success(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintln(v.Err, color.GreenString("✓ %s", msg))
}
func (v *View) Info(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintln(v.Err, msg)
}
Error() and Warning() already write to v.Err, so this just makes the pattern consistent. Status messages still appear on the terminal (stderr is displayed by default), but stdout carries only the structured data payload.
This is a ~4-line change in one file with no API breakage. It follows standard Unix convention where structured output goes to stdout and human-readable messages go to stderr.
Description
When using
-o json, several commands write human-readable status messages to stdout alongside the JSON payload. This makes stdout invalid JSON, breakingjq,json.load(), and any pipeline that expects clean machine-readable output.Examples
notes create -o jsonprepends a success banner:contacts search -o jsonprepends a count line:notes list -o jsonappends pagination text after the JSON:{ "results": [...] } More results available. Use --after abc123 to get the next page.Impact
Any scripting that pipes hspt output through
jqor parses it programmatically fails without preprocessing (e.g.tail -n +2 | jq). This defeats the purpose of--output json.Root cause
In
internal/view/view.go, theSuccess(),Info(),Print(), andPrintln()methods all write tov.Out(stdout). TheJSON()method also writes tov.Out, so status messages and JSON data are interleaved on the same stream.Suggested fix
Route
Success(),Info(),Print(), andPrintln()tov.Err(stderr):Error()andWarning()already write tov.Err, so this just makes the pattern consistent. Status messages still appear on the terminal (stderr is displayed by default), but stdout carries only the structured data payload.This is a ~4-line change in one file with no API breakage. It follows standard Unix convention where structured output goes to stdout and human-readable messages go to stderr.