Mirror the contents of a Feishu knowledge base to a local Markdown knowledge base. The default direction is Feishu → local/Git/Obsidian/VitePress; this skill does not write to Feishu by default, and two-way sync must first establish document ownership, conflict policy, and write permissions.
Customer-readable description
What this skill can do
| Customer wants | Skill will do | Customer can see |
|---|---|---|
| Sync Feishu knowledge base to local | Traverse knowledge space nodes, read readable documents, and generate Markdown | Local mirror directory, directory hierarchy, source links, sync manifest |
| Back up to Git | Place generated content into the customer-specified Git repository and check differences | commit/push receipt, file changes, and failure list |
| View with Obsidian | Save rules, mirrors, and local supplements in a separate vault | A vault that can be opened directly with Obsidian |
| Display with VitePress | Generate the site sidebar and build a static site | Local development server or build artifact |
| Check spreadsheet/bitable export capabilities | Parse actual resource types, permissions, and available export formats | Read-only probe results; will not generate Excel files by default |
| Sync standalone or embedded Sheets | Read Sheets within the scope explicitly authorized by the private configuration; can automatically cover all standalone Sheet nodes, and embedded Sheets are identified from the document XML before reading | Bounded Markdown table snapshots; no longer write authorized Sheet nodes as body placeholders |
| Preserve Sheet formulas, styles, comments, and chart information | Save metadata snapshots of cells, layout, charts, and floating images for the selected range | Local fidelity JSON alongside Markdown tables; not disguised as an editable workbook |
| Initialize complete Sheets and reports | After confirmation, export the entire Sheet workbook in batches | .xlsx Fidelity copy, preserving formulas, styles, comments, charts, pivot tables, and cell images |
| Mirror specified bitables | Read fields, limited records, and optional views of specified Base tables, and generate Markdown and snapshots | Bounded table content, schema/record snapshots; by default does not read any Base data |
| Initialize bitables and multi-person reports | After confirmation, export the complete Base in batches; selected tables can also read dashboard and report block metadata | .base Fidelity copy; dashboard snapshots are written to JSON, not disguised as an interactive web page |
| Localize assets and navigation | After confirmation, download document images/attachments, or download attachments of selected bitable records, and change internal document links and subpage lists to local navigation | Local assets, relative links, and optional subpage navigation |
| Initialize knowledge base files | After confirmation, download Wiki in batches file Raw binary of the node |
Local links; ZIP, DMG, EXE, etc. are only saved, never executed, mounted, or extracted |
| View sync changes | After configuration, generate a local change ledger and restricted diff of additions, modifications, moves, and remote deletions | Statistics, change list, and diff details for this sync |
| Plan bidirectional sync | Distinguish read-only mirrors, managed documents, and local supplements | Conflict/permission risk description, does not automatically overwrite Feishu |
How customers use it
- Confirm
soia-cwork-feishu-clihas completed Feishu app credential login, and the bot can read the target knowledge space. - Fill in the knowledge space ID, output directory, and source URL template in the local private configuration; do not commit the App Secret, token, or enterprise private paths to a public skill repository.
- For first use, run dry-run first to verify the space, node count, and target directory.
- First use an isolated single-node pilot to verify tables, resources, and style snapshots;
--pilot-node-tokenwrites only explicitly selected nodes to a separate pilot directory, and will not fill in placeholder files for other nodes in an empty directory. - Execute mirror sync. By default it only writes local files and sync metadata, does not modify Feishu content, and does not delete local history files.
- After sync writes, it automatically validates the manifest, file existence, frontmatter, failure placeholders, sidebar coverage, resource references, unarchived embedded Sheets, unarchived Sheets with embedded Base,
all_docxhistorical documents for which the XML semantic scan has not yet been completed in modeall_nodes, and standalone Sheets for which a real table has not yet been generated in modefailed/staleor semantic gaps are found, it returns a non-zero result; blank placeholders or partial content must not be treated as complete success. - If you need to inspect table export, first do a
drive +inspect/help/schema probe; a capability probe does not equal authorized export. - Only after the customer explicitly confirms the export scope, format, file count, and local directory, call
drive +exportordrive +export-download。 - If you need to mirror Sheets, first specify the scope in the private configuration: use
sync.sheets.enabled: trueandall_nodes: trueto automatically discover each grid subtable for all standalone Sheets, or specify item by item inselectionsthenode_token, stablesheet_id, and bounded A1range; both methods must set row, column, cell, and returned character limits. When the response reachesmax_chars, you must continue reading from the next row according toactual_range; truncation must not be treated as failure of the entire table. Mixed workbooks also require explicitly settinginclude_bitable_tabs: trueand the record limit, then routing Base subtables to Bitable reading. After enablingsync.embedded_sheets.enabledfor document-embedded Sheets, you must also chooseall_docx: trueornode_tokens. Enablesync.sheets.preserveon demand to save metadata such as formulas, styles, comments, layout, and charts. When not enabled, semantic gaps must be displayed and validated; you must not silently delete<sheet>or treat a standalone Sheet placeholder as complete archiving. - If you need to mirror Bitable, specify item by item the
sync.bitables.selectionsofnode_token、table_idandmax_records, then enablesync.bitables.enabledor pass in--sync-bitables; attachment binaries also require enablingdownload_attachmentsseparately, and dashboard/report metadata requiresinclude_dashboards。 - Only after the user explicitly confirms the source, format, file count, output directory, and Git strategy can the full initialization be executed: Sheet settings
sync.sheets.workbook_exports.enabled=trueandall_nodes=true, Base settingssync.bitables.base_exports.enabled=trueandall_nodes=true, Wiki file settingssync.files.downloads.enabled=trueandall_nodes=true. Each item must be configured withbatch_size, repeat execution until deferred is zero. - If offline resources, local jumps between documents, subpage navigation, or a change ledger are needed, first enable them item by item in the private configuration
download_assets、localize_internal_links、render_sub_page_navigation、change_ledger; they are disabled by default for compatibility with existing mirrors. - Before batch initialization, first pilot with a representative scope to confirm resource counts, failure categories, and local rendering; after synchronization is complete, then run Git diff, site build, and necessary manual spot checks.
Recommended command:
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --dry-run
# 试点只写所选节点到独立目录;仍会核验它是否属于当前知识库
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --output-dir <pilot-output-dir> \
--pilot-node-token <node_token> --incremental
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental
# 没有事件订阅时,按 wiki +node-get 的远端更新时间判断正文是否变化
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental --probe-remote-metadata
# 复用已完整正文,优先补独立 Sheet stub、未扫描的内嵌 Sheet 文档,再退避重试失败项
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --retry-failed
# 事件适配器已经拿到变动 ID 时,只拉对应节点;可重复传入多个 ID
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--changed-node-token <node_token> --changed-obj-token <obj_token>
# 只修复指定节点的本地格式,复用 manifest 中的其他文档,不重试历史失败项
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--rebuild-tree --rebuild-tree-only --only-node-token <node_token> --skip-assets
# 官方 webhook/长连接适配器写入 JSON/NDJSON 后,按事件目标增量拉取
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--event-file <events.ndjson>
# 仅在确认历史生成目录曾经扁平化时执行一次结构迁移
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --retry-failed --rebuild-tree
# 大型知识库遇到限流时分批补偿;重复执行直到 --validate-only 通过
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> \
--retry-failed --retry-batch-size 100 --skip-assets
# 如果只需要修复本地目录层级、暂时不请求飞书
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --rebuild-tree --rebuild-tree-only
# 从飞书刷新最新目录层级和兄弟节点顺序,但复用现有本地正文
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--rebuild-tree --refresh-tree-only
# 下载图片到本地镜像并把正文中的远程 URL 改成相对路径
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--download-assets
# 将私有配置中明确选择的 Sheet 范围渲染为 Markdown 表格
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--sync-sheets
# 将已授权文档中的嵌入式 Sheet 追加为有界 Markdown 表格快照
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--sync-embedded-sheets
# 将私有配置中明确选择的多维表格镜像为 Markdown 与 JSON 快照
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--sync-bitables
# 完整初始化仍使用同一同步命令;私有配置中 workbook_exports/base_exports/files.downloads
# 的 enabled 与 all_nodes 必须都为 true,并按 batch_size 分批重复执行
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --incremental \
--sync-sheets --sync-bitables
# 只校验最近一次同步生成的本地镜像,不访问飞书
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --validate-only
Three working modes
mirror: default mode. The knowledge base is the source, and the locally generated10_knowledge-base/should not be edited manually.local: only maintain the local20_本地补录/, which will not be overwritten by mirror synchronization and will not be automatically uploaded to Feishu.managed: intended in the future for explicitly designated bidirectional hosted documents. Write permissions, conflict rules, and publish actions must be confirmed document by document; the current script only provides a read-only mirror foundation and does not disguise it as completed bidirectional synchronization.
ID incremental synchronization and event push
node_tokenis the synchronization primary key, andobj_tokenis the object key for body reading and event mapping; title changes, moves, and duplicate names should not change these two IDs.--only-node-tokenis the single-document repair switch; when used together with--rebuild-tree-only, it only locates nodes from the existing manifest, does not re-traverse the Feishu tree, and will not retry other nodes because of their historical failures.- The first synchronization establishes a complete baseline, recording
obj_edit_time/remote_updated_atanddocs +fetchreturned byrevision_id。 - Subsequent
--incrementalwill still first rebuild the tree byparent_node_token, but will only read the document bodies of newly added, failed, event-hit, or remotely edited-time-changed documents; unchanged nodes reuse local Markdown. - Sibling node order directly preserves the Feishu order returned by
wiki +node-list, without re-sorting by title; therefore the VitePress/Obsidian directory should be consistent with the manual ordering of the Feishu knowledge base. - When there is no event target, by default use
wiki +node-getfor metadata probing; this generates many lightweight metadata requests, but avoids repeatedly downloading document bodies. For large spaces, you can switch to official event subscription and pass--event-file。 - Events only provide a hint of "which object may have changed" and cannot replace Wiki tree reconciliation; creation, deletion, title changes, and unrecognized events still require re-reconciling the node tree.
- Official event subscription, permissions, and
drive.file.*coverage boundaries are described in references/events.yml. Currentlylark-cli event listdoes not expose cloud documentdrive.file.*events, so this script does not claim to already be resident listening within the CLI; an external long connection/webhook adapter can pass JSON/NDJSON to--event-file。
Dependencies and installation
| Dependency | Type | Installation / configuration | What to do if missing |
|---|---|---|---|
soia-cwork-feishu-cli |
Hard dependency | Install and configure the official Feishu lark-cli app credentials |
Stop, first complete bot login and permission checks |
lark-cli |
Hard dependency | See soia-cwork-feishu-cli installation instructions |
Stop and report the installation command |
| Python 3.10+ | Hard dependency | Use system Python or project Python | Stop |
| PyYAML | Hard dependency | python3 -m pip install pyyaml |
Stop and report missing dependencies |
| Git | Optional enhancement | Install Git | Local images can still be generated, but cannot be committed/pushed |
| VitePress | Optional enhancement | Provided by the target documentation repository | Can still sync to Obsidian |
| Obsidian | Optional enhancement | Installed on the user's machine | Plain Markdown can still be generated |
Default location of private configuration:
~/.config/soia-skills/soia-open-skills/cwork/soia-cwork-feishu-doc-git-sync/config.yml
You can also use SOIA_CWORK_FEISHU_DOC_GIT_SYNC_CONFIG_FILE to specify the configuration file. Reference assets/config.example.yml。
Minimal configuration example:
version: 1
provider:
cli: lark-cli
profile: <configured-cli-profile>
brand: feishu
identity: bot
space:
id: <wiki-space-id>
source_url_template: https://<tenant>.feishu.cn/wiki/{node_token}
paths:
output_dir: <git-repository>/docs/feishu-knowledge
generated_dir: 10_<knowledge-base-name>
sync:
mode: mirror
prune: false
Permission recommendation: For the first round, request only read-only permissions for the knowledge base, documents, and Sheets. Downloading images and attachments is an optional enhancement; when cloud drive/export permissions are involved, request them separately; write-back bidirectional permissions are never used as default permissions.
Sync rules
- Only use
--as botread with the app identity; by default, no user identity token is required. - Traverse the knowledge space via node tokens, and use document tokens to read
docxcontent. paths.generated_dirmust use the real name or stable English name of the target knowledge base; for example10_后端技术支持库, do not nest another generalizedfeishu-knowledgedirectory.- If the existing output directory contains only one
10_*generated directory and the configuration does not specifypaths.generated_dir, the skill will reuse it; if multiple candidate directories are found, it will stop and require explicit configuration first, to avoid automatically creating duplicate directories. - Each generated Markdown records the source URL, space ID, node token, object token, parent node, and content hash.
- Use
sync-state.jsonto preserve the mapping from node tokens to local paths; when titles change, keep paths as stable as possible, and recalculate tree positions from the latestparent_node_token. manifest.jsonandsync-state.jsonrecordobj_edit_time、remote_updated_at、revision_id, used for incremental selection and auditing.- A single local path component is at most 48 characters; overly long titles keep the full title in frontmatter/sidebar, and a short node ID suffix is appended to the folder or file name to avoid overly long paths in Obsidian, macOS, and VitePress/Rollup.
- The synchronizer converts custom
figure/source/grid/callout, media tokens, and XML fragments exported from Feishu into a form parseable by Markdown/VitePress; this only changes local rendering and does not write back to Feishu. - Feishu document references are resolved into clickable Feishu Wiki links with
node_tokentaking priority andobj_tokenas fallback; user references are preserved as@显示名. Static Markdown does not replicate Feishu's hover cards and member avatar interactions, but it is no longer incorrectly downgraded to code style. - A Feishu node with child nodes must generate a directory with the same name and place the body in an index Markdown with the same name inside the directory:
父目录/节点名/节点名.md; only leaf nodes directly generate节点名.md. Do not generate a sibling "same-name file + same-name folder". - If Feishu itself has same-name leaves and expandable nodes, parent-child same names, or duplicate expandable nodes at the same level, a stable short node ID suffix will be appended to the directory/file; this is to avoid same-level conflicts in the local file system, and the manifest still uses
node_tokento distinguish real nodes. --retry-failedwill reuse existing complete bodies and add failed nodes, standalone Sheet stubs, and historical documents that have not completed XML semantic scanning to the compensation queue; suitable for continuing to fill in gaps after encountering Feishu API rate limits or enabling a new Sheet strategy.- If an old placeholder such as "the current synchronizer only reads the document body" is retained in the historical mirror, even if the Feishu node later changes from Sheet to Docx and the manifest status is still
ok, acceptance will still be judged as failed, and the highest-priority node in the--retry-failedqueue will be re-pulled; the old placeholder after a type drift must not be treated as successful body text. --retry-batch-size Nis used only with--retry-failed, compensating at most N failed or semantically missing nodes each time; large spaces should run it repeatedly, and end when--validate-only'sfailed_records=0、unmirrored_sheet_nodes=0、unscanned_embedded_sheet_documents=0is the termination condition.--retry-faileduses the previous manifest as the node list, and no longer performs metadata probing over all documents first; when the Sheet full strategy is enabled, it first fills in standalone Sheet stubs and historical Docx files missing the semantic scan marker, then retries ordinary failed items, and continues to be protected by global throttling. For large spaces, run it repeatedly with--retry-batch-sizeuntilunmirrored_sheet_nodesandunscanned_embedded_sheet_documentsare both 0.- Only one sync process is allowed in the same output directory at a time; if the previous round is still exiting or the user starts it repeatedly, subsequent processes will stop and report, and will not concurrently overwrite the manifest.
- Global request throttling and exponential backoff apply across sync workers;
sync.min_request_interval_secondsdefaults to 0.5 seconds, to avoid a large knowledge base concurrently triggering99991400rate limiting. - If body reading fails but old body text already exists locally, the node is marked as
staleand the old body text is retained; the next incremental sync will continue to retry, and validation will not treat it as successful. - Local acceptance runs after every non-dry-run sync;
--validate-onlycan independently recheck the most recent result. When acceptance fails, the exit code is 2, and a machine-readable summary is retained in thevalidationnode of the manifest. prune: falsedoes not delete local files corresponding to nodes that have disappeared; the nodes are marked as deleted in the manifest, to avoid data loss caused by a single permission or network anomaly.20_本地补录/and90_同步元数据/will not be overwritten by knowledge base sync.--rebuild-treeonly migratespaths.generated_dirold flat files generated by the synchronizer, and does not touch20_本地补录/。--rebuild-tree-onlyonly reuses the existing manifest and generated files to perform directory migration, and does not initiate Feishu body requests; if asset localization is also enabled, it may still read documents containing assets only to refresh expired media URLs.--refresh-tree-onlyre-reads the Feishu node tree and sibling order, and rebuilds the local directory and sidebar according to the latestparent_node_token, but reuses the existing local body text; when asset localization is enabled, it additionally refreshes documents that still contain unlocalized assets; it must be used together with--rebuild-tree.manifest.json/sync-state.json'stree_order: feishu_node_listindicates that the directory order comes from the Feishu node list, not title sorting.- Images retain remote URLs by default; after setting
sync.download_assets: trueor passing--download-assets, the skill downloads remote images in the body and<source token="...">media blocks topaths.generated_dir/_assets/, and rewrites Markdown/HTML references to relative paths. Downloaded Feishu attachment cards must further be rewritten as standard Markdown local links; if the link is wrapped by a separate<p>, that HTML container must also be removed, ensuring both Obsidian and VitePress can click to open it. Cards that were not downloaded still retain the original remote reference and report failure. - When downloading assets, deduplicate preferentially by Feishu media token; the same attachment or image is kept as only one local asset even if it has different short-lived signed URLs. Assets without a token are still addressed by URL content.
sync.localize_internal_links: true, already-synced Wiki/document references will be changed to relative local Markdown links;sync.render_sub_page_navigation: true, the Feishu-exported<sub-page-list>will be changed to local Markdown subpage navigation. Both are disabled by default and do not affect existing external link behavior.sync.change_ledger: true, a change ledger of additions, modifications, moves, and remote deletions generated per run will be produced under the sync metadata; modified items retain only the diff limited bychange_ledger_max_diff_lines, without copying the full document text, and without changing the generated mirror or the local supplemental directory.sheetBy default, only a metadata stub is generated. After the user explicitly authorizes a bounded snapshot of the entire library, setsync.sheets.enabled: trueandall_nodes: true, and the synchronizer will usesheets +workbook-infoto automatically discover the grid subtables of each standalone Sheet, then according tomax_rows、max_columns、max_cells、max_charscall within the upper limitssheets +csv-get; when the response is truncated, continue reading line by line according toactual_range. You can also continue to useselectionsto precisely declare a single range. Base subtables in a mixed workbook are read in a bounded manner according toinclude_bitable_tabs: trueonly whenmax_bitable_records, otherwise the node fails and cannot be silently skipped; if Base permissions are insufficient, the ordinary Sheets already read are still written to disk, but the node remainsfailedand an explicit gap marker is written.all_nodesAcceptance will reject anymetadata_stub, Sheet nodes missing a real table marker, or unarchived Base subtables. For the complete contract, see references/sheet-mirroring.yml。- Feishu Markdown may return only
<sheet>references without returning any cells. The synchronizer must not delete that reference and still report complete success: when drill-down is not authorized, keep the visible gap and make acceptance fail; only whensync.embedded_sheets.enabledandall_docx: trueornode_tokensis explicitly selected will the document XML be read and an "embedded table" snapshot be generated within the configured upper limits. Enabling it for the first time will backfill old documents; the snapshot will not be disguised as an interactive original table. For the complete scope contract, see references/sheet-mirroring.yml。 sync.sheets.preserve.enabled: truewill save, within the same selected range, a JSON snapshot of cell values, formulas, styles, comments, and worksheet layout, chart, and floating image metadata; pivot table, filter, conditional formatting, and sparkline metadata can additionally be enabled. It does not disguise charts or images as native Markdown. A complete workbook requires, after explicit user approval, setting bothsync.sheets.workbook_exports.enabled: trueandall_nodes: true, and the synchronizer will usedrive +exportto generate in batches.xlsxand link them in the Sheet index.- When the asynchronous Sheet export does not complete in the first polling round, the export task ticket will be saved in a private snapshot in the generated directory; subsequent batches only poll the same task, and once ready use
drive +export-downloadto download, without repeatedly creating the same workbook export task.manifest.stats.sheet_workbooks_pendingindicates the quantity still being processed on the Feishu side. - Image/attachment localization is an explicit opt-in local data download; you cannot download all assets just because the user only asks to "check images". The
sync.download_assets: truein the persistent configuration can only be regarded as the user's previous explicit authorization for that resource scope and must not be extended to authorization for Sheet or Bitable exports. If structural initialization of Sheet/Base/file is performed first and--skip-assetsis used to avoid output lock conflicts, a separate resource localization batch must be executed after initialization ends;--skip-assetswill not mark images or attachments as downloaded. bitableand unauthorizedsheetBy default, only a metadata stub is generated and no in-table data is read; a standalone Sheet withsync.sheets.all_nodesenabled is not allowed to retain a stub. After settingsync.bitables.enabled: true, you must declare item by itemnode_token、table_idandmax_records; the synchronizer usesbase +field-list、base +record-listand the optionalbase +view-list/base +dashboard-list/base +dashboard-block-listto generate Markdown and JSON snapshots.sync.bitables.download_attachments: trueis a separate binary download authorization, usingbase +record-download-attachmentand only processes read-record attachments of selected tables. A full Base export still requires explicitly setting bothsync.bitables.base_exports.enabled: trueandall_nodes: trueafter the user explicitly approves it; the synchronizer usesdrive +exportto generate in batches.base。- Wiki
filenodes generate only metadata stubs by default; after the user explicitly confirms a full initialization, set bothsync.files.downloads.enabled: trueandall_nodes: true, and only then will the synchronizer calldrive +downloadin batches. Files are saved as raw binaries and will not be executed, mounted, extracted, or parsed for ZIP, DMG, EXE, or other formats. - Feishu image URLs may expire quickly. When localization is enabled and
--refresh-asset-urlsis used, the URLs of associated documents are refreshed before downloading, and all body content is not unconditionally re-fetched; after download, they are uniformly written as local HTMLaltwith escaped<img>, and PNG/JPEG/GIF/WebP/SVG are identified by file binary, correcting incorrect cached suffixes. - Event targets,
--changed-*-token、--only-node-tokenor isolated pilot runs process only the resources of documents re-read in the current round, and will not re-add historical resources of the entire library to the download queue; a full resource backfill does not pass a target and runs in batches byasset_batch_size. - Image downloads use URL content-addressed filenames, and repeated syncs reuse existing resources; ordinary URLs in Markdown and
(<https://…>)angle-bracket URLs both enter the same download queue. You can useasset_workers、asset_timeout_seconds、asset_download_attempts、max_asset_bytesto limit concurrency, timeout, media retries, and single-file size; the default cap per resource is 50 MiB, and it can be lowered or raised in private configuration according to the confirmed download scope. Attachments carrying Feishu media tokens are retried within the cap on transient or unclassified CLI failures; clear insufficient permissions, nonexistent resources, orasset_too_largeare not retried blindly. Large existing mirrors can set a positive integerasset_batch_size: only that number of resources not yet written to disk are requested each time, and already downloaded resources are still localized and rewritten;asset_refreshed_batch_sizecan limit the number of new links downloaded from the same batch of refreshed documents. Run repeatedly and confirm completion with--validate-only. The manifest reports this round'sassets_deferred. Failed downloads keep only the original URL and are reported in theassets_failedcount in the manifest; auth URLs are not written to logs or manifests. <source token="...">or URL-less<img token="...">will call the officialdocs +media-download; when a remote URL cannot be read directly, you need to adddocs:document.media:downloadordrive:file:downloadaccording to the permission list, and enable local resource download in private configuration. Without download permission, you must not assume local resources are complete.
Security Rules
- Do not write App Secret, access token, or cookie into public skills, Obsidian vaults, Markdown, Git commits, terminal output, or final replies.
- Terminal logs, progress receipts, and final replies must not output local absolute paths, specific local filenames, operating system usernames, usernames, passwords, App Secret, access token, or private download URLs; use redacted placeholders uniformly, and report only status, counts, and error categories. See references/output-redaction.yml。
- Do not call Feishu create, update, or delete APIs by default.
drive +export、drive +export-download、docs +media-downloadand attachment downloads are both data export/download actions; when the user says "see if it can be exported," only do inspect, help, schema, or dry-run, and do not directly create local files.- Both Sheet and Base value mirrors are sensitive data written to disk: they can be enabled only after the user explicitly confirms the need to sync the selected range/table to the local workspace and writes a bounded
sync.sheets.selectionsor restrictedsync.bitables.selectionsin the private configuration; this confirmation does not authorize any workbook export, download of unselected attachments, or automatic Git commit. - Before an actual export, the source, type, format, estimated number of files, output directory, and Git tracking strategy must be clearly acknowledged; "checking capability" is not the same as "authorizing export".
- Exported files are by default placed in a temporary directory or a directory explicitly specified by the user; only a complete mirror initialization explicitly confirmed by the user may write to
_exports/or_assets/under the generated directory, and it must not automatically commit to Git, automatically push to a remote, or write back to Feishu. - Personal cloud drives or private resources that the bot has no permission to access must be reported as invisible, and it must not switch to user OAuth to read them on the user's behalf.
- Do not overwrite local supplementary data, delete historical files, or push to remote Git by default; these are write/publish actions that require explicit confirmation.
- Before execution, check the target repository, current branch, and remote address; if anything differs from expectations, stop and report.
Logs and completion acknowledgment
The terminal and final reply must report at least:
started: space, identity, configuration source, and target directory (do not print secrets).processed: number of nodes, documents, skipped items, and failures.created/updated: number of Markdown, manifest, and sidebar files generated or updated.skipped/failed: failed nodes, reasons, and whether they can be retried.verification: results of Git diff, VitePress build, sampled documents, and source link checks.next_step: whether additional permissions are needed, Git push needs confirmation, or bidirectional sync needs to be planned.- For file changes, report only the count and category, not local paths or file names; for identity, report only non-sensitive statuses such as
bot identity/user identity.
Resources
- Layered permissions and permission requests:references/permissions.yml
- Feishu CLI commands and permission request process: in the same repository,
soia-cwork-feishu-cli/references/cli-workflows.mdandsoia-cwork-feishu-cli/references/permissions.md。 - Event subscription and incremental targets:references/events.yml
- Sync strategy:references/sync-policy.yml
- Document format conversion:references/block-mapping.yml
- Sheet range mirroring:references/sheet-mirroring.yml
- Rich resource mirroring capabilities and boundaries:references/rich-resource-mirroring.yml
- Spreadsheet/Bitable export security policy:references/export-policy.yml
- Log and reply redaction policy:references/output-redaction.yml
- Git and VitePress integration:references/git-vitepress.yml
- Private configuration template:assets/config.example.yml
Validation
python3 scripts/sync_feishu_wiki.py --help
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --dry-run
python3 scripts/sync_feishu_wiki.py --config <private-config.yml> --validate-only
git diff --check
Forward test
Before a real sync, run a dry-run or a small authorized representative scope and verify the tree, stable node-ID mapping, ordering, incremental selection, asset references, and failure receipt. After every write, require the built-in validation gate to pass; a zero exit code alone is not evidence that the mirror is complete.