feat(w1): agent terminal theme renderer — 4090-claude convergence - #1065
Conversation
New tool: pmoves/tools/agent_terminal_theme.py — reads flat 1.0.0 agent_signatures.yaml + node-agent-specialization.yaml, renders ANSI 24-bit themed output (banners, status bars, session headers, agent roster). Cross-platform (Windows UTF-8 reconfigure). Functions: load_agent(), render_banner(), render_status_bar(), session_header(), colorize(), render_roster(). Integration-ready for sign_trail.py and pr_hedge_trim.py. Claims W1 (terminal renderer + Gate 3) in AGNOTE4482PHI.t1.md and AGNOTE4482_ROADMAP_W1-W5.md Agent Claim Register. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughA new Python CLI tool is introduced to render ANSI-styled terminal output for agents, along with documentation entries tracking the work assignment. The tool loads agent and node configuration from YAML files, supports ANSI capability detection, and provides multiple rendering modes including banners, status bars, and session headers. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
pmoves/tools/agent_terminal_theme.py (1)
125-145: Consolidate duplicated agent-mapping logic.
load_agentandload_all_agentsrepeat the same field mapping. Extracting a helper reduces drift risk.Refactor sketch
+def _to_agent_theme(agent_id: str, entry: Dict[str, Any]) -> AgentTheme: + return AgentTheme( + agent_id=entry.get("agent_id", agent_id), + display_name=entry.get("display_name", agent_id), + glyph=entry.get("glyph", "?"), + color=entry.get("color", "#FFFFFF"), + accent=entry.get("accent", "#CCCCCC"), + voice=entry.get("voice", "analytical"), + resonance=entry.get("resonance", []), + description=entry.get("description", ""), + co_author=entry.get("co_author", ""), + specialization=entry.get("specialization", ""), + routes_to=entry.get("routes_to", []), + ) + def load_agent(agent_id: str) -> Optional[AgentTheme]: @@ - return AgentTheme( - agent_id=entry.get("agent_id", agent_id), - display_name=entry.get("display_name", agent_id), - glyph=entry.get("glyph", "?"), - color=entry.get("color", "#FFFFFF"), - accent=entry.get("accent", "#CCCCCC"), - voice=entry.get("voice", "analytical"), - resonance=entry.get("resonance", []), - description=entry.get("description", ""), - co_author=entry.get("co_author", ""), - specialization=entry.get("specialization", ""), - routes_to=entry.get("routes_to", []), - ) + return _to_agent_theme(agent_id, entry) @@ - agents.append(AgentTheme( - agent_id=entry.get("agent_id", aid), - display_name=entry.get("display_name", aid), - glyph=entry.get("glyph", "?"), - color=entry.get("color", "#FFFFFF"), - accent=entry.get("accent", "#CCCCCC"), - voice=entry.get("voice", "analytical"), - resonance=entry.get("resonance", []), - description=entry.get("description", ""), - co_author=entry.get("co_author", ""), - specialization=entry.get("specialization", ""), - routes_to=entry.get("routes_to", []), - )) + agents.append(_to_agent_theme(aid, entry))Also applies to: 147-166
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pmoves/tools/agent_terminal_theme.py` around lines 125 - 145, Both load_agent and load_all_agents duplicate the same mapping from a signature dict to an AgentTheme; create a small helper (e.g., _theme_from_entry(entry: dict, default_id: str) -> AgentTheme) that centralizes the mapping logic used to build AgentTheme instances (map agent_id, display_name, glyph, color, accent, voice, resonance, description, co_author, specialization, routes_to with the same defaults currently in load_agent), then replace the construction in load_agent and the corresponding block in load_all_agents to call this helper (use _load_yaml/_SIGNATURES_PATH as before and pass appropriate default_id).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pmoves/docs/AGENTS/AGNOTE4482_ROADMAP_W1-W5.md`:
- Line 280: Update the W1 section so its owner/status matches the claimed row:
change the "Owner: Unclaimed" text in the W1 section to reflect the new claim
"4090-claude" and set status to "CLAIMED" (or mirror the exact phrasing used in
the table row "W1 (terminal renderer + Gate 3) | 4090-claude | 2026-03-22 |
CLAIMED | feat/w1-agent-terminal-theme") so the section and the claim row are
consistent.
In `@pmoves/tools/agent_terminal_theme.py`:
- Around line 112-123: The _load_yaml function currently lets file I/O and YAML
parse errors propagate; wrap the file open and yaml.safe_load call in a
try/except that catches exceptions from reading/parsing (e.g., OSError,
yaml.YAMLError, or a broad Exception), print a concise warning to stderr that
includes the path and the exception message, and return {} on any failure; keep
the existing ImportError handling for missing pyyaml and the existing behavior
when path.exists() is false. Use the function name _load_yaml and reference
yaml.safe_load and path to locate where to add the try/except and error message.
- Around line 288-291: The --roster branch currently returns 0 even when no
agents were loaded; change it to fail closed by checking the result of
load_all_agents() and returning a non-zero exit code (or raising SystemExit)
when agents is empty or loading failed: after calling load_all_agents() in the
args.roster block, detect if agents is falsy or len(agents) == 0, print a clear
error message about missing/failed signature loading, and return 1 (or raise
SystemExit(1)) instead of returning 0; keep the successful path that prints
render_roster(agents) and returns 0.
- Around line 302-304: When args.session is set, the code currently calls
load_node(args.node or args.agent) which silently falls back to agent-only
output if an explicit --node is unknown; change the logic to call load_node with
args.node when args.node is provided, then if args.node was supplied and
load_node returns falsy, emit an explicit error (e.g., print to stderr or log
via the same logger) and exit with a non-zero status instead of continuing;
otherwise proceed to call session_header(agent, node) as before. Ensure you
modify the block that uses args.session, referencing load_node, args.node,
args.agent, agent, node, and session_header.
---
Nitpick comments:
In `@pmoves/tools/agent_terminal_theme.py`:
- Around line 125-145: Both load_agent and load_all_agents duplicate the same
mapping from a signature dict to an AgentTheme; create a small helper (e.g.,
_theme_from_entry(entry: dict, default_id: str) -> AgentTheme) that centralizes
the mapping logic used to build AgentTheme instances (map agent_id,
display_name, glyph, color, accent, voice, resonance, description, co_author,
specialization, routes_to with the same defaults currently in load_agent), then
replace the construction in load_agent and the corresponding block in
load_all_agents to call this helper (use _load_yaml/_SIGNATURES_PATH as before
and pass appropriate default_id).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3be28772-1ae8-4d47-8abd-a1fc7ffcac5e
📒 Files selected for processing (3)
pmoves/docs/AGENTS/AGNOTE4482PHI.t1.mdpmoves/docs/AGENTS/AGNOTE4482_ROADMAP_W1-W5.mdpmoves/tools/agent_terminal_theme.py
| | W4 (partial: content stubs) | 5090-claude | 2026-03-19 | SHIPPED `2a681471` | main | | ||
| | W4 (beats pipeline runner) | claude-opus | 2026-03-20 | SHIPPED #1039 | main | | ||
| | W5 (partial: TZ models, TACs) | 5090-claude | 2026-03-19 | SHIPPED `2a681471` | main | | ||
| | W1 (terminal renderer + Gate 3) | 4090-claude | 2026-03-22 | CLAIMED | feat/w1-agent-terminal-theme | |
There was a problem hiding this comment.
Update W1 owner/status text for internal consistency.
This new claim row marks W1 as actively claimed, but the W1 section still says “Owner: Unclaimed” (Line 52). Please reconcile both sections in this doc.
As per coding guidelines, "pmoves/docs/**: Check docs for operational accuracy: Keep status claims aligned with evidence in runbooks and smokes."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/docs/AGENTS/AGNOTE4482_ROADMAP_W1-W5.md` at line 280, Update the W1
section so its owner/status matches the claimed row: change the "Owner:
Unclaimed" text in the W1 section to reflect the new claim "4090-claude" and set
status to "CLAIMED" (or mirror the exact phrasing used in the table row "W1
(terminal renderer + Gate 3) | 4090-claude | 2026-03-22 | CLAIMED |
feat/w1-agent-terminal-theme") so the section and the claim row are consistent.
| def _load_yaml(path: Path) -> Dict[str, Any]: | ||
| """Load YAML file, returning empty dict on failure.""" | ||
| try: | ||
| import yaml # type: ignore[import-untyped] | ||
| except ImportError: | ||
| print("WARNING: pyyaml not installed, using fallback", file=sys.stderr) | ||
| return {} | ||
| if not path.exists(): | ||
| return {} | ||
| with open(path, encoding="utf-8") as f: | ||
| return yaml.safe_load(f) or {} | ||
|
|
There was a problem hiding this comment.
Handle YAML/file errors explicitly in _load_yaml.
Malformed YAML or read failures currently raise and terminate the CLI. This should fail gracefully and return {} with an error message.
Suggested fix
def _load_yaml(path: Path) -> Dict[str, Any]:
"""Load YAML file, returning empty dict on failure."""
try:
import yaml # type: ignore[import-untyped]
except ImportError:
print("WARNING: pyyaml not installed, using fallback", file=sys.stderr)
return {}
if not path.exists():
return {}
- with open(path, encoding="utf-8") as f:
- return yaml.safe_load(f) or {}
+ try:
+ with open(path, encoding="utf-8") as f:
+ loaded = yaml.safe_load(f) or {}
+ except (OSError, yaml.YAMLError) as exc:
+ print(f"ERROR: Failed to load {path}: {exc}", file=sys.stderr)
+ return {}
+ if not isinstance(loaded, dict):
+ print(f"ERROR: Unexpected YAML root type in {path}", file=sys.stderr)
+ return {}
+ return loaded🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/tools/agent_terminal_theme.py` around lines 112 - 123, The _load_yaml
function currently lets file I/O and YAML parse errors propagate; wrap the file
open and yaml.safe_load call in a try/except that catches exceptions from
reading/parsing (e.g., OSError, yaml.YAMLError, or a broad Exception), print a
concise warning to stderr that includes the path and the exception message, and
return {} on any failure; keep the existing ImportError handling for missing
pyyaml and the existing behavior when path.exists() is false. Use the function
name _load_yaml and reference yaml.safe_load and path to locate where to add the
try/except and error message.
| if args.roster: | ||
| agents = load_all_agents() | ||
| print(render_roster(agents)) | ||
| return 0 |
There was a problem hiding this comment.
--roster should fail closed when signatures cannot be loaded.
This path currently exits 0 even if the file is missing/empty and prints a 0-agent roster, which masks configuration errors.
Suggested fix
if args.roster:
agents = load_all_agents()
+ if not agents:
+ print("ERROR: Could not load agent_signatures.yaml", file=sys.stderr)
+ return 1
print(render_roster(agents))
return 0📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if args.roster: | |
| agents = load_all_agents() | |
| print(render_roster(agents)) | |
| return 0 | |
| if args.roster: | |
| agents = load_all_agents() | |
| if not agents: | |
| print("ERROR: Could not load agent_signatures.yaml", file=sys.stderr) | |
| return 1 | |
| print(render_roster(agents)) | |
| return 0 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/tools/agent_terminal_theme.py` around lines 288 - 291, The --roster
branch currently returns 0 even when no agents were loaded; change it to fail
closed by checking the result of load_all_agents() and returning a non-zero exit
code (or raising SystemExit) when agents is empty or loading failed: after
calling load_all_agents() in the args.roster block, detect if agents is falsy or
len(agents) == 0, print a clear error message about missing/failed signature
loading, and return 1 (or raise SystemExit(1)) instead of returning 0; keep the
successful path that prints render_roster(agents) and returns 0.
| if args.session: | ||
| node = load_node(args.node or args.agent) | ||
| print(session_header(agent, node)) |
There was a problem hiding this comment.
Return an error when an explicit --node is unknown.
If --node is provided but not found, the command silently degrades to agent-only output. That makes bad input hard to detect.
Suggested fix
if args.session:
node = load_node(args.node or args.agent)
+ if args.node and node is None:
+ print(f"ERROR: Node '{args.node}' not found in {_NODE_SPEC_PATH}", file=sys.stderr)
+ return 1
print(session_header(agent, node))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/tools/agent_terminal_theme.py` around lines 302 - 304, When
args.session is set, the code currently calls load_node(args.node or args.agent)
which silently falls back to agent-only output if an explicit --node is unknown;
change the logic to call load_node with args.node when args.node is provided,
then if args.node was supplied and load_node returns falsy, emit an explicit
error (e.g., print to stderr or log via the same logger) and exit with a
non-zero status instead of continuing; otherwise proceed to call
session_header(agent, node) as before. Ensure you modify the block that uses
args.session, referencing load_node, args.node, args.agent, agent, node, and
session_header.
Adds missing docstring for CodeRabbit >=80% coverage gate. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
pmoves/tools/agent_terminal_theme.py— reads flat 1.0.0agent_signatures.yaml+node-agent-specialization.yaml, renders ANSI 24-bit themed terminal outputTerminal renderer features
--banner— full themed banner with glyph, name, voice, resonance--status "text"— single-line themed status bar--session— combined agent+node identity display (hardware, strengths)--roster— compact roster of all 11 agents--demo— renders all agents with full bannersz890 handoff validation
Test plan
python pmoves/tools/agent_terminal_theme.py --demo— all 11 agents renderpython pmoves/tools/agent_terminal_theme.py --agent 4090-claude --session— session header with node specpython pmoves/tools/agent_terminal_theme.py --roster— compact roster🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes