SuperAPI AI Directory

feishu-cli-markdown

Office & productivity

View repository

Updated 10-16 01:48

About this skill

Feishu Drive native Markdown (markdown create/fetch/overwrite/diff)

markdown The command group treats the .md as an ordinary file for whole-file read/write, preserving the original Markdown source,does not perform Markdown ↔ Feishu docx block conversion.

feishu-cli: if not yet installed, please go to riba2534/feishu-cli to get the installation method.

Core concept: the difference from doc import / doc export difference

Command Behavior Type created Use case
doc import Markdown → Feishu docx blocks (headings/lists/tables/Callout/Mermaid whiteboard…) docx (online collaborative document) For people to read, needs formatting, needs team comments
doc export docx → Markdown (blocks parsed back to markdown source) Local .md Save from Feishu docx to disk into Git
markdown create/fetch/overwrite Upload/download the .md as a whole,without conversion file (ordinary Drive file) AI agent writes the original .mddirectly to disk, preserving fenced code block indentation, for use as an image host/dense code blocks/version management

Deciding which path to take:

  • Want Feishu docx rendering (human-readable, formatting, table chunking, whiteboard rendering) → doc import
  • Want the original .md text preserved unchanged on Drive (AI agent reads back exactly the same; docx block parsing is lossy) → markdown create
  • Want to repeatedly overwrite the same .md, keeping file_token unchanged (share link persists) → markdown overwrite

Prerequisites

  • Authentication: all markdown commands default to User Access Token(run feishu-cli auth login login)
  • Precheck:feishu-cli auth check --scope "drive:file:upload drive:file:download"

Command quick reference

1. markdown create — upload a new .md to Drive

# 从字符串内容创建
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

Key flag:

flag Description
--name Remote filename,must .md end with。create:--content required when,--content-file can be omitted to use the local basename when.overwrite:--content required when,--content-file can be omitted to use the local basename when; explicitly passing it = also renames
--content String content (choose one of the two with --content-file )
--content-file Local .md file path
--file Official lark-cli compatible alias, equivalent to --content-file
--folder-token Target folder (defaults to Drive root)
-o json JSON output (including file_token / file_name / size_bytes)
--user-access-token Override login state

Validation: empty content errors out directly (creating an empty .md)。

2. markdown fetch — download a Drive .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

Key flag:

flag Description
--file-token Markdown file token (required)
--output-path Local save path;when a directory, append <fileToken>.md; defaults to printing to stdout
--overwrite Overwrite when the local file already exists (defaults to erroring out directly)
-o, --output json JSON output; when --output-path is not passed, includes content string

3. markdown overwrite — overwrite existing .md (preserve 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

Key flag:

flag Description
--file-token Target file token (required)
--content / --content-file New content (choose one of two)
--name Filename after overwrite (.md ending;--content required when;--content-file defaults to local basename)
-o json JSON output

Core value:file_token remains unchanged → share links persist, links bookmarked by others don't break; for multi-iteration scenarios (an AI agent updating the same .md) is better than "delete and recreate".

4. markdown diff — compare locally against the remote latest/historical version (read-only, does not modify the remote)

Download the remote Markdown content and compute a unified diff locally,does not modify the remote file. Three comparison modes (determined by parameter combinations, mutually exclusive):

# 模式 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

Key flag:

flag Description
--file-token Target Markdown file token (required)
--file Local .md path (mode 1: compare with remote latest)
--from-version Starting remote version number (mode 2/3)
--to-version Target remote version number (mode 3, requires --from-version)
--context-lines Number of unchanged context lines to keep above and below each hunk (default 3)
--dry-run Only print the comparison plan, do not download/compare
--format json|pretty|table|ndjson|csv structured output; by default prints unified diff text
--jq Built-in gojq to filter structured output (e.g. --jq '.added_lines', no external jq needed)
-o json Compatible alias, equivalent to --format json

Structured output structure(--format json / -o json, 9 top-level fields, can directly --jq get change count / determine consistency):

{
  "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": "新内容" }
      ]
    }
  ]
}

Common built-in --jq(no external jq needed):--jq '.identical' determine whether consistent,--jq '.added_lines, .removed_lines' get change count,--jq '.hunks[].lines[] | select(.op=="+") | .text' pull all added lines.

With overwrite combined: before overwriting, first diff --file ./local.md preview local changes relative to remote latest, then overwrite after confirmation, to avoid accidental overwrites.

Underlying implementation & pitfalls

1. SDK does not expose file_token field —— build multipart yourself

Feishu Go SDK v3.5.3's UploadAllFileReqBody only exposes file_name / parent_type / parent_node / size / checksum / file,no file_token field.

But the official API POST /open-apis/drive/v1/files/upload_all does support file_token :With file_token it overwrites the original file preserving the token and refreshing version/size; without it, creates a new file in the specified directory by parent_type+parent_node Create in the specified directory。

To bypass the SDK limitation,internal/client/markdown.go:OverwriteFileWithToken uses client.Post + *larkcore.Formdata to build multipart itself (the translator detects *Formdata and automatically switches to the FileUpload multipart serialization path, see SDK core/reqtranslator.go:payload). The endpoint is still the official upload_all, referring to the lark-cli shortcuts/markdown/helpers.go:uploadMarkdownFileAll implementation.

2. File size limit ≤ 20MB

upload_all Single upload API limit 20MB, this command group only uses the single upload path:

  • markdown create calls client.UploadFileWithToken(when > 20MB this function automatically switches to the chunked pipeline, but markdown create main scenario is text .md far smaller than 20MB)
  • markdown overwrite only supports small files ≤ 20MB. Overwriting large files requires the chunked upload API, which this command group does not implement. If it is a few tens of MB .md(rare, e.g., massive log posts), use the drive upload chunked pipeline to create a new file, which cannot preserve the original file_token.

markdown diff There is also a separate line count/matrix limit(unrelated to the 20MB upload limit, to prevent LCS matrix OOM):

  • one side ≤ 20000 lines: if either side exceeds it, an error is reported 内容过大(N 行 / M 行),单侧超过行数上限 20000,建议用外部 diff 工具
  • the product of the line counts of both sides ≤ 20 million cells: LCS uses a full (n+1)×(m+1) int matrix, and memory grows with the product of the line counts of both sidesproductgrowth; if the product exceeds the limit, an error is reported 内容过大(N × M 行),LCS 矩阵超过 20000000 单元上限(防 OOM),建议用外部 diff 工具
  • both are intercepted after download completes and before computing the LCS before(cmd/markdown_diff.go:checkDiffSize). Long .md(massive log posts, full code dumps) when running diff and hitting this error, switch to local diff / git diff and other external tools, or first use markdown fetch to write both sides to disk and then compare externally.

3. .md suffix is strictly validated

--name(create) and --name(overwrite) both enforce a .md ending(lowercase suffix check); anything other than .md reports an error directly. If you want to store .markdown / .mdx use the drive upload old path.

4. Empty content reports an error directly

--content "" / empty --content-file are both rejected ("Markdown content is empty; creating/overwriting as an empty file is not supported"). If you need "clear" semantics, use markdown overwrite --content " " to write a placeholder space.

5. fetch path output and JSON output are separated

markdown fetch uses --output-path to save locally, and uses -o json to output JSON:

  • --output-path ./x.md → write to disk at ./x.md
  • -o json → print {file_token, content, size_bytes} to stdout
  • The two can be combined: write to disk + also output a JSON summary to stdout

6. --output-path fallback filename when it is a directory

markdown fetch --output-path ./downloads/ will be concatenated into ./downloads/<fileToken>.md(does not parse the original filename from the response headers, consistent with drive download behavior). To preserve the original filename, add a query step yourself and then build the path.

When to switch to doc import/export

  • If you want the Feishu docx rendering experience(human-readable, table rows, Mermaid converted to boards, Callout highlighting) → doc import(see feishu-cli-import skill)
  • If you want to write Feishu docx to disk into a Git repository(block parsing back to markdown) → doc export(see feishu-cli-export skill)
  • If you want to sanity check Markdown before conversion(Mermaid braces, table 9×9 limit, 6 Callout types) → ../feishu-cli-import/references/doc-guide.md

When to switch to drive upload

  • Large files > 20MB(rare, but e.g. full code dumps, long logs) → feishu-cli drive upload --file xxx.md uses chunking, which creates a new file_token
  • Non- .md extension(.mdx / .markdown / .txt)→ drive upload

Permission requirements

Command Required scope
markdown create drive:file:upload(or drive:drive)
markdown fetch drive:file:download(or drive:drive)
markdown overwrite drive:file:upload + drive:drive.metadata:readonly; and the User Token has edit permission on the target file
markdown diff drive:file:download(or drive:drive)

Recommended practice: after executing feishu-cli auth login to log in, use auth check --scope "drive:file:upload drive:file:download" for precheck; if a scope is missing, follow the prompt to auth login apply for it.

Typical workflow

Workflow A: An AI agent iterates on the same .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

Workflow B: From cloud drive .md write to local

# 读回原汁原味 Markdown 源码
feishu-cli markdown fetch --file-token boxcnxxx --output-path ./local.md --overwrite

# 编辑后写回
feishu-cli markdown overwrite --file-token boxcnxxx --content-file ./local.md

Workflow C: With doc import collaborate (first write .md then batch 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

Notes

  • Default User Access Token: all markdown commands uniformly prompt when not logged in feishu-cli auth login
  • No Markdown conversion: this command group preserves .md byte stream unchanged,does notperform Feishu docx block conversion,does nottrigger enhancement logic such as image hosting, whiteboard rendering, Callout, etc. — for these use doc import
  • upload_all single ≤ 20MB:create / overwrite share the endpoint, large file overwrite is not implemented, work around with drive upload(creates a new file_token)
  • fetch output parameters: paths use --output-path, formats use -o/--output

.md suffix mandatory, empty content rejection are already covered in the "Core flags" and "Pitfalls" sections and will not be repeated here, see overwrite flag table and 踩坑 §3/§4。

common errors

actual error examples (see cmd/markdown_overwrite.go + internal/client/markdown.go:OverwriteFileWithToken):

trigger condition error message troubleshooting direction
User Token has no edit permission on the target file 覆盖文件失败: code=<非 0>, msg=<飞书返回>(common 1061004 forbidden、1061045 no permission) In Feishu cloud drive, right-click the file → Share → grant the current user "Can edit"; or switch to the file owner's token; or check whether scope drive:file:upload has been granted
File size exceeds 20MB (--content) --content 大小 <N> 字节超过 20MB API 上限 split the file or switch to drive upload(will create a new file_token, share link becomes invalid)
File size exceeds 20MB (--content-file) --content-file 大小 <N> 字节超过 20MB API 上限,请切分或用多次 fetch+overwrite same as above; CLI os.Stat already intercepted before reading the disk, no upload bandwidth wasted
--name does not end with .md ending --name 必须以 .md 结尾,得到 "xxx.txt" change to .md suffix;.mdx / .markdown must use drive upload
--content-file path does not exist 读取本地文件失败: open <path>: no such file or directory Check whether the path is relative to the current directory; passing an absolute path is recommended
--content-file is a directory --content-file 必须指向文件,不是目录 point to a specific .md file
both provided --content and provided --content-file --content 与 --content-file 不能同时使用 choose one
neither provided 请提供 --content 或 --content-file provide at least one
--content mode missing --name 使用 --content 时必须提供 --name 指定远端文件名(保留原名请加 --name <现有文件名>.md) explicit --name existing.md(will not automatically fallback fileToken.md)
content is empty bytes Markdown 内容为空,不支持把 .md 覆盖为空文件 for clearing semantics pass --content " "(placeholder space)
HTTP layer exception 覆盖文件失败: HTTP <status>, body: <raw> network/proxy issue, includes raw body for troubleshooting
response parsing failed 解析覆盖响应失败: <json error> Feishu side returned non-JSON (extremely rare, usually a gateway error page)

when permission is insufficient the Feishu API directly returns code != 0 business error (e.g. 1061004、1061045), CLI layer passes through msg field; if scope is also missing it will be intercepted at the auth check stage, it is recommended to run feishu-cli auth check --scope "drive:file:upload"。

comparison with old commands

Old command New markdown command Difference
drive upload --file x.md markdown create --content-file x.md markdown mandatory .md suffix + empty content validation + AI agent friendly
drive download --file-token xxx markdown fetch --file-token xxx markdown prints to stdout by default (text scenario) + directory path automatically joined .md
None markdown overwrite Old commands have no overwrite semantics (can only "delete and recreate", file_token changes)

Old drive upload/download is still available(binary, non- .md uses the old path), new capabilities are concentrated in markdown overwrite(preserves file_token for overwrite).

v1 PR quality-pass hardening

  • drive/v1/files/upload_all single upload ≤ 20MB(create / overwrite share the endpoint). The CLI layer does a pre-check in --content / --content-file both, and errors out directly if exceeded without wasting bandwidth
  • overwrite must explicitly --name(when using --content ): no longer falls back to <fileToken>.md silently renaming the remote file; to keep the original name, explicitly --name <existing>.md