| name | discord-thread-archiver |
| description | Smart Discord thread archiving. Use when: (1) running periodic thread cleanup, (2) evaluating whether Discord threads should be archived. Agent lists active threads, reads messages, judges conversation status, archives resolved threads, and produces a structured report. |
| metadata | {"version":"1.2.2"} |
Discord Thread Archiver
Scan active Discord threads, judge whether conversations have concluded, archive resolved ones, and produce a structured report.
Parameters
The caller provides:
guildId — Discord guild to scan
channelId — Parent channel whose active threads should be scanned
logChannel — Channel ID for the report
operationalThreadPrefixes — Optional comma-separated prefixes for bot-created operational threads. Default: 🤖
mode — Optional: apply (default) or dry-run. In dry-run mode, report decisions but do not archive.
Workflow
1. List threads
Call thread-list exactly once with both guildId and channelId. This returns active threads only under the specified parent channel. Do NOT call without channelId — never scan the entire guild.
message(action="thread-list", channel="discord", guildId="<guildId>", channelId="<channelId>")
If empty → send "⏸️ 无 Thread" report (see format below) and stop.
2. Load judgment rules
Read the full judgment guide before evaluating any thread:
read("references/judgment-guide.md")
3. Evaluate each thread
Skip threads with last_pin_timestamp present → mark skipped (pinned).
For all others, read the last 5 messages:
message(action="read", channel="discord", target="channel:<thread_id>", limit=5)
3a. Bot-only lookback
All 5 messages from bots → expand to limit=20 to find earlier human participation.
3b. Thread type split
Before applying normal human-conversation gates, identify whether the thread is operational:
- Thread name starts with any
operationalThreadPrefixes value, default 🤖
- AND the expanded message window contains no human messages
Operational threads are bot-created task/status threads. They do not require human closure confirmation. Evaluate them with the Operational Thread Policy in judgment-guide.md.
After reading messages, normalize each thread into this JSON shape and run the classifier script. Use the script verdict unless it returns uncertain and the judgment guide clearly resolves the case:
{"name":"thread name","pinned":false,"lastMessageAgeMinutes":123,"messages":[{"content":"...","isBot":true}],"operationalThreadPrefixes":["🤖 "]}
python3 <skill_dir>/scripts/classify-thread.py < /tmp/thread-facts.json
If a prefixed thread has any human message in the expanded window, the classifier will treat it as a normal human-bot collaboration thread.
3c. Hard gate checks for normal threads
Apply these mechanical checks first. If ANY gate triggers → verdict is keep, skip classification.
| # | Condition | Verdict |
|---|
| G1 | Last message from bot AND contains "?" or "吗" or ends with question | keep — 等待回复 |
| G2 | Last message < 24h old AND no human closure signal found AND no final-answer idle condition | keep — 近期无关闭 |
| G3 | Human-bot collaboration (lookback found human messages) AND < 24h AND no human closure signal found AND no final-answer idle condition | keep — 协作中 |
Closure signals (must come from a human, not bot): 好了, 搞定, done, 结束, 结束吧, 谢谢, thanks, 确认, 没问题, OK, 可以了, 完成, 完成了, 已完成, 完成吧, 收尾, 收工, 不再需要讨论, 不需要讨论了, 无需讨论, 不用讨论, 可以归档, 归档吧, 可以关闭, 关闭吧. Negated/question forms such as “还没完成” or “完成了吗” are NOT closure.
Final-answer idle condition: for human-bot collaboration within 24h, archive as collab_answered_idle when the latest message is a bot answer, it has been idle for at least 60 minutes, and the latest bot message has no question, wait/result, running, blocker, approval, or user-action signal.
3d. Classify
Only threads that pass ALL hard gates reach this step. Apply the classification table from the judgment guide.
Key rule: "task completed" = entire discussion resolved with human acknowledgment, not a single sub-step done. If the thread has multiple topics and any is unresolved → keep.
4. Archive
For each thread judged archive, run the archive script:
bash <skill_dir>/scripts/archive-thread.sh <thread_id>
In dry-run mode, run:
bash <skill_dir>/scripts/archive-thread.sh --dry-run <thread_id>
Non-2xx response → note in report (e.g. archive_failed_403).
5. Report
Icon-verdict mapping (STRICT — never mix these up):
| Icon | Verdict | Meaning |
|---|
| ✅ | archive | Thread was archived |
| ⏸️ | keep | Thread is kept (NOT ✅) |
| ⏭️ | skip | Thread is pinned, skipped |
When threads exist (regardless of whether any were archived):
🗂️ Thread 归档 · HH:MM
✅ thread名 — 归档:reason_code,一句话原因
⏸️ thread名 — 保留:reason_code,一句话原因
⏭️ thread名 — 跳过(pinned)
归档 X / 保留 Y / 跳过 Z
In dry-run mode, replace the title with 🗂️ Thread 归档 dry-run · HH:MM and use 🧪 for would-archive decisions.
When thread-list returned empty:
🗂️ Thread 归档 · HH:MM
⏸️ 无 Thread
Every evaluated thread MUST appear in the report with its verdict icon and reason code. Use only the icons that apply to each thread.
6. Deliver
Send the report:
message(action="send", channel="discord", target="channel:<logChannel>")