back
loading skill details...
飞书文档增量同步与表格操作指南。覆盖:表格更新策略(delete+insert)、header_row 设置、insert action、列宽算法、三阶段 diff 同步。当需要同步本地文档到飞书、操作飞书表格、或做增量编辑时激活。
---
name: feishu-doc-sync
description: |
飞书文档增量同步与表格操作指南。覆盖:表格更新策略(delete+insert)、header_row 设置、insert action、列宽算法、三阶段 diff 同步。当需要同步本地文档到飞书、操作飞书表格、或做增量编辑时激活。
---
# Feishu Document Tool
Single tool `feishu_doc` with action parameter for all document operations, including table creation for Docx.
## Token Extraction
From URL `https://xxx.feishu.cn/docx/ABC123def` → `doc_token` = `ABC123def`
## Actions
### Read Document
```json
{ "action": "read", "doc_token": "ABC123def" }
```
Returns: title, plain text content, block statistics. Check `hint` field — if present, structured content (tables, images) exists that requires `list_blocks`.
### Write Document (Replace All)
```json
{ "action": "write", "doc_token": "ABC123def", "content": "# Title\n\nMarkdown content..." }
```
Replaces entire document with markdown content. Supports: headings, lists, code blocks, quotes, links, images (`` auto-uploaded), bold/italic/strikethrough, **and Markdown tables**.
### Append Content
```json
{ "action": "append", "doc_token": "ABC123def", "content": "Additional content" }
```
Appends markdown to end of document. Supports the same content types as `write`, including Markdown tables.
### Insert Content (Positioned)
```json
{
"action": "insert",
"doc_token": "ABC123def",
"after_block_id": "doxcnXXX",
"content": "Markdown content including tables..."
}
```
Inserts content after a specific block. Uses Descendant API internally, supports all block types including tables.
**Key usage:** This is the primary way to insert content at a precise position. Use `list_blocks` first to find the `after_block_id`.
### Create Document
```json
{ "action": "create", "title": "New Document", "owner_open_id": "ou_xxx" }
```
With folder:
```json
{
"action": "create",
"title": "New Document",
"folder_token": "fldcnXXX",
"owner_open_id": "ou_xxx"
}
```
**Important:** Always pass `owner_open_id` with the requesting user's `open_id` (from inbound metadata `sender_id`) so the user automatically gets `full_access` permission on the created document. Without this, only the bot app has access.
### List Blocks
```json
{ "action": "list_blocks", "doc_token": "ABC123def" }
```
Returns full block data including tables, images. Use this to read structured content and find block IDs for positioning.
### Get Single Block
```json
{ "action": "get_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" }
```
### Update Block Text
```json
{
"action": "update_block",
"doc_token": "ABC123def",
"block_id": "doxcnXXX",
"content": "New text with **bold** and `code`"
}
```
Supports Markdown inline styles: `**bold**`, `` `code` ``, `[link](url)`, `~~strike~~`.
**Supported block types:** Text, Heading, Bullet, Ordered, Code, Quote, Todo.
**⚠️ NOT for tables:** Using `update_block` on a table cell replaces content as plain text, destroying all inline formatting (inline_code, bold, etc.).
### Delete Block
```json
{ "action": "delete_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" }
```
### Create Table
```json
{
"action": "create_table",
"doc_token": "ABC123def",
"row_size": 2,
"column_size": 2,
"column_width": [200, 200]
}
```
**⚠️ Positioning unreliable:** `parent_block_id` and `index` parameters do not work correctly for tables — tables always end up at the document end. Use `insert` action with `after_block_id` instead for positioned tables.
### Write Table Cells
```json
{
"action": "write_table_cells",
"doc_token": "ABC123def",
"table_block_id": "doxcnTABLE",
"values": [
["A1", "B1"],
["A2", "B2"]
]
}
```
Writes plain text into table cells. Supports Markdown inline styles in cell values.
**Note:** This clears and rebuilds cell children. Suitable for initial table filling or full-cell replacement, but not for partial cell edits.
### Create Table With Values (One-step)
```json
{
"action": "create_table_with_values",
"doc_token": "ABC123def",
"row_size": 2,
"column_size": 2,
"column_width": [200, 200],
"values": [["A1", "B1"], ["A2", "B2"]]
}
```
**⚠️ Same positioning caveat as `create_table`** — use `insert` action for positioned tables.
### Table Row/Column Operations
```json
{ "action": "insert_table_row", "doc_token": "...", "table_block_id": "...", "row_index": -1 }
{ "action": "insert_table_column", "doc_token": "...", "table_block_id": "...", "column_index": -1 }
{ "action": "delete_table_rows", "doc_token": "...", "block_id": "...", "row_start": 1, "row_count": 1 }
{ "action": "delete_table_columns", "doc_token": "...", "block_id": "...", "column_start": 0, "column_count": 1 }
{ "action": "merge_table_cells", "doc_token": "...", "table_block_id": "...", "row_start": 0, "row_end": 2, "column_start": 0, "column_end": 2 }
```
**Note for `delete_table_rows`:** Uses `block_id` (not `table_block_id`), `row_start` (not `row_index`).
### Upload Image
```json
{ "action": "upload_image", "doc_token": "ABC123def", "url": "https://example.com/image.png" }
```
Or local path with position control:
```json
{
"action": "upload_image",
"doc_token": "ABC123def",
"file_path": "/tmp/image.png",
"parent_block_id": "doxcnParent",
"index": 5
}
```
Or base64 data URI:
```json
{
"action": "upload_image",
"doc_token": "ABC123def",
"image": "data:image/png;base64,iVBOR..."
}
```
**Note:** Image display size is determined by the uploaded image's pixel dimensions. For small images (e.g. 480x270), scale to 800px+ width before uploading.
### Upload File Attachment
```json
{ "action": "upload_file", "doc_token": "ABC123def", "url": "https://example.com/report.pdf" }
```
Or local path:
```json
{ "action": "upload_file", "doc_token": "ABC123def", "file_path": "/tmp/report.pdf", "filename": "Q1-report.pdf" }
```
### Color Text
```json
{
"action": "color_text",
"doc_token": "ABC123def",
"block_id": "doxcnXXX",
"content": "colored text",
"color": "red"
}
```
## Reading Workflow
1. Start with `action: "read"` — get plain text + statistics
2. Check `block_types` in response for Table, Image, Code, etc.
3. If structured content exists, use `action: "list_blocks"` for full data
---
## Incremental Sync Best Practices
### When to Use Incremental vs Full Sync
- **< 5 blocks changed** → incremental (delete + insert individual blocks)
- **> 80% blocks changed** → full sync (`write` action to replace all)
- **Single table changed** → delete old table + insert new table
### Table Update Strategy: Delete + Insert
Tables should NEVER be edited in-place. Always:
```
1. list_blocks → find the block before the table (anchor_block_id)
2. delete_block(block_id = old_table_id)
3. insert(after_block_id = anchor_block_id, content = "| new | table |...")
```
**Why not edit in-place?**
- `update_block` on table cell → strips inline formatting (inline_code, bold, links all become plain text)
- `write_table_cells` → clears and rebuilds cell children, similar formatting loss
- `create_table` / `create_table_with_values` → positioning unreliable
### Multi-Block Batch Replacement
When changes span multiple consecutive blocks:
1. Find anchor (last unchanged block before the changed region)
2. Delete changed blocks in **reverse order** (to avoid index drift)
3. Single `insert` call with all new content (supports mixed block types)
### Diff Strategy (from feishu-doc-sync)
1. Block signature matching — (type, content) hash for alignment, style hash for change detection
2. LCS alignment of local ↔ remote blocks
3. Three-phase execution: **UPDATE** (text blocks) → **DELETE** (reverse order) → **INSERT** (forward order)
4. Tables: if structure is identical, cell-level diff is possible; if structure differs, full table replacement
5. When change rate > 80%, fall back to full sync
---
## Table Header Row (Dark Background)
The `header_row: true` property gives tables a dark header background. This is a common requirement.
### ⚠️ Critical: `header_row` can ONLY be set at creation time
**The PATCH API (`update_table_property`) does NOT work for `header_row`** — it returns `code: 0` (success) but silently fails to apply the property. This applies to both single-block PATCH and `batch_update`.
### How to Create Tables with Header Row
Since `insert` action (Descendant API) and `create_table` action do not support `header_row`, you must use the raw Feishu API:
```bash
# Step 1: Get tenant_access_token
TOKEN=$(curl -s https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal \
-H "Content-Type: application/json" \
-d '{"app_id":"'$APP_ID'","app_secret":"'$APP_SECRET'"}' \
| python3 -c "import json,sys; print(json.load(sys.stdin)['tenant_access_token'])")
# Step 2: Create empty table with header_row via document_block_children.create
curl -s -X POST \
"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_token}/blocks/{doc_token}/children?document_revision_id=-1" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"children": [{
"block_type": 31,
"table": {
"property": {
"row_size": 3,
"column_size": 2,
"header_row": true,
"column_width": [300, 400]
}
}
}],
"index": 5
}'
# Step 3: Fill cells via feishu_doc write_table_cells
# (use the new table's block_id from step 2 response)
```
**Credentials:** Read from `/root/.openclaw/openclaw.json` → `channels.feishu.appId` / `channels.feishu.appSecret`.
### Workflow for Positioned Table with Header Row
1. `list_blocks` → find the anchor block and target index
2. (If replacing) `delete_block` → remove old table
3. Raw API `document_block_children.create` → create empty table with `header_row: true` at correct index
4. `feishu_doc write_table_cells` → fill content
5. Verify: `get_block` → check `table.property.header_row`
### Column Width Algorithm
Auto-calculated when using `insert` action (Descendant API):
- Total width: ~730px
- CJK characters count as 2x width
- Per-column: proportional distribution, clamped to 50–400px
- When using raw API, specify `column_width` manually
---
## Sync Prerequisites
- **Always `git pull` before reading local repo files** — ensures latest content
- **Compare before writing** — use `read` or `list_blocks` to check current state
- **Preserve formatting** — inline_code, bold, links in table cells require careful handling
## Configuration
```yaml
channels:
feishu:
tools:
doc: true # default: true
```
**Note:** `feishu_wiki` depends on this tool — wiki page content is read/written via `feishu_doc`.
## Permissions
Required: `docx:document`, `docx:document:readonly`, `docx:document.block:convert`, `drive:drive`
don't have the plugin yet? install it then click "run inline in claude" again.