feat(tag,help): lowercase-english-only --tag + colorized help#36
Merged
Conversation
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>
Owner
Author
CI status — green except the pre-existing CodeQL legacy findingsAll gates pass except CodeQL Self-Gate (javascript-typescript), which fails on 8 PRE-EXISTING findings in untouched code — not a regression from this PR:
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 This PR's own new code introduced one CodeQL finding (
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
--tagis lowercase-english ONLYAccepted 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:validateTag()(features/render/tag.ts) is the gate, called inparseArgsso every send path fails before touching Telegram. The validated tag is stored trimmed, so theanswer→--reply-togate (a literal compare) can't be bypassed by a padded value like--tag " answer ".TAG_ALIASES(Cyrillic) removed;resolveTaguses a canonicalSetand stays total for the renderer. The now-unreachable unknown-tag stderr warning + itsresolveTagimport were dropped from the entrypoint.tg --help/--format-help, the install-skillSKILL.md+ always-on blurb,AGENTS.md, and theprefix.tstag contract. The skill badge table now matches the realTAG_PILL_FALLBACK(🔵 ANSWER, …).2. Colorized
--helptg --helpandtg --format-helpwere the only ecosystem CLI helps printed flat. Newfeatures/render/help-color.tscolorizes section headers (bold cyan) and option names (green) to match review/rig/draw — dependency-free ANSI, the same\033[CODEmscheme as rig's_c. Auto-disabled when stdout/stderr is not a TTY orNO_COLORis set, so piped/redirected help stays plain. Only color codes are injected: strip the escapes and the text is byte-identical.Tests
bun test— 1014 pass / 0 fail. New coverage:validateTagaccepts the four lowercase tags; rejects uppercase / mixed-case / Cyrillic / unknown with the 3-part error (unit +parseArgs+ subprocess, asserting non-zero exit).answer-without---reply-togate (regression guard).colorizeHelpheaders + option flags get the right escapes; piped--helpis plain (no\x1b[); strip-ANSI == input.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 literalanswergate — now stored trimmed + tested).Do not merge — CTO verifies.
🤖 Generated with Claude Code