Prettifying Cursor CLI Agent's Stream Format
Using Cursor from the CLI is a handy way to automate some tasks. For example, I have an script that detects changes since the readme was last updated and uses cursor to determine if any changes need to be made. The one bugbear of mine is that the command just sits there saying nothing until it's complete even with --output-format text.
It can stream messages as JSON which is better but hard to read:
With the help of jq, we can get a much nicer format:

Usage
|
You can also create an alias in your shellrc to make it even easier:
For fish shell:
JQ Script
You can also make this script executable with chmod +x format-chat.jq so you can run it directly.
#!/usr/bin/env -S jq -r -f
# Helper function to truncate long strings
:
if str | length > max_length then
str + "..."
else
str
end;
# Helper function to format arguments with truncation
:
if args == null then
""
else
" " + \(.key)\(.value | truncate_string(50))\(.key)\(.value | length)\(.key)\(.value | keys | length)\(.key)\(.value)
end;
if .type == "user" then
"001b[36m[USER]001b[0m\(.message.content[0].text)"
elif .type == "assistant" then
"001b[32m[ASSISTANT]001b[0m \(.message.content[0].text)"
elif .type == "tool_call" and .subtype == "started" then
if .tool_call.shellToolCall then
"001b[33m[SHELL]001b[0m \(.tool_call.shellToolCall.args.command)"
elif .tool_call.readToolCall then
"001b[33m[READ]001b[0m \(.tool_call.readToolCall.args.path)\(if .tool_call.readToolCall.args.offset then " (offset: \(.tool_call.readToolCall.args.offset), limit: \(.tool_call.readToolCall.args.limit))" else "" end)"
elif .tool_call.editToolCall then
"001b[33m[EDIT]001b[0m \(.tool_call.editToolCall.args.path)"
elif .tool_call.grepToolCall then
"001b[33m[GREP]001b[0m \(.tool_call.grepToolCall.args.pattern) in \(.tool_call.grepToolCall.args.path)"
elif .tool_call.lsToolCall then
"001b[33m[LS]001b[0m \(.tool_call.lsToolCall.args.path)\(if .tool_call.lsToolCall.args.ignore and (.tool_call.lsToolCall.args.ignore | length) > 0 then " (ignore: \(.tool_call.lsToolCall.args.ignore | join(", ")))" else "" end)"
elif .tool_call.globToolCall then
"001b[33m[GLOB]001b[0m \(.tool_call.globToolCall.args.globPattern) in \(.tool_call.globToolCall.args.targetDirectory)"
elif .tool_call.todoToolCall then
"001b[33m[TODO]001b[0m \(.tool_call.todoToolCall.args.merge // false | if . then "merge" else "create" end) \(.tool_call.todoToolCall.args.todos | length) todos" +
elif .tool_call.updateTodosToolCall then
"001b[33m[UPDATE_TODOS]001b[0m \(.tool_call.updateTodosToolCall.args.merge // false | if . then "merge" else "create" end) \(.tool_call.updateTodosToolCall.args.todos | length) todos" +
elif .tool_call.writeToolCall then
"001b[33m[WRITE]001b[0m \(.tool_call.writeToolCall.args.path) (\(.tool_call.writeToolCall.args.fileText | length) chars) " +
elif .tool_call.deleteToolCall then
"001b[33m[DELETE]001b[0m \(.tool_call.deleteToolCall.args.path)"
else
"001b[33m[TOOL]001b[0m \(.tool_call | keys[0])" +
\(.key)\(.value | if length > 50 then .[0:50] + "..." else . end)\(.key)\(.value | length)\(.key)\(.value | keys | length)\(.key)\(.value)
end
elif .type == "tool_call" and .subtype == "completed" then
if .tool_call.shellToolCall then
if .tool_call.shellToolCall.result.success then
"001b[90m✓ Exit \(.tool_call.shellToolCall.result.success.exitCode)001b[0m"
else
"001b[91m✗ Failed001b[0m"
end
elif .tool_call.readToolCall then
if .tool_call.readToolCall.result.success then
"001b[90m✓ Read \(.tool_call.readToolCall.result.success.totalLines) lines001b[0m"
else
"001b[91m✗ Read failed001b[0m"
end
elif .tool_call.editToolCall then
if .tool_call.editToolCall.result.success then
"001b[90m✓ Edited001b[0m"
else
"001b[91m✗ Edit failed001b[0m"
end
elif .tool_call.grepToolCall then
if .tool_call.grepToolCall.result.success then
"001b[90m✓ Found \(.tool_call.grepToolCall.result.success.workspaceResults | to_entries[0].value.content.totalMatchedLines) matches001b[0m"
else
"001b[91m✗ Grep failed001b[0m"
end
elif .tool_call.lsToolCall then
if .tool_call.lsToolCall.result.success then
"001b[90m✓ Listed \(.tool_call.lsToolCall.result.success.directoryTreeRoot.childrenFiles | length) files, \(.tool_call.lsToolCall.result.success.directoryTreeRoot.childrenDirs | length) dirs001b[0m"
else
"001b[91m✗ List failed001b[0m"
end
elif .tool_call.globToolCall then
if .tool_call.globToolCall.result.success then
"001b[90m✓ Found \(.tool_call.globToolCall.result.success.totalFiles) files001b[0m"
else
"001b[91m✗ Glob failed001b[0m"
end
elif .tool_call.todoToolCall then
if .tool_call.todoToolCall.result.success then
"001b[90m✓ Updated todos001b[0m" +
else
"001b[91m✗ Todo update failed001b[0m"
end
elif .tool_call.updateTodosToolCall then
if .tool_call.updateTodosToolCall.result.success then
"001b[90m✓ Updated todos001b[0m" +
else
"001b[91m✗ Todo update failed001b[0m"
end
elif .tool_call.writeToolCall then
if .tool_call.writeToolCall.result.success then
"001b[90m✓ Wrote \(.tool_call.writeToolCall.result.success.linesCreated) lines (\(.tool_call.writeToolCall.result.success.fileSize) bytes) to \(.tool_call.writeToolCall.args.path)001b[0m"
else
"001b[91m✗ Write failed001b[0m"
end
elif .tool_call.deleteToolCall then
if .tool_call.deleteToolCall.result.success then
"001b[90m✓ Deleted \(.tool_call.deleteToolCall.args.path)001b[0m"
elif .tool_call.deleteToolCall.result.rejected then
"001b[91m✗ Delete rejected: \(.tool_call.deleteToolCall.result.rejected.reason // "unknown reason")001b[0m"
else
"001b[91m✗ Delete failed001b[0m"
end
else
"001b[90m✓ Completed001b[0m"
end
elif .type == "result" then
"001b[35m[RESULT]001b[0m \(.subtype) (\(.duration_ms)ms)"
else
empty
end
Update readme prompt
Since I mentioned the update readme script in this post, I thought I'd share the prompt I use for it:
Use this command to find files changed since the last README update:
`git diff --find-renames --find-copies -U0 "$(git log -n1 --format=%H -- README.md)..HEAD"`
Review the changed files list. Determine if any changes warrant README updates (new features, API changes, setup instructions, breaking changes). Ignore: refactors, internal changes, minor fixes.
For relevant changes only:
- Use git diff to get specific file diffs if needed
- Read/find files to understand context
- Update README with minimal, precise changes
- Keep existing structure and style
- No fluff or rewrites
If no updates needed, say so and stop.
I use this with a fish function that will load prompts from files in ~/.prompts. So I can run cursh update-readme to update the readme. It registers an autocomplete function for the prompt names so I can tab complete them.
# Autocomplete function for prompt names
# Register the autocomplete