Templates gallery
19 ready-made messages. Each shows a live bubble preview, the exact code that built it, and a “Send to me” button once you connect a bot.
Agent status card
statusA live status line an agent posts as it works.
● ci-runner running
Building
packages/core — step 3/7message([
bold(text("● ci-runner")), text(" "), italic(text("running")), text("\n"),
text("Building "), code("packages/core"), text(" — step 3/7"),
])Connect a bot first
Needs input — question
interactiveAgent asks the user to choose; typed callback buttons.
Which migration strategy?
planner needs a decision to continue
Blue/greenRolling
Cancel
message([bold(text("Which migration strategy?")), text("\n"),
italic(text("planner needs a decision to continue"))])
// + inlineKeyboard(callbackButton("Blue/green", migAction, {choice:"bg"}), ...)Connect a bot first
Approval gate
interactiveApprove/reject a proposed action before it runs.
🔐 Approval required
Delete
prod/backups/* (14 objects)✅ Approve✕ Reject
message([text("🔐 "), bold(text("Approval required")), text("\n"),
text("Delete "), code("prod/backups/*"), text(" ("), bold(text("14 objects")), text(")")])
// approve/reject buttons carry an HMAC-signed pointer payloadConnect a bot first
Live progress (edit loop)
statusOne message edited many times via LiveMessage coalescing.
Deploying hz2
▓▓▓▓▓▓▓░░░ 70%
uploading layer 4/6
const live = client.liveMessage({ chatId, minEditIntervalMs: 1500 });
live.update("▓▓▓▓░░░░ 40% — layer 2/6");
live.update("▓▓▓▓▓▓▓░ 70% — layer 4/6"); // bursts coalesce to the edit cadenceConnect a bot first
Completion report
statusA task finished — summary + duration.
✓ Build passed
152 tests,
16.4s
all packages green; jsr dry-run clean
message([bold(text("✓ Build passed")), text("\n"),
text("152 tests, "), code("16.4s"), text("\n"),
blockquote(text("all packages green; jsr dry-run clean"))])Connect a bot first
Failure w/ expandable stack
richError headline with the stack trace behind an expandable quote.
✗ deploy failed
ECONNREFUSED
at connect (net.js:1128) at Socket.emit (events.js:519) at TCPConnectWrap (net.js:1637)
message([bold(text("✗ deploy failed")), text(" "), code("ECONNREFUSED"), text("\n"),
expandableBlockquote(text(stackTrace))]) // collapsed by default in TelegramConnect a bot first
Fleet overview (image)
imageA rendered card summarizing every agent in the fleet.
Fleet — 4 agents
2 running · 1 idle · 1 error
// server: fleetOverviewCard({ title, agents, chart, timestampIso }) -> PNG
captioned([bold(text("Fleet — 4 agents")), ...], "/api/images/fleet-overview")Connect a bot first
Quota warning
statusA soft warning as usage approaches a limit.
⚠️ Token budget 85%
212k / 250k used this run
message([text("⚠️ "), bold(text("Token budget 85%")), text("\n"),
text("212k / 250k used this run")])Connect a bot first
Daily digest
richA compact end-of-day summary in a quote block.
Daily digest — Jul 9
PRs merged: 4 CI runs: 31 (2 flaky) Deploys: 2 Incidents: 0
message([bold(text("Daily digest — Jul 9")), text("\n"),
blockquote(text("PRs merged: 4\nCI runs: 31 ("), bold(text("2 flaky")), text(")\n..."))])Connect a bot first
Deploy notification
statusA release went out — version, env, link.
🚀 v0.1.0 → production
gramforge/gramforge @
d05267c
release notesmessage([bold(text("🚀 v0.1.0 → production")), text("\n"),
text("gramforge/gramforge @ "), code("d05267c"), text("\n"),
link("https://.../releases/v0.1.0", text("release notes"))])Connect a bot first
PR review request
interactiveAsks a reviewer to look at a pull request.
@andrew review requested
feat(core): typed RichText AST
+842 −0 · 11 files
👀 View diff✅ Approve
message([mentionUsername("andrew"), text(" review requested\n"),
bold(text("feat(core): typed RichText AST")), text("\n+842 −0 · 11 files")])Connect a bot first
CI status matrix
richA monospaced matrix of jobs × platforms.
CI matrix
job linux macos win
build ✓ ✓ ✓
test ✓ ✓ ✗
publish ✓ — —message([bold(text("CI matrix")), text("\n"),
pre(matrixText, { kind: "known", value: "text" })])Connect a bot first
Long log (collapsible)
richA noisy log tucked into an expandable quote.
build.log 42 lines
[12:00:01] resolve deps [12:00:03] compile core [12:00:05] compile render [12:00:09] bundle [12:00:11] done
message([bold(text("build.log")), text(" "), italic(text("42 lines")), text("\n"),
expandableBlockquote(text(logBody))])Connect a bot first
Chart card (image)
imageAn ECharts-rendered chart delivered as a photo.
Tests over time
last 5 runs
// server: chartCard({ spec: { kind:"line", ... } }) -> ECharts SSR -> PNG
captioned([bold(text("Tests over time")), ...], "/api/images/chart-card")Connect a bot first
Media album report
imageA captioned lead image for a 2–10 item album.
Screenshots — 3 of 3
visual diff attached
client.sendMediaGroup({ chatId, items: [
{ type:"photo", source:{kind:"url",url}, caption:{kind:"known", value: cap} }, ...2-10
]})Connect a bot first
Poll / question
interactiveA quick multiple-choice prompt.
Ship it Friday?
team vote
YesNoAbstain
message([bold(text("Ship it Friday?")), text("\n"), italic(text("team vote"))])Connect a bot first
Countdown / timer
statusA ticking countdown, edited in place each second.
⏳ Deploy window closes in 03:00 (edited)
02:59
const live = client.liveMessage({ chatId, minEditIntervalMs: 1000 });
setInterval(() => live.update(`⏳ closes in ${mmss(left)}`), 1000);Connect a bot first
Onboarding / help menu
interactiveA welcome message with a menu of next steps.
Welcome to Gramforge 👋
Author typed messages — no escaping ever.
📚 Docs
🧩 Templates
💬 Support
message([bold(text("Welcome to Gramforge 👋")), text("\n"),
text("Author "), underline(text("typed")), text(" messages — no "),
spoiler(text("escaping")), text(" ever.")])Connect a bot first
Kitchen sink (everything)
interactiveOne message with every text entity plus typed-callback buttons that each do a different real thing.
🧪 Agent run struck · spoiler
inline
4c1a9f
italic · underline · code · read the Bot API
ping @gramforge
$ pnpm check
✓ 154 tests · jsr dry-run cleantyped messages — no escaping, ever
Entities travel as UTF-16 offsets; nothing is HTML-escaped. Tap a button — each decodes a signed callback and does something different: acknowledge, decide (edits this message), vote (alert), or open a paginated log.
👍 Ack🔁 Retry
✅ Approve⛔ Reject
YesNoAbstain
📄 Open log
message([
bold(text("🧪 Agent run ")), code("4c1a9f"), text("\n"),
italic(text("italic")), underline(text("underline")), strikethrough(text("struck")),
spoiler(text("spoiler")), code("code"),
link("https://core.telegram.org/bots/api", text("Bot API")),
mentionUsername("gramforge"),
pre("$ pnpm check", { kind: "known", value: "bash" }),
blockquote(...), expandableBlockquote(...),
])
// interactive: typed-callback rows — ack (toast), decide (edits message),
// vote (alert), page (paginates the log). Decoded + routed in the webhook.Connect a bot first