Skip to content

feat(tag,help): lowercase-english-only --tag + colorized help#36

Merged
alex-mextner merged 2 commits into
mainfrom
fix/tag-lowercase-english-and-help-color
Jun 17, 2026
Merged

feat(tag,help): lowercase-english-only --tag + colorized help#36
alex-mextner merged 2 commits into
mainfrom
fix/tag-lowercase-english-and-help-color

Conversation

@alex-mextner

Copy link
Copy Markdown
Owner

Implements two ROADMAP items (CTO 2026-06-16): "tg --tag: lowercase-english only" and the tg side of "ONE shared help-formatter" (match the ecosystem color scheme until the Python migration lands the real shared lib).

1. --tag is lowercase-english ONLY

Accepted values: answer / decision / problem / report. Russian aliases (ОТВЕТ/РЕШЕНИЕ/ПРОБЛЕМА/ОТЧЁТ) and uppercase / mixed-case / unknown words are now rejected at parse time with a 3-part error and a non-zero exit, replacing the old soft-render-and-warn:

$ tg --tag ANSWER --reply-to 1 "x"
Error: invalid --tag 'ANSWER': tags must be lowercase english. Use one of: answer, decision, problem, report
$ echo $?
1
  • validateTag() (features/render/tag.ts) is the gate, called in parseArgs so every send path fails before touching Telegram. The validated tag is stored trimmed, so the answer--reply-to gate (a literal compare) can't be bypassed by a padded value like --tag " answer ".
  • TAG_ALIASES (Cyrillic) removed; resolveTag uses a canonical Set and stays total for the renderer. The now-unreachable unknown-tag stderr warning + its resolveTag import were dropped from the entrypoint.
  • Docs updated to lowercase-english only: tg --help / --format-help, the install-skill SKILL.md + always-on blurb, AGENTS.md, and the prefix.ts tag contract. The skill badge table now matches the real TAG_PILL_FALLBACK (🔵 ANSWER, …).

2. Colorized --help

tg --help and tg --format-help were the only ecosystem CLI helps printed flat. New features/render/help-color.ts colorizes section headers (bold cyan) and option names (green) to match review/rig/draw — dependency-free ANSI, the same \033[CODEm scheme as rig's _c. Auto-disabled when stdout/stderr is not a TTY or NO_COLOR is set, so piped/redirected help stays plain. Only color codes are injected: strip the escapes and the text is byte-identical.

Tests

bun test1014 pass / 0 fail. New coverage:

  • validateTag accepts the four lowercase tags; rejects uppercase / mixed-case / Cyrillic / unknown with the 3-part error (unit + parseArgs + subprocess, asserting non-zero exit).
  • the padded-answer-without---reply-to gate (regression guard).
  • colorizeHelp headers + option flags get the right escapes; piped --help is plain (no \x1b[); strip-ANSI == input.
  • install-skill badge table matches TAG_PILL_FALLBACK.
    Existing tag/render/reply-gate/install-skill tests migrated off Cyrillic + uppercase inputs.

Version bumped to 1.12.0 with a CHANGELOG entry.

Reviewed twice via review --staged (codex / opus / glm). Every finding addressed, including a real trim-vs-answer-gate regression the second pass caught (stored untrimmed tag bypassed the literal answer gate — now stored trimmed + tested).

Do not merge — CTO verifies.

🤖 Generated with Claude Code

alex-mextner and others added 2 commits June 16, 2026 23:26
Two ROADMAP items (CTO 2026-06-16): "tg --tag: lowercase-english only" and the
tg side of "ONE shared help-formatter" (match the ecosystem color scheme).

--tag — lowercase-english ONLY
  Accepted: answer / decision / problem / report (the lowercase spellings of the
  canonical tags). Russian aliases (ОТВЕТ/РЕШЕНИЕ/ПРОБЛЕМА/ОТЧЁТ) and uppercase /
  mixed-case / unknown values are now REJECTED at parse time with a 3-part error
  and a non-zero exit, replacing the old soft-render-and-warn:

    invalid --tag 'ANSWER': tags must be lowercase english.
    Use one of: answer, decision, problem, report

  - validateTag() (features/render/tag.ts) is the gate; parseArgs calls it so
    every send path fails before touching Telegram, and stores the TRIMMED
    canonical tag so the `answer` --reply-to gate (a literal compare) can't be
    bypassed by a padded value.
  - TAG_ALIASES (Cyrillic) removed; resolveTag uses a canonical Set and stays
    total for the renderer. Dropped the now-unreachable unknown-tag warning and
    the resolveTag import from the entrypoint.
  - Docs (tg --help/--format-help, install-skill SKILL.md + blurb, AGENTS.md,
    prefix.ts contract) updated to lowercase-english only; the skill badge table
    now matches the real TAG_PILL_FALLBACK (🔵 ANSWER, …).

--help / --format-help — colorized
  New features/render/help-color.ts: section headers bold-cyan, option names
  green, matching review/rig/draw. Dependency-free ANSI, auto-disabled when
  stdout/stderr is not a TTY or NO_COLOR is set (piped help stays plain). Only
  color codes are injected — strip them and the text is byte-identical.

Version bumped to 1.12.0 with a CHANGELOG entry. Full suite green (1014 tests).

Reviewed twice via `review --staged` (codex/opus/glm); fixed every finding,
including a real trim-vs-answer-gate regression the second pass caught.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…deQL

CodeQL js/incomplete-sanitization fired on the format-help assertion's
`BOLD.replace('[', '\\[')` — String.replace(string) only swaps the first match.
The escape happened to be correct (one '[' in the code) but the pattern is a
smell. Rewrite the assertion to find a colored section header by its literal
BOLD+CYAN prefix + RESET, no regex-escaping needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alex-mextner

Copy link
Copy Markdown
Owner Author

CI status — green except the pre-existing CodeQL legacy findings

All gates pass except CodeQL Self-Gate (javascript-typescript), which fails on 8 PRE-EXISTING findings in untouched code — not a regression from this PR:

rule file (NOT touched here)
js/incomplete-multi-character-sanitization features/auto-attach/transmitter.ts:51, features/render/rich.ts:244
js/double-escaping features/auto-attach/transmitter.ts:52, tests/table.test.ts:124
js/incomplete-sanitization features/render/rich.ts:87
js/file-system-race features/install-skill/install.ts:218,248, features/transport/telegram.ts:67

These are the same 8 legacy findings tracked at #31 ("fix-or-justify the 8 CodeQL TS findings") — same situation as #25, which merged with this exact justification (the self-gate firing on legacy features/**, not a regression).

This PR's own new code introduced one CodeQL finding (js/incomplete-sanitization in tests/help-color.test.ts — a single-occurrence String.replace) — already fixed in commit 2bb4755, which dropped the gated count from 10 → 8. So everything this PR adds is clean; the residual 8 are #31's.

test gate is green (1014 tests pass).

@alex-mextner alex-mextner merged commit aeb82fa into main Jun 17, 2026
7 of 8 checks passed
@alex-mextner alex-mextner deleted the fix/tag-lowercase-english-and-help-color branch June 17, 2026 04:27
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.

1 participant