# Changelog

所有重要的项目变更都会记录在此文件。

版本格式：[MAJOR.MINOR.PATCH](https://semver.org/lang/zh-CN/)

## [v1.36.0] - 2026-07-22

本版为一次全域能力补齐：消息读取发送者名字服务端回填、CLI 交互健壮性守卫、OKR 全量接线、多维表格结构化过滤 DSL、电子表格类型保真读取闭环、大文档选择性读取、卡片交互回调与审批 v4 事件订阅，以及邮件/会议/纪要/云盘/任务/日历多域新命令。

### 新增 — 消息发送者名字服务端回填（with_sender_name）

- 所有读消息路径（`msg history/get/mget/thread-messages`、merge_forward 展开、线程展开）统一带 `with_sender_name=true`，服务端直接回填发送者显示名——**Bot 与外部租户用户也能解析**（此前 Bot 恒为空、外部用户约 42% 覆盖，实测内部群解析率 ~100%）。
- `ResolveSenderNames` 升级三步解析：服务端回填（权威）→ mentions 免费映射 → contact basic_batch 兜底；进程级注册表旁路采集（`internal/client/sender_names.go`）。

### 新增 — CLI 交互健壮性守卫

- **未知子命令不再静默成功**：嵌套命令组收到错拼子命令时返回错误 + 拼写建议 + 非 0 退出码（此前打印帮助并 exit 0，Agent 会误判执行成功）；裸命令组仍显示帮助。
- **flag 拼写建议**：未知 flag 报错时按编辑距离/前缀给出最相近候选（如 `--contaner-id` → `你是不是想用: --container-id`）。

### 新增 — OKR 全量接线 + `--as` 身份

- 新命令：`okr cycle detail`（周期下全部目标+关键结果）、`okr progress get/update/delete`、`okr upload-image`。
- 命令组新增 `--as bot|user|auto`（默认 bot 保持既有行为）；实测身份墙按端点分化：`cycle list` 仅收 Tenant Token，其余端点同时支持 user/tenant。

### 新增 — 多维表格结构化过滤（filter tuple DSL，实测验证）

- `bitable record list` 新增 `--filter-json` / `--sort-json`：无需关键词的纯结构化筛选（GET 端点 query 参数下发）。
- filter DSL 语法完整文档化（operator 全集 + 各字段类型 value 写法），7 种形态真实 API 验证通过；`record search` 的 filter 与 keyword 做交集的语义同步澄清。
- `record batch-update` 文档化两种 body 形态：统一 patch 与逐记录差异化（`update_records` map，实测均可用）。

### 新增 — 电子表格类型保真读取 `sheet table-put` 闭环

- 新命令 `sheet table-get`：按列类型保真读取整表（数字/日期/布尔自动推断 dtype，日期归一 ISO），输出与 `table-put` 输入完全对称，支持 get → 修改 → put 的 round-trip（已实测闭环）。

### 新增 — 大文档选择性读取 `doc read`

- `--outline` 标题大纲（层级 + block_id）、`--heading` 按标题取节（止于同级/更高级标题，代码围栏防误判）、`--keyword` 正则定位（`--context` 控制上下文），避免大文档整篇导出撑爆上下文。

### 新增 — 事件系统：卡片回调 + 审批 v4 订阅

- 新 EventKey `card.action.trigger`：卡片按钮/表单回调（独立回调帧通道），交互式 Bot 闭环补齐；`application.bot.menu_v6` Bot 菜单事件。
- 审批事件升级 v4 类型（`approval.instance/task.status_changed_v4`），consume 启动时自动以 User 身份注册服务端订阅关系（INVOLVED/MANAGED，此前旧 key 缺订阅注册收不到事件）。

### 新增 — 智能纪要入口 `vc note`

- `vc note detail <note_id>` 纪要详情、`vc note transcript <note_id>` 统一逐字稿导出（自动翻页，`--format markdown|text`，`--output` 落文件）。

### 新增 — 会议与妙记增强

- `vc detail <meeting_id|会议号>`：一条命令聚合会议基础信息 + `note_id`（智能纪要）+ `minute_token`（妙记），进行中会议不报错（部分产物缺失在 `hint` 标注）；会议号路径自动 90 天窗口反查。
- `minutes search`：按关键词/owner/时间搜索妙记；`minutes apply-permission --perm view|edit`：申请妙记权限；`minutes get --wait-ready`：轮询等待妙记转写就绪（`--wait-timeout`/`--wait-interval`）。

### 新增 — 群聊能力

- `chat list`：列出当前身份加入的全部群（User 身份实测 5677 群完整翻页），支持 `--sort-type`/`--page-all`。
- `chat member list --page-all`：自动翻页拉全量成员；命中服务端安全设置截断（返回条数 < member_total）时 stderr 中文告警，避免静默漏数据。

### 新增 — 邮件管理

- `mail message-modify`：批量加/删 label、移动文件夹（≤20 封，实测端点 `batch_modify`）；`mail message-trash`：批量软删进废纸篓（`--yes` 跳过确认，可用 message-modify 移回）；`mail draft-send`：发送已有草稿（沿用 `--confirm-send` 保护）。

### 新增 — 云盘与知识库

- `wiki move-to-drive`：把 wiki 节点移出知识库到云盘文件夹（异步轮询），补齐与 `wiki move-docs` 相反方向的闭环。
- `drive secure-label list/set`：密级标签查询与设置（User 身份）。
- `file version revert`：文件回滚到历史版本。
- `drive upload --file-token`：原地覆盖上传新版本（不改变权限设置）。
- `drive push` 命中 1062507（父目录子节点超 1500）按**父目录隔离**处理：已满目录及其子树内的条目标记失败跳过，其余目录继续镜像，收尾汇总列出已满目录与中文清理建议（此前对剩余文件盲目 continue 反复撞墙）。

### 新增 — 任务与日历

- `task search`：走服务端搜索端点，按创建者/执行者/关注者/完成态/截止时间/关键词过滤，支持 `--page-all`；已适配服务端硬限（page_size ≤30 自动收敛、翻页 offset 上限 150 优雅截断提示），5 并发补全任务详情。
- `calendar create-event/update-event --rrule`：RFC5545 重复日程（实测建→读回 recurrence 一致→改→删全链路）。

### 修复

- 未完成任务的 `completed_at` 哨兵值 `"0"` 此前被格式化成 1970 年时间，`task my` / `task get` / `task search` 现正确显示为未完成。

### 新增 — 平台工程化

- `msg send --idempotency-key`：发消息幂等键（≤50 字符，实测同 key 两次返回同一 message_id）。
- `doctor` 新增 `user_identity` / `bot_identity` 身份就绪诊断项。
- `auth logout` 默认先吊销服务端 token 再清理本地文件（`--no-revoke` 跳过；吊销失败不阻断本地清理）。
- `install.sh` 支持 checksums.txt sha256 校验（缺失时告警但兼容旧 release）。

### 重构 — Claude Code Skills 领域化

- 将 29 个顶层 Skill 整合为 9 个领域 Skill，原有细粒度能力下沉到各领域的 `references/workflows/`，降低路由歧义和常驻上下文占用。
- 新增 `skills/manifest.yaml` 作为能力归属清单，并新增 `scripts/check_skills.py` 校验目录结构、工作流引用、Go 源码中的 Skill 路径和 CLI 命令覆盖；`make check-skills` 会先重新构建，当前 405 个可执行命令（含隐藏命令）均有唯一归属。
- 为 9 个领域 Skill 补齐 33 个工作流执行评测，并增加每领域 8 个正例 + 8 个近邻负例的触发评测；同时修正认证、审批、消息、云盘、文档导入和可视化等工作流中与实际 CLI 行为不一致的说明。
- 同步更新 README、CLAUDE.md（及其软链接 AGENTS.md）中的安装清单、能力映射、迁移说明和脚本路径。

## [v1.35.0] - 2026-07-12

本版新增统一可视化设计系统、HTMLBox 编排动画生成器和多维表格记录搜索便捷模式，并修复 Wiki 导出、评论回复身份和安装脚本的已知问题。

### 新增 — 统一可视化设计系统（`feishu-cli-dataviz`）

- 新增图表形式选择、明暗主题色板、反模式清单和多载体配色规范，统一 board、HTMLBox、card 和 doc import 的可视化输出。
- `validate_palette.js` 新增 categorical、circular、all-pairs、HTMLBox 深色画布和 ordinal 共 9 组定稿门禁；重复色、首尾区分度和色盲安全性均可自动检查。
- `check_docs.js` 扫描技能文档中的 3/4/6/8 位 CSS hex，未登记的非 canonical 色值会直接阻断发布。

### 新增 — HTMLBox Agent 编排动画生成器

- `animate_diagram.py` 将结构化 JSON 转为自包含 SVG 动画 HTML，支持自动播放、暂停、上一步、下一步和进度定位。
- 补齐 `prefers-reduced-motion`、安全字幕渲染、Bezier/store 几何裁切和短时间线回归测试。

### 新增 — 多维表格记录搜索便捷模式

- `bitable record search` 新增 `--keyword`、可重复 `--search-field`、`--filter-json`、`--sort-json`、`--view-id`、`--offset` 和 `--limit`。
- `--config` / `--config-file` 保留完整请求体逃生舱，与便捷参数严格互斥；字段名中的逗号不再被错误拆分。

### 修复 — Wiki 树导出媒体路径

- `wiki export-tree --download-images` 为每篇文档使用独立素材目录，并将媒体引用改写为相对 Markdown 文件的路径。
- 覆盖 Quote、Callout、表格单元格内的多图片和 video，同时跳过 fenced/inline code、普通链接和未下载素材。
- 补齐 Windows 路径、空格、绝对/相对路径和转义反引号边界。

### 修复 — 评论删除与回复身份

- `comment delete` 不再伪装成可用的整条评论删除接口，改为返回删除自己回复或将评论标记为已解决的可执行指引。
- `comment reply add/delete` 默认统一使用当前 App/Bot 身份；只有显式提供 User Token 时才切换为用户身份，避免默认添加后无法默认删除。

### 修复 — 安装脚本版本获取

- `install.sh` 将过程日志统一写入 stderr，防止 GitHub API fallback 日志污染版本号和下载 URL。
- 增加版本号格式校验，异常响应会在发起下载前直接报错。

### 验证

- `go test -count=1 ./...`、`go vet ./...`、`gofmt`、Dataviz 九组色板门禁和 HTMLBox 5 个 Python 回归测试全部通过。
- 最终二进制实跑 Bitable/Comment 本地 mock 7 个场景全部成功；真实只读 Wiki 导出生成 3 个 Markdown 和 1 个素材，本地媒体引用 0 损坏。

## [v1.34.0] - 2026-06-28

新增 5 项能力 + 1 项修复，全部经真实飞书 API round-trip 验证。

### 新增 — 电子表格按列类型保真写入（`sheet table-put`）

把 pandas DataFrame 形状的 JSON（`to_json(orient="split")`）按列 dtype 写入电子表格，让数字/日期/文本列不被误判类型。

- 日期列写 Excel 序列号 + 给该列设日期 formatter（`yyyy/MM/dd`），飞书识别为「真日期」（可排序/可透视/ISNUMBER=TRUE），而非文本
- 数字列保数值精度（大整数不退化为科学计数法）；文本列用 `@` formatter 防止 ID/邮编等数字串被识别为数字（前导零保真，如 `007`）
- dtype 映射：`int*/uint*/float*/complex*`→number（`interval*` 除外，按文本）、`bool/boolean`→bool、`datetime*`→date、其他→string
- 空值（null/NaN）写空文本元素；单批 ≤ 5000 单元格自动按行分批
- ⚠️ 当前为写侧实现：**就地覆盖 A1 起的矩形区域、不清除区域外旧行、不自动扩容**（写入前用 `sheet add-rows` 预扩容）；仅支持单 sheet；读侧 round-trip（`table-get`）与 auto-grow 待后续

### 新增 — 画板服务端 SVG 导出（`board svg-export`）

`POST /board/v1/whiteboards/{id}/export`（export_type=svg），由服务端整板渲染为 SVG（base64 解码）。

- 与 `board export-code` 的区别：export-code 仅拼接 svg 节点的 svg_code，对 mermaid/plantuml/原生节点无效；svg-export 对任意画板有效，产出可二次编辑的完整 SVG
- 配合 `board import` / `svg_to_board.py` 实现「导出 → 编辑 → 回写」闭环
- 读类命令，登录后自动用 User Token，未登录回落 App Token

### 新增 — 审批实例指定节点审批人/抄送人（`approval instance create`）

- `--node-approver` / `--node-approver-file`、`--node-cc` / `--node-cc-file`：发起审批时按节点指定审批人/抄送人，格式 `[{"node_id":"n1","value":["ou_xxx"]}]`
- 新增 `skills/feishu-cli-approval/references/form-control-values.md`：14 类表单控件 value 结构速查 + 不支持清单 + 取值来源

### 新增 — `schema` pretty 输出渲染枚举值

`schema <service>.<resource>.<method>` 的 pretty 模式现在渲染字段的枚举取值（来自飞书归一化端点的 `options`/`enum`，含枚举描述），此前白白丢弃。数字型枚举值也正确渲染。

### 新增 — `apps html-publish` 单 .html 文件 10MB 上限

对齐妙搭服务端「单个 `.html` 文件 ≤ 10MB」硬约束，客户端提前拦截并点名超限文件。

- 实跑超限直接拒绝；`--dry-run` 回填 `oversize_html` 详情，并新增统一的 `would_block` / `block_reasons` 字段，便于脚本/Agent 单字段判断是否可发布

### 修复 — 电子表格图片上传适配 office 导入表格

`UploadSheetImageMedia` 的 `parent_type` 此前固定 `sheet_image`，对从 `.xlsx` 等导入的 office 表格（token 以 `fake_office_` 开头）会上传失败。现按 token 前缀自动选择 `office_sheet_file` / `sheet_image`。

## [v1.33.0] - 2026-06-27

### 新增 — 妙搭（Miaoda）应用：HTML 秒搭一键部署（`apps`）

新增 `apps` 命令组，把妙搭（Miaoda）低代码应用平台的「一份 HTML 秒级发布成可分享的飞书应用」能力搬进 feishu-cli。全部走 User 身份（user_access_token），需要 spark scope。

- `apps create` —— 创建 HTML 妙搭应用（`POST /open-apis/spark/v1/apps`），返回 `app.app_id`（CLI 已剥掉飞书响应的 `data` 外层，jq 用 `.app.app_id`）
- `apps html-publish` —— 把 `--path`（单 HTML 文件或整目录）打包成 tar.gz，单次 multipart POST 上传并发布（`/apps/{id}/upload_and_release_html_code`），返回 `url`（jq 用 `.url`，一键部署）。客户端侧：要求根目录有 `index.html`、未压缩 ≤ 200MB / 打包后 tar.gz ≤ 20MB、默认拦截凭证文件（`.env` / `.npmrc` / `.netrc` / `.git-credentials` / `.aws/credentials` / `.docker/config.json` / `.kube/config`，`--allow-sensitive` 放行）
- `apps update` —— 部分更新名称/描述（`PATCH /apps/{id}`）
- `apps access-scope-get` / `apps access-scope-set` —— 查看/设置访问范围（`specific` / `public` / `tenant`，映射后端 `Range` / `All` / `Tenant`）
- `apps list` —— 列出当前用户的应用（隐藏命令，游标分页）
- 统一支持 `--format json|pretty|table|ndjson|csv` + `--jq`；写命令支持 `--dry-run`
- 权限：`spark:app:write`（create/update/html-publish/access-scope-set）、`spark:app:read`（list/access-scope-get）。⚠️ feishu-cli 的 `auth login --scope` 是「替换」不是「合并」，请把 spark scope 并入完整 scope 串一起登录，避免丢失已有权限

### 改进 — Markdown 表格单元格图片真嵌入（#164）

Markdown 表格单元格内的本地/网络图片此前在转换阶段被静默丢弃（带 alt 只剩 alt 文本、无 alt 整格变空）。现在 `doc import` 会在表格填充完成后（阶段 2.5）真正嵌入为单元格内的 Image 子块。

- 新增 `ConvertOptions.EmbedTableImages` 开关，仅 `doc import` 启用真嵌入；非导入场景（`doc add/content-update`）单元格图片降级为 `[图片: 说明]` 占位文本，杜绝任何路径的静默丢失。
- 导入 JSON 输出新增 `cell_image_total/success/failed` 统计字段。

### 修复 — 发版前 code review 收尾

- **表格单元格图片（#164）**：纯图片单元格若带 alt（如 `![架构图](./a.png)`），alt 文本不再经填充兜底路径泄漏成单元格里多余的标题；嵌入阶段改用 `GetTableCellIDs`（`block.Table.Cells`，与填充/导出同源）定位单元格，不再依赖 list-blocks 是否为表格块填充 `children`；单元格数与图片索引不一致或获取失败时，被跳过的图片计入失败统计而非静默丢弃；单元格图片上传失败时删除孤儿空 Image 块并补占位文本
- **`apps` dry-run**：`--dry-run` 预览现在同样尊重 `--format/--jq`（此前固定 JSON，与 help 列出的 flag 不符）
- **`apps html-publish` 凭证扫描**：目录形态下不再因根的「父目录」恰好叫 `.aws/.docker/.kube` 而把根下普通 `credentials`/`config` 文件误判为凭证
- **文档**：`apps` 输出 jq 路径勘误（`data.app.app_id` → `.app.app_id`、`data.url` → `.url`，CLI 已剥掉 `data` 外层）
- **一致性**：内联图片占位统一为中文「[图片: …]」前缀（此前本地路径分支用英文 `[Image:` 且直出原始路径）；`apps html-publish` 打包错误信息改为中文

## [v1.32.0] - 2026-06-06

### 新功能 — 多维表格支持 `--as bot|user|auto` 身份切换

`bitable` 命令组此前在 CLI 侧**硬性强制 User Token**（未登录直接报错），但底层飞书 `base/v3` 与 `bitable/v1` API 本身一直**同时支持 User / Tenant(App) 两种身份**（client 早已声明 `SupportedAccessTokenTypes:[User, Tenant]`，瓶颈纯在命令封装层）。本版按 `--as bot|user|auto` 身份模式放开：

- 新增命令组 persistent flag **`--as bot|user|auto`**（默认 `auto`），所有 bitable 子命令通用：
  - `auto`（默认）：User 优先、Tenant 兜底——已登录用 User Token，**未登录/过期自动回落 App Token**
  - `bot`（= `tenant`/`app`）：强制 App Token，**无需 `auth login`、永不过期，适合 cron / 无人值守 / 脚本自动抓取多维表格**
  - `user`：强制 User Token（缺失报错，提示可改用 `--as bot`）
- 新增 `resolveIdentityToken`（`cmd/utils.go`）统一身份解析，替换 9 处咽喉点的 `resolveRequiredUserToken`/`requireUserToken`（`bitable_base`/`field`/`form_crud`/`output`/`record_attachment`）。客户端层零改动
- 修复后实测：在未登录环境用 App Token 读到此前因 token 过期而判定"不可入 cron"的真实多维表格全部 102 条记录

### 测试与文档

- 新增 `cmd/bitable_identity_test.go`：覆盖 `bot`/`tenant`/`app` 三别名（含大小写/空白）、显式 user token、非法 `--as` 报错、persistent flag 注册与子命令继承
- `feishu-cli-bitable` 技能 SKILL.md 补「身份选择 `--as`」表格 + cron 示例 + `91403` 协作者排错；description 补 cron / App Token 触发词
- `CLAUDE.md` Token 策略由「三类」扩为「四类」，新增 `resolveIdentityToken` 身份可选类目

## [v1.31.0] - 2026-06-05

### 新功能 — 妙笔BOX（htmlbox）HTML 小组件命令

飞书文档里**唯一能跑动画、可交互内容**的载体落地为正式命令。妙笔BOX 是 AddOns HTML 小组件块（`block_type=40`），把一整页 HTML 存进 `add_ons.record`，飞书在 iframe 沙箱里真实执行 CSS/JS——CSS 动画、ECharts、Three.js、Canvas、真实地图、3D 图表都能动（区别于画板的 SVG 节点会被服务端栅格化成静态图）。

**新增命令** `feishu-cli doc htmlbox {create|update|get|delete}`：

- `create`：往文档插入妙笔BOX 块（`--html` / `--html-file` / stdin 三选一，`--index` / `--parent-id` 控制位置）
- `update`：更新块 HTML。飞书 API 不支持原地改 `add_ons`（PATCH 返回 `1770001`），改走「**先建后删**、同位置重建」——新块在原位置创建成功后才删旧块，中途失败不丢数据，返回 `new_block_id`
- `get`：读回块 HTML（`--raw` 逐字节输出便于存文件/再编辑，默认输出含 `html` 字段的结构）
- `delete`：删除妙笔BOX 块（仅限 `block_type=40`，防误删其他块）
- 统一接入 `--format` / `--jq` / `--dry-run`；默认 Bot 身份（操作自建文档无需登录）

**配套 `feishu-cli-htmlbox` 技能**：SKILL.md + 3 个 references——`mechanism.md`（块机制 / iframe 沙箱边界 / 与画板的 trade-off）、`html-recipes.md`（CSS 动画 / ECharts / Canvas / Dashboard 自包含范例）、`pitfalls.md`（真实创建大批量图沉淀的 9 类实战踩坑：JS 报错白屏不报错、CDN 加载时序、真实地图 `registerMap`、`record` 双重编码、批量追加限流等）。

### 测试与质量

- 新增 7 个单测：record JSON 编码转义、`loadHTMLInput`（含「不 TrimSpace 保原文」这一 `get --raw` 还原保证）、`<script>` / HTML 注释 / `U+2028` payload roundtrip、unicode/emoji roundtrip
- 指针解引用统一用 `client.StringVal`，`update` 补 `len==0` 防御，`get --raw` 空内容时 stderr 告警

## [v1.30.0] - 2026-06-04

### 性能与功能 — 表格批量填充提速 25-30x，列宽可自定义

**① 表格填充 batch_update 优化（issue #159）**

`doc import` / `doc add` / `doc content-update` 三个入口的 Markdown 表格填充重写：

- 阶段二开始预热文档级 cellID → textBlockID 映射（一次 `GetAllBlocks` 替代 N 次 `GetBlockChildren`）
- single-group cell（占绝大多数）走 `batch_update` API，每批 ≤30 个一次写入；多块 cell（含 `<br/>`）保留原 update-first-empty 路径作为兜底
- 整批失败自动降级 per-cell，避免一颗坏 cell 污染整张表
- 新增文档级 3 QPS 写限流器（`docWriteLimiter`），下沉到 `CreateBlock`/`UpdateBlock`/`DeleteBlocks`/`BatchUpdateBlocks` 4 个底层写函数，所有间接调用者（`InsertTableRow`/`AppendTableRows`/`ReplaceImage` 等）自动受限，避免触发 99991400

**典型场景**：4 张 6×8 表（共 ~120 cells）从 ~70s 降到 ~3s（25-30x），与 issue #159 实测数据吻合。`BatchUpdateBlocks` 改为返回 `(*BatchUpdateBlocksResult, http.Header, error)`，让 retry 层拿到 `x-ogw-ratelimit-reset` 做精确退避。

**② 表格列宽自定义（issue #156）**

之前列宽完全由内容启发式计算（中文 14px / 英文 8px），用户无法干预。现在两种方式可覆盖：

- **紧邻表格上方注释**（推荐，单表精控）：

  ```markdown
  <!-- feishu-colwidth: 80,200,120,* -->
  | 列1 | 列2 | 列3 | 列4 |
  |-----|-----|-----|-----|
  ```

  单位支持 `px` 整数、`30%` 百分比（按 700px 文档宽度换算）、`*` 或空（该列走 auto）。注释独占一行才生效；中间夹任何块（heading/段落/列表/代码块/link-ref-def）会清空注释，避免悬浮注释污染下游表格。

- **CLI flag 全局覆盖**：`--table-column-width=auto|fixed|N1,N2,...`
  - `auto`（默认）：保留启发式
  - `fixed`：所有列等分文档宽度
  - 像素列表（如 `80,200,*,120`）：显式声明每列宽度，`*` 表示该列走 auto

**优先级**：注释 > CLI flag explicit > flag fixed > auto。所有路径最终都会过 `[80, 400]` 像素 clamp。注释/flag 列宽数量与表实际列数不一致时，stderr 打印警告（多写截断、少写补 auto）。

### 新增 — 消息搜索 enrich / 多维表格补全 / 输出工程化（jq + 表格/CSV）

补齐三类此前未覆盖的场景。

**① 消息搜索 enrich（`search messages`）**

新增 `--enrich` opt-in 富化：在消息 ID 基础上补全 **内容 / 发送者 / 群名 / 时间**（对齐 lark `+messages-search`）。默认行为保持原 ID 输出，完全向后兼容：

- 默认（无 `--enrich`）：仅返回消息 ID，`-o json` / `--format json` 返回旧 schema `{MessageIDs,HasMore,PageToken}`，与升级前一致
- `--enrich`：search → 消息 ID → `BatchGetMessages` 取详情 → 解析发送者名/群名 → 人类可读视图；`-o json` / `--format json` 返回 `[]{message_id,msg_type,chat_id,chat_name,sender_id,sender_name,create_time,time,text}` 数组
- `--format json|pretty|table|ndjson|csv` + `--jq` 结构化输出对两种模式均生效；`--page-all/--page-limit` 自动翻页

**② 多维表格补全（`bitable`）**

补齐 base/v3 + bitable/v1 公开 API 支持、此前缺失的命令：

- `bitable dashboard list|copy`（仪表盘）
- `bitable form get|patch` + `bitable form field list|patch`（表单及表单问题）
- `bitable role member list|create|delete|batch-create|batch-delete`（角色协作者）
- `bitable workflow enable|disable`（工作流启停）
- `bitable update`（多维表格本体重命名 / 高级权限开关）
- `bitable record batch-get`（批量获取记录）
- 新增 `internal/client/bitable_v1.go`：dashboard copy / role member / app update / workflow 启停 base/v3 无对应端点，走 bitable/v1（无需 `X-App-Id` header）。新命令经统一执行器 `bitableRun` + 请求描述符 `bitableReq` 路由 base/v3 与 bitable/v1，写命令支持 `--dry-run` 预览

> 注：dashboard block / form submit 已在后续一轮补齐（见下方「④ 多维表格 / 表格 / vc / mail / markdown 再补全」）。lark-base 的 view 独立 filter-sort 端点走飞书私有扩展 API（`base-api.feishu.cn`），公开 OpenAPI 无对应，未做专用命令——可用 `feishu-cli api` 裸调兜底。

**③ 输出工程化（`internal/output`）**

新增统一输出包，并接入 `api` 与新命令：

- `--format json|pretty|table|ndjson|csv`（`api` / `search messages` / bitable 新增命令）
- `--jq <expr>`（内置 gojq v0.12.17，纯 Go，无需外部 jq；保持 `go 1.21` 兼容）
- `api` 命令新增 `--format`/`--jq`（显式指定时走内置渲染，否则保持原 pretty/raw 行为）
- 大整数精度：渲染链路用 `json.Number`（飞书 19 位 message_id/chat_id 不被 float64 截断）
- table/csv 渲染 CJK 宽度对齐、单元格换行净化

**新增依赖**：`github.com/itchyny/gojq v0.12.17`（pin 该版本保持 `go 1.21` 兼容，未抬升 go directive）。

### 新增 — ④ 多维表格 / 表格 / vc / mail / markdown 再补全

补齐上一轮仍缺的仪表盘 / 表单 / 工作流 / 附件 / 浮图 / 筛选视图 / 下拉 / 会议机器人 / 邮箱签名 / Markdown diff。

**多维表格（`bitable`）**

- `bitable dashboard create|get|update|delete|arrange`（仪表盘 CRUD + 服务端智能排版，原有 `list|copy`）；`create|update` 支持便捷字段 `--name`/`--theme-style` 或 `--config`/`--config-file` 完整请求体二选一
- `bitable dashboard block create|get|list|update|delete`（仪表盘块 CRUD）；`create` 支持 `--type`（column/bar/line/pie/ring/area/combo/scatter/funnel/wordCloud/radar/statistics/text）+ `--data-config` 便捷字段
- `bitable form create|delete|detail|submit`（表单 CRUD + 按分享 token 取详情/提交，原有 `get|patch`）；`detail`/`submit` 走 `share-token`（shr 前缀）无需 base_token；`submit` 不处理附件上传
- `bitable form field create|delete`（表单问题批量增删，单次 ≤ 10，别名 `questions`，原有 `list|patch`）；`create` 用 `--questions` 数组，`delete` 用 `--question-ids`
- `bitable workflow create|get|update`（工作流 CRUD，`update` 为整体替换 PUT，原有 `enable|disable|list`）
- `bitable record upload-attachment|download-attachment|remove-attachment`（记录附件上传 / 下载 / 移除；upload/download 为 2 步编排，单次 ≤ 50 附件）

**表格（`sheet`）**

- `sheet image get|update|media-upload|write-image`（浮图获取 / 更新锚点尺寸偏移 / 上传素材取 file_token / 本地图片写入单元格，原有 `add|list|delete`）
- `sheet filter-view get|update`（筛选视图获取 / 更新名称范围，原有 `create|list|delete`）
- `sheet filter-view condition create|get|update|delete|list`（筛选条件 CRUD，按列字母定位）
- `sheet dropdown get|update|delete`（下拉菜单数据验证获取 / 更新 / 删除，原有 `set`）；`update` 支持多范围 / 多选 `--multiple` / 高亮 `--colors`
- `sheet batch-set-style`（批量为多范围设置单元格样式，`--data` 传 `{ranges,style}` 数组）

**会议（`vc`）**

- `vc bot meeting-join|meeting-leave|meeting-events`（会议机器人按会议号入会 / 离会 / 查会议事件，对应 `/open-apis/vc/v1/bots/{join,leave,events}`）

**邮件（`mail`）**

- `mail signature`（列出 / 查看邮箱签名，`--detail <签名ID>` 取单个详情）

**Markdown（`markdown`）**

- `markdown diff`（下载远端 Markdown 在本地算 unified diff，不改远端；三模式：远端最新 vs 本地 / 远端某版本 vs 最新 / 版本 A vs 版本 B）；输出支持 `--format`/`--jq`（`-o json` 作兼容别名），默认仍打印 unified diff 文本

**云盘下载（`drive` / `file` / `msg`）**

- `drive download` / `file download` 支持 User Token 直连下载 + 大文件自动 HTTP Range 分片兜底（突破单次下载大小限制；Bot/Tenant 路径仍保留 100MB 客户端上限）
- `msg resource-download` 支持 `--user-access-token` 用户身份下载消息图片/文件（可取 Bot 不可见的历史资源），大文件同样自动分片

## [v1.27.0] - 2026-05-21

### 新增 — `event` 模块（WebSocket 实时事件订阅 + daemon 进程管理）

新增 `feishu-cli event` 命令族（list / schema / consume / status / stop），
通过飞书 WebSocket 长连接接收应用事件并以 NDJSON 输出到 stdout。

**背景**：feishu-cli 之前完全没有事件订阅能力，AI Agent 想做 bot 实时响应只能自写 WebSocket
客户端。本 PR 在 feishu-cli 代码风格下重新实现，让单工具栈即可完成消息接收、群成员变更监听、审批
事件订阅等长连接场景。

**新增子命令**：

- `event list [--json]` — 列出所有支持的 EventKey（按 domain 分组：im / contact / calendar / drive / approval / vc 共 22+ 个）
- `event schema <key> [--json]` — 查看某个 EventKey 的 EventType / scope / payload schema 示例
- `event consume <key>` — 启动 WebSocket 长连接订阅（阻塞，事件流→stdout NDJSON）
- `event status [--json]` — 查看本机所有 consume 进程（PID/EventKey/启动时间/uptime）
- `event stop {--pid N | --event-key K | --all} [--force] [--json]` — 停止 consume 进程

**Consume 关键 flag**：

- `--max-events N` — 接收 N 条事件后退出（0=不限制）
- `--timeout 30s` — 运行时长上限（0=不限制）
- `--jq .event.message` — 极简点路径过滤（只接受 `.a.b.c`，不支持完整 jq 语法，用 pipe 接外部 jq）
- `--output-dir ./events` — 每条事件 dump 为 `<event_id>.json` 落盘（只接受安全相对路径）
- `--quiet` — 抑制 stderr 诊断（不影响 stdout 事件流；ready marker 仍会输出，便于父进程判断就绪）

**Daemon / 进程模型**：

- 每个 `event consume` 进程 = 一个独立 OS 进程 + 一个 WebSocket 长连接（一个 EventKey）
- 状态文件 `~/.feishu-cli/events/<app_id>/bus.json`（每个 AppID 一个目录）：consume 启动写入 PID/EventKey/启动时间，退出时移除
- 跨进程互斥 `~/.feishu-cli/events/<app_id>/bus.lock`（flock 文件锁，fd 关闭自动释放）
- 原子写：tmp + os.Rename 防止半写
- 进程探活：signal(0) 检测 PID 存活，status 命令自动清理僵尸条目
- 重连策略：复用 oapi-sdk-go v3 `ws.Client.WithAutoReconnect(true)`，断线无限重试（间隔 2 分钟 + 首次随机抖动）
- 架构取舍：不跑独立 daemon + Unix socket 做事件 fan-out；feishu-cli 简化为每个 consume 直连 WebSocket，
  不做事件分发——足够覆盖 AI Agent 单 EventKey 订阅的主线场景，省去 IPC 复杂度

**Subprocess 协议**（兼容 AI Agent 子进程调度）：

- 启动后 stderr 立即输出 `[event] ready event_key=<key>`，父进程应阻塞 stderr 等该行出现后再读 stdout
- 非 TTY 模式下 stdin EOF = shutdown 信号（适配 `< /dev/null` / `nohup` 等场景）
- 退出码 0：正常退出（达到 --max-events / --timeout / SIGTERM / Ctrl-C），非 0：startup 失败或 ws 不可恢复错误

**Scope 要求**：默认 App Token；具体 scope 因 EventKey 而异（`event schema <key>` 查看）。已加入
`--domain event --recommend` 推荐列表，覆盖 IM/联系人/日历/云盘/审批/VC 常用 scope 并集。

**代码影响范围**：

- 新增 `cmd/event.go`（顶层命令）+ `cmd/event_{list,schema,consume,status,stop}.go`（5 个子命令）
- 新增 `internal/event/{keys,bus,runtime}.go`（EventKey 注册表 + bus.json 状态管理 + WebSocket runtime）
- 新增 `cmd/event_test.go` + `internal/event/{keys,bus,runtime}_test.go`（mock 单测）
- 新增 `cmd/event_smoke_test.go`（`//go:build smoke` 本地真实 WebSocket 端到端测试）
- 修改 `internal/registry/domain_alias.go`：新增 `event` domain scope 推荐列表
- 修改 `go.sum`：补全 `larksuite/oapi-sdk-go/v3/ws` 子包的传递依赖（gorilla/websocket、gogo/protobuf；均为 indirect，无新顶层依赖）
### 新增 — `attendance` 考勤查询模块

新增 `attendance` 顶层命令组（别名 `att`），覆盖飞书考勤 OpenAPI 两类查询：

- `feishu-cli attendance user-task query` —— 按日期范围查询用户上下班打卡记录
  （`POST /open-apis/attendance/v1/user_tasks/query`，单次最多 50 用户）
- `feishu-cli attendance user-stats query` —— 查询日度 / 月度考勤统计
  （`POST /open-apis/attendance/v1/user_stats_datas/query`，单次最多 200 用户，
  起止跨度 ≤ 31 天）

**特性**：

- 日期参数同时接受 `YYYY-MM-DD` 与 `YYYYMMDD`，自动转换为 API 所需的 `yyyyMMdd` 整数
- 输出双模：默认 `text` 人类可读（打卡时间 / 结果 / 加班标记 / 统计字段标题），
  `-o json` 直出归一化结构体，便于 AI Agent 与脚本消费
- 同时打印 `invalid_user_ids` / `unauthorized_user_ids`，提示无效或无权限用户
- user-task ≤ 50、user-stats ≤ 200 用户数本地预校验，避免无谓远程请求
- user-stats 起止跨度 > 31 天本地预校验，避免触发 OpenAPI 报错
- 全部命令走 tenant_access_token（即应用身份）：larksuite/oapi-sdk-go v3.5.3 中
  `Attendance.UserTask.Query` / `Attendance.UserStatsData.Query` 的
  `SupportedAccessTokenTypes` 仅含 `Tenant`，传入 user token 会被 SDK 拒绝

**权限要求**：应用需在飞书开放平台「应用权限管理」页面获得
`attendance:task:readonly` 权限（tenant 级），无需 `auth login`。
### 新增 — `msg flag`：消息书签（收藏 / 列表 / 取消）

新增 `feishu-cli msg flag {create,list,cancel}` 三个子命令，对应飞书 OpenAPI `/im/v1/flags`，
覆盖消息书签的完整生命周期。

**支持的两层书签模型**：

| item_type  | flag_type | 场景                                |
| ---------- | --------- | ----------------------------------- |
| default    | message   | 消息层书签（最常见，默认值）        |
| thread     | feed      | topic-style 话题群 feed 层（侧边栏）|
| msg_thread | feed      | 普通群消息线程 feed 层              |

其余组合服务端会拒绝，CLI 默认值为 `default + message` 即可覆盖 90% 用例。

**实现说明**：飞书 Open SDK v3 当前未封装 flag 接口，使用通用 HTTP client（`client.Post` /
`client.Get`）直接调用，与 `comment reply add` 同套路。

**权限要求**：User Token；`list` 需要 `im:feed.flag:read`，`create/cancel` 需要 `im:feed.flag:write`
### 新增 — `okr` 模块：OKR 周期和进展记录

新增 `feishu-cli okr` 命令组，覆盖 OKR 最高频的 3 个操作：

- `okr cycle list` — 获取当前租户的所有 OKR 周期（`/open-apis/okr/v1/periods`，租户级全局列表，自动分页）
- `okr progress list --objective-id 7xxx | --key-result-id 7xxx` — 列出某个目标 / 关键结果下的所有进展记录
- `okr progress create --objective-id 7xxx | --key-result-id 7xxx --content "..."` — 创建一条进展记录，
  支持纯文本（`--content`，自动包装为 ContentBlock）或原始富文本（`--content-json`）；
  可附带 `--progress-percent` + `--progress-status` 标记进度；
  `--source-url` 飞书侧必填，CLI 默认填 `https://www.feishu.cn/okr/progress` placeholder，可显式覆盖

**实现要点**：

- `progress create` 走飞书 Open SDK v3.5.3 的 `Okr.ProgressRecord.Create`；
  `cycle list` 走通用 HTTP client 直调 `/open-apis/okr/v1/periods`（v1/periods 是租户级，不按用户过滤）；
  `progress list` 走通用 HTTP client 直调 `/open-apis/okr/v2/...`
- 所有命令默认使用 User Token，会自动读取 `~/.feishu-cli/token.json`；
  也可以通过 `--user-access-token` 或 `FEISHU_USER_ACCESS_TOKEN` 显式覆盖
- 时间戳统一格式化为本地时区 `YYYY-MM-DD HH:MM:SS`，方便人眼阅读

**权限要求（User Token scope）**：

| 命令              | scope                                              |
|-------------------|----------------------------------------------------|
| `cycle list`      | `okr:okr:readonly` 或 `okr:okr.period:readonly`    |
| `progress list`   | `okr:okr:readonly` 或 `okr:okr.progress:readonly`  |
| `progress create` | `okr:okr` 或 `okr:okr.progress:writeonly`          |

**使用示例**：

```bash
feishu-cli auth login --domain event --recommend

# 列出所有 EventKey
feishu-cli event list

# 查看 IM 接收消息事件的字段
feishu-cli event schema im.message.receive_v1

# 订阅（Ctrl-C 退出）
feishu-cli event consume im.message.receive_v1

# 调试：抓 5 条事件后自动退出
feishu-cli event consume im.message.receive_v1 --max-events 5 --timeout 60s

# 并发订阅多个 EventKey（每个进程一个 EventKey）
feishu-cli event consume im.message.receive_v1 > receive.ndjson 2> receive.log &
feishu-cli event consume im.chat.member.user.added_v1 > member.ndjson 2> member.log &
feishu-cli event status                       # 查看活跃进程
feishu-cli event stop --all                   # 一键停止
```

### 新增 — `doctor` 命令（健康检查 / 配置 / 认证 / 网络 / 依赖一把验）

新增 `feishu-cli doctor` 命令，跑一组本地诊断快速验证 CLI 状态。

**6 项检查**：
- `config_file` — app_id / app_secret 是否就位
- `user_token` — token.json 状态（valid / needs_refresh / expired）
- `endpoint_open` — `open.feishu.cn` HTTPS 可达性 + RTT
- `endpoint_larksuite` — `open.larksuite.com` HTTPS 可达性 + RTT
- `proxy` — HTTPS_PROXY 与 NO_PROXY 配置（缺飞书域 warn）
- `dependencies` — Go 版本 + larksuite/oapi-sdk-go 版本

**flag**：`--json` 机器可读输出 / `--offline` 跳过网络检查 / `--only user_token,proxy` 仅运行指定项。

**退出码**：0 = 全 pass / 1 = 任一 fail。

**使用示例**：

```bash
feishu-cli doctor                              # pretty 输出全检查
feishu-cli doctor --json                       # JSON 输出（AI agent 自检友好）
feishu-cli doctor --offline                    # 跳过网络
feishu-cli doctor --only user_token,proxy      # 仅跑指定项
```

**代码影响范围**：新增 `cmd/doctor.go`（6 项检查 + pretty/JSON 输出）和 `cmd/doctor_test.go`（parseOnly / shouldRun / proxy / dependencies 单测）；不引入新依赖。

### 新增 — `slides` 模块：Slides 演示文稿创建与媒体上传

新增 `feishu-cli slides` 顶层命令，提供两个子命令支撑 Slides 演示文稿的最小可用工作流：

- `slides create [--title <name>] [--width <px>] [--height <px>] [--output json]`
  调用 `POST /open-apis/slides_ai/v1/xml_presentations` 创建空白演示文稿，返回 `xml_presentation_id` /
  `revision_id` / `title`。默认尺寸 960x540。
- `slides media-upload --file <path> --presentation-token <xml_presentation_id> [--output json]`
  本地图片走 `/open-apis/drive/v1/medias/upload_all` 上传到指定演示文稿，返回 `file_token`
  可直接作为 slide XML 中 `<img src="...">` 引用。

**关键实现细节**：

- 上传 `parent_type` 固定为 `slide_file`（实测：`slide_image` / `slides_image` /
  `slides_file` 都会被拒）；`parent_node` 必须为目标 `xml_presentation_id`
- 单文件上限 20 MB（多分片 `upload_prepare` 不接受 `parent_type=slide_file`）
- 共用 `internal/client/drive.go::UploadMediaWithExtra` 上传链路

**权限要求**：

- 创建：`slides:presentation:create` 或 `slides:presentation:write_only`
- 上传：`docs:document.media:upload`
### 新增 — `mail` 高级能力：CID 内联图片 + 邮件模板（MVP）

为 `mail` 模块补齐两块进阶能力：直接发送与邮件模板。

**1. `mail send --inline-images-auto-scan`（CID 内联图片）**

HTML body 中所有 `<img src="本地相对/绝对路径">` 会被自动扫描：

1. 跳过已经是 `cid:` / `http(s):` / `data:` / `//` scheme 的引用
2. 同一本地路径只上传一次（去重）
3. 每张图独立生成 20-hex CID
4. 走 `drive/v1/medias/upload_all`（`parent_type=email`，`parent_node = 当前登录用户 open_id`）
5. EML 走 `multipart/related`：HTML 段 + 每张图一个 `Content-ID: <cid>`、`Content-Disposition: inline` 的 part
6. 改写 `src` 为 `cid:<cid>` 后回写到 body

依赖 `~/.feishu-cli/user_profile.json` 中缓存的 open_id（`auth login` 后自动写入）。

**2. `mail template create` / `mail template list`（邮件模板 MVP）**

- `mail template create --name xxx --subject xxx --body xxx [--to ... --cc ... --bcc ... --plain-text]`
  调用 `POST /open-apis/mail/v1/user_mailboxes/{id}/templates`
- `mail template list [--mailbox me]`
  调用 `GET /open-apis/mail/v1/user_mailboxes/{id}/templates`（接口不分页，一次返回所有 id+name）

底层 client 也实现了 `GetMailTemplate` / `UpdateMailTemplate` / `DeleteMailTemplate`，但 CLI 层目前只暴露 create/list（MVP）。

**权限要求**：

- User Access Token
- `mail:user_mailbox:readonly` / `mail:user_mailbox.message:modify` / `mail:user_mailbox.message:send`
- 模板相关 scope：`mail:user_mailbox:readonly`、`mail:user_mailbox.message:modify`

⚠️ **模板接口依赖邮箱读写相关权限** —— 命令本身实现完整、参数校验完整、EML/JSON payload
正确；如果调用模板 API 时返回 scope 校验失败（401/permission denied），请补开邮箱读写权限后重试。
CID 内联图片功能不依赖此 scope，已可正常使用。
### 新增 — `profile`：多配置（profile）管理

新增 `feishu-cli profile` 顶层命令，让一台机器在多个飞书账号 / 应用之间快速切换。
解决长期痛点：原 `~/.feishu-cli/{config.yaml,token.json}` 单实例布局，切账号必须手动备份/恢复
或者来回 `mv`，对同时需要 work / personal、或者 feishu.cn / larksuite.com 双端的用户极不友好。

**子命令**：

- `profile add <name> [--app-id ... --app-secret ... --base-url ... --use]` 新建 profile
- `profile list` (alias `ls`) 列出所有 profile，标注 active 列；`--json` 适合脚本/AI Agent
- `profile use <name>` (alias `switch`/`checkout`) 切换 active；`use -` 切回上一个
- `profile current` 显示当前 active profile 名 + 目录
- `profile rename <old> <new>` (alias `mv`) 重命名，自动同步指针
- `profile remove <name>` (alias `rm`/`delete`) 删除 profile；`--force` 跳过二次确认
- `profile migrate [--name default] [--force]` 把旧布局 `~/.feishu-cli/{config,token}.json` 拷到 `profiles/<name>/`（原文件保留，让用户确认无误后手动清理）

**目录布局**：

```
~/.feishu-cli/
  config.yaml                # 旧布局，profile 系统未启用时仍读这里（无感升级）
  token.json
  active-profile             # 一行文本：当前 profile 名
  previous-profile           # 一行文本：上一个 profile 名（支持 use -）
  profiles/
    work/
      config.yaml
      token.json
      user_profile.json
    personal/
      ...
```

**向后兼容设计**：

- 没有任何 profile 时，`internal/config` 和 `internal/auth` 仍走旧路径，老用户零感知升级
- `profile add` **不会** 自动迁移旧文件——避免静默丢数据；要迁就显式 `profile migrate`
- `FEISHU_PROFILE=<name>` 环境变量临时覆盖（不写指针文件），适合 CI / 一次性切换

**安全**：

- profile 名仅允许 `[A-Za-z0-9_-]{1,64}`，禁止 `.`/`..`/路径分隔符等注入字符
- 保留名 `profiles` / `cache` 不可作为 profile 名
- 写入操作通过进程内 mutex 串行化；指针文件原子写（`.tmp` + rename）
- 所有 profile 目录默认 `0700` 权限，含 token.json 等敏感文件

**测试**：`internal/profile/store_test.go` 21 个测试，全部用 `t.TempDir()` 隔离，覆盖
ValidateName / List 字典序 / Create / Remove / Rename / Use 含 `-` 切换 / `MigrateLegacy`
含 `--force` 覆盖 / `FEISHU_PROFILE` 环境变量优先级 / `ActiveDir` 新旧布局切换。

**示例**：

```bash
# 创建一个标题为 "Q2 OKR" 的演示文稿
feishu-cli slides create --title "Q2 OKR" --output json

# 把封面图上传到该演示文稿
feishu-cli slides media-upload --file ./cover.png \
    --presentation-token <xml_presentation_id>
```
### 新增 — `schema` 命令：本地浏览飞书 OpenAPI 方法（path / 参数 / scope）

新增 `feishu-cli schema [service.resource.method]` 子命令，无需 token、纯本地查询飞书
开放平台 OpenAPI 方法的 HTTP path / 动词 / 参数 / 请求体 / 响应体 / scope / 文档链接。
便于 AI Agent 和脚本作者快速查找参数。

**用法**：

```bash
feishu-cli schema                                # 列出所有可用 service（12 个）
feishu-cli schema im                             # 列出 im 域下所有 resource.method
feishu-cli schema im.messages                    # 列出 messages 资源下所有 method
feishu-cli schema im.messages.delete             # 查看具体 method 详情
feishu-cli schema im.messages.delete --format json   # JSON 输出（AI Agent 推荐）
feishu-cli schema list --service drive           # 等价于 schema drive，支持 --format json
```

**数据源**：`internal/registry/meta_data.json`（编译期 embed），与认证模块复用同一份元数据。
当前覆盖 12 个 service：approval / attendance / calendar / drive / im / mail / minutes /
sheets / slides / task / vc / wiki。

**输出含**：HTTP verb + 完整 path、parameters（含 path / query / required 标记）、
requestBody（嵌套字段）、responseBody、accessTokens（user / tenant）、scopes、docUrl。

# 查询本人最近一周打卡
feishu-cli attendance user-task query \
    --employee-type open_id \
    --user-ids ou_xxxxxxxxx \
    --start 2026-05-01 --end 2026-05-18

# 查询本月日度统计（JSON 输出）
feishu-cli attendance user-stats query \
    --employee-type open_id \
    --user-ids ou_xxxxxxxxx --current-user-id ou_xxxxxxxxx \
    --stats-type daily --start 2026-05-01 --end 2026-05-31 -o json
# 收藏消息（消息层）
feishu-cli msg flag create om_xxx

# feed 层书签（自动识别 thread/msg_thread）
feishu-cli msg flag create om_xxx --flag-type feed

# 列出当前用户所有书签
feishu-cli msg flag list --page-size 50

# 取消书签（默认尽量取消消息层 + feed 层）
feishu-cli msg flag cancel om_xxx

# feed 层书签（普通群线程）
feishu-cli msg flag create om_xxx --item-type msg_thread --flag-type feed
```
feishu-cli auth login --scope "okr:okr"
feishu-cli okr cycle list
feishu-cli okr progress list --objective-id 7xxx
feishu-cli okr progress create --key-result-id 7xxx --content "本周完成核心模块联调"
```

**MVP 范围说明**：本次只覆盖最常用的 3 个动词，progress update / delete / get 和图片上传暂不暴露
为命令行（client 层已有实现，后续按需补 CLI）。
### 新增 — `approval` 流程：实例详情 / 发起 / 撤回 / 抄送 / 通过 / 拒绝 / 转交

补齐审批模块的核心能力，原本只有 `approval get`（定义查询）和 `approval task query`（任务列表查询）两条只读命令，现在可以覆盖官方当前可执行的实例/任务主路径：

- `feishu-cli approval instance get` — 获取单个审批实例详情，对齐官方 `instances/uat_get`
- `feishu-cli approval instance create` — 发起一条审批实例，`--form` 或 `--form-file` 传表单 JSON
- `feishu-cli approval instance cancel` — 撤回（取消）已发起的审批实例
- `feishu-cli approval instance cc` — 把审批实例抄送给一个或多个用户（`--cc-user-ids ou_a,ou_b`）
- `feishu-cli approval task approve` — 通过指定审批任务，可附 `--comment`
- `feishu-cli approval task reject` — 拒绝指定审批任务，建议在 `--comment` 中填写原因
- `feishu-cli approval task transfer` — 转交审批任务给其他用户，对齐官方 `tasks/uat_transfer`

**权限要求**：`instance get`、`task query`、`instance cancel/cc`、`task approve/reject/transfer` 使用 User Token，分别需要 `approval:instance:read`、`approval:task:read`、`approval:instance:write`、`approval:task:write`；`instance create` 是本项目额外应用态能力，使用 tenant_access_token，需要 `approval:approval`。

**底层 API**：

- `GET /open-apis/approval/v4/instances/uat_get`
- `POST /open-apis/approval/v4/instances`
- `POST /open-apis/approval/v4/instances/uat_cancel`
- `POST /open-apis/approval/v4/instances/uat_cc`
- `POST /open-apis/approval/v4/tasks/uat_approval`
- `POST /open-apis/approval/v4/tasks/uat_reject`
- `POST /open-apis/approval/v4/tasks/uat_transfer`

官方 skill 文案中提到但当前官方可执行 schema 未开放的能力仍不在本次范围：`tasks/rollback`（退回）、`tasks/add_sign`（加签）、`tasks/remind`（催办）。

**代码影响范围**：

- `internal/client/approval.go`：新增审批实例详情、实例写、任务写/转交 client 函数 + 对应 Options 结构 + 共享请求 helper
- `cmd/approval_instance.go`：新增 `approval instance` 父命令
- `cmd/approval_instance_{get,create,cancel,cc}.go`：4 条实例侧子命令
- `cmd/approval_task_{approve,reject,transfer}.go`：3 条任务侧子命令
### 新增 — `sheet filter-view` + `sheet dropdown`：筛选视图与下拉菜单

补齐两块电子表格高级能力：

- **筛选视图 CRUD（V3 API）**：用 SDK `SpreadsheetSheetFilterView` 实现
  - `feishu-cli sheet filter-view create --token <t> --sheet-id <s> --range "<sheetId>!A1:H14" [--name 视图名 --filter-view-id 自定义ID]`
  - `feishu-cli sheet filter-view list --token <t> --sheet-id <s>`
  - `feishu-cli sheet filter-view delete --token <t> --sheet-id <s> --filter-view-id <fv>`
  - `--range` 不带 sheetId 前缀时自动补全为 `<sheet-id>!<range>`
- **下拉菜单（V2 dataValidation API）**：list 类型数据验证
  - `feishu-cli sheet dropdown set --token <t> --range "<sheetId>!A1:A100" --options "待办,处理中,已完成" [--multiple --colors "#FF4D4F,#FAAD14,#52C41A"]`
  - `--options-json '["a, b","c"]'`：选项内含逗号时绕过 CSV 解析
  - 传 `--colors` 自动开启 `highlightValidData`，颜色数量需与选项一致

**权限**：`sheets:spreadsheet`（User Token 或 App Token 均可），命令默认 `resolveOptionalUserTokenWithFallback` 自动读取登录态。

**代码影响范围**：

- `internal/client/sheets.go`：新增 `CreateFilterView` / `ListFilterViews` / `DeleteFilterView` / `SetDropdown`
- `cmd/sheet_filter_view.go`、`cmd/sheet_dropdown.go`：CLI 入口
### 新增 — `markdown {create,fetch,overwrite}`：Drive 原生 .md 文件 CRUD

新增 `feishu-cli markdown` 顶层命令，把 Drive 上的 `.md` 当作普通文件整体读写，
保留原始 Markdown 格式（**不做** Markdown ↔ 飞书 docx 块的转换）。

**与 `doc import` / `doc export` 的区别**：

| 命令 | 行为 | 创建出的文档类型 |
|------|------|------------------|
| `doc import/export` | Markdown ↔ 飞书 docx 块（标题/列表/表格/Callout…） | docx |
| `markdown create/...` | 把 `.md` 整体上传/下载，不做转换 | file（普通 Drive 文件） |

适合 AI agent 把生成的 Markdown 直接落盘到飞书 Drive、下次读回时仍是原汁原味
Markdown 源码的场景。

**子命令**：

- `markdown create --name xxx.md --content "..." | --content-file path.md | --file path.md [--folder-token fldxxx]`
  从字符串或本地文件创建 `.md`；强制 `.md` 后缀；空内容报错；底层走
  `client.UploadFileWithToken`（≤ 20MB 单次上传，> 20MB 复用现成分片管线）。

- `markdown fetch --file-token boxcnxxx [--output-path path] [-o json]`
  缺省 `--output-path` 时直接打印到 stdout；
  指定路径则落盘，目录会拼 `fileToken.md`，`--overwrite` 防误覆。

- `markdown overwrite --file-token boxcnxxx --name existing.md --content "..." | --content-file path.md | --file path.md [--name renamed.md]`
  覆盖现有 `.md` 的内容，`file_token` 保持不变；`--content` 时 `--name` 必填，`--content-file` 缺省使用本地 basename。
  **实现细节**：飞书 Go SDK v3.5.3 的 `UploadAllFileReqBody` 没有暴露 `file_token`
  字段，因此本命令用 `client.Post` + `*larkcore.Formdata` 自己拼 multipart，
  endpoint 仍是官方的 `POST /open-apis/drive/v1/files/upload_all`，
  `shortcuts/markdown/helpers.go` 的写法。

**权限**：User Access Token + `drive:file:upload` / `drive:file:download`
（或 `drive:drive`）。

# 内联图片
feishu-cli mail send --to user@example.com --subject "周报" \
    --body '<p>看附图</p><img src="./screenshot.png">' \
    --inline-images-auto-scan --confirm-send

# 模板创建+列表
feishu-cli mail template create --name "周报" --subject "本周进度" --body "<p>模板</p>"
feishu-cli mail template list
### 新增 — `calendar` 智能化三件套（suggestion / room-find / rsvp）

针对 AI Agent 自动排会场景，补齐三条飞书日历开放能力，使整条「选时段 → 选会议室 → 答复邀请」
流水线全部可在 CLI 完成。

- **`calendar suggestion`**：智能时段建议。直调 `POST /open-apis/calendar/v4/freebusy/suggestion`，
  按 `--attendee-ids ou_xxx,oc_yyy` + `--duration 30m/1h30m/90` 推荐可用时段；支持
  `--start`/`--end` 搜索窗口（默认当天）、`--exclude start~end,...` 排除午休/已占用时段、
  `--event-rrule` 周期性规则、`--timezone`。返回带「推荐理由」+「AI 行动指引」。
- **`calendar room-find`**：会议室查找。直调 `POST /open-apis/calendar/v4/freebusy/room_find`，
  支持多个 `--slot start~end` 并发查询（worker=10），可按 `--city`/`--building`/`--floor`/
  `--room-name`（逗号分隔多个）/`--min-capacity`/`--max-capacity` 多维度过滤；可选
  `--attendee-ids` 让服务端结合参与者位置筛选。
- **`calendar rsvp`**：答复日程邀请。走 SDK Reply 接口，`--calendar-id`（可省略，默认主日历）+
  `--event-id` + `--action accept|decline|tentative`。与既有的 `calendar event-reply`
  位置参数风格互为补充——rsvp 全 flag 风格、calendar-id 可省，更适合 AI Agent 调度。

**SDK 现状**：v3.5.3 暴露 `Reply` 但未暴露 `freebusy/suggestion` 和 `freebusy/room_find`，
故 suggestion / room-find 走 `client.Post` 通用 HTTP 直调 OpenAPI；新增 client 函数集中在
`internal/client/calendar_smart.go`，包括 `SuggestFreebusy`、`FindMeetingRoom`、
`FindMeetingRoomBatch`（并发+排序）、`SplitAttendeeIDs`（按 `ou_`/`oc_` 前缀分流）。

**权限要求**：
- suggestion / room-find：`calendar:calendar.free_busy:read`（User Token 或 App Token 均可）
- rsvp：`calendar:calendar.event:reply`（推荐 User Token，以本人身份答复）

**典型用法**：

```bash
# 1. 先让飞书推荐可用时段
feishu-cli calendar suggestion --attendee-ids ou_aaa,ou_bbb --duration 30m

# 2. 锁定时段后查会议室
feishu-cli calendar room-find \
  --slot 2024-01-22T09:00:00+08:00~2024-01-22T09:30:00+08:00 \
  --building "飞书大厦" --min-capacity 6

# 3. 收到邀请后答复
feishu-cli calendar rsvp --event-id EVENT_xxx --action accept
# 从旧布局开始（已有 config.yaml 和 token.json）
feishu-cli profile migrate                              # → profiles/default/，指针指 default
feishu-cli profile add personal --use --app-id cli_yyy  # 新建 personal 并切过去
feishu-cli profile list                                 # 看哪个 active
feishu-cli profile use -                                # 切回 default
FEISHU_PROFILE=personal feishu-cli msg send ...         # 一次性临时切换
```

### 新增 — `comment reply add`：为已有评论添加回复

新增命令 `feishu-cli comment reply add <file_token> <comment_id> --text "..."`，补齐评论回复
生命周期的最后一块拼图（此前只有 list / delete）。

**背景**：飞书 Open SDK v3.5.3 的 `fileCommentReply` 只暴露 `List`/`Delete`/`Update`，没有
`Create` 方法，而 Open API 本身是支持的（`POST /drive/v1/files/:token/comments/:comment_id/replies`）。
此 PR 不依赖 SDK 升级，用通用 HTTP client（`client.Post`）直接调用 API 实现。

**同时改进**：

- `comment reply add` / `delete` / `list` 全部加上 `--user-access-token` 参数支持，并走
  `resolveOptionalUserTokenWithFallback` 自动读取登录态，和 msg/chat/doc export 等模块保持一致
- **重要修复**：`comment reply delete` 在 App Token（Bot 身份）下调用飞书侧会返回 `1069303
  forbidden`——飞书只允许回复作者本人删除。现在命令默认优先使用 User Token（如果已登录），
  行为才符合用户预期。命令帮助中也显式说明了这个权限模型
- `comment reply add` 默认也走 User Token fallback，回复会以用户身份发布（而非显示为 Bot），
  且该回复能被后续 `reply delete` 正常删除

**权限要求**：`docs:document.comment:create`（User Token）

**使用示例**：

```bash
feishu-cli auth login                       # 确保有 User Token
feishu-cli comment reply add <file_token> <comment_id> --text "已处理"
feishu-cli comment reply delete <file_token> <comment_id> <reply_id>  # 自动用 User Token
```

**代码影响范围**：

- `internal/client/comment.go`：新增 `CreateCommentReply`（HTTP client 直调），
  `ListCommentReplies` / `DeleteCommentReply` 签名增加 `userAccessToken` 参数
- `cmd/comment_reply.go`：新增 `addReplyCmd`，三个子命令统一加 `--user-access-token` flag
- `cmd/comment.go`：Long help 中补充 reply add 示例

### Features — 新增 `wiki move-docs` 命令（移动云空间文档至知识空间）

新增 `feishu-cli wiki move-docs <obj_token> --space-id <id>` 命令，对应飞书 OpenAPI `POST /open-apis/wiki/v2/spaces/{space_id}/nodes/move_docs_to_wiki`。

**解决的问题**：之前要把"我的空间 / 共享空间"里已存在的 docx / sheet / mindnote / bitable / file 挂到知识库下，只有两条路——(1) 飞书客户端手动点"添加到知识库"；(2) 走 `wiki create` 新建空文档再重写内容。前者不能自动化，后者丢原文档权限和历史。新命令一步到位。

**用法**：

```bash
# 把 drive docx 移入知识空间根目录
feishu-cli wiki move-docs doccnXXXXXX --space-id 7012345678901234567

# 移入指定父节点
feishu-cli wiki move-docs doccnXXXXXX --space-id 7012345678901234567 --parent-node wikcnYYYYYY

# 移动电子表格
feishu-cli wiki move-docs shtcnXXXXXX --space-id 7012345678901234567 --obj-type sheet

# 无 move 权限时提交迁入申请
feishu-cli wiki move-docs doccnXXXXXX --space-id 7012345678901234567 --apply

# 用用户身份调用（企业版 wiki 空间不接受 app 成员，必须 user token）
feishu-cli wiki move-docs doccnXXXXXX --space-id 7012345678901234567 --user-access-token u-xxx
```

**返回三种情况**：`wiki_token`（立即完成）/ `task_id`（异步任务）/ `applied=true`（权限不足已提交申请）。

**Scope 要求**：`wiki:node:move` 或 `wiki:wiki`，已加入 `--domain wiki --recommend` 推荐列表。

**代码影响范围**：
- 新增 `cmd/move_docs_to_wiki.go`（命令）和 `internal/client/wiki.go` 的 `MoveDocsToWiki` 函数
- `internal/registry/domain_alias.go` 的 `wiki` domain 补上 `wiki:node:move`
- README 知识库操作段落补一行命令

---

### Breaking Changes — 移除 `config add-scopes` 命令

`feishu-cli config add-scopes` 子命令及其 `--domain` / `--scopes` / `--print-only` flag 全部删除。

**删除理由**：

1. **命令几乎不可用** — 硬编码的 `scopeDomains` 字典里多数 scope 名已过时（`docx:document` / `sheets:spreadsheet` / `bitable:app` / `im:chat:readonly` / `drive:export:readonly` / `vc:room:readonly` 等都是飞书不支持的粗粒度名称），生成的申请链接里多数 scope 会被后台拒绝
2. **权限开通不适合自动化** — 飞书开放平台的权限申请通常需要 tenant 管理员审批，scope 选择也是业务决策而非技术"默认值"。CLI 自动化只会造成"看起来装好了但后台还没批"的幻觉
3. **有更简单的替代** — 飞书开放平台的应用权限管理页面支持"导入权限 JSON"入口，复制 [README 权限要求](../README.md#权限要求) 章节里的完整权限清单一次性粘贴即可开通 400+ 个 scope

**迁移指引**：

旧：
```bash
feishu-cli config add-scopes --domain all
```

新：
1. 打开飞书开放平台 → 你的应用 → 权限管理页面
2. 复制 README 的完整权限 JSON（tenant + user 两套 400+ scope）
3. 粘贴到"导入权限"入口，一键开通全部
4. 等待 tenant 管理员审批（如果需要）

**代码影响范围**：
- 删除 `cmd/config_add_scopes.go` 整个文件
- `cmd/auth_check.go` 的 `suggestion` 文案改为引导用户去开放平台开通（不再推荐 `config add-scopes`）
- README / CLAUDE.md / AGENTS.md / 6 个 skill 的 `config add-scopes` 引用全部更新为"去开放平台开通"
- 保留 `config create-app --save`（Device Flow 创建应用）不变

---

### Breaking Changes — 多维表格（bitable）切换到 `base/v3` API

**旧实现**：`bitable` 模块全部调用 `/open-apis/bitable/v1/apps/{app_token}/...` 老 API，覆盖 ~30 个基础 CRUD 命令。
**新实现**：全面切换到 `/open-apis/base/v3/bases/{base_token}/...` 新 API，覆盖 48 个命令，支持深度能力（视图完整配置读写、记录 upsert、修改历史、角色 CRUD、高级权限、数据聚合、工作流查询）。

#### 命令名迁移表

| 旧命令 | 新命令 |
|---|---|
| `bitable tables <app>` | `bitable table list --base-token <t>` |
| `bitable create-table <app>` | `bitable table create --base-token <t> --name x` |
| `bitable rename-table <app> <tbl>` | `bitable table update --base-token <t> --table-id <tbl> --name x` |
| `bitable delete-table <app> <tbl>` | `bitable table delete --base-token <t> --table-id <tbl>` |
| `bitable fields <app> <tbl>` | `bitable field list --base-token <t> --table-id <tbl>` |
| `bitable create-field` | `bitable field create` |
| `bitable update-field` | `bitable field update`（method 改为 `PUT`） |
| `bitable delete-field` | `bitable field delete` |
| `bitable records <app> <tbl>` | `bitable record list --base-token <t> --table-id <tbl>` |
| `bitable get-record` | `bitable record get` |
| `bitable add-record` | `bitable record upsert --base-token <t> --table-id <tbl> --config '...'` |
| `bitable add-records --data-file` | `bitable record batch-create --config-file ...` |
| `bitable update-record` | `bitable record upsert --record-id ...`（根据是否传 id 自动 PATCH/POST） |
| `bitable delete-records` | `bitable record delete --record-id ...` |
| `bitable views` | `bitable view list` |
| `bitable create-view` | `bitable view create` |
| `bitable delete-view` | `bitable view delete` |
| `bitable view-filter get/set` | `bitable view view-filter-get / view-filter-set` |
| `bitable dashboard list`（v1） | **暂不支持**（v3 dashboard CRUD 留待下次迭代） |
| `bitable form list` | **暂不支持** |
| `bitable role list` | `bitable role list`（新增 get/create/update/delete） |
| `bitable workflow list/enable` | `bitable workflow list`（改为 POST /workflows/list） |
| `bitable advperm enable/disable` | 同名但底层改为 `PUT .../advperm/enable?enable=true/false` |
| `bitable data-query` | 同名但路径从 table 级改为 base 级：`POST .../bases/{t}/data/query` |

#### 新增能力

- **视图配置完整写入**：`view-sort-set` / `view-group-set` / `view-visible-fields-set` / `view-timebar-set` / `view-card-set`（老 v1 只能写 filter）
- **记录修改历史**：`bitable record history-list --record-id xxx`
- **角色 CRUD**：`bitable role create/update/delete`（老 v1 只有 list）
- **字段选项搜索**：`bitable field search-options`
- **Base create 支持时区**：`--time-zone Asia/Shanghai`

#### Flag 变化
- **删除 `--app-token` 别名**：只保留 `--base-token`（与 base/v3 API 命名一致，不再做兼容别名）
- `bitable create` 的 `--description` 被删除（base/v3 不支持），新增 `--time-zone`
- `bitable data-query` 的 `--table-id` 被删除（v3 端点挂在 base 下）

#### 删除的文件
- `internal/client/bitable.go` / `bitable_test.go`（v1 实现）
- `cmd/bitable_create.go` / `bitable_get.go` / `bitable_copy.go` / `bitable_advperm.go` / `bitable_dashboard.go` / `bitable_data_query.go` / `bitable_form.go` / `bitable_record_upload_attachment.go` / `bitable_role.go` / `bitable_view_config.go` / `bitable_workflow.go`

#### 新增的文件
- `internal/client/base.go`（`BaseV3Call` + `BaseV3Path` helper + `X-App-Id` header 自动注入）
- `cmd/bitable_base.go` / `bitable_misc.go`（所有 base/v3 命令的注册）
- `cmd/bitable_table.go` / `bitable_field.go` / `bitable_record.go` / `bitable_view.go` 全部重写

---

### Breaking Changes — VC（视频会议）改造升级

- **`vc search`**：底层 API 从 `GET /meeting_list` 切换到 `POST /meetings/search`。
  - 新增 flag：`--query` / `--organizer-ids` / `--participant-ids` / `--room-ids`
  - 删除 flag：`--meeting-no` / `--meeting-status`
  - 必须指定至少一个过滤条件
- **`vc notes`**：
  - flag 从 `--meeting-id` / `--minute-token`（单数）改为 `--meeting-ids` / `--minute-tokens`（复数，支持 CSV 批量最多 50）
  - 新增第三路径 `--calendar-event-ids`：从日历事件自动反查会议 / 妙记
  - 新增开关 `--with-artifacts`（获取 AI 产物）/ `--download-transcript --output-dir`（下载逐字稿）
- **所有 vc / minutes 命令默认 User Access Token**，未登录时统一报错提示 `feishu-cli auth login`

#### 新增命令
- `vc recording --meeting-ids/-calendar-event-ids`：查询会议录制并自动提取 `minute_token`
- `minutes download --minute-tokens x,y,z --output ./dir`：批量下载妙记音视频媒体（SSRF 防护 / 重定向校验 / Content-Disposition 解析 / 文件名去重 / 5 req/s 速率限制 / `--url-only` 预览链接）
- `minutes get <token> --with-artifacts`：新增 AI 产物合并输出

---

### Added — `drive` 云盘命令组（8 个命令）

新增独立的 `drive` 子命令组，与现有 `file` / `media` / `doc media-*` 命令并存，提供增强能力：

| 命令 | 相比老命令的增强 |
|---|---|
| `drive upload` | 大文件自动分块（>20MB 走 `upload_prepare/part/finish` 三步式，每片独立重试 3 次；支持 User Token） |
| `drive download` | 流式下载 + 路径校验 + `--overwrite` / `--timeout` |
| `drive export` | 新增 **markdown 快捷路径**：docx → markdown 走 `/docs/v1/content` 直接拉取，不跑异步 export task；支持 sheet / bitable 按 `--sub-id` 导出 CSV；有界轮询（10×5s）+ 超时返回 resume 命令 |
| `drive export-download` | 通过 `file_token` 直接下载已完成的导出任务产物，配合 `drive export` 超时后接力完成 |
| `drive import` | **切换到 `/medias/upload_*` 端点 + `parent_type=ccm_import_open` + `extra` 字段**（不再在用户云盘留下中间文件）；格式特定大小限制（docx 20MB / sheet 20MB / bitable 100MB）；有界轮询 + resume |
| `drive move` | 文件夹移动自动轮询 `task_check`（30×2s），文件移动同步返回 |
| `drive add-comment` | 支持**富文本 `reply_elements`**（text / mention_user / link）+ `--block-id` 局部评论（docx）+ **wiki URL 自动解析**成 docx token |
| `drive task-result` | 通用异步任务查询（`--scenario import/export/task_check`），配合 drive export / import / move 的超时 resume |

**保留不动**：`file list / delete / mkdir / copy / shortcut / quota / meta / stats / version` + `media upload / download` + `doc media-download / media-insert` + `comment list / resolve / delete / reply`

---

### Added — `mail` 飞书邮箱模块（10 个命令，从零新建）

**全新命令组**。首期不支持附件和 CID 内联图片，仅支持纯文本和 HTML body。所有命令默认 User Access Token。

| 命令 | 功能 |
|---|---|
| `mail message --message-id x` | 获取单封邮件（`--format full/plain_text_full/raw`） |
| `mail messages --message-ids a,b,c` | 批量获取多封邮件 |
| `mail thread --thread-id x` | 获取邮件线程 |
| `mail triage` | 列出 / 搜索邮件（`--folder INBOX --label x --query xxx --unread-only --list-folders --list-labels`），`--query` 走专用 `POST /search` 端点 |
| `mail send` | 发送邮件（**默认保存为草稿**，加 `--confirm-send` 立即发送，安全兜底） |
| `mail draft-create` | 仅创建草稿 |
| `mail draft-edit --draft-id x` | 编辑已有草稿（全量覆盖） |
| `mail reply --message-id x --body "..."` | 回复邮件（自动 `Re: ` 前缀 + 引用块 + `In-Reply-To` / `References` header 继承） |
| `mail reply-all` | 全部回复（包含 To 和 CC，自动排除自己） |
| `mail forward --message-id x --to y` | 转发（自动 `Fwd: ` 前缀 + 原文正文引用） |

**关键技术点**：
- RFC 5322 EML 构建 + base64 URL-safe 编码，`POST /drafts` body `{"raw":"..."}`
- HTML 自动检测（`<html>/<div>/<b>/<br>` 等标签），可用 `--plain-text` / `--html` 强制
- 发件人地址默认从 `/user_mailboxes/{mailbox}/profile` 读取
- 地址格式支持 `"Name <email>"` 和 `"email"`
- Subject 去重：`reply` 自动避免 `Re: Re:`，`forward` 自动避免 `Fwd: Fwd:`

---

### Fixed

- **`mail reply` 引用块缺日期占位符**：之前的 quote header 模板第一个 `%s` 传空字符串，会输出 `"在 ，xxx 写道:"`，已修正为 `"{email} 写道:"`
- **分片上传 fd 泄漏**：`uploadFileMultipart` 之前每片每次重试都 `os.Open + Seek`，现改为外层打开一次 + `io.NewSectionReader`，大文件不稳定网络下重试时节省 N×syscall
- **`mail reply` 重复 `GetMailboxProfile` 调用**：之前在 `runMailReply` 里调用 2 次（一次取 selfEmail 一次取 from/fromName），现合并为 1 次，省 1 个 API RTT
- **`drive import` 上传端点错误**：之前走 `/files/upload_all` 会在用户云盘留下中间文件，现改为官方的 `/medias/upload_all` + `parent_type=ccm_import_open` + `extra`
- **`mail triage --query` 静默失效**：之前把 query 当 list 端点的查询参数，飞书会忽略；现改走专用的 `POST /search` 端点

### Refactor（内部代码清理，用户感知较小）

- 新增 `requireUserToken(cmd, cmdName)` helper，统一所有新命令的 "需要 User Access Token" 错误信息格式
- 删除重复的 `GetWikiNodeByToken`（58 行），改用已有的 `GetWikiNode`
- 删除 `internal/client/mail.go` 的 `joinPath`，用 `strings.Join`
- `dedupStrings` 从 `vc_recording.go` 移到 `vc_common.go`
- `runBaseV3WithJSON` 重构，抽出 `runBaseV3WithBody` 让命令层直接传已构造的 body
- `bitable view create/rename` 去掉 `cmd.Flags().Set("config", ...)` + `MarkHidden` 的 hack 模式
- 删除 `runBaseV3Simple` / `addBaseTokenFlag` / `exactlyOneNonEmpty` 三处死参数/死变量
- 所有文件统一 `gofmt`

---

## [v1.18.0] - 未发布

### Breaking Changes — OAuth 认证全面切换到 Device Flow

彻底删除 Authorization Code Flow，只保留 Device Flow（RFC 8628）。本地桌面、SSH 远程、容器、CI 全环境统一使用同一条命令，**无需任何重定向 URL 白名单配置**。

#### 删除的 flag

`auth login` 命令删除以下 flag：

- `--manual` — SSH 远程手动粘贴回调模式（Device Flow 下 SSH 和本地一视同仁）
- `--no-manual` — 强制本地回调模式（本地回调 HTTP server 已移除）
- `--port` — 本地回调端口（不再需要）
- `--print-url` — 非交互两步式第一步（改用 `--no-wait` + `--device-code`）
- `--method` — 授权方式选择（Device Flow 是唯一方式）
- `--scopes` — 请求 OAuth scope（飞书 token v2 端点实际忽略此参数，返回应用预配置的全部 scope）

#### 删除的子命令

- `auth callback <url> --state <state>` — Authorization Code Flow 换 token 专用，整体删除

#### 删除的代码

- `internal/auth/oauth.go`：`Login` / `loginLocal` / `loginManual` / `buildAuthURL` / `GenerateAuthURL` / `ParseCallbackURL` / `ExchangeToken` 等函数
- `internal/auth/browser.go`：`isLocalEnvironment()` 函数（曾在 macOS 上无条件返回 true 导致 SSH 远程 Darwin bug）
- `cmd/auth_callback.go`：整个文件

#### 修复的 bug

- **Issue #95**：飞书错误码 20029（重定向 URL 有误）。根因是 Authorization Code Flow 需要用户在飞书开放平台配置 `http://127.0.0.1:9768/callback` 白名单，Device Flow 直接绕过此要求
- **Darwin SSH bug**：`isLocalEnvironment()` 在 macOS 上无条件返回 `true`，SSH 到 Mac 服务器时错误走本地回调模式会 2 分钟超时失败。已通过删除该函数消除

### 新增 — `auth login` 的 JSON 事件流模式

- **`auth login --json`**：阻塞轮询 + JSON 事件流输出到 stdout。AI Agent 推荐配合 Claude Code 的 `run_in_background=true` 使用
  - 首次输出：`{"event":"device_authorization","verification_uri":"...","verification_uri_complete":"...","user_code":"...","device_code":"...","expires_in":240,"interval":5}`
  - 成功输出：`{"event":"authorization_success","expires_at":"...","refresh_expires_at":"...","scope":"..."}`

- **`auth login --no-wait --json`**：两步模式第一步。只请求 `device_code` 并立即输出 JSON，不启动轮询。适合 AI Agent 希望把"请求"和"轮询"拆到两次独立 Bash 调用的场景

- **`auth login --device-code <code> --json`**：两步模式第二步。用已有的 `device_code` 继续轮询直到授权完成

### 新增 — `auth check` 子命令

预检当前 Token 是否包含指定 scope，专为 AI Agent 在执行业务命令前做前置判断而设计：

```bash
feishu-cli auth check --scope "search:docs:read"
feishu-cli auth check --scope "search:docs:read im:message:readonly"
```

输出 JSON：

```json
{
  "ok": true,
  "granted": ["search:docs:read"],
  "missing": null
}
```

或失败情况：

```json
{
  "ok": false,
  "error": "not_logged_in",
  "missing": ["search:docs:read"],
  "suggestion": "feishu-cli auth login"
}
```

退出码 0 = 满足，非 0 = 缺少或未登录，AI Agent 可直接分支。

### 不变

- **Token 存储格式**：`~/.feishu-cli/token.json` 仍是明文 JSON，数据结构完全兼容。升级后**不需要重新登录**
- **Token 自动刷新**：`ResolveUserAccessToken()` 路径和 `RefreshAccessToken()` 逻辑保持不动，access_token 过期时用 refresh_token 自动刷新
- **`config create-app`** 命令完全不变（它本来就用 Device Flow）
- **`auth status`** / **`auth logout`** 行为不变
- **所有业务命令**（doc/msg/search/wiki/task/calendar/...）行为不变

### 迁移指引

#### 人类用户

无需任何迁移。一条命令通吃所有场景：

```bash
feishu-cli auth login
```

本地桌面会自动开浏览器，SSH 远程需要手动复制 stderr 里的链接在本机浏览器打开，一模一样的命令。

#### AI Agent / 脚本用户

旧的两步式：
```bash
feishu-cli auth login --print-url --scopes "..."
feishu-cli auth callback "<回调URL>" --state "<state>"
```

迁移为以下**任一**方案：

**方案 A（推荐）**：阻塞 + 后台运行：
```bash
# run_in_background=true
feishu-cli auth login --json
# 读 stdout 第一行拿 verification_uri_complete，展示给用户
# 等后台进程退出，读第二行 stdout 拿 authorization_success
```

**方案 B**：两步模式：
```bash
# 第一步
feishu-cli auth login --no-wait --json  # → device_code JSON
# 把链接展示给用户等待授权
# 第二步
feishu-cli auth login --device-code <code> --json  # → authorization_success
```

#### CI / 无头脚本

**Authorization Code Flow 本来就无法无头完成**（需要浏览器授权），Device Flow 同样需要人类介入一次。如果 CI 需要 User Token，应该预先在本地通过 `auth login` 拿到 token.json 然后把它作为 secret 部署到 CI 环境，**不需要任何迁移**。

### 详细对比

| 方面 | v1.17.0 及以前 | v1.18.0 |
|---|---|---|
| OAuth Flow | Authorization Code Flow（默认）+ Device Flow（`--method device`） | 仅 Device Flow |
| 子命令 | `login` / `callback` / `status` / `logout` | `login` / `check` / `status` / `logout` |
| `auth login` 的 flag | `--port` / `--manual` / `--no-manual` / `--print-url` / `--scopes` / `--method` | `--json` / `--no-wait` / `--device-code` |
| 重定向 URL 白名单 | 必须（Authorization Code Flow 前置条件） | 不需要 |
| SSH 远程支持 | 要么手动粘贴（`--manual`）要么非交互两步（`--print-url`） | 一条命令通吃 |
| AI Agent 非交互方案 | `--print-url` + `auth callback` | `--json` + `run_in_background` 或 `--no-wait` / `--device-code` |
| `offline_access` 注入 | 用户手动通过 `--scopes` 传 | CLI 强制注入，用户无需操心 |
| scope 预检 | 手动解析 `auth status` JSON 的 scope 字段 | `auth check --scope "..."` |

---

更早的版本请参考 [GitHub Releases](https://github.com/riba2534/feishu-cli/releases)。
