Skip to content

Commit cf3482b

Browse files
committed
feat(output): structured warn/info/error lines + clearer ALL_TASKS_DONE message
User feedback after a real plan run: > warn: agent signalled ALL_TASKS_DONE but 6 plan checkbox(es) remain unchecked > 1. this is strange output for user > 2. I'd expect warn, err, so on highlighted Two fixes: 1. Wrap the bare \`warn:\` / \`info:\` lines in a structured prefix with a glyph + bold colour so they pop visually from the [TS] iteration lines: \`⚠ warn:\` bold yellow, \`✖ error:\` bold red, \`ℹ info:\` bold cyan. Falls back to plain \`warn: …\` / \`info: …\` when colour is off (NO_COLOR, RALPHTERM_NO_COLOR, non-TTY). 2. Rephrase the ALL_TASKS_DONE-with-unchecked-boxes line. Old: warn: agent signalled ALL_TASKS_DONE but 6 plan checkbox(es) remain unchecked New: ⚠ warn: agent declared the plan done while 6 checkboxes remained unchecked — accepting on the agent's word (see the iteration log above for its reasoning) Explains WHY we're accepting (the agent's own narration is right above) instead of just stating a discrepancy. Also handles the 1/N plural case ("checkbox" vs "checkboxes"). Call sites migrated: - runner.rs: 3 \`eprintln!("warn: ...")\` → \`warn_line(...)\` - preflight.rs: resume notice → \`info_line(...)\` Same warn-line wording for the iteration crash/no-output-file paths got the "— continuing to next iteration" suffix swapped from "; continuing" for readability.
1 parent 88a1011 commit cf3482b

5 files changed

Lines changed: 60 additions & 9 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ralphterm"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
edition = "2021"
55
rust-version = "1.85"
66
description = "Programmable PTY for the official Claude Code and Codex CLIs. Runs long multi-task plans unattended — iterates, validates, commits per task, and gates merge behind a parallel review pipeline."

src/color.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const CYAN: &str = "\x1b[36m";
1515
const MAGENTA: &str = "\x1b[35m";
1616
const GREEN: &str = "\x1b[32m";
1717
const YELLOW: &str = "\x1b[33m";
18+
const RED: &str = "\x1b[31m";
19+
const YELLOW_BOLD: &str = "\x1b[1;33m";
20+
const RED_BOLD: &str = "\x1b[1;31m";
21+
const CYAN_BOLD: &str = "\x1b[1;36m";
1822

1923
fn enabled() -> bool {
2024
static ON: OnceLock<bool> = OnceLock::new();
@@ -55,6 +59,39 @@ pub fn green(text: &str) -> String {
5559
pub fn yellow(text: &str) -> String {
5660
wrap(YELLOW, text)
5761
}
62+
pub fn red(text: &str) -> String {
63+
wrap(RED, text)
64+
}
65+
66+
/// Format a structured warning line: `⚠ warn: <msg>` in bold yellow
67+
/// for the prefix, regular text for the message. Falls back to a plain
68+
/// `warn: <msg>` when colour is disabled.
69+
pub fn warn_line(msg: impl AsRef<str>) -> String {
70+
if enabled() {
71+
format!("{YELLOW_BOLD}⚠ warn:{RESET} {}", msg.as_ref())
72+
} else {
73+
format!("warn: {}", msg.as_ref())
74+
}
75+
}
76+
77+
/// Format a structured error line: `✖ error: <msg>` in bold red.
78+
pub fn error_line(msg: impl AsRef<str>) -> String {
79+
if enabled() {
80+
format!("{RED_BOLD}✖ error:{RESET} {}", msg.as_ref())
81+
} else {
82+
format!("error: {}", msg.as_ref())
83+
}
84+
}
85+
86+
/// Format a structured info line: `ℹ info: <msg>` in bold cyan. Use
87+
/// for non-failure heads-up messages (resume notice, status changes).
88+
pub fn info_line(msg: impl AsRef<str>) -> String {
89+
if enabled() {
90+
format!("{CYAN_BOLD}ℹ info:{RESET} {}", msg.as_ref())
91+
} else {
92+
format!("info: {}", msg.as_ref())
93+
}
94+
}
5895

5996
/// True if the line looks like a section header from an agent's
6097
/// formatted response — short, no leading bullet marker, no embedded

src/preflight.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,13 @@ impl<'a> Preflight<'a> {
6161
if resuming {
6262
let dirty = collect_uncommitted_paths(self.repo_root)?;
6363
if !dirty.is_empty() {
64+
let plural = if dirty.len() == 1 { "" } else { "s" };
6465
eprintln!(
65-
"resuming on branch {branch} with {} uncommitted file(s) — the next iteration will pick up where the previous run was interrupted",
66-
dirty.len()
66+
"{}",
67+
crate::color::info_line(format!(
68+
"resuming on branch {branch} with {} uncommitted file{plural} — the next iteration will pick up where the previous run was interrupted",
69+
dirty.len()
70+
))
6771
);
6872
}
6973
}

src/runner.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -503,13 +503,19 @@ async fn run_plan_default(options: RunOptions) -> Result<String> {
503503
}
504504
if run.crashed_before_done {
505505
eprintln!(
506-
"warn: iteration {iteration} agent exited (code={}) before writing the output file; continuing",
507-
run.exit_code
506+
"{}",
507+
crate::color::warn_line(format!(
508+
"iteration {iteration} agent exited (code={}) before writing the output file — continuing to next iteration",
509+
run.exit_code
510+
))
508511
);
509512
} else if !run.done_via_file {
510513
eprintln!(
511-
"warn: iteration {iteration} produced no valid output file at {}; continuing",
512-
run.output_path.display()
514+
"{}",
515+
crate::color::warn_line(format!(
516+
"iteration {iteration} produced no valid output file at {} — continuing to next iteration",
517+
run.output_path.display()
518+
))
513519
);
514520
}
515521
}
@@ -527,8 +533,12 @@ async fn run_plan_default(options: RunOptions) -> Result<String> {
527533
anyhow::bail!("hit max iterations ({max_iterations}) without ALL_TASKS_DONE");
528534
}
529535
if agent_declared_done && remaining_unchecked > 0 {
536+
let plural = if remaining_unchecked == 1 { "" } else { "es" };
530537
eprintln!(
531-
"warn: agent signalled ALL_TASKS_DONE but {remaining_unchecked} plan checkbox(es) remain unchecked"
538+
"{}",
539+
crate::color::warn_line(format!(
540+
"agent declared the plan done while {remaining_unchecked} checkbox{plural} remained unchecked — accepting on the agent's word (see the iteration log above for its reasoning)"
541+
))
532542
);
533543
}
534544

0 commit comments

Comments
 (0)