[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70FXA6F0MYKY8HDQJXAEE95":3,"skill-same:01M70FXA6F0MYKY8HDQJXAEE95":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":52,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M70FXA6F0MYKY8HDQJXAEE95","slack-output-format",null,"Slack 频道结构化输出规范 + Web API 写法——标题主消息、Block Kit thread、6 档 markdown、跨调用 anchor、curl\u002Ftoken\u002F读写消息命令。Use when 准备向非 DM 频道推送 cron 报告\u002F审批卡\u002F反思\u002F周报\u002F持仓\u002F书签\u002F调研，或需要用 Slack API 读写消息、管理 thread、发送 Block Kit\u002Fattachment、更新并回读验证。","KarryViber\u002FOrb","KarryViber","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F223777671?v=4","01M70FTGZ485AP97T3CJCYW1Z2","Karryz","01M70FTGZRWF3FEQBYZ3JWTSPR",66,"MIT","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Fslack-output-format\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000005","邮件与沟通","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"output","slack","format","slack output format","cron","反思","报告","token","web api","block","markdown","slack api","web","curl","api","when","thread","use","kit","anchor","2026-11-10 10:12:30",false,"---\nname: slack-output-format\ndescription: Slack 频道结构化输出规范 + Web API 写法——标题主消息、Block Kit thread、6 档 markdown、跨调用 anchor、curl\u002Ftoken\u002F读写消息命令。Use when 准备向非 DM 频道推送 cron 报告\u002F审批卡\u002F反思\u002F周报\u002F持仓\u002F书签\u002F调研，或需要用 Slack API 读写消息、管理 thread、发送 Block Kit\u002Fattachment、更新并回读验证。\n---\n\n# Slack 输出格式规范\n\n所有向 Slack 频道（非 DM、非 thread 内回复）推送的结构化内容统一采用「标题主消息 + Block Kit thread」两步走。Slack Web API 主要通过 `curl` + Bot token 调用；token 存在 `~\u002FOrb\u002F.env`，不要直接 `source ~\u002FOrb\u002F.env`，而是单独提取变量。\n\n## When to Use\n\n- 准备向非 DM 频道推送 cron 报告、审批卡片、反思、周报、持仓、书签、调研等结构化内容\n- 需要设计 Slack 主消息、thread Block Kit、markdown 层级或跨调用 thread 聚合\n- 需要用 Slack Web API 读写消息、管理 thread、发送 Block Kit \u002F attachment、更新或回读消息\n\n## 频道主消息（1 条，text only）\n\n格式：`{emoji} {任务名} MM\u002FDD｜{一句话总结}`\n\n- emoji 选取按任务性质：🪞 反思、📈 持仓、📋 汇总、🔭 周报、📚 书签、🧘 冥想、🔀 路由、🔍 调研\n- 一句话总结必须有信息量（核心发现 \u002F 关键数据），不能是\"完成\"\u002F\"成功\"\n- 示例：`🪞 夜间反思 04\u002F15｜8 条要点｜知识提取见 thread`\n- 主消息**禁用 markdown 标题**（`#` 留给 thread reply）\n- 主消息 text 段 ≤ 300 字（Slack 通知预览截断点）。超出的内容必须放 thread。\n\n## Thread 正文（Block Kit）\n\n结构：\n\n```text\nheader  : \"{任务名} MM\u002FDD\"\nsection : \"• 概况数据（窗口 \u002F 会话数 \u002F 消息数 \u002F 标的数 等）\"\ndivider\nsection : \"## :emoji: 段标题\n正文...\"\ndivider\nsection : \"## :emoji: 段标题\n正文...\"\ndivider\n```\n\n规则：\n\n- 第 1 block 必须是 `header` 类型\n- 每个正文 section 用 `## :emoji: 标题` + 换行 + 正文（adapter 自动转成 `*xxx*` 独立行 + 上空行）\n- 段与段之间用 `divider` 分隔\n- 风格：短句、高密度、可扫读\n- 不要文档感，不要长段落\n\n## 语义 → markdown 对照表（核心）\n\n完整 6 档映射见 `~\u002FOrb\u002Fprofiles\u002F\u003Cyour-profile>\u002Fworkspace\u002FCLAUDE.md` § Slack 输出格式。这里只列 cron \u002F 反思类高频用法：\n\n| 需要的视觉 | 写 | 渲染成 |\n|---|---|---|\n| 段标题 | `## :emoji: 标题` | `*:emoji: 标题*` 独立行 |\n| 子段 | `### 子段` | `*子段*` 独立行 |\n| 备注 \u002F metadata | `#### 备注` | `_备注_` 独立行 |\n| 句内重点 | `**关键词**`（一段 ≤1 个） | `*关键词*` 行内 |\n| 数字 \u002F ID \u002F 路径 \u002F 命令 | `` `xxx` `` | 灰底 |\n| 弱强调 \u002F 引用 \u002F 术语 | `*xxx*` | `_xxx_` 行内 |\n\n铁律：\n\n- 段标题写 `## :emoji: 标题`，**禁止**直接写 `*标题*` 充当段标题\n- 一段最多一个 `**bold**`；多了改用 `` `code` `` \u002F 「中文引号」 \u002F emoji 前缀（▸ → ✅）替代\n\n## 小字（italic \u002F context 灰字）使用频次\n\n**目标**：把正常字号的信噪比拉高。Karry 一眼能 grep 到「需要回应的部分」，辅助信息全沉到小字。\n\n**默认走小字**（`*xxx*` → 渲染成 `_xxx_` italic；多行可用 context block）：\n\n- 来源标注：`_来源：plaud bc4b8b1d \u002F canon L1 \u002F commit ce0fe16_`\n- 时间戳 \u002F 路径 \u002F 文件名（非链接的）：`_2026-05-14 08:34 JST_` \u002F `_src\u002Fadapters\u002Fslack-format.js:601_`\n- 工具 \u002F API 副作用回执：`_freee PUT 200 · receipt 关联完成_`\n- 推理链 \u002F 思考路径（「我先 X 再 Y」类的过程交代）\n- 备选方案 \u002F Plan B \u002F 可选项的展开\n- 兜底说明 \u002F 「如需展开说一声」\n- cron 身份戳 \u002F window 元数据\n- 引用片段 \u002F 节录原文（Karry 已经知道是引用的）\n\n**正常字号留给（信号位）**：\n\n- 结论 \u002F 判断 \u002F 推荐\n- 下一步动作 \u002F 待 Karry 决策\n- 反问 \u002F AskUserQuestion 之外的关键追问\n- 风险 \u002F 警告 \u002F 失败原因\n- 改动结果 ≤ 2 行的核心摘要\n\n**禁忌**：\n\n- 任何「写完就行」的回执（diff、日记、scratchpad）→ 完全静默，不写小字（runtime 已关掉 dailyNotes\u002Fdiff context 自动注入，2026-05-14）\n- 一条消息 ≥ 3 段连续灰字 → 折叠合并为一段，或干脆删掉\n- 结论藏小字 = 没说\n\n## Slack API 写法\n\n### 1. 提取 token\n\n```bash\nSLACK_BOT_TOKEN=$(grep -E '^SLACK_BOT_TOKEN=' ~\u002FOrb\u002F.env | head -1 | cut -d= -f2- | tr -d \"'\" '\"')\n```\n\n### 2. 发最小消息\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fchat.postMessage\" \\\n  -d \"channel=C01ABC123\" \\\n  -d \"text=Hello, Slack!\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n发 thread reply、update、delete、schedule、unfurl 时同样使用 Bot token 和对应 Web API。发完消息后，优先用 `conversations.replies` 或 `conversations.history` 回读验证，不要只看 `ok=true`。\n\n### 3. 读取一个 Thread\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fconversations.replies\" \\\n  -d \"channel=C01ABC123\" \\\n  -d \"ts=1775307969.013919\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n### 4. 列频道\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fconversations.list\" \\\n  -d \"limit=200\" \\\n  -d \"types=public_channel,private_channel\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n### 5. 查用户\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fusers.info\" \\\n  -d \"user=U01ABC123\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n### 6. 添加 Reaction（任务完成 \u002F 确认标记）\n\n```bash\ncurl -s https:\u002F\u002Fslack.com\u002Fapi\u002Freactions.add \\\n  -d \"channel=C01ABC123\" \\\n  -d \"timestamp=1775308000.100001\" \\\n  -d \"name=white_check_mark\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n`name` = 不带冒号的 emoji slug（`thumbsup` \u002F `white_check_mark` \u002F `eyes` \u002F `x`）。需要 Bot scope `reactions:write`。\n\n## 收到用户附件时的处理\n\nOrb adapter 对不同文件类型的处理结果：\n\n| 文件类型 | agent 拿到 | 应对方式 |\n|---|---|---|\n| .txt \u002F .md \u002F .json \u002F .py 等文本类 | `--- 文件: xxx ---\\n内容\\n--- EOF ---` 注入 prompt | 直读，正常响应 |\n| 图片（.png \u002F .jpg \u002F .gif 等） | imagePaths，作为多模态 image block | 直接视觉分析 |\n| **PDF** | `[附件: xxx (application\u002Fpdf, N bytes)]` | 调 `pdf` skill：`pymupdf` 提取文本 |\n| **docx** | `[附件: xxx (application\u002Fvnd.openxmlformats..., N bytes)]` | 调 `ocr-and-documents` skill：`python-docx` |\n| **xlsx \u002F csv** | `[附件: xxx (application\u002Fvnd.openxmlformats..., N bytes)]` | 调 `xlsx` skill：`openpyxl` \u002F `pandas` |\n| 超 100KB 文本 | `[附件: xxx — 超出大小限制]` | 告知用户文件过大，建议拆分或提供关键片段 |\n\n**铁律**：收到 `[附件: xxx]` 格式提示时，不能沉默——必须明确告知用户「已收到文件，正在用工具提取内容」，并主动调对应 skill，不要假装没看见或只输出文件名。\n\n## 跨调用 Anchor 持久化\n\n**触发场景**：同一类项目在一天内会被多次产出（new seed、bookmark 卡、cron 推送等），但希望它们**聚合到同一个 main thread**（首条创建、后续作为 thread reply 追加），跨日自动轮换到新 thread。\n\n**反例（踩过的坑）**：依赖运行时猜测「今天有没有发过」——拉 conversations.history、对比时间戳、按文本匹配——任意一个都不可靠（频道有别人消息、time skew、文案变动），最终散成多个 main thread。已知案例：seeds_importers.py 早期实现散发，2026-04-27 才补 `data\u002Fseeds-daily-anchor.json` 闭环。\n\n**正解**：把 anchor 持久化为显式 JSON 状态文件。\n\n文件位置：`~\u002FOrb\u002Fprofiles\u002F\u003Cyour-profile>\u002Fdata\u002F\u003Ctask>-daily-anchor.json`\n\n最小 schema：\n\n```json\n{\n  \"date\": \"2026-04-28\",\n  \"channel\": \"C0123456789\",\n  \"main_ts\": \"1777306842.573899\"\n}\n```\n\n调用流程（每次要发新内容时）：\n\n1. 读 anchor JSON，比对 `date` 字段：\n   - 与今天（JST）一致 + `channel` 匹配 → 复用 `main_ts` 作为 thread_ts，发 thread reply\n   - 不一致（跨日 \u002F 切频道） → 走步骤 2\n2. `chat.postMessage` 发 main 消息 → 拿到新 `ts`\n3. 覆盖 anchor JSON：`{\"date\":\"\u003Ctoday JST>\",\"channel\":\"\u003CC…>\",\"main_ts\":\"\u003C新 ts>\"}`\n4. 当天后续投递回到步骤 1\n\n**铁律**：\n\n- anchor 文件名必须含 `\u003Ctask>-` 前缀，避免不同任务串台\n- 跨日判定用 JST 日历日（`TZ=Asia\u002FTokyo date +%Y-%m-%d`），不要用 UTC\n- 写 anchor 必须**先发消息拿到 ts、再写文件**，反过来会留下坏 anchor 指向不存在的 ts\n- 不要靠扫频道历史「猜」今天是不是已经发过——历史会被别人插话污染\n\n**适用任务清单**（已用 \u002F 待补）：\n\n- ✅ seeds_importers.py（new seed 当日聚合 → `seeds-daily-anchor.json`）\n- ⏳ bookmark 卡片（如果想要当天书签都进同一个 thread）\n- ⏳ cron 报告（多个 cron 想共享日级 anchor 时）\n\n## Assistant Thread \u002F Stream API（只读参考，不要直接调）\n\n以下 API 归 Orb adapter\u002Fscheduler 独占管理，**外部脚本和 agent 不要直接调用**，否则会踩 `message_not_owned_by_app` \u002F `message_not_in_streaming_state`：\n\n| API | 归属 | 说明 |\n|---|---|---|\n| `assistant.threads.setStatus` | adapter | 显示 \"Cooking…\" \u002F 工具名等气泡，turn 期间自动刷新（每 20s re-arm） |\n| `assistant.threads.setTitle` | adapter | 设置 assistant thread 标题 |\n| `assistant.threads.setSuggestedPrompts` | adapter | 设置建议提示词 |\n| `chat.startStream` | scheduler | 启动 task card timeline stream |\n| `chat.appendStream` | scheduler | 向 stream 追加 task_update chunks |\n| `chat.stopStream` | scheduler | 关闭 stream 并写入最终 markdown\u002Fblocks |\n\n**handler 脚本**（`profiles\u002F\u003Cyour-profile>\u002Fscripts\u002Fhandlers\u002F`）收到 block_action 回调后用 `chat.update` 更新审批卡，**不要**尝试用 stream API 改 bot 发的消息——stream 所有权归 daemon，handler 拿不到。\n\n### Stream chunks 双发陷阱（写 stream renderer \u002F 改 stopStream 前必读）\n\nSlack stream 的 `task_update.details` 对**同一个 id 的 chunk** 会**跨 `appendStream` concat 累积**——这是官方未明文但实测稳定的行为。\n\n**事故签名**：finalize 后文本重复，例如 `Distilled from N probesDistilled from N probes`。\n\n**根因**：result\u002Ffinalize 路径先 `appendStream(chunks)` 再 `stopStream({ chunks })` 发送同一批 final chunks，Slack 把 details 又拼了一次。\n\n**正确姿势**：\n\n- 实时增量**只**通过 `appendStream` 发 delta\n- result\u002Ffinalize **只**调用 `stopStream({ chunks: lastChunks })`——不在 stop 前再 append 同一批\n- catch \u002F fallback \u002F abnormal exit 路径也别补发 final chunks\n\n**审查 stream renderer 时盯这几条路径**：\n\n- `cc_event result`\n- abnormal exit finalize\n- deferred stream stop\n- 任何 catch\u002Ffallback 里补发 final chunks 的逻辑\n\n参考 lesson：`profiles\u002F\u003Cyour-profile>\u002Fdata\u002Flessons\u002Fslack-stream-stopstream-chunks-pitfall.md`\n\n## Reference 索引\n\n命令语法、URL→ts 转换、`reactions.add`、文件上传 v2（`getUploadURLExternal`）、`chat.getPermalink`、频道\u002F用户查询、cursor 分页、常见报错全集，见 skill **`slack-cli-api-reference`**（搜索关键词 `invalid_blocks` \u002F `reactions.add` \u002F `getUploadURLExternal` \u002F `chat.getPermalink` \u002F `conversations.replies` 时自动触发）。\n\n## Gotchas\n\n- 默认优先 top-level `blocks`，只有明确需要色条时才用 `attachments`。\n- 长内容优先拆成多条 thread reply，不要把风险压在一条超长消息上。\n- 用户反馈\"没发全\"时，先回读 Slack 实际落地结果，再决定补发或更新。\n- 跨调用聚合发布必须用磁盘 anchor，禁止靠运行时猜测。\n- 收到 `[附件: xxx (mime, size)]` 时必须主动调对应 skill 提取内容，不能忽略或只输出文件名。\n- 遇到 `invalid_blocks`、只剩首行、空壳消息、`source ~\u002FOrb\u002F.env` 失败等问题，查 `slack-cli-api-reference` skill。\n","# Slack 输出格式规范\n\n所有向 Slack 频道（非 DM、非 thread 内回复）推送的结构化内容统一采用「标题主消息 + Block Kit thread」两步走。Slack Web API 主要通过 `curl` + Bot token 调用；token 存在 `~\u002FOrb\u002F.env`，不要直接 `source ~\u002FOrb\u002F.env`，而是单独提取变量。\n\n## When to Use\n\n- 准备向非 DM 频道推送 cron 报告、审批卡片、反思、周报、持仓、书签、调研等结构化内容\n- 需要设计 Slack 主消息、thread Block Kit、markdown 层级或跨调用 thread 聚合\n- 需要用 Slack Web API 读写消息、管理 thread、发送 Block Kit \u002F attachment、更新或回读消息\n\n## 频道主消息（1 条，text only）\n\n格式：`{emoji} {任务名} MM\u002FDD｜{一句话总结}`\n\n- emoji 选取按任务性质：🪞 反思、📈 持仓、📋 汇总、🔭 周报、📚 书签、🧘 冥想、🔀 路由、🔍 调研\n- 一句话总结必须有信息量（核心发现 \u002F 关键数据），不能是\"完成\"\u002F\"成功\"\n- 示例：`🪞 夜间反思 04\u002F15｜8 条要点｜知识提取见 thread`\n- 主消息**禁用 markdown 标题**（`#` 留给 thread reply）\n- 主消息 text 段 ≤ 300 字（Slack 通知预览截断点）。超出的内容必须放 thread。\n\n## Thread 正文（Block Kit）\n\n结构：\n\n```text\nheader  : \"{任务名} MM\u002FDD\"\nsection : \"• 概况数据（窗口 \u002F 会话数 \u002F 消息数 \u002F 标的数 等）\"\ndivider\nsection : \"## :emoji: 段标题\n正文...\"\ndivider\nsection : \"## :emoji: 段标题\n正文...\"\ndivider\n```\n\n规则：\n\n- 第 1 block 必须是 `header` 类型\n- 每个正文 section 用 `## :emoji: 标题` + 换行 + 正文（adapter 自动转成 `*xxx*` 独立行 + 上空行）\n- 段与段之间用 `divider` 分隔\n- 风格：短句、高密度、可扫读\n- 不要文档感，不要长段落\n\n## 语义 → markdown 对照表（核心）\n\n完整 6 档映射见 `~\u002FOrb\u002Fprofiles\u002F\u003Cyour-profile>\u002Fworkspace\u002FCLAUDE.md` § Slack 输出格式。这里只列 cron \u002F 反思类高频用法：\n\n| 需要的视觉 | 写 | 渲染成 |\n|---|---|---|\n| 段标题 | `## :emoji: 标题` | `*:emoji: 标题*` 独立行 |\n| 子段 | `### 子段` | `*子段*` 独立行 |\n| 备注 \u002F metadata | `#### 备注` | `_备注_` 独立行 |\n| 句内重点 | `**关键词**`（一段 ≤1 个） | `*关键词*` 行内 |\n| 数字 \u002F ID \u002F 路径 \u002F 命令 | `` `xxx` `` | 灰底 |\n| 弱强调 \u002F 引用 \u002F 术语 | `*xxx*` | `_xxx_` 行内 |\n\n铁律：\n\n- 段标题写 `## :emoji: 标题`，**禁止**直接写 `*标题*` 充当段标题\n- 一段最多一个 `**bold**`；多了改用 `` `code` `` \u002F 「中文引号」 \u002F emoji 前缀（▸ → ✅）替代\n\n## 小字（italic \u002F context 灰字）使用频次\n\n**目标**：把正常字号的信噪比拉高。Karry 一眼能 grep 到「需要回应的部分」，辅助信息全沉到小字。\n\n**默认走小字**（`*xxx*` → 渲染成 `_xxx_` italic；多行可用 context block）：\n\n- 来源标注：`_来源：plaud bc4b8b1d \u002F canon L1 \u002F commit ce0fe16_`\n- 时间戳 \u002F 路径 \u002F 文件名（非链接的）：`_2026-05-14 08:34 JST_` \u002F `_src\u002Fadapters\u002Fslack-format.js:601_`\n- 工具 \u002F API 副作用回执：`_freee PUT 200 · receipt 关联完成_`\n- 推理链 \u002F 思考路径（「我先 X 再 Y」类的过程交代）\n- 备选方案 \u002F Plan B \u002F 可选项的展开\n- 兜底说明 \u002F 「如需展开说一声」\n- cron 身份戳 \u002F window 元数据\n- 引用片段 \u002F 节录原文（Karry 已经知道是引用的）\n\n**正常字号留给（信号位）**：\n\n- 结论 \u002F 判断 \u002F 推荐\n- 下一步动作 \u002F 待 Karry 决策\n- 反问 \u002F AskUserQuestion 之外的关键追问\n- 风险 \u002F 警告 \u002F 失败原因\n- 改动结果 ≤ 2 行的核心摘要\n\n**禁忌**：\n\n- 任何「写完就行」的回执（diff、日记、scratchpad）→ 完全静默，不写小字（runtime 已关掉 dailyNotes\u002Fdiff context 自动注入，2026-05-14）\n- 一条消息 ≥ 3 段连续灰字 → 折叠合并为一段，或干脆删掉\n- 结论藏小字 = 没说\n\n## Slack API 写法\n\n### 1. 提取 token\n\n```bash\nSLACK_BOT_TOKEN=$(grep -E '^SLACK_BOT_TOKEN=' ~\u002FOrb\u002F.env | head -1 | cut -d= -f2- | tr -d \"'\" '\"')\n```\n\n### 2. 发最小消息\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fchat.postMessage\" \\\n  -d \"channel=C01ABC123\" \\\n  -d \"text=Hello, Slack!\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n发 thread reply、update、delete、schedule、unfurl 时同样使用 Bot token 和对应 Web API。发完消息后，优先用 `conversations.replies` 或 `conversations.history` 回读验证，不要只看 `ok=true`。\n\n### 3. 读取一个 Thread\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fconversations.replies\" \\\n  -d \"channel=C01ABC123\" \\\n  -d \"ts=1775307969.013919\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n### 4. 列频道\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fconversations.list\" \\\n  -d \"limit=200\" \\\n  -d \"types=public_channel,private_channel\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n### 5. 查用户\n\n```bash\ncurl -s \"https:\u002F\u002Fslack.com\u002Fapi\u002Fusers.info\" \\\n  -d \"user=U01ABC123\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n### 6. 添加 Reaction（任务完成 \u002F 确认标记）\n\n```bash\ncurl -s https:\u002F\u002Fslack.com\u002Fapi\u002Freactions.add \\\n  -d \"channel=C01ABC123\" \\\n  -d \"timestamp=1775308000.100001\" \\\n  -d \"name=white_check_mark\" \\\n  -H \"Authorization: Bearer $SLACK_BOT_TOKEN\"\n```\n\n`name` = 不带冒号的 emoji slug（`thumbsup` \u002F `white_check_mark` \u002F `eyes` \u002F `x`）。需要 Bot scope `reactions:write`。\n\n## 收到用户附件时的处理\n\nOrb adapter 对不同文件类型的处理结果：\n\n| 文件类型 | agent 拿到 | 应对方式 |\n|---|---|---|\n| .txt \u002F .md \u002F .json \u002F .py 等文本类 | `--- 文件: xxx ---\\n内容\\n--- EOF ---` 注入 prompt | 直读，正常响应 |\n| 图片（.png \u002F .jpg \u002F .gif 等） | imagePaths，作为多模态 image block | 直接视觉分析 |\n| **PDF** | `[附件: xxx (application\u002Fpdf, N bytes)]` | 调 `pdf` skill：`pymupdf` 提取文本 |\n| **docx** | `[附件: xxx (application\u002Fvnd.openxmlformats..., N bytes)]` | 调 `ocr-and-documents` skill：`python-docx` |\n| **xlsx \u002F csv** | `[附件: xxx (application\u002Fvnd.openxmlformats..., N bytes)]` | 调 `xlsx` skill：`openpyxl` \u002F `pandas` |\n| 超 100KB 文本 | `[附件: xxx — 超出大小限制]` | 告知用户文件过大，建议拆分或提供关键片段 |\n\n**铁律**：收到 `[附件: xxx]` 格式提示时，不能沉默——必须明确告知用户「已收到文件，正在用工具提取内容」，并主动调对应 skill，不要假装没看见或只输出文件名。\n\n## 跨调用 Anchor 持久化\n\n**触发场景**：同一类项目在一天内会被多次产出（new seed、bookmark 卡、cron 推送等），但希望它们**聚合到同一个 main thread**（首条创建、后续作为 thread reply 追加），跨日自动轮换到新 thread。\n\n**反例（踩过的坑）**：依赖运行时猜测「今天有没有发过」——拉 conversations.history、对比时间戳、按文本匹配——任意一个都不可靠（频道有别人消息、time skew、文案变动），最终散成多个 main thread。已知案例：seeds_importers.py 早期实现散发，2026-04-27 才补 `data\u002Fseeds-daily-anchor.json` 闭环。\n\n**正解**：把 anchor 持久化为显式 JSON 状态文件。\n\n文件位置：`~\u002FOrb\u002Fprofiles\u002F\u003Cyour-profile>\u002Fdata\u002F\u003Ctask>-daily-anchor.json`\n\n最小 schema：\n\n```json\n{\n  \"date\": \"2026-04-28\",\n  \"channel\": \"C0123456789\",\n  \"main_ts\": \"1777306842.573899\"\n}\n```\n\n调用流程（每次要发新内容时）：\n\n1. 读 anchor JSON，比对 `date` 字段：\n   - 与今天（JST）一致 + `channel` 匹配 → 复用 `main_ts` 作为 thread_ts，发 thread reply\n   - 不一致（跨日 \u002F 切频道） → 走步骤 2\n2. `chat.postMessage` 发 main 消息 → 拿到新 `ts`\n3. 覆盖 anchor JSON：`{\"date\":\"\u003Ctoday JST>\",\"channel\":\"\u003CC…>\",\"main_ts\":\"\u003C新 ts>\"}`\n4. 当天后续投递回到步骤 1\n\n**铁律**：\n\n- anchor 文件名必须含 `\u003Ctask>-` 前缀，避免不同任务串台\n- 跨日判定用 JST 日历日（`TZ=Asia\u002FTokyo date +%Y-%m-%d`），不要用 UTC\n- 写 anchor 必须**先发消息拿到 ts、再写文件**，反过来会留下坏 anchor 指向不存在的 ts\n- 不要靠扫频道历史「猜」今天是不是已经发过——历史会被别人插话污染\n\n**适用任务清单**（已用 \u002F 待补）：\n\n- ✅ seeds_importers.py（new seed 当日聚合 → `seeds-daily-anchor.json`）\n- ⏳ bookmark 卡片（如果想要当天书签都进同一个 thread）\n- ⏳ cron 报告（多个 cron 想共享日级 anchor 时）\n\n## Assistant Thread \u002F Stream API（只读参考，不要直接调）\n\n以下 API 归 Orb adapter\u002Fscheduler 独占管理，**外部脚本和 agent 不要直接调用**，否则会踩 `message_not_owned_by_app` \u002F `message_not_in_streaming_state`：\n\n| API | 归属 | 说明 |\n|---|---|---|\n| `assistant.threads.setStatus` | adapter | 显示 \"Cooking…\" \u002F 工具名等气泡，turn 期间自动刷新（每 20s re-arm） |\n| `assistant.threads.setTitle` | adapter | 设置 assistant thread 标题 |\n| `assistant.threads.setSuggestedPrompts` | adapter | 设置建议提示词 |\n| `chat.startStream` | scheduler | 启动 task card timeline stream |\n| `chat.appendStream` | scheduler | 向 stream 追加 task_update chunks |\n| `chat.stopStream` | scheduler | 关闭 stream 并写入最终 markdown\u002Fblocks |\n\n**handler 脚本**（`profiles\u002F\u003Cyour-profile>\u002Fscripts\u002Fhandlers\u002F`）收到 block_action 回调后用 `chat.update` 更新审批卡，**不要**尝试用 stream API 改 bot 发的消息——stream 所有权归 daemon，handler 拿不到。\n\n### Stream chunks 双发陷阱（写 stream renderer \u002F 改 stopStream 前必读）\n\nSlack stream 的 `task_update.details` 对**同一个 id 的 chunk** 会**跨 `appendStream` concat 累积**——这是官方未明文但实测稳定的行为。\n\n**事故签名**：finalize 后文本重复，例如 `Distilled from N probesDistilled from N probes`。\n\n**根因**：result\u002Ffinalize 路径先 `appendStream(chunks)` 再 `stopStream({ chunks })` 发送同一批 final chunks，Slack 把 details 又拼了一次。\n\n**正确姿势**：\n\n- 实时增量**只**通过 `appendStream` 发 delta\n- result\u002Ffinalize **只**调用 `stopStream({ chunks: lastChunks })`——不在 stop 前再 append 同一批\n- catch \u002F fallback \u002F abnormal exit 路径也别补发 final chunks\n\n**审查 stream renderer 时盯这几条路径**：\n\n- `cc_event result`\n- abnormal exit finalize\n- deferred stream stop\n- 任何 catch\u002Ffallback 里补发 final chunks 的逻辑\n\n参考 lesson：`profiles\u002F\u003Cyour-profile>\u002Fdata\u002Flessons\u002Fslack-stream-stopstream-chunks-pitfall.md`\n\n## Reference 索引\n\n命令语法、URL→ts 转换、`reactions.add`、文件上传 v2（`getUploadURLExternal`）、`chat.getPermalink`、频道\u002F用户查询、cursor 分页、常见报错全集，见 skill **`slack-cli-api-reference`**（搜索关键词 `invalid_blocks` \u002F `reactions.add` \u002F `getUploadURLExternal` \u002F `chat.getPermalink` \u002F `conversations.replies` 时自动触发）。\n\n## Gotchas\n\n- 默认优先 top-level `blocks`，只有明确需要色条时才用 `attachments`。\n- 长内容优先拆成多条 thread reply，不要把风险压在一条超长消息上。\n- 用户反馈\"没发全\"时，先回读 Slack 实际落地结果，再决定补发或更新。\n- 跨调用聚合发布必须用磁盘 anchor，禁止靠运行时猜测。\n- 收到 `[附件: xxx (mime, size)]` 时必须主动调对应 skill 提取内容，不能忽略或只输出文件名。\n- 遇到 `invalid_blocks`、只剩首行、空壳消息、`source ~\u002FOrb\u002F.env` 失败等问题，查 `slack-cli-api-reference` skill。\n",{"name":5,"description":7},"main","cb33813b8990e4602b7c2d501659f77ebba5999f","profiles\u002F.claude\u002Fskills\u002Fslack-output-format",8,"https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb","https:\u002F\u002Fgithub.com\u002FKarryViber","2026-05-14 18:44:03",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add KarryViber\u002FOrb --skill slack-output-format",{"tool":62,"command":63},"bunx","bunx skills add KarryViber\u002FOrb --skill slack-output-format",{"tool":65,"command":66},"pnpm","pnpm dlx skills add KarryViber\u002FOrb --skill slack-output-format","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Fslack-output-format\u002FSKILL.md",true,[],[71,102,117,138,155,167],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":75,"licenseFileUrl":76,"sourceHtmlUrl":77,"categories":78,"keywords":85,"publishedAt":44},"01M70G0637CFCHAY3DYTMYS07K","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Fwebapp-testing\u002FSKILL.md",[79,82],{"categoryNo":80,"categoryName":81,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":83,"categoryName":84,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[86,87,88,89,90,91,92,36,93,94,95,96,97,98,99,100,101],"webapp","testing","webapp testing","playwright","screenshots","ui","behavior","for","with","browser","logs","toolkit","debugging","frontend","local","applications",{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":44},"01M70FZXCJ5AHJ3JW2Q12F04TA","vendor-doc-actionability","对外合作文档（交付文档 \u002F POC 文档 \u002F Solution Doc \u002F 提案书）必须为每个「需对方执行」的动作写清具体操作入口（URL \u002F 工单系统 \u002F 脚本路径 \u002F 联系人 + 联系方式 \u002F 步骤编号）。Use when 给客户、vendor、合作方写交付文档、POC 文档、Solution Doc、提案书；或检视已有对外文档是否抽象到无法执行。","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Fvendor-doc-actionability\u002FSKILL.md",[],[109,110,111,112,113,41,114,39,115,116],"vendor","doc","vendor doc actionability","poc","文档","提案","URL","user",{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":131,"publishedAt":44},"01M70FZARY5P7VRJKVFATTCVX9","vendor-api-param-validation","第三方 API \u002F SDK 集成开工前，强制先列参数 checklist 并对照官方文档逐项验证（名称 \u002F 类型 \u002F 必填 \u002F 默认 \u002F 上限），通过后再编码——不是「写完再调通」。Use when 新接 vendor API\u002FSDK（Slack \u002F LongPort \u002F freee \u002F LLM API 等）、既有集成追加新 endpoint 或新字段、用户说「调通某接口」「接入 X」「集成 X」。","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Fvendor-api-param-validation\u002FSKILL.md",[123,126],{"categoryNo":124,"categoryName":125,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":127,"categoryName":128,"taxonomyType":129,"taxonomyTypeDesc":130},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[132,133,109,38,25,134,135,113,136,41,39,116,137],"vendor api param validation","validation","sdk","llm api","checklist","endpoint",{"skillNo":139,"name":140,"nameZh":6,"description":141,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":142,"categories":143,"keywords":145,"publishedAt":44},"01M70FYW81TRN7VAS8WTN08STH","truth-ladder","状态放行三层阶梯（attempted \u002F observed \u002F confirmed）—— 汇报任务、API、命令、消息或外部动作状态时强制分层，避免把\"动作已发起\"误写成\"结果已达成\"。Use when 准备写「任务已完成」「API 返回成功」「exit code 0」「用户已收到」「ok:true」类状态汇报；或在 cron 报告 \u002F Slack 回报 \u002F project status \u002F receipt 中表达完成度时。","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Ftruth-ladder\u002FSKILL.md",[144],{"categoryNo":127,"categoryName":128,"taxonomyType":129,"taxonomyTypeDesc":130},[146,147,148,38,149,39,41,150,151,152,30,28,153,25,154],"ladder","truth ladder","ok","code","exit","true","status","project","project status",{"skillNo":156,"name":157,"nameZh":6,"description":158,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":159,"categories":160,"keywords":161,"publishedAt":44},"01M70FYTESF5WQYBVRX8BXYJQF","thread-object-realignment","长 thread 切题后的对象重定协议——thread 历史 ≥5 条且检测到切题信号时，回复前必做「用户最后显式问题对象 vs 待发回复对象」一致性校验，不一致则改写或反问。Use when Slack thread 历史 ≥5 条且当前用户消息出现切题信号（主题词突变、问句指向新实体、时间线跳跃、出现「另外」「话说」「回到 X」「换个话题」等切题语，或 ≥3 turn 后用户显式问题对象与最近 assistant 回复对象不同）。","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Fthread-object-realignment\u002FSKILL.md",[],[162,40,163,25,164,116,165,39,166,41],"object","thread object realignment","校验","slack thread","assistant",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":75,"licenseFileUrl":171,"sourceHtmlUrl":172,"categories":173,"keywords":177,"publishedAt":44},"01M70FYFC1Y2J16FJFEFQ5WYRE","theme-factory","Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors\u002Ffonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Ftheme-factory\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FKarryViber\u002FOrb\u002Fblob\u002Fmain\u002Fprofiles\u002F.claude\u002Fskills\u002Ftheme-factory\u002FSKILL.md",[174],{"categoryNo":175,"categoryName":176,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",[178,179,180,181,182,183,184,185,94,186,187,97,188,189,190,191,192,93,193,194],"theme factory","factory","theme","apply","styling","fly","generate","artifact","fonts","can","artifacts","you","themes","pages","landing","slides","SET"]