skills/feishu-cli-markdown/SKILL.md
飞书云盘原生 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 版本"时使用。
npx skillsauth add riba2534/feishu-cli feishu-cli-markdownInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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 缩进、当图床/密集代码块/版本管理用 |
判断走哪条:
doc import.md 文本 unchanged 保留在云盘(AI agent 读回完全一致;docx 块解析有损)→ markdown create.md、保持 file_token 不变(分享链接持久)→ markdown overwritemarkdown 命令默认走 User Access Token(执行 feishu-cli auth login 登录)feishu-cli auth check --scope "drive:file:upload drive:file:download"markdown create — 上传新 .md 到云盘# 从字符串内容创建
feishu-cli markdown create --name plan.md --content "# Plan\n\n- todo 1"
# 从本地文件创建(--name 缺省时取本地 basename)
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
# JSON 输出(拿 file_token 给后续步骤)
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)。
markdown fetch — 下载云盘 .md# 打印到 stdout(缺省 --output-path 时,行为与 lark-cli markdown +fetch 一致)
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
# JSON 输出(含 content 字符串)
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 字符串 |
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
# 覆盖 + 改名(必须 .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)优于"删了重建"。
markdown diff — 本地比对远端最新/历史版本(只读,不改远端)下载远端 Markdown 内容并在本地计算 unified diff,不修改远端文件。三种比对模式(由参数组合决定,互斥):
# 模式 1:远端最新 vs 本地文件
feishu-cli markdown diff --file-token boxcnxxx --file ./local.md
# 模式 2:远端某版本 vs 远端最新
feishu-cli markdown diff --file-token boxcnxxx --from-version 3
# 模式 3:远端版本 A vs 版本 B(--to-version 需配合 --from-version)
feishu-cli markdown diff --file-token boxcnxxx --from-version 2 --to-version 5 --context-lines 1
# 结构化输出 + 内置 jq 过滤(--format/--jq 与其它新命令一致;-o json 作兼容别名)
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 # 兼容写法,等价 --format 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", // 比对模式:local_vs_remote(模式 1)/ remote_vs_remote(模式 2/3)
"from": "remote (latest)", // 左侧来源名(模式 1="remote (latest)";模式 2/3="remote@version=N")
"to": "local: ./local.md", // 右侧来源名(模式 1="local: <path>";模式 2="remote (latest)";模式 3="remote@version=N")
"size_bytes_before": 1024, // 左侧内容字节数
"size_bytes_after": 1088, // 右侧内容字节数
"identical": false, // 是否无差异(等价于 hunks 为空)
"added_lines": 5, // 新增行数(统计所有 hunk 中 op="+")
"removed_lines": 2, // 删除行数(统计所有 hunk 中 op="-")
"hunks": [ // unified diff 的 hunk 数组(无差异时为空数组)
{
"old_start": 10, "old_lines": 4, // 左侧起始行 / 行数
"new_start": 10, "new_lines": 7, // 右侧起始行 / 行数
"lines": [
{ "op": " ", "text": "上下文行" }, // op: " "=未变 / "-"=删除 / "+"=新增
{ "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,避免误覆盖。
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 的写法。
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):
内容过大(N 行 / M 行),单侧超过行数上限 20000,建议用外部 diff 工具(n+1)×(m+1) int 矩阵,内存随两侧行数乘积增长;乘积超限报错 内容过大(N × M 行),LCS 矩阵超过 20000000 单元上限(防 OOM),建议用外部 diff 工具cmd/markdown_diff.go:checkDiffSize)。长 .md(海量日志贴、全量代码 dump)跑 diff 命中该报错时,改用本地 diff / git diff 等外部工具,或先用 markdown fetch 落盘两侧再外部比对。.md 后缀强制校验--name(create)和 --name(overwrite)都强制 .md 结尾(lowercase 检查后缀),非 .md 直接报错。如果想存 .markdown / .mdx 走 drive upload 老路径。
--content "" / 空 --content-file 都被拒绝("Markdown 内容为空,不支持创建/覆盖为空文件")。需要"清空"语义的话走 markdown overwrite --content " " 写一个占位空格。
fetch 的路径输出与 JSON 输出分离markdown fetch 使用 --output-path 保存到本地,使用 -o json 输出 JSON:
--output-path ./x.md → 落盘到 ./x.md-o json → 把 {file_token, content, size_bytes} 打到 stdout--output-path 是目录时的兜底文件名markdown fetch --output-path ./downloads/ 会拼成 ./downloads/<fileToken>.md(不从响应头解析原文件名,与 drive download 行为一致)。要保留原文件名请自己加查询步骤再拼路径。
doc import/exportdoc import(参 feishu-cli-import skill)doc export(参 feishu-cli-export skill)../feishu-cli-import/references/doc-guide.mddrive uploadfeishu-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 补申请。
.md# Day 1:创建一次拿到 file_token
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
# Day 2+:覆盖(file_token 不变,分享链接持久)
feishu-cli markdown overwrite --file-token "$(cat ~/.cache/daily-summary.token)" --content-file ./summary.md
.md 落盘到本地# 读回原汁原味 Markdown 源码
feishu-cli markdown fetch --file-token boxcnxxx --output-path ./local.md --overwrite
# 编辑后写回
feishu-cli markdown overwrite --file-token boxcnxxx --content-file ./local.md
doc import 协作(先落盘 .md 再批量 import)# 上游产出 .md 到云盘(保留源码备份)
feishu-cli markdown create --name design.md --content-file ./design.md --folder-token fldxxx
# 下游再走 doc import 生成飞书 docx 给团队阅读
feishu-cli doc import ./design.md --title "设计稿" --upload-images
markdown 命令未登录时统一提示 feishu-cli auth login.md 字节流不变,不做飞书 docx 块转换,不触发图床、画板渲染、Callout 等增强逻辑——需要这些走 doc importupload_all 单次 ≤ 20MB:create / overwrite 共用 endpoint,大文件覆盖未实现,绕路走 drive upload(创建新 file_token)fetch 输出参数:路径走 --output-path,格式走 -o/--output
.md后缀强制、空内容拒绝两条已经在「核心 flag」与「踩坑」章节出现,这里不再重复,详见overwriteflag 表 和踩坑 §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 覆盖)。
drive/v1/files/upload_all 单次上传 ≤ 20MB(create / overwrite 共用 endpoint)。CLI 层在 --content / --content-file 都做 pre-check,超过直接报错不浪费带宽overwrite 必须显式 --name(用 --content 时):不再 fallback <fileToken>.md 默默改名远端文件;保留原名请显式 --name <existing>.mdtools
往飞书文档里插入/更新/读取/删除「妙笔BOX」HTML 小组件块——飞书文档里**唯一能跑动画和可交互内容**的载体。 把一整页 HTML(CSS/JS)塞进块,在 iframe 沙箱里真实执行:CSS @keyframes 动画、ECharts/Three.js 图表、 Canvas、可拖拽力导向图、Dashboard、打字机/进度条/状态机动画等都能动。 当用户请求"飞书文档里做动画/能动的图/可交互图表/数据大屏/Dashboard/真实地图/地理飞线/3D 图表(map3D、3D 曲面、Three.js)/在飞书文档里放 ECharts 可视化/批量做一套图表演示"、"妙笔BOX"、"HTML 小组件"、 "在飞书文档里跑 ECharts/CSS 动画/JavaScript"、"嵌入网页/HTML 到飞书文档"、"飞书文档里的图怎么动起来"时使用。 注意:要"动"必须用本技能(妙笔BOX);画板(feishu-cli-board)的 SVG 节点会被服务端栅格化成静态图,不会动。
tools
飞书 OpenAPI 裸调。api GET/POST/PUT/DELETE/PATCH <path> 直接调用任意飞书 OpenAPI 接口, 覆盖 feishu-cli 尚未封装的接口(对齐 lark-cli 的 api 能力)。支持 --params(query)/--data(body JSON)/--data-file(从文件读 body)/ --as auto|user|bot 身份/--dry-run 预览/-o 二进制下载/--format/--jq。 当用户请求"调用 X API"、"裸调飞书接口"、"feishu-cli 没封装的接口怎么调"、"raw api"、 "用 api 命令发请求"、"下载飞书媒体/文件 binary"时使用。 不适用:仅查 schema 不调用(用 feishu-cli schema);已有专用命令的高频场景(用对应 feishu-cli <模块>)。
tools
飞书电子表格高级能力(筛选视图 + 筛选条件 + 下拉单元格 + 浮动图片 + 批量样式)。 filter-view CRUD 管理筛选视图,filter-view condition CRUD 写筛选条件(V3 API); dropdown set/get/update/delete 管理单元格下拉框(V2 dataValidation); image get/update/media-upload/write-image 操作浮动图片与单元格写图; batch-set-style 批量设置多范围单元格样式。 基础读写(read/write/style/add-rows/add-sheet)仍在 feishu-cli 主命令 sheet/bitable, 本 skill 专注高级能力。 当用户请求"筛选视图"、"筛选条件"、"加下拉框"、"数据验证"、"列下拉"、"浮动图片"、"插入图片"、"批量样式"时使用。
tools
飞书 OpenAPI 方法本地浏览 + 通用 API 透传调用。schema `service.resource.method` 三段式查路径/参数/scope(无需联网); `feishu-cli api <method> <path>` 直接调任意端点(v1.29+,覆盖 2500+ 未封装 API)。 当用户请求"飞书有没有 XX API"、"X API 的参数是什么"、"X 方法需要什么 scope"、 "OpenAPI 方法浏览"、"看 SDK 怎么调用"、"调一个没封装的飞书 API"时使用。 不适用:查在线最新 schema(请用 OpenAPI Explorer,本地元数据可能落后于飞书开放平台)。