feat(claudbg-d8ht): color-coded transcript label prefixes in CLI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
8d72b0e971
commit
c4646cf0e2
@ -0,0 +1,40 @@
|
||||
---
|
||||
# claudbg-d8ht
|
||||
title: Color-coded transcript output in CLI
|
||||
status: completed
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-03-31T00:32:52Z
|
||||
updated_at: 2026-03-31T04:28:11Z
|
||||
parent: claudbg-qpfe
|
||||
---
|
||||
|
||||
Apply ANSI colors to transcript output in `sessions transcribe` and `agents transcribe`:
|
||||
- `[assistant]` → orange
|
||||
- `[user]` → grey
|
||||
- `[tool: Foo]` → blue
|
||||
- `[tool_result]` → green
|
||||
- `[tool_result (error)]` → red
|
||||
|
||||
Colors are enabled by default in interactive terminals (isatty check). Controlled by the --[no-]color flag and NO_COLOR env var.
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
Added ANSI color support to transcript output in both `sessions transcribe` and `agents transcribe`.
|
||||
|
||||
### New file: `src/output/color.rs`
|
||||
A small color helper module with five functions — `orange`, `grey`, `blue`, `green`, `red` — each taking a `&str` and a `color_enabled: bool` flag. When disabled the string is returned unchanged; when enabled the string is wrapped with the appropriate ANSI 256-color (or standard) escape code followed by `\x1b[0m` reset. Includes 3 unit tests.
|
||||
|
||||
### Modified: `src/output/mod.rs`
|
||||
Declared and exported the new `color` sub-module.
|
||||
|
||||
### Modified: `src/commands/sessions.rs` and `src/commands/agents.rs`
|
||||
Updated `render_entry_text` in both files (they are independent copies) to:
|
||||
- Call `opts.color_enabled()` once and pass the flag to color helpers.
|
||||
- Color `[assistant]` labels orange (`\x1b[38;5;208m`).
|
||||
- Color `[user]` labels grey (`\x1b[38;5;245m`).
|
||||
- Color `[tool: X]` labels blue (`\x1b[38;5;33m`).
|
||||
- Color `[tool_result]` labels green (`\x1b[32m`).
|
||||
- Color `[tool_result (error)]` labels red (`\x1b[31m`).
|
||||
|
||||
Only the label prefix is colored; the message body is left plain. No new crate dependencies were added.
|
||||
@ -0,0 +1,10 @@
|
||||
---
|
||||
# claudbg-qpfe
|
||||
title: Transcript color coding
|
||||
status: todo
|
||||
type: epic
|
||||
created_at: 2026-03-31T00:32:44Z
|
||||
updated_at: 2026-03-31T00:32:44Z
|
||||
---
|
||||
|
||||
Color-code transcript output to visually distinguish message types. Covers CLI color output, --[no-]color global flag, NO_COLOR env var, and TUI color toggle.
|
||||
@ -0,0 +1,94 @@
|
||||
//! ANSI color helpers for terminal output.
|
||||
//!
|
||||
//! All functions take a `color_enabled` flag; when `false` the string is
|
||||
//! returned unchanged so callers never need to branch on their own.
|
||||
|
||||
/// Wrap `s` in ANSI escape codes when `color_enabled` is true.
|
||||
///
|
||||
/// `open` is the escape sequence to start the color and `RESET` (`\x1b[0m`)
|
||||
/// is appended automatically.
|
||||
fn colorize(s: &str, open: &str, color_enabled: bool) -> String {
|
||||
if color_enabled {
|
||||
format!("{open}{s}\x1b[0m")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Orange — used for `[assistant]` labels.
|
||||
///
|
||||
/// Uses 256-color code 208 (bright orange).
|
||||
pub fn orange(s: &str, color_enabled: bool) -> String {
|
||||
colorize(s, "\x1b[38;5;208m", color_enabled)
|
||||
}
|
||||
|
||||
/// Grey — used for `[user]` labels.
|
||||
///
|
||||
/// Uses 256-color code 245 (mid-grey).
|
||||
pub fn grey(s: &str, color_enabled: bool) -> String {
|
||||
colorize(s, "\x1b[38;5;245m", color_enabled)
|
||||
}
|
||||
|
||||
/// Blue — used for `[tool: X]` labels.
|
||||
///
|
||||
/// Uses 256-color code 33 (dodger blue).
|
||||
pub fn blue(s: &str, color_enabled: bool) -> String {
|
||||
colorize(s, "\x1b[38;5;33m", color_enabled)
|
||||
}
|
||||
|
||||
/// Green — used for `[tool_result]` labels.
|
||||
///
|
||||
/// Uses 256-color code 34 (green).
|
||||
pub fn green(s: &str, color_enabled: bool) -> String {
|
||||
colorize(s, "\x1b[32m", color_enabled)
|
||||
}
|
||||
|
||||
/// Red — used for `[tool_result (error)]` labels.
|
||||
///
|
||||
/// Uses standard ANSI red (code 31).
|
||||
pub fn red(s: &str, color_enabled: bool) -> String {
|
||||
colorize(s, "\x1b[31m", color_enabled)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// When color is disabled, the string is returned unchanged.
|
||||
#[test]
|
||||
fn color_disabled_returns_plain_string() {
|
||||
assert_eq!(orange("hello", false), "hello");
|
||||
assert_eq!(grey("hello", false), "hello");
|
||||
assert_eq!(blue("hello", false), "hello");
|
||||
assert_eq!(green("hello", false), "hello");
|
||||
assert_eq!(red("hello", false), "hello");
|
||||
}
|
||||
|
||||
/// When color is enabled, output contains the escape code and a reset.
|
||||
#[test]
|
||||
fn color_enabled_wraps_with_escapes() {
|
||||
let out = orange("[assistant]", true);
|
||||
assert!(out.starts_with("\x1b["), "should start with escape");
|
||||
assert!(out.ends_with("\x1b[0m"), "should end with reset");
|
||||
assert!(out.contains("[assistant]"), "should contain original text");
|
||||
}
|
||||
|
||||
/// Each color function produces distinct output.
|
||||
#[test]
|
||||
fn each_color_function_is_distinct() {
|
||||
let text = "X";
|
||||
let colors = [
|
||||
orange(text, true),
|
||||
grey(text, true),
|
||||
blue(text, true),
|
||||
green(text, true),
|
||||
red(text, true),
|
||||
];
|
||||
// All pairs must differ.
|
||||
for i in 0..colors.len() {
|
||||
for j in (i + 1)..colors.len() {
|
||||
assert_ne!(colors[i], colors[j], "colors[{i}] == colors[{j}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue