Six index commands parse --since with int(since.rstrip("d")) and no validation, at
cli.py:300, 330, 367, 397, 438, 471. Malformed input produces a raw ValueError traceback rather
than a CLI error. rstrip("d") only strips trailing ds, so plausible typos all crash:
--since 90days, --since 3w, --since abc, --since d.
Location: devrag/cli.py:300-471 (six call sites)
Suggested fix:
Extract one shared parser and call it from all six sites:
def _parse_since(since: str | None) -> int | None:
"""Parse a lookback period like '90d' into days."""
if not since:
return None
m = re.fullmatch(r"(\d+)d", since.strip())
if not m:
raise typer.BadParameter(
f"invalid --since value {since!r}; expected a day count like '90d'"
)
return int(m.group(1))
typer.BadParameter renders as a proper CLI usage error with exit code 2 instead of a traceback.
The regex also rejects the currently-accepted-but-odd 90dd. If other units are wanted later
(3w, 6m), this is the single place to extend.
Source: Surfaced by /engineer-agent audit-code (June run, cited line 300 only); re-verified by hand against HEAD d97b2eb on 2026-07-15 and scope corrected to six call sites, confidence high.
Six index commands parse
--sincewithint(since.rstrip("d"))and no validation, atcli.py:300, 330, 367, 397, 438, 471. Malformed input produces a rawValueErrortraceback ratherthan a CLI error.
rstrip("d")only strips trailingds, so plausible typos all crash:--since 90days,--since 3w,--since abc,--since d.Location:
devrag/cli.py:300-471(six call sites)Suggested fix:
Extract one shared parser and call it from all six sites:
typer.BadParameterrenders as a proper CLI usage error with exit code 2 instead of a traceback.The regex also rejects the currently-accepted-but-odd
90dd. If other units are wanted later(
3w,6m), this is the single place to extend.Source: Surfaced by
/engineer-agent audit-code(June run, cited line 300 only); re-verified by hand against HEADd97b2ebon 2026-07-15 and scope corrected to six call sites, confidence high.