Skip to content
Agentmart
Submit a tool

reply-maker

Draft copy-paste-ready X replies - two options per reply-worthy tweet from tracked accounts, topics, or lists (default), or ready-to-post responses to…

Skill by aeonfun

IndexedMIT★ 782
Write a review

Chalk Labs

Need help deploying reply-maker?

Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.

Book a call

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/aeonfun-reply-maker

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Draft copy-paste-ready X replies - two options per reply-worthy tweet from tracked accounts, topics, or lists (default), or ready-to-post responses to engagement opps in recent logs (from-logs)

Use cases: writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/reply-maker

What's inside

Skill name
reply-maker
Description
Draft copy-paste-ready X replies - two options per reply-worthy tweet from tracked accounts, topics, or lists (default), or ready-to-post responses to engagement opps in recent logs (from-logs)
License
MIT
Metadata
var: empty = auto-discover reply-worthy tweets and draft two options each; @handle / numeric X list ID / topic = scope the drafting to that; from-logs (or --from-logs [@handle|project]) = turn flagged engagement opps from recent logs into ready-to-post replies · title: Reply Maker · commits: false · category: productivity
Instructions
360 lines in SKILL.md · view on GitHub

Before installing

  • Instructions call the network

Agent Skills spec issues

  • files: referenced but missing: scripts/telegram-route.sh

Files (1)

  • SKILL.md

SKILL.md (first lines)

<!-- autoresearch: variation B — sharper output via specificity gates, anti-sycophancy lint, post-write self-edit, and skip-gate for low-leverage tweets -->

> **${var}** — selects the mode and scope:
> - **empty** → **Mode A (Reply Drafting):** auto-discover reply-worthy tweets across your areas of interest (from recent logs + memory) and draft two reply options for each.
> - **`@handle` / numeric X list ID / topic** → **Mode A (Reply Drafting)** scoped to that handle, list, or topic.
> - **`from-logs`** (or **`--from-logs`**, optionally followed by an `@handle` or project name to narrow the scan) → **Mode B (From-Logs Engagement):** scan recent logs for flagged engagement opportunities and turn them into copy-paste-ready responses.
> - **`revise:<instruction>`** → **Revise branch:** reload the last drafted replies and refine them per the instruction (the Telegram force-reply shape, e.g. `revise:make them shorter`).

## Preamble (both modes)

Read `memory/MEMORY.md` for context on active projects and open engagement follow-ups.

Then read `memory/logs/` — the window depends on the mode:
- **Mode A:** the last 2 days of `memory/logs/` for recent `fetch-tweets`, `narrative-tracker`, and prior `reply-maker` outputs (used as a candidate pool and for reply de-duplication).
- **Mode B:** the last 7 days of `memory/logs/` for engagement opportunities flagged by other skills (`mention-radar`, `fetch-tweets`, `reply-maker`) or noted in MEMORY.md "Known Follow-ups".

**Parse `${var}` to pick the branch** (trim whitespace, compare case-insensitively):
- If `${var}` starts with `revise:` — run the **Revise branch** (below) and stop. This is the shape `scripts/telegram-route.sh` sends when the operator replies to a "refine these replies?" force-reply prompt; catch it before mode parsing.
- If `${var}` is `from-logs` or `--from-logs` — optionally followed by a whitespace-separated `@handle` or project name — run **Mode B (From-Logs Engagement)**. Treat any trailing token as an optional filter that narrows the opportunity scan to that handle/project.
- Otherwise run **Mode A (Reply Drafting)**, treating `${var}` as the scope: empty, `@handle`, numeric X list ID, or a topic string.

## Voice

If soul files exist (`soul/SOUL.md`, `soul/STYLE.md`, `soul/examples/`), read them and **mirror that voice in every reply**. Match sentence length, vocabulary choices, punctuation habits, and the kinds of things the operator would never say.

If no soul files exist (or the bodies are empty placeholders), write replies that are:
- Direct and substantive — no fluff, no sycophancy
- Under 280 characters each (X replies; DMs and GitHub comments may run longer — see Mode B)
- Opinionated but grounded in specifics
- The kind of reply that adds to the conversation, not noise

Either way, when responding to someone who cosigned/mentioned/attributed the operator (Mode B): **acknowledge without groveling** — no "thanks so much for the kind words!", just the actual response.

---

# Revise branch (`revise:…` — Telegram force-reply)

The operator tapped the "refine these replies?" prompt and sent a free-text revision instruction. Handle it before Mode A/B:

1. **Strip the prefix.** The instruction is `${var#revise:}` (keep any inner colons). Trim whitespace — e.g. `make them shorter`, `less formal`, `drop reply B on #2`.
2. **Load the last draft.** Read `memory/drafts/reply-maker-latest.md` — the stable path every normal run saves to (see the save steps in A4 / B6). If it's missing or empty, there's nothing to refine: send `./notify "Nothing to revise yet — run reply-maker first, then reply here to refine the drafts."` and **end the run**.
3. **Apply the instruction.** Read `soul/` for voice, then regenerate the saved replies applying the operator's instruction. Keep the same set of target tweets and the same **A/B two-option** structure (Mode A) or ready-to-post list (Mode B) — you're refining wording, not re-discovering candidates. Re-enforce the hard reply rules: ≤280 chars for X replies, no sycophancy (see **Banned sycophancy phrases**), specifics not gestures.
4. **Re-save** the revised drafts to `memory/drafts/reply-maker-latest.md` (overwrite), so a further `revise:` refines the newest version.
5. **Re-send** via `./notify` in the same format the originating mode uses, with a first line flagging it as a revision, e.g. `revised (${var#revise:}):`. Use `./notify -f <file>` for multi-line output.
6. **Re-offer** a further revision (the operator is actively iterating, so this is expected, not a nag — skip the daily dedup guard here):
   ```bash
   ./notify "Want another pass? Reply with a change and I'll revise again." \
     --force-reply --placeholder "e.g. make them shorter" \
     --context "reply-maker::revise"
   ```
7. **Log** under `### reply-maker` with `- **Mode:** revise` and the instruction (see **Log**), then **end the run** — do NOT run Mode A or B.

---

# Mode A — Reply Drafting

Generate **two reply options** for **5 reply-worthy tweets** from tracked X accounts, a list, or a topic.

### A1. Gather candidate tweets

Goal: assemble **10–15 candidates** posted in the **last 6 hours** (the high-leverage reply window — the algorithm rewards early replies, and the OP is still likely to engage back). **Recency fallback:** if the 6h window yields fewer than 3 candidates after the skip gate, widen to **12h** and retry before failing the run.

For every candidate, capture: `@handle`, full tweet text, tweet URL, `posted_at` (ISO), engagement counts (likes, replies, retweets if available), and a one-line **why-this-tweet** note.

**Path A - twitterapi.io (primary, except `list:`).** `TWITTER_API_KEY` is injected into this skill's environment (declared in `requires:`). Handle and topic scopes use structured twitterapi.io fetches (full contract in **Fetching**). twitterapi.io has **no `list:` operator**, so a numeric list ID stays on Path B (xAI) as its primary. Preflight the keys: read the "Keys set for this run" line in your run notes, never a shell test (those are refused and cost a turn). `TWITTER_API_KEY` on that line = `TW_PRESENT`, else `TW_UNSET`; `XAI_API_KEY` on it = `XAI_PRESENT`, else `XAI_UNSET`.

**Shell shape.** Each Bash call is one allowed command (pipes are fine). The run refuses a call that sets a variable (`FROM_DATE=$(date ...)`, `HTTP=$(./secretcurl ...)`, `HANDLE="${var#@}"`), uses `if` / `for` / `while`, or joins commands with `;`, `&&` or `||`, and each refusal costs a model turn. So get each date with a bare `date` call and write the printed value into later commands; write the handle, topic, list ID and dates into each command literally (the `<FROM_DAY>`, `<HANDLE>` style placeholders in the snippets), jq filters included (`>= "<FROM_DAY>"`, never `--arg` with a `$name`: refused); run one `jq` per call. A `>` redirect into a file is refused too, so the xAI body is piped straight into `./secretcurl ... --data-binary @-` and notify bodies are written with the Write tool.

Get the window dates, each `date` as its own Bash call:
```bash
date -u +%Y-%m-%dT%H:%M:%SZ
```
```bash
date -u -d '6 hours ago' +%Y-%m-%dT%H:%M:%SZ
```
```bash
date -u -d '6 hours ago' +%Y-%m-%d
```
The first prints TO_DATE, the second FROM_DATE, the third FROM_DAY.

**If `${var}` looks like a `@handle`** - pull that account's recent timeline, then filter to the 6h window client-side (`/user/last_tweets` has no server-side date filter). `createdAt` is Twitter-native; normalize with `strptime` before the compare. `<HANDLE>` is `${var}` without the leading `@`, written in literally:
```bash
./secretcurl -s -o /tmp/tw-rm.json -w 'twitterapi http=%{http_code} bytes=%{size_download}\n' --max-time 60 -G "https://api.twitterapi.io/twitter/user/last_tweets" \
  --data-urlencode "userName=<HANDLE>" -H "X-API-Key: {TWITTER_API_KEY}"
```
On `HTTP=200` with a non-empty `.data.tweets[]`, parse, drop replies/RTs (`.isReply`, or text starting `RT @`), keep originals inside the window, and mark `twitterapi=ok`. FROM_DAY is written in literally:
```bash
jq -r '.data.tweets[] | select((.isReply | not) and ((try (.createdAt | strptime("%a %b %d %H:%M:%S %z %Y") | strftime("%Y-%m-%d")) catch "0000-00-00") >= "<FROM_DAY>")) | [.id, .author.userName, .text, .url, .createdAt, .likeCount, .retweetCount, .replyCount] | @tsv' /tmp/tw-rm.json
```

**If `${var}` is a topic** (or empty) - `advanced_search` with the topic (or the top 2-3 topics from `memory/MEMORY.md` when empty) and `since:<FROM_DAY>` inside the query, both written in literally. When empty, also pull tweet candidates surfaced in the last 2 days of `fetch-tweets` logs as a backup pool:
```bash
./secretcurl -s -o /tmp/tw-rm.json -w 'twitterapi http=%{http_code} bytes=%{size_download}\n' --max-time 60 -G "https://api.twitterapi.io/twitter/tweet/advanced_search" \
  --data-urlencode "query=<TOPIC> since:<FROM_DAY>" --data-urlencode "queryType=Latest" \
  -H "X-API-Key: {TWITTER_API_KEY}"
```
On `HTTP=200` with a non-empty `.tweets[]`, parse originals (skip replies) and mark `twitterapi=ok`:
```bash
jq -r '.tweets[] | [.id, .author.userName, .text, .url, .createdAt, .likeCount, .retweetCount, .replyCount] | @tsv' /tmp/tw-rm.json
```
Then keep only posts inside the 6h window (strptime-normalize `.createdAt`).

**If `${var}` looks like an X list ID** (numeric): skip Path A (no twitterapi `list:` operator) and go to Path B.

**Path B - xAI Grok `x_search` (fallback; primary for `list:`).** Only if Path A returned non-2xx / empty / timeout, `TWITTER_API_KEY` is unset, or `${var}` is a numeric list ID. Direct `curl` to `https://api.x.ai/v1/responses`. `x_search` searches X live and takes 30-120s - **set the Bash tool `timeout` to ≥180000 when you run this** (a slow curl is not a missing key). Build the payload per `${var}` (three shapes below) with `jq -n` and pipe it straight into the `./secretcurl` send, one call. It prints the HTTP status (secretcurl also prints an `http=... bytes=...` line on stderr).

On `HTTP=200` with a non-empty body, parse `/tmp/xai-rm.json` and mark `xai=ok`:
```bash
jq -r '.output[] | select(.type == "message") | .content[] | select(.type == "output_text") | .text' /tmp/xai-rm.json
```

**The payload depends on `${var}`.** Whichever branch matches, build it with `jq -n` (never a shell-interpolated string) and pipe it into `./secretcurl ... --data-binary @-` in the same call. Do not write it to a file first or pass values with `--arg`: a `>` redirect into a file and a jq `$name` variable are both refused in this run.

In each shape the list ID, handle or topic and both dates are written into the jq filter literally (`<LIST_ID>`, `<HANDLE>`, `<TOPIC>`, `<FROM_DATE>`, `<TO_DATE>`); a `"` inside a value becomes `\"`, and reword any `'`.

**If `${var}` looks like an X list ID** (numeric):
```bash
jq -n '{
  model: "grok-4.7",
  input: [{role: "user", content: "Look at X list https://x.com/i/lists/<LIST_ID>. Return the 12 most reply-worthy original posts (not retweets, not replies) by members of this list between <FROM_DATE> and <TO_DATE>. Reply-worthy = has a take, claim, question, or framing worth engaging — NOT pure self-promo, breaking news without analysis, or threads already past 500 replies. For each: @handle, full tweet text, tweet URL, posted_at ISO timestamp, like/reply/retweet counts."}],

From aeonfun/aeon, MIT.

More from aeonfun/aeon

Signals

Stars
782
Forks
292
Open issues
0
Last commit (this folder)
Oct 1, 2026 (1 month ago)
Last release
Jul 9, 2026 (3 months ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from aeonfun

Done for you

Rather not set it up yourself?

Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.

See how we build AI agents →Read the case studies

Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.

launches shipped
50+
launches shipped
media placements
500+
media placements
founders on every account
2
founders on every account

Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block

Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.

Get reply-maker running in your business

Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying reply-maker?

CallGet help
Cite this page

reply-maker skill. Agent Mart. https://agentmart.dev/skills/aeonfun-reply-maker (last updated 2026-10-24)