SoMark To Feishu
Convert SoMark Markdown and JSON into a Feishu cloud document, electronic
spreadsheet, or Bitable record payload. Preserve SoMark's original Markdown
and JSON. Use the route that matches the user's target rather than forcing all
content through a cloud document.
Workflow
- Resolve the source:
- For a raw PDF, image, Word, or PPT file, the Agent must invoke the
separately installed official
somark-document-parser Skill exactly once
in the task and request both Markdown and JSON. Feishu adapter scripts do
not include or discover the parser.
- Bypass parsing only when the user explicitly supplies the exact matching
Markdown and JSON paths. Never discover or reuse adjacent, historical,
same-name, hashed, indexed, or recently modified results.
- When a source path and an explicit matching pair are both supplied, treat
the pair as authoritative formatting/structure artifacts and use the
source path only as provenance; state this choice instead of silently
switching inputs.
- Select the target route before writing:
- Default to a Feishu cloud document when the user asks to read, edit, or
share a document.
- Use a Feishu electronic spreadsheet for layout-preserving tables.
- Use Bitable only when the user asks for structured records, views, or
workflow-ready data. Default to creating a new Base. Use an existing Base
only when the user's prompt explicitly asks to write into one.
- Use the official general SoMark Skill defaults. Do not modify or replace
somark-document-parser, and do not change parser settings based on the
destination platform unless the user explicitly asks for a different
profile. The parser returns Markdown + JSON with image URLs; platform-only
downloading and upload belong to this adapter:
{
"element_formats": {
"image": "url",
"formula": "latex",
"table": "html",
"cs": "image"
},
"feature_config": {
"enable_title_level_recognition": false,
"enable_inline_image": true,
"enable_table_image": true,
"enable_image_understanding": true,
"keep_header_footer": false
}
}
- For the cloud-document route, use the normal Markdown + JSON parser output.
Do not request ZIP; cloud documents import URL-based images and do not need
local table-image assets. Generate the compatibility draft:
python <skill-dir>\scripts\convert_to_feishu.py `
--markdown <somark.md> `
--json <somark.json> `
--title "<Feishu document title>"
- When the converter exits successfully and the generated
_feishu.md exists,
start the Drive import immediately. The converter writes the compatibility
draft and manifest in the same invocation. Do not pause to inspect the
manifest, summarize transformation statistics, or ask the user to confirm
before starting the import. A blank Markdown/JSON payload with a successful
SoMark response is still a successful parse; stop only for an actual
conversion or import error.
- Import only the generated
_feishu.md through the Drive import route:
lark-cli.cmd drive +import --file <relative-path-to-feishu.md> `
--type docx --name "<title>" --as user
- When import returns
ready=false, run the returned next_command. Wait
until a final document URL and token are available.
- Immediately send the document URL to the user so they can start browsing
the imported content. Clearly state that the base document is available and
that background enhancement is still applying image captions, native table
images/equations, formula headings, post-import formula validation and
repair, and other planned Block API updates. Do not describe the document as fully
processed yet. Formula validation must not delay this preview boundary.
- After sending the URL, read the generated manifest and continue applying
the native-block plan. If the manifest is not ready or its targets do not
match the imported document safely, keep the base document available and
report that enhancement was skipped or stopped. Use
--dry-run first when
testing a new document shape, then run the command without it:
python <skill-dir>\scripts\apply_feishu_blocks.py `
--document <Feishu-document-URL-or-token> `
--manifest <source_feishu.manifest.json> `
--dry-run
The updater treats each enhancement type independently. Body-image matching
excludes images already nested in table cells. It uses surrounding-text
anchors for body images, writes captions only to uniquely matched Image
Blocks, and skips ambiguous body images. Table-cell image locations come
from JSON row/column coordinates: first preserve the historically verified
isolated image paragraph so Drive can import it natively; only when the
target cell contains no native image, download and insert the image into
that exact TableCell. Never insert a duplicate table image.
A table-count mismatch skips table formulas, while uniquely located image
captions and formula headings may still continue. Only a request-integrity
conflict stops all writes. It also audits native equation elements against
the manifest, repairs uniquely located formula paragraphs whose Markdown
boundaries were damaged by Drive import, rebuilds uniquely located display
formulas that retained raw $$ delimiters, and fetches the blocks again
after writing to verify that no planned formula is still missing.
10. After the Block API stage finishes, tell the user whether enhancement was
applied, partially applied, skipped, or stopped by a safety mismatch. Mention
only actual conversion/upload failures. An image whose source JSON has no
description is intentional: do not call it missing, skipped, ambiguous, or
degraded in the final report. A successful import with degraded elements is
"created with degradation", not a fully faithful success.
Conversion Rules
The bundled scripts perform deterministic V0.2.2 compatibility processing:
- Preserve an empty successful SoMark result without adding a second content
gate in the parser layer.
- Convert
footnote blocks in source order to numbered editable endnotes such
as > [尾注 1] ..., matching the DingTalk rule. When the JSON
footnote marker is empty and has a bbox, merge consecutive following text
blocks contained by that region into one footnote. Stop at the first text
block outside the region or the first non-text block. Without geometry,
conservatively use only the immediately following text block.
- Convert multi-line
choice blocks to Markdown unordered lists (- ...),
preserving the original option labels.
- Keep image understanding enabled, import images with an empty Markdown
alt, and store each description plus its nearest preceding/following text
anchors in the manifest. After import, write the description to the
corresponding native Image Block caption.content. If Drive import drops an
body image, apply only uniquely anchored captions. Use a source description
exactly when JSON supplies one; never synthesize a caption when it does not.
- In HTML table cells, isolate every Markdown image with blank lines before and
after it to preserve the live-tested native Drive import behavior. Also store
its exact JSON-derived row, column, URL, and description for a TableCell API
fallback. Empty descriptions are valid and never trigger a warning.
- Preserve valid code fences, close an unmatched fence, and recover an
unfenced JSON
code block only when its exact text is present.
- Convert numbered headings wrapped entirely in formula syntax, such as
### $1.1\quad Title$, into ordinary Markdown headings so Feishu retains
them in the document outline.
- Record any remaining Markdown heading that contains an inline formula. If
Drive import turns it into a Text Block, recreate it at the same position as
the original Heading Block level while reusing its native equation and text
elements, then delete the degraded paragraph.
- Preserve HTML tables and their merged-cell structure. Only remove a
conflicting header
rowspan when it would cause Feishu to drop the next
complete data row.
- Wrap safe bare superscript fragments such as
^{[42]} and ^{a,b,c,*} as
inline formulas outside code and existing formulas; apply the same rule in
HTML cells. Record these repaired superscripts plus already balanced,
explicit $...$ and $$...$$ formulas. After import, locate each target
cell uniquely and replace the marker with a native equation text element
through the Block API.
- Do not normalize Unicode superscripts, rewrite ordinary table text, or
attempt to unify table fonts. Leave other malformed or unbalanced formulas as raw text rather than
guessing a repair.
- Convert supported
\ce{...} reactions to standard Feishu-compatible KaTeX
with upright chemical symbols, subscripts, arrows, and reaction conditions.
Convert a simple bracketed polymer repeat such as [CH2CH2]n to a native
repeat-unit formula with visible terminal chain bonds, for example
\left[\mathrm{-CH_{2}-CH_{2}-}\right]_{n}.
Fall back to readable text only when the syntax cannot be converted safely.
- Inside balanced Markdown formulas only, convert raw
< and > comparisons
to \lt and \gt so Feishu's Markdown/HTML import layer cannot consume the
formula boundary. Record the expected equations and comparison-formula
paragraphs in the manifest for post-preview validation and targeted repair.
- Remove the first H1 only when it exactly equals the requested Feishu title.
Do not rewrite OCR prose, infer missing model content, or change heading
levels. Treat vertical-text and alignment recognition failures as model issues.
Output Contract
For source.md, the script creates:
source_feishu.md: Feishu import draft.
source_feishu.manifest.json: source hashes, transformation counts,
warnings, degradation records, validation status, image-caption plans,
table-formula and formula-heading plans, and structural-element mappings for
footnotes and choices.
source_feishu.manifest.block-report.json: Block API validation or write
report.
Keep these files with the original Markdown and JSON for auditability.
Feishu Sheets Workflow
Use this route when the user asks for a Feishu electronic spreadsheet rather
than a Feishu cloud document. It consumes SoMark JSON table blocks directly;
do not round-trip them through Markdown.
- Before spending a SoMark parse call, run
lark-cli auth status --json --verify once. If Sheets authorization is missing, complete only the
required user authorization first. Do not repeat the same auth check after
parsing unless a Feishu command returns an authentication error.
- Parse once with the official normal Markdown + JSON output and
element_formats.image=url. Do not request ZIP or require changes to the
generic parser. The table JSON is the source of truth for image location;
Markdown image URLs are only a fallback when they occur inside the same
table cell.
- Generate the layout payload exactly once. An explicitly supplied local
asset directory remains an optional cache/compatibility input, but is not
required. Never infer an asset directory from the JSON filename or inspect
neighboring folders. Do not download table images or inspect the manifest before
creating the workbook:
python <skill-dir>\scripts\prepare_sheets_payload.py `
<somark.json> <output-dir>
- As soon as
sheets_payload.json exists, create the workbook with the
preview-only helper. It writes content only, saves the exact workbook token
in preview_checkpoint.json, and exits before styles, merges, sizes, or
images can run. Do not pause to read manifest.json, summarize statistics,
run a dry-run, or ask the user to confirm:
python <skill-dir>\scripts\create_sheets_preview.py `
<output-dir> `
--title "<title>"
The helper owns the required payload-directory working directory and the
quoted @sheets_payload.json argument. Do not replace it with a combined
create-and-enhance command.
- Once Feishu returns a workbook URL, send it to the user immediately in an
intermediate update. Say that the content-only workbook is available for
preview and that styles, merged cells, and table images are still being
applied. Continue in the same task without waiting for a reply. This
is a hard response boundary: do not run another tool before emitting the
URL. If the host buffers intermediate assistant messages, end the response
with the URL and resume enhancement on the next turn instead of withholding
the preview until all work is complete.
- After sending the URL, apply the prepared styles, row and column sizes,
merged cells, and table images. The enhancer first reuses a matched local
file when one is explicitly available; otherwise it downloads the HTTP(S)
URL recorded for that exact JSON table cell, caches it, and calls the native
cell-image API. This mode never rewrites worksheet values:
python <skill-dir>\scripts\apply_sheets_payload.py `
<output-dir> `
--preview-checkpoint <output-dir>\preview_checkpoint.json `
--enhance-only
- The enhancer batches style and merge operations, retries Feishu rate limits,
and uploads images separately with
+cells-set-image. One image download or
upload failure must not stop the other images or invalidate the usable
content-only workbook; preserve the URL text and report a local degradation.
If it is interrupted,
rerun the same --enhance-only command. Add --reset-merges only when a
previous enhancement partially applied merge ranges:
python <skill-dir>\scripts\apply_sheets_payload.py `
<output-dir> `
--preview-checkpoint <output-dir>\preview_checkpoint.json `
--enhance-only `
--reset-merges
- Keep post-preview checks small. A successful enhancer result plus one
+workbook-info call is sufficient for the normal path. Use targeted
+cells-get, image readback, manifest inspection, or a dry-run only when the
enhancer reports a mismatch, the document shape is new and risky, or the
user explicitly asks for verification. Never delay the preview link for
these checks. Report enhancement failures without withdrawing the usable
content-only workbook.
Spreadsheet Conversion Rules
- Create exactly one worksheet per SoMark
table element. Do not generate
separate layout, data, hybrid, or analysis copies.
- Preserve expanded cells, merge ranges, borders, wrapping, and estimated column
widths for visual comparison with the source document.
- Before applying merges, keep cell values and skip only a merge range that is
invalid, overlaps an already accepted range, or would discard a non-anchor
value. Continue all other enhancements and report every skipped merge as a
local degradation.
- Write every non-empty cell with the
object dtype. Do not infer or write native
number, date, time, percentage, or formula values.
- Convert only supported simple LaTeX fragments to readable Unicode text, such as
m³, 4°, φ, ≥, and ±. Preserve unsupported complex LaTeX verbatim.
- Do not guess-correct SoMark recognition output such as
_ 130.
- Treat the result as a layout-restoration workbook for viewing, checking,
filling, and archiving. Do not claim that it is ready for calculation,
numeric filtering, sorting, charts, or pivots.
Feishu Bitable Workflow
Use this route when the user wants records for a Base view, calendar, Gantt
chart, form, dashboard, or workflow. Default to a new Base for every request.
Use an existing Base only when the user explicitly says to write into an
existing Base or supplies a Base URL/token as the destination. Do not reuse a
recent Base merely because it is available.
Do not ask the user to confirm headers, field mapping, or a destination Base
before SoMark parsing. After a successful SoMark JSON result exists, treat its
HTML table.content as the sole source of truth for Bitable headers. Never
open, render, screenshot, visually inspect, or run another OCR pass on the
original PDF/image to check headers. Do not use the Markdown output as a second
header source. If the SoMark header is incomplete or duplicated, report that
result after parsing; do not fall back to OCR unless the user explicitly asks
for a separate OCR comparison.
Briefly remind the user, without waiting for a reply, that clear headers improve
Bitable field mapping. Missing data cells and blank rows are supported. Empty
headers become 文本, 文本2, and so on; duplicate headers retain the first name
and receive numeric suffixes. Preserve the original header and column index in
the mapping audit. These repairs are warnings, not prerequisites.
Use the normal JSON parser output. The implemented mapping supports text,
number, and date fields and does not write attachment/image fields. If
Bitable attachment mapping is implemented later, acquire its files inside this
adapter from the source URLs rather than changing the generic parser.
This route is create-only. It deliberately does not generate Source Key,
hash the source file, read existing records, update matching records, or keep a
local record-ID map. Repeating the same task creates another set of records.
Duplicate-upload management belongs to the calling product or user workflow,
not to this one-time document conversion path.
Default new-Base path
- Start SoMark parsing immediately. Do not inspect the original file before or
after this parse for header discovery.
- Run
auto-prepare directly on the SoMark JSON. It derives fields from the
selected SoMark header, records any safe-name repairs, conservatively infers
text, number, or date from the parsed cells, writes the mapping, and prepares the records
in one command. It does not perform OCR or open the source file:
python <skill-dir>\scripts\somark_to_bitable.py auto-prepare `
--json <somark.json> `
--source-file <original-file.pdf> `
--mapping-output <field-mapping.json> `
--output <records.json>
Use the only table and row 1 as its header. With multiple tables, stop after
listing the reported table candidates and ask the user to choose; rerun with
--table-index. Use --header-row when the SoMark JSON clearly identifies a
different header row. Determine these only from JSON. Record fields remain
optional, completely empty rows are preserved, and no required field may
stop creation. If requires_confirmation=true, show the mapping warnings;
when the user confirms or already insists on Bitable, reuse the prepared
mapping and continue. Use manual prepare only when the user explicitly provides
a custom mapping or an existing Base requires different target field names.
- Create a new Base with an empty first table. The script derives Feishu
text, number, and datetime fields from the mapping and saves the
returned URL, Base token, and table ID in <base-target.json>. Do not add an
internal Source Key field. Do not send or open the Base URL at this stage:
python <skill-dir>\scripts\somark_to_bitable.py create-base `
--mapping <field-mapping.json> `
--name "<source name> - SoMark" `
--table-name "数据" `
--output <base-target.json>
create-base saves a sanitized successful CLI response beside the target
before decoding resource IDs. It reads the new table ID from
data.table.table_id or data.table.id, never from a recursive generic
id search. If table ID decoding or recovery fails after the remote Base
was created, rerun this exact command with the same --output path. It will
reuse the saved response, query +table-list, match --table-name exactly,
and write the target without calling +base-create again. Do not change the
output path, delete the response snapshot, or retry with a fresh Base.
- Start record creation with
start-create. This command writes the first
batch of up to 200 records before returning preview_url, and writes any
unsubmitted records to <remaining-records.json>. Do not run a dry-run or
a remote pre-read because either delays first usable content:
python <skill-dir>\scripts\somark_to_bitable.py start-create `
--target <base-target.json> `
--payload <records.json> `
--remaining-output <remaining-records.json>
- Only after
start-create returns phase=first_batch_written and a positive
created_record_count, send its preview_url to the user in a progress
message. Say that the first records are available and that remaining
records, views, or other finishing work may still be in progress. This is a
hard response boundary: emit the URL before another tool call. If the first
batch fails, report the failure and do not expose the still-empty Base.
- If
remaining_record_count is greater than zero, continue with the saved
continuation payload. Skip this command when the count is zero:
python <skill-dir>\scripts\somark_to_bitable.py create `
--target <base-target.json> `
--payload <remaining-records.json>
- The writer performs no remote pre-read and writes at most 200 records per
batch. When writing completes, report the total created count and any schema
assumptions. If continuation fails, keep the already shared partially
filled Base available and report the failed stage. Configure calendar,
Gantt, or other views only after the required typed fields exist.
Do not use the combined run command for the default new-Base path because a
single long-running command prevents the Agent from sharing the preview link
after the first successful record batch and before continuation writing.
Explicit existing-Base path
Use this path only when the user's prompt explicitly names an existing Base as
the destination. Parse the source first, then resolve the Base URL/token, read
its real fields, and build a compatible mapping. Generate <records.json> with
prepare, then write it directly with the existing Base token and table ID:
python <skill-dir>\scripts\somark_to_bitable.py create `
--base-token <base-token> `
--table-id <table-id> `
--payload <records.json>
For an explicitly selected existing Base, run remains available after the
mapping has been built from the parsed result:
python <skill-dir>\scripts\somark_to_bitable.py run `
--json <somark.json> `
--mapping <field-mapping.json> `
--source-file <original-file.pdf> `
--payload-output <records.json> `
--base-token <base-token> `
--table-id <table-id>
1---2name: somark-to-feishu3description: Convert PDF, image, Word, or PPT files through SoMark and create editable Feishu cloud documents, layout-preserving Feishu spreadsheets, or mapped records in Feishu Bitable. Use when a user asks to parse an unstructured or scanned document with SoMark and import the result into Feishu, or when existing SoMark Markdown and JSON need a Feishu-compatible document, spreadsheet, or Bitable output.4---5
6# SoMark To Feishu
7
8Convert SoMark Markdown and JSON into a Feishu cloud document, electronic
9spreadsheet, or Bitable record payload. Preserve SoMark's original Markdown
10and JSON. Use the route that matches the user's target rather than forcing all
11content through a cloud document.
12
13## Workflow
14
151. Resolve the source:
16 - For a raw PDF, image, Word, or PPT file, the Agent must invoke the
17 separately installed official `somark-document-parser` Skill exactly once
18 in the task and request both Markdown and JSON. Feishu adapter scripts do
19 not include or discover the parser.
20 - Bypass parsing only when the user explicitly supplies the exact matching
21 Markdown and JSON paths. Never discover or reuse adjacent, historical,
22 same-name, hashed, indexed, or recently modified results.
23 - When a source path and an explicit matching pair are both supplied, treat
24 the pair as authoritative formatting/structure artifacts and use the
25 source path only as provenance; state this choice instead of silently
26 switching inputs.
272. Select the target route before writing:
28 - Default to a Feishu cloud document when the user asks to read, edit, or
29 share a document.
30 - Use a Feishu electronic spreadsheet for layout-preserving tables.
31 - Use Bitable only when the user asks for structured records, views, or
32 workflow-ready data. Default to creating a new Base. Use an existing Base
33 only when the user's prompt explicitly asks to write into one.
343. Use the official general SoMark Skill defaults. Do not modify or replace
35 `somark-document-parser`, and do not change parser settings based on the
36 destination platform unless the user explicitly asks for a different
37 profile. The parser returns Markdown + JSON with image URLs; platform-only
38 downloading and upload belong to this adapter:
39
40```json
41{
42 "element_formats": {
43 "image": "url",
44 "formula": "latex",
45 "table": "html",
46 "cs": "image"
47 },
48 "feature_config": {
49 "enable_title_level_recognition": false,
50 "enable_inline_image": true,
51 "enable_table_image": true,
52 "enable_image_understanding": true,
53 "keep_header_footer": false
54 }
55}
56```
57
584. For the cloud-document route, use the normal Markdown + JSON parser output.
59 Do not request ZIP; cloud documents import URL-based images and do not need
60 local table-image assets. Generate the compatibility draft:
61
62```powershell
63python <skill-dir>\scripts\convert_to_feishu.py `
64 --markdown <somark.md> `
65 --json <somark.json> `
66 --title "<Feishu document title>"
67```
68
695. When the converter exits successfully and the generated `_feishu.md` exists,
70 start the Drive import immediately. The converter writes the compatibility
71 draft and manifest in the same invocation. Do not pause to inspect the
72 manifest, summarize transformation statistics, or ask the user to confirm
73 before starting the import. A blank Markdown/JSON payload with a successful
74 SoMark response is still a successful parse; stop only for an actual
75 conversion or import error.
766. Import only the generated `_feishu.md` through the Drive import route:
77
78```powershell
79lark-cli.cmd drive +import --file <relative-path-to-feishu.md> `
80 --type docx --name "<title>" --as user
81```
82
837. When import returns `ready=false`, run the returned `next_command`. Wait
84 until a final document URL and token are available.
858. Immediately send the document URL to the user so they can start browsing
86 the imported content. Clearly state that the base document is available and
87 that background enhancement is still applying image captions, native table
88 images/equations, formula headings, post-import formula validation and
89 repair, and other planned Block API updates. Do not describe the document as fully
90 processed yet. Formula validation must not delay this preview boundary.
919. After sending the URL, read the generated manifest and continue applying
92 the native-block plan. If the manifest is not ready or its targets do not
93 match the imported document safely, keep the base document available and
94 report that enhancement was skipped or stopped. Use `--dry-run` first when
95 testing a new document shape, then run the command without it:
96
97```powershell
98python <skill-dir>\scripts\apply_feishu_blocks.py `
99 --document <Feishu-document-URL-or-token> `
100 --manifest <source_feishu.manifest.json> `
101 --dry-run
102```
103
104 The updater treats each enhancement type independently. Body-image matching
105 excludes images already nested in table cells. It uses surrounding-text
106 anchors for body images, writes captions only to uniquely matched Image
107 Blocks, and skips ambiguous body images. Table-cell image locations come
108 from JSON row/column coordinates: first preserve the historically verified
109 isolated image paragraph so Drive can import it natively; only when the
110 target cell contains no native image, download and insert the image into
111 that exact TableCell. Never insert a duplicate table image.
112 A table-count mismatch skips table formulas, while uniquely located image
113 captions and formula headings may still continue. Only a request-integrity
114 conflict stops all writes. It also audits native equation elements against
115 the manifest, repairs uniquely located formula paragraphs whose Markdown
116 boundaries were damaged by Drive import, rebuilds uniquely located display
117 formulas that retained raw `$$` delimiters, and fetches the blocks again
118 after writing to verify that no planned formula is still missing.
11910. After the Block API stage finishes, tell the user whether enhancement was
120 applied, partially applied, skipped, or stopped by a safety mismatch. Mention
121 only actual conversion/upload failures. An image whose source JSON has no
122 description is intentional: do not call it missing, skipped, ambiguous, or
123 degraded in the final report. A successful import with degraded elements is
124 "created with degradation", not a fully faithful success.
125
126## Conversion Rules
127
128The bundled scripts perform deterministic V0.2.2 compatibility processing:
129
130- Preserve an empty successful SoMark result without adding a second content
131 gate in the parser layer.
132- Convert `footnote` blocks in source order to numbered editable endnotes such
133 as `> [尾注 1] ...`, matching the DingTalk rule. When the JSON
134 footnote marker is empty and has a `bbox`, merge consecutive following text
135 blocks contained by that region into one footnote. Stop at the first text
136 block outside the region or the first non-text block. Without geometry,
137 conservatively use only the immediately following text block.
138- Convert multi-line `choice` blocks to Markdown unordered lists (`- ...`),
139 preserving the original option labels.
140- Keep image understanding enabled, import images with an empty Markdown
141 `alt`, and store each description plus its nearest preceding/following text
142 anchors in the manifest. After import, write the description to the
143 corresponding native Image Block `caption.content`. If Drive import drops an
144 body image, apply only uniquely anchored captions. Use a source description
145 exactly when JSON supplies one; never synthesize a caption when it does not.
146- In HTML table cells, isolate every Markdown image with blank lines before and
147 after it to preserve the live-tested native Drive import behavior. Also store
148 its exact JSON-derived row, column, URL, and description for a TableCell API
149 fallback. Empty descriptions are valid and never trigger a warning.
150- Preserve valid code fences, close an unmatched fence, and recover an
151 unfenced JSON `code` block only when its exact text is present.
152- Convert numbered headings wrapped entirely in formula syntax, such as
153 `### $1.1\quad Title$`, into ordinary Markdown headings so Feishu retains
154 them in the document outline.
155- Record any remaining Markdown heading that contains an inline formula. If
156 Drive import turns it into a Text Block, recreate it at the same position as
157 the original Heading Block level while reusing its native equation and text
158 elements, then delete the degraded paragraph.
159- Preserve HTML tables and their merged-cell structure. Only remove a
160 conflicting header `rowspan` when it would cause Feishu to drop the next
161 complete data row.
162- Wrap safe bare superscript fragments such as `^{[42]}` and `^{a,b,c,*}` as
163 inline formulas outside code and existing formulas; apply the same rule in
164 HTML cells. Record these repaired superscripts plus already balanced,
165 explicit `$...$` and `$$...$$` formulas. After import, locate each target
166 cell uniquely and replace the marker with a native `equation` text element
167 through the Block API.
168- Do not normalize Unicode superscripts, rewrite ordinary table text, or
169 attempt to unify table fonts. Leave other malformed or unbalanced formulas as raw text rather than
170 guessing a repair.
171- Convert supported `\ce{...}` reactions to standard Feishu-compatible KaTeX
172 with upright chemical symbols, subscripts, arrows, and reaction conditions.
173 Convert a simple bracketed polymer repeat such as `[CH2CH2]n` to a native
174 repeat-unit formula with visible terminal chain bonds, for example
175 `\left[\mathrm{-CH_{2}-CH_{2}-}\right]_{n}`.
176 Fall back to readable text only when the syntax cannot be converted safely.
177- Inside balanced Markdown formulas only, convert raw `<` and `>` comparisons
178 to `\lt` and `\gt` so Feishu's Markdown/HTML import layer cannot consume the
179 formula boundary. Record the expected equations and comparison-formula
180 paragraphs in the manifest for post-preview validation and targeted repair.
181- Remove the first H1 only when it exactly equals the requested Feishu title.
182
183Do not rewrite OCR prose, infer missing model content, or change heading
184levels. Treat vertical-text and alignment recognition failures as model issues.
185
186## Output Contract
187
188For `source.md`, the script creates:
189
190- `source_feishu.md`: Feishu import draft.
191- `source_feishu.manifest.json`: source hashes, transformation counts,
192 warnings, degradation records, validation status, image-caption plans,
193 table-formula and formula-heading plans, and structural-element mappings for
194 footnotes and choices.
195- `source_feishu.manifest.block-report.json`: Block API validation or write
196 report.
197
198Keep these files with the original Markdown and JSON for auditability.
199
200## Feishu Sheets Workflow
201
202Use this route when the user asks for a Feishu electronic spreadsheet rather
203than a Feishu cloud document. It consumes SoMark JSON `table` blocks directly;
204do not round-trip them through Markdown.
205
2061. Before spending a SoMark parse call, run `lark-cli auth status --json
207 --verify` once. If Sheets authorization is missing, complete only the
208 required user authorization first. Do not repeat the same auth check after
209 parsing unless a Feishu command returns an authentication error.
2102. Parse once with the official normal Markdown + JSON output and
211 `element_formats.image=url`. Do not request ZIP or require changes to the
212 generic parser. The table JSON is the source of truth for image location;
213 Markdown image URLs are only a fallback when they occur inside the same
214 table cell.
2153. Generate the layout payload exactly once. An explicitly supplied local
216 asset directory remains an optional cache/compatibility input, but is not
217 required. Never infer an asset directory from the JSON filename or inspect
218 neighboring folders. Do not download table images or inspect the manifest before
219 creating the workbook:
220
221```powershell
222python <skill-dir>\scripts\prepare_sheets_payload.py `
223 <somark.json> <output-dir>
224```
225
2264. As soon as `sheets_payload.json` exists, create the workbook with the
227 preview-only helper. It writes content only, saves the exact workbook token
228 in `preview_checkpoint.json`, and exits before styles, merges, sizes, or
229 images can run. Do not pause to read `manifest.json`, summarize statistics,
230 run a dry-run, or ask the user to confirm:
231
232```powershell
233python <skill-dir>\scripts\create_sheets_preview.py `
234 <output-dir> `
235 --title "<title>"
236```
237
238 The helper owns the required payload-directory working directory and the
239 quoted `@sheets_payload.json` argument. Do not replace it with a combined
240 create-and-enhance command.
241
2425. Once Feishu returns a workbook URL, send it to the user immediately in an
243 intermediate update. Say that the content-only workbook is available for
244 preview and that styles, merged cells, and table images are still being
245 applied. Continue in the same task without waiting for a reply. This
246 is a hard response boundary: do not run another tool before emitting the
247 URL. If the host buffers intermediate assistant messages, end the response
248 with the URL and resume enhancement on the next turn instead of withholding
249 the preview until all work is complete.
2506. After sending the URL, apply the prepared styles, row and column sizes,
251 merged cells, and table images. The enhancer first reuses a matched local
252 file when one is explicitly available; otherwise it downloads the HTTP(S)
253 URL recorded for that exact JSON table cell, caches it, and calls the native
254 cell-image API. This mode never rewrites worksheet values:
255
256```powershell
257python <skill-dir>\scripts\apply_sheets_payload.py `
258 <output-dir> `
259 --preview-checkpoint <output-dir>\preview_checkpoint.json `
260 --enhance-only
261```
262
2637. The enhancer batches style and merge operations, retries Feishu rate limits,
264 and uploads images separately with `+cells-set-image`. One image download or
265 upload failure must not stop the other images or invalidate the usable
266 content-only workbook; preserve the URL text and report a local degradation.
267 If it is interrupted,
268 rerun the same `--enhance-only` command. Add `--reset-merges` only when a
269 previous enhancement partially applied merge ranges:
270
271```powershell
272python <skill-dir>\scripts\apply_sheets_payload.py `
273 <output-dir> `
274 --preview-checkpoint <output-dir>\preview_checkpoint.json `
275 --enhance-only `
276 --reset-merges
277```
278
2798. Keep post-preview checks small. A successful enhancer result plus one
280 `+workbook-info` call is sufficient for the normal path. Use targeted
281 `+cells-get`, image readback, manifest inspection, or a dry-run only when the
282 enhancer reports a mismatch, the document shape is new and risky, or the
283 user explicitly asks for verification. Never delay the preview link for
284 these checks. Report enhancement failures without withdrawing the usable
285 content-only workbook.
286
287### Spreadsheet Conversion Rules
288
289- Create exactly one worksheet per SoMark `table` element. Do not generate
290 separate layout, data, hybrid, or analysis copies.
291- Preserve expanded cells, merge ranges, borders, wrapping, and estimated column
292 widths for visual comparison with the source document.
293- Before applying merges, keep cell values and skip only a merge range that is
294 invalid, overlaps an already accepted range, or would discard a non-anchor
295 value. Continue all other enhancements and report every skipped merge as a
296 local degradation.
297- Write every non-empty cell with the `object` dtype. Do not infer or write native
298 number, date, time, percentage, or formula values.
299- Convert only supported simple LaTeX fragments to readable Unicode text, such as
300 `m³`, `4°`, `φ`, `≥`, and `±`. Preserve unsupported complex LaTeX verbatim.
301- Do not guess-correct SoMark recognition output such as `_ 130`.
302- Treat the result as a layout-restoration workbook for viewing, checking,
303 filling, and archiving. Do not claim that it is ready for calculation,
304 numeric filtering, sorting, charts, or pivots.
305
306## Feishu Bitable Workflow
307
308Use this route when the user wants records for a Base view, calendar, Gantt
309chart, form, dashboard, or workflow. Default to a new Base for every request.
310Use an existing Base only when the user explicitly says to write into an
311existing Base or supplies a Base URL/token as the destination. Do not reuse a
312recent Base merely because it is available.
313
314Do not ask the user to confirm headers, field mapping, or a destination Base
315before SoMark parsing. After a successful SoMark JSON result exists, treat its
316HTML `table.content` as the sole source of truth for Bitable headers. Never
317open, render, screenshot, visually inspect, or run another OCR pass on the
318original PDF/image to check headers. Do not use the Markdown output as a second
319header source. If the SoMark header is incomplete or duplicated, report that
320result after parsing; do not fall back to OCR unless the user explicitly asks
321for a separate OCR comparison.
322
323Briefly remind the user, without waiting for a reply, that clear headers improve
324Bitable field mapping. Missing data cells and blank rows are supported. Empty
325headers become `文本`, `文本2`, and so on; duplicate headers retain the first name
326and receive numeric suffixes. Preserve the original header and column index in
327the mapping audit. These repairs are warnings, not prerequisites.
328
329Use the normal JSON parser output. The implemented mapping supports `text`,
330`number`, and `date` fields and does not write attachment/image fields. If
331Bitable attachment mapping is implemented later, acquire its files inside this
332adapter from the source URLs rather than changing the generic parser.
333
334This route is create-only. It deliberately does not generate `Source Key`,
335hash the source file, read existing records, update matching records, or keep a
336local record-ID map. Repeating the same task creates another set of records.
337Duplicate-upload management belongs to the calling product or user workflow,
338not to this one-time document conversion path.
339
340### Default new-Base path
341
3421. Start SoMark parsing immediately. Do not inspect the original file before or
343 after this parse for header discovery.
3442. Run `auto-prepare` directly on the SoMark JSON. It derives fields from the
345 selected SoMark header, records any safe-name repairs, conservatively infers
346 `text`, `number`, or `date` from the parsed cells, writes the mapping, and prepares the records
347 in one command. It does not perform OCR or open the source file:
348
349```powershell
350python <skill-dir>\scripts\somark_to_bitable.py auto-prepare `
351 --json <somark.json> `
352 --source-file <original-file.pdf> `
353 --mapping-output <field-mapping.json> `
354 --output <records.json>
355```
356
357 Use the only table and row 1 as its header. With multiple tables, stop after
358 listing the reported table candidates and ask the user to choose; rerun with
359 `--table-index`. Use `--header-row` when the SoMark JSON clearly identifies a
360 different header row. Determine these only from JSON. Record fields remain
361 optional, completely empty rows are preserved, and no `required` field may
362 stop creation. If `requires_confirmation=true`, show the mapping warnings;
363 when the user confirms or already insists on Bitable, reuse the prepared
364 mapping and continue. Use manual `prepare` only when the user explicitly provides
365 a custom mapping or an existing Base requires different target field names.
366
3673. Create a new Base with an empty first table. The script derives Feishu
368 `text`, `number`, and `datetime` fields from the mapping and saves the
369 returned URL, Base token, and table ID in `<base-target.json>`. Do not add an
370 internal `Source Key` field. Do not send or open the Base URL at this stage:
371
372```powershell
373python <skill-dir>\scripts\somark_to_bitable.py create-base `
374 --mapping <field-mapping.json> `
375 --name "<source name> - SoMark" `
376 --table-name "数据" `
377 --output <base-target.json>
378```
379
380 `create-base` saves a sanitized successful CLI response beside the target
381 before decoding resource IDs. It reads the new table ID from
382 `data.table.table_id` or `data.table.id`, never from a recursive generic
383 `id` search. If table ID decoding or recovery fails after the remote Base
384 was created, rerun this exact command with the same `--output` path. It will
385 reuse the saved response, query `+table-list`, match `--table-name` exactly,
386 and write the target without calling `+base-create` again. Do not change the
387 output path, delete the response snapshot, or retry with a fresh Base.
388
3894. Start record creation with `start-create`. This command writes the first
390 batch of up to 200 records before returning `preview_url`, and writes any
391 unsubmitted records to `<remaining-records.json>`. Do not run a dry-run or
392 a remote pre-read because either delays first usable content:
393
394```powershell
395python <skill-dir>\scripts\somark_to_bitable.py start-create `
396 --target <base-target.json> `
397 --payload <records.json> `
398 --remaining-output <remaining-records.json>
399```
400
4015. Only after `start-create` returns `phase=first_batch_written` and a positive
402 `created_record_count`, send its `preview_url` to the user in a progress
403 message. Say that the first records are available and that remaining
404 records, views, or other finishing work may still be in progress. This is a
405 hard response boundary: emit the URL before another tool call. If the first
406 batch fails, report the failure and do not expose the still-empty Base.
4076. If `remaining_record_count` is greater than zero, continue with the saved
408 continuation payload. Skip this command when the count is zero:
409
410```powershell
411python <skill-dir>\scripts\somark_to_bitable.py create `
412 --target <base-target.json> `
413 --payload <remaining-records.json>
414```
415
4167. The writer performs no remote pre-read and writes at most 200 records per
417 batch. When writing completes, report the total created count and any schema
418 assumptions. If continuation fails, keep the already shared partially
419 filled Base available and report the failed stage. Configure calendar,
420 Gantt, or other views only after the required typed fields exist.
421
422Do not use the combined `run` command for the default new-Base path because a
423single long-running command prevents the Agent from sharing the preview link
424after the first successful record batch and before continuation writing.
425
426### Explicit existing-Base path
427
428Use this path only when the user's prompt explicitly names an existing Base as
429the destination. Parse the source first, then resolve the Base URL/token, read
430its real fields, and build a compatible mapping. Generate `<records.json>` with
431`prepare`, then write it directly with the existing Base token and table ID:
432
433```powershell
434python <skill-dir>\scripts\somark_to_bitable.py create `
435 --base-token <base-token> `
436 --table-id <table-id> `
437 --payload <records.json>
438```
439
440For an explicitly selected existing Base, `run` remains available after the
441mapping has been built from the parsed result:
442
443```powershell
444python <skill-dir>\scripts\somark_to_bitable.py run `
445 --json <somark.json> `
446 --mapping <field-mapping.json> `
447 --source-file <original-file.pdf> `
448 --payload-output <records.json> `
449 --base-token <base-token> `
450 --table-id <table-id>
451```