| name | feishu-cli-markdown |
| description | 飞书云盘原生 Markdown 文件操作(与 doc import/export 互补)。 markdown create 上传新 .md 到云盘;markdown fetch 下载为本地 .md; markdown overwrite 用本地 .md 覆盖云盘已有文件(保 file_token 不变,分享链接持久); markdown diff 在本地比对远端最新/历史版本,不改远端。 当 doc 文档不适合(图床、密集代码块、版本管理)走 .md 原生文件路径。 Drive upload_all + 自拼 Formdata multipart(SDK 不暴露 file_token field)。 当用户请求"上传 markdown"、"下载 md"、"覆盖云盘 md"、"md diff"、"比对 markdown 版本"时使用。 |
| argument-hint | create | fetch | overwrite | diff |
| user-invocable | true |
| allowed-tools | Bash(feishu-cli markdown:*), Bash(feishu-cli drive:*), Bash(feishu-cli auth:*), Read |
飞书云盘原生 Markdown(markdown create/fetch/overwrite/diff)
markdown 命令组把 Drive 上的 .md 当作普通文件整体读写,保留原始 Markdown 源码,不做 Markdown ↔ 飞书 docx 块的转换。
feishu-cli:如尚未安装,请前往 riba2534/feishu-cli 获取安装方式。
核心概念:与 doc import / doc export 的区别
| 命令 | 行为 | 创建出的类型 | 适用场景 |
|---|
doc import | Markdown → 飞书 docx 块(标题/列表/表格/Callout/Mermaid 画板…) | docx(在线协同文档) | 给人读、要排版、要团队评论 |
doc export | docx → Markdown(块解析回 markdown 源码) | 本地 .md | 从飞书 docx 落盘到 Git |
markdown create/fetch/overwrite | 把 .md 整体上传/下载,不转换 | file(Drive 普通文件) | AI agent 直接落盘原汁原味 .md、保留 fenced code block 缩进、当图床/密集代码块/版本管理用 |
判断走哪条:
- 想要飞书 docx 渲染(人读、排版、表格分块、画板渲染)→
doc import
- 想要原始
.md 文本 unchanged 保留在云盘(AI agent 读回完全一致;docx 块解析有损)→ markdown create
- 想反复覆盖同一份
.md、保持 file_token 不变(分享链接持久)→ markdown overwrite
前置条件
- 认证:所有
markdown 命令默认走 User Access Token(执行 feishu-cli auth login 登录)
- 预检:
feishu-cli auth check --scope "drive:file:upload drive:file:download"
命令速查
1. markdown create — 上传新 .md 到云盘
feishu-cli markdown create --name plan.md --content "# Plan\n\n- todo 1"
feishu-cli markdown create --content-file ./local.md
feishu-cli markdown create --file ./local.md
feishu-cli markdown create --name draft.md --content-file ./tmp.md --folder-token fldxxx
feishu-cli markdown create --name plan.md --content-file ./plan.md -o json
关键 flag:
| flag | 说明 |
|---|
--name | 远端文件名,必须 .md 结尾。create:--content 时必填、--content-file 时可省取本地 basename。overwrite:--content 时必填、--content-file 时可省取本地 basename;显式传入 = 同时改名 |
--content | 字符串内容(与 --content-file 二选一) |
--content-file | 本地 .md 文件路径 |
--file | 官方 lark-cli 兼容别名,等价于 --content-file |
--folder-token | 目标文件夹(缺省 Drive 根目录) |
-o json | JSON 输出(含 file_token / file_name / size_bytes) |
--user-access-token | 覆盖登录态 |
校验:空内容直接报错(不允许创建空 .md)。
2. markdown fetch — 下载云盘 .md
feishu-cli markdown fetch --file-token boxcnxxx
feishu-cli markdown fetch --file-token boxcnxxx --output-path ./local.md
feishu-cli markdown fetch --file-token boxcnxxx --output-path ./local.md --overwrite
feishu-cli markdown fetch --file-token boxcnxxx -o json
关键 flag:
| flag | 说明 |
|---|
--file-token | Markdown 文件 token(必填) |
--output-path | 本地保存路径;目录时拼 <fileToken>.md;缺省打印 stdout |
--overwrite | 本地文件已存在时覆盖(缺省直接报错) |
-o, --output json | JSON 输出;不传 --output-path 时包含 content 字符串 |
3. markdown overwrite — 覆盖已有 .md(保 file_token)
feishu-cli markdown overwrite --file-token boxcnxxx --name existing.md --content "新内容"
feishu-cli markdown overwrite --file-token boxcnxxx --content-file ./new.md
feishu-cli markdown overwrite --file-token boxcnxxx --file ./new.md
feishu-cli markdown overwrite --file-token boxcnxxx --content-file ./new.md --name renamed.md
关键 flag:
| flag | 说明 |
|---|
--file-token | 目标文件 token(必填) |
--content / --content-file | 新内容(二选一) |
--name | 覆盖后文件名(.md 结尾;--content 时必填;--content-file 缺省使用本地 basename) |
-o json | JSON 输出 |
核心价值:file_token 保持不变 → 分享链接持久、其他人收藏的链接不失效;多次迭代场景(AI agent 每天更新同一份 .md)优于"删了重建"。
4. markdown diff — 本地比对远端最新/历史版本(只读,不改远端)
下载远端 Markdown 内容并在本地计算 unified diff,不修改远端文件。三种比对模式(由参数组合决定,互斥):
feishu-cli markdown diff --file-token boxcnxxx --file ./local.md
feishu-cli markdown diff --file-token boxcnxxx --from-version 3
feishu-cli markdown diff --file-token boxcnxxx --from-version 2 --to-version 5 --context-lines 1
feishu-cli markdown diff --file-token boxcnxxx --file ./local.md --format json
feishu-cli markdown diff --file-token boxcnxxx --file ./local.md --jq '.added_lines'
feishu-cli markdown diff --file-token boxcnxxx --file ./local.md --format table --jq '{identical,added_lines,removed_lines}'
feishu-cli markdown diff --file-token boxcnxxx --file ./local.md -o json
关键 flag:
| flag | 说明 |
|---|
--file-token | 目标 Markdown 文件 token(必填) |
--file | 本地 .md 路径(模式 1:与远端最新比对) |
--from-version | 起始远端版本号(模式 2/3) |
--to-version | 目标远端版本号(模式 3,需配合 --from-version) |
--context-lines | 每个 hunk 上下保留的未变更上下文行数(默认 3) |
--dry-run | 只打印比对计划,不下载/不比对 |
--format | json|pretty|table|ndjson|csv 结构化输出;缺省打印 unified diff 文本 |
--jq | 内置 gojq 过滤结构化输出(如 --jq '.added_lines',无需外部 jq) |
-o json | 兼容别名,等价 --format json |
结构化输出结构(--format json / -o json,顶层 9 字段,可直接 --jq 取改动量 / 判一致):
{
"detection": "local_vs_remote",
"from": "remote (latest)",
"to": "local: ./local.md",
"size_bytes_before": 1024,
"size_bytes_after": 1088,
"identical": false,
"added_lines": 5,
"removed_lines": 2,
"hunks": [
{
"old_start": 10, "old_lines": 4,
"new_start": 10, "new_lines": 7,
"lines": [
{ "op": " ", "text": "上下文行" },
{ "op": "-", "text": "旧内容" },
{ "op": "+", "text": "新内容" }
]
}
]
}
常用内置 --jq(无需外部 jq):--jq '.identical' 判是否一致、--jq '.added_lines, .removed_lines' 取改动量、--jq '.hunks[].lines[] | select(.op=="+") | .text' 拉所有新增行。
与 overwrite 配合:覆盖前先 diff --file ./local.md 预览本地相对远端最新的改动,确认后再 overwrite,避免误覆盖。
底层实现 & 踩坑
1. SDK 不暴露 file_token field —— 自拼 multipart
飞书 Go SDK v3.5.3 的 UploadAllFileReqBody 只暴露 file_name / parent_type / parent_node / size / checksum / file,没有 file_token 字段。
但官方 API POST /open-apis/drive/v1/files/upload_all 是支持 file_token 的:带 file_token 时覆盖原文件保留 token、刷新 version/size;不带时按 parent_type+parent_node 在指定目录新建。
为绕开 SDK 限制,internal/client/markdown.go:OverwriteFileWithToken 用 client.Post + *larkcore.Formdata 自己拼 multipart(translator 检测到 *Formdata 会自动切到 FileUpload 多部分序列化路径,见 SDK core/reqtranslator.go:payload)。endpoint 仍是官方 upload_all,参考 lark-cli shortcuts/markdown/helpers.go:uploadMarkdownFileAll 的写法。
2. 文件大小上限 ≤ 20MB
upload_all 单次上传 API 上限 20MB,本命令组只走单次上传路径:
markdown create 调 client.UploadFileWithToken(> 20MB 时该函数会自动切到分片管线,但 markdown create 主场景是文本 .md 远小于 20MB)
markdown overwrite 只支持 ≤ 20MB 小文件,大文件覆盖需要分片接口,本命令组未实现。如果是几十 MB 的 .md(罕见,比如海量日志贴)走 drive upload 分片管线创建新文件,无法保留原 file_token。
markdown diff 另有独立的行数/矩阵上限(与 20MB 上传上限无关,防 LCS 矩阵 OOM):
- 单侧 ≤ 20000 行:任一侧超过即报错
内容过大(N 行 / M 行),单侧超过行数上限 20000,建议用外部 diff 工具
- 两侧行数乘积 ≤ 2000 万单元:LCS 用完整
(n+1)×(m+1) int 矩阵,内存随两侧行数乘积增长;乘积超限报错 内容过大(N × M 行),LCS 矩阵超过 20000000 单元上限(防 OOM),建议用外部 diff 工具
- 两条都在下载完成、计算 LCS 之前拦截(
cmd/markdown_diff.go:checkDiffSize)。长 .md(海量日志贴、全量代码 dump)跑 diff 命中该报错时,改用本地 diff / git diff 等外部工具,或先用 markdown fetch 落盘两侧再外部比对。
3. .md 后缀强制校验
--name(create)和 --name(overwrite)都强制 .md 结尾(lowercase 检查后缀),非 .md 直接报错。如果想存 .markdown / .mdx 走 drive upload 老路径。
4. 空内容直接报错
--content "" / 空 --content-file 都被拒绝("Markdown 内容为空,不支持创建/覆盖为空文件")。需要"清空"语义的话走 markdown overwrite --content " " 写一个占位空格。
5. fetch 的路径输出与 JSON 输出分离
markdown fetch 使用 --output-path 保存到本地,使用 -o json 输出 JSON:
--output-path ./x.md → 落盘到 ./x.md
-o json → 把 {file_token, content, size_bytes} 打到 stdout
- 两者可叠加:落盘 + 同时 stdout JSON 摘要
6. --output-path 是目录时的兜底文件名
markdown fetch --output-path ./downloads/ 会拼成 ./downloads/<fileToken>.md(不从响应头解析原文件名,与 drive download 行为一致)。要保留原文件名请自己加查询步骤再拼路径。
何时转走 doc import/export
- 要飞书 docx 渲染体验(人读、表格分行、Mermaid 转画板、Callout 高亮)→
doc import(参 feishu-cli-import skill)
- 要从飞书 docx 落盘到 Git 仓库(块解析回 markdown)→
doc export(参 feishu-cli-export skill)
- 要把 Markdown 转换前 sanity check(Mermaid 花括号、表格 9×9 限制、Callout 6 种类型)→
../feishu-cli-import/references/doc-guide.md
何时转走 drive upload
- 大文件 > 20MB(罕见,但比如全量代码 dump、长 log)→
feishu-cli drive upload --file xxx.md 走分片,会创建新 file_token
- 非
.md 扩展名(.mdx / .markdown / .txt)→ drive upload
权限要求
| 命令 | 所需 scope |
|---|
markdown create | drive:file:upload(或 drive:drive) |
markdown fetch | drive:file:download(或 drive:drive) |
markdown overwrite | drive:file:upload + drive:drive.metadata:readonly;且 User Token 对目标文件有编辑权限 |
markdown diff | drive:file:download(或 drive:drive) |
推荐做法:执行 feishu-cli auth login 登录后,由 auth check --scope "drive:file:upload drive:file:download" 预检;缺 scope 时按提示 auth login 补申请。
典型工作流
工作流 A:AI agent 每天迭代同一份 .md
FT=$(feishu-cli markdown create --name daily-summary.md --content-file ./summary.md -o json | jq -r '.file_token')
echo "$FT" > ~/.cache/daily-summary.token
feishu-cli markdown overwrite --file-token "$(cat ~/.cache/daily-summary.token)" --content-file ./summary.md
工作流 B:从云盘 .md 落盘到本地
feishu-cli markdown fetch --file-token boxcnxxx --output-path ./local.md --overwrite
feishu-cli markdown overwrite --file-token boxcnxxx --content-file ./local.md
工作流 C:与 doc import 协作(先落盘 .md 再批量 import)
feishu-cli markdown create --name design.md --content-file ./design.md --folder-token fldxxx
feishu-cli doc import ./design.md --title "设计稿" --upload-images
注意事项
- 默认 User Access Token:所有
markdown 命令未登录时统一提示 feishu-cli auth login
- 不做 Markdown 转换:本命令组保留
.md 字节流不变,不做飞书 docx 块转换,不触发图床、画板渲染、Callout 等增强逻辑——需要这些走 doc import
upload_all 单次 ≤ 20MB:create / overwrite 共用 endpoint,大文件覆盖未实现,绕路走 drive upload(创建新 file_token)
fetch 输出参数:路径走 --output-path,格式走 -o/--output
.md 后缀强制、空内容拒绝两条已经在「核心 flag」与「踩坑」章节出现,这里不再重复,详见 overwrite flag 表 和 踩坑 §3/§4。
常见错误
实际报错示例(参 cmd/markdown_overwrite.go + internal/client/markdown.go:OverwriteFileWithToken):
| 触发条件 | 错误信息 | 排查方向 |
|---|
| User Token 对目标文件无编辑权限 | 覆盖文件失败: code=<非 0>, msg=<飞书返回>(常见 1061004 forbidden、1061045 no permission) | 在飞书云盘右键文件 → 共享 → 给当前用户加「可编辑」;或换文件所有者的 token;或检查 scope drive:file:upload 是否已授予 |
文件大小超 20MB(--content) | --content 大小 <N> 字节超过 20MB API 上限 | 切分文件或换 drive upload(会创建新 file_token,分享链接失效) |
文件大小超 20MB(--content-file) | --content-file 大小 <N> 字节超过 20MB API 上限,请切分或用多次 fetch+overwrite | 同上;CLI 在读盘前 os.Stat 已拦截,不浪费上传带宽 |
--name 不以 .md 结尾 | --name 必须以 .md 结尾,得到 "xxx.txt" | 改 .md 后缀;.mdx / .markdown 必须走 drive upload |
--content-file 路径不存在 | 读取本地文件失败: open <path>: no such file or directory | 检查路径是否相对当前目录;建议传绝对路径 |
--content-file 是目录 | --content-file 必须指向文件,不是目录 | 指向具体 .md 文件 |
既给 --content 又给 --content-file | --content 与 --content-file 不能同时使用 | 二选一 |
| 都没给 | 请提供 --content 或 --content-file | 至少传一个 |
--content 模式漏 --name | 使用 --content 时必须提供 --name 指定远端文件名(保留原名请加 --name <现有文件名>.md) | 显式 --name existing.md(不会自动 fallback fileToken.md) |
| 内容为空字节 | Markdown 内容为空,不支持把 .md 覆盖为空文件 | 要清空语义传 --content " "(占位空格) |
| HTTP 层异常 | 覆盖文件失败: HTTP <status>, body: <raw> | 网络/代理问题,附带原始 body 便于排查 |
| 响应解析失败 | 解析覆盖响应失败: <json error> | 飞书侧返回非 JSON(极罕见,通常网关错误页) |
权限不足时飞书 API 直接返回 code != 0 的业务错(如 1061004、1061045),CLI 层透传 msg 字段;若同时缺 scope 会在 auth check 阶段就拦截,建议执行前先跑 feishu-cli auth check --scope "drive:file:upload"。
与老命令的对照
| 老命令 | 新 markdown 命令 | 差异 |
|---|
drive upload --file x.md | markdown create --content-file x.md | markdown 强制 .md 后缀 + 空内容校验 + AI agent 友好 |
drive download --file-token xxx | markdown fetch --file-token xxx | markdown 默认打印 stdout(文本场景)+ 目录路径自动拼 .md |
| 无 | markdown overwrite | 老命令没有覆盖语义(只能"删了重建",file_token 变化) |
老 drive upload/download 仍然可用(二进制、非 .md 走老路径),新能力集中在 markdown overwrite(保 file_token 覆盖)。
v1 PR quality-pass 加固
drive/v1/files/upload_all 单次上传 ≤ 20MB(create / overwrite 共用 endpoint)。CLI 层在 --content / --content-file 都做 pre-check,超过直接报错不浪费带宽
overwrite 必须显式 --name(用 --content 时):不再 fallback <fileToken>.md 默默改名远端文件;保留原名请显式 --name <existing>.md