Skip to content

fix(tui): make "/tools list" show real colors instead of "?[32m" etc.…#9091

Closed
dsocolobsky wants to merge 2 commits into
NousResearch:mainfrom
dsocolobsky:fix-tools-list-garbled-output
Closed

fix(tui): make "/tools list" show real colors instead of "?[32m" etc.…#9091
dsocolobsky wants to merge 2 commits into
NousResearch:mainfrom
dsocolobsky:fix-tools-list-garbled-output

Conversation

@dsocolobsky

@dsocolobsky dsocolobsky commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a display bug where /tools list, /tools enable, and /tools disable showed garbled text like ?[32m✓ enabled?[0m instead of green/red marks inside the interactive TUI.

Why it happens: the color() helper emits real ANSI escape codes (\x1b[32m...). When those go through print() inside the TUI, prompt_toolkit's patch_stdout proxy treats \x1b as an unprintable control character and replaces it with ?. Same issue as in #2262 (already documented in cli.py:6229-6232 for /tool-progress).

The fix: in _handle_tools_command, capture the backend's stdout into a buffer and replay each line through _cprint(), which routes ANSI properly via prompt_toolkit's renderer. Two small details:

  • We subclass StringIO and override isatty() to return True, so when color() checks whether it's writing to a terminal it gets a yes and emits the escape codes, otherwise it would strip colors out before we could re-render them.
  • The capture path only runs inside the TUI; standalone CLI and tests fall straight through to real stdout where colors already work.

Related Issue

Fixes #9017

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • cli.py_handle_tools_command: added a _run_capture() inner helper that captures stdout from tools_disable_enable_command() into a TTY-claiming StringIO and replays each line through _cprint(). Applied to all three subcommands (list, disable, enable) since disable/enable also emit colored success/error lines via print_success/print_error. Wrapped in a getattr(self, "_app", None) is None guard so non-TUI callers (standalone scripts, tests) bypass the capture and go straight to stdout.

How to Test

  • Run /tools list, tools disable web, tools enable web and verify the output is correct with colors.
  • From the shell run hermes tools list and verify the CLI colors still work.
  • Run pytest tests/cli/test_cli_tools_command.py tests/hermes_cli/test_tools_disable_enable.py -q , all 27 tests pass.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15 (Darwin 24.6.0)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings)
  • I've updated cli-config.yaml.example if I added/changed config keys
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide
  • I've updated tool descriptions/schemas if I changed tool behavior

Screenshots / Logs

Before:
image

After:
image

… gibberish

The colored ✓/✗ marks in /tools list, /tools enable, and /tools disable
  were showing up as "?[32m✓ enabled?[0m" instead of green and red. The
  colors come out as ANSI escape codes, but the tui eats
  the ESC byte and replaces it with "?" when those codes are printed
  straight to stdout. They need to go through prompt_toolkit's renderer.

  Fix: capture the command's output and re-print each line through
  _cprint(), the same workaround used elsewhere for NousResearch#2262. The capture
  buffer fakes isatty()=True so the color helper still emits escapes
  (StringIO.isatty() is False, which would otherwise strip colors).
  The capture path only runs inside the TUI; standalone CLI and tests
  go straight through to real stdout where colors already work.
teknium1 added a commit that referenced this pull request Apr 20, 2026
- Fix duplicate 'timezone' import in e2e conftest
- Fix test_text_before_command_not_detected asserting send() is awaited
  when no agent is present in mock setup (text messages don't produce
  command output)
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #13187 (#13187). Your commit was cherry-picked onto current main with your authorship preserved in git log. Thanks @dsocolobsky!

@teknium1 teknium1 closed this Apr 20, 2026
ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
…ousResearch#9091, NousResearch#13131

- Fix duplicate 'timezone' import in e2e conftest
- Fix test_text_before_command_not_detected asserting send() is awaited
  when no agent is present in mock setup (text messages don't produce
  command output)
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…ousResearch#9091, NousResearch#13131

- Fix duplicate 'timezone' import in e2e conftest
- Fix test_text_before_command_not_detected asserting send() is awaited
  when no agent is present in mock setup (text messages don't produce
  command output)
Luminet2023 pushed a commit to Luminet2023/hermes-agent that referenced this pull request May 1, 2026
…ousResearch#9091, NousResearch#13131

- Fix duplicate 'timezone' import in e2e conftest
- Fix test_text_before_command_not_detected asserting send() is awaited
  when no agent is present in mock setup (text messages don't produce
  command output)
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…ousResearch#9091, NousResearch#13131

- Fix duplicate 'timezone' import in e2e conftest
- Fix test_text_before_command_not_detected asserting send() is awaited
  when no agent is present in mock setup (text messages don't produce
  command output)
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…ousResearch#9091, NousResearch#13131

- Fix duplicate 'timezone' import in e2e conftest
- Fix test_text_before_command_not_detected asserting send() is awaited
  when no agent is present in mock setup (text messages don't produce
  command output)
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…ousResearch#9091, NousResearch#13131

- Fix duplicate 'timezone' import in e2e conftest
- Fix test_text_before_command_not_detected asserting send() is awaited
  when no agent is present in mock setup (text messages don't produce
  command output)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Improve the display format of the /tools list command

2 participants