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