/** * One renderer per event type, all built on one skeleton — approved by * the owner on 20.08.2026 after ~15 live rounds in the test forum: * * #type #instance * icon Type: action * * Label: value *
quoted text from someone else — commit body, task body
* * Group * #N (overdue): title * * Label: value ← actions/directions * * Three levels of styling, never mixed: a field is a bold, capitalized * label plus a plain value; a group is italic+underline, no bold and no * colon; line 2 (the type) follows the same field rule. A blank line * separates BLOCKS BY MEANING (header / body / actions), not mechanically * after every line. */ import { ICON, type NotifyEvent } from './events.ts'; /** Escapes EVERYTHING that comes from outside — only the template adds tags. */ export declare const esc: (v: unknown) => string; /** * Telegram cuts a message at 4096 characters — we cut it ourselves first, * on a line boundary where possible. * * Two traps, both caused a SILENT loss of the message: * 1. Cutting strictly at the last `\n` does not work: if a long chunk runs * as one line (a stack trace, command output — the most common `detail` * on an incident), the last line break sits BEFORE it, and the whole * content got dropped — only the heading arrived, with not a single * fact about what broke. * 2. Cutting in the middle of an HTML tag or entity does not work either: * Telegram replies `400 can't parse entities`, and we treat a 4xx as a * permanent error and do not retry — the message disappeared for good. */ export declare const clampMessage: (text: string, limit?: number, marker?: string) => string; /** * GitHub Markdown, read in Telegram. A PR or issue body arrives as the author * wrote it for GitHub, and Telegram knows none of it: `## Как проверял` stood * as two hash signs, a screenshot as `![after 375](https://…png)` in full, the * PR template's HTML comment as a paragraph, a table as a fence of bars. On * the 49 PR and issue cards of the week of 25.08.2026 that was the "wall of * text" the owner read. So the body is translated, not pasted (03.09.2026): * * - a comment `` is the template talking to the author — dropped; * - `## Heading` → bold line; `**x**` → bold; `` `x` `` → code; a fenced * block → `
`;
 *   - an image `![alt](https://…)` → a link named by its alt (or `image`);
 *     a link `[text](https://…)` → a link; a link to a repo path (no scheme)
 *     keeps only its text — the address would not open from a phone anyway;
 *   - a table: the `|---|` rule is dropped, a row's cells are joined by ` · `;
 *   - `Closes #N` / `Fixes #N` lines are GitHub's own bookkeeping — dropped;
 *   - runs of blank lines collapse to one.
 *
 * Everything is escaped FIRST, then the markup is added on the escaped text:
 * the patterns contain no `<>&"`, so nothing the author typed can become a tag.
 */
export declare const markdownToTelegram: (text: string) => string;
/**
 * A session has no name of its own, so the incident's title is built out of
 * what happened to it: `burning the limit` → `Claude session is burning the
 * limit`. That is the whole of what the separate `session` card ever said
 * differently — every other row it had is an incident row now.
 *
 * The 36-character id is still not printed: he cannot type it, cannot search
 * it and cannot act on it. It reaches the card only inside the `rm` command at
 * the bottom, which is the one place it is of any use.
 */
export declare const sessionTitle: (action: string | undefined) => string;
export declare const slug: (raw: string) => string;
/**
 * The instance tag: exactly which concrete event this is (branch,
 * environment, task, number) — the parser uses it to match a 🔴 against a
 * later green card of the SAME instance. An explicit `key` always wins;
 * without one, it is derived from the type's most stable fields (branch/
 * environment outrank the title, because a recurring task's title does not
 * change, while for a report the title is exactly the one stable field it
 * has).
 */
export declare const eventKey: (e: NotifyEvent) => string;
/**
 * The third tag is the OUTCOME, and it is always there. The owner: "I'm
 * missing a fail tag or something like it, so failures can be grouped and
 * ok can be grouped." One tap in Telegram collects every failure of a
 * project at once, no matter what type it arrived as — a deploy, a check,
 * a scheduled task, an incident.
 *
 * The value comes from the ICON, never from the status word. The icon is
 * already the single source of truth for the sound, and a second list of "what
 * counts as broken" would drift from the first — it already did once, when a
 * red card arrived silent.
 *
 * One icon meaning, one tag. A watchdog that SWITCHED SOMETHING OFF is not a
 * failure and must not be filed under the same word as one: the owner read
 * `#fail` under a 🚫 and said so. Nor is a task that has simply gone quiet —
 * nobody knows yet whether it broke, and `#unknown` is the honest word for it.
 */
/** Every icon the package can print — the key space of the outcome table. */
type Icon = (typeof ICON)[keyof typeof ICON];
/** The five words the third tag is allowed to be, and there is no sixth. */
type OutcomeTag = 'ok' | 'fail' | 'off' | 'unknown' | 'info';
/**
 * The type is `Record` over EVERY icon, not over `string`. A new icon added to
 * `ICON` without a word here now fails to compile. Under the old loose type it
 * fell through a `?? 'info'` default instead: a card whose outcome nobody had
 * decided was indistinguishable from a card that is genuinely just news, and
 * nothing anywhere went red. `info` is therefore written out for each icon
 * that means it, never left to a fallback.
 */
export declare const OUTCOME_TAG: Readonly>;
export declare const outcomeTag: (e: NotifyEvent) => OutcomeTag;
/**
 * Renders an event into finished HTML text, cut to Telegram's limit.
 *
 * Assembly is TAIL-FIRST (v2.1): the parts that must survive any cut — the
 * tag line (the human filter and the parser's machine key), the pointer
 * block (`Log`/`Check`/`Source`) and the cut marker — are measured before
 * the body is clamped, and the body gets what is left. Under the old order
 * the pointer was part of the body, so the longest cards lost exactly the
 * line saying where to look.
 */
export declare const render: (e: NotifyEvent) => string;
export {};