Lark Hirono — Feishu Document Toolkit
Upload markdown as styled Feishu documents, optimize existing pages, fetch/analyze/highlight/verify.
Skill Files Location
This skill's supporting files are located relative to this SKILL.md:
references/optimization-guide.md— Quality standards and reference patterns for document transformationreferences/mermaid-guide.md— When and how to generate mermaid diagrams; examples for common patterns
Running the CLI
Determine the correct invocation once at the start of each session, trying each step in order:
- Global install — check if
lark-hironois on PATH:command -v lark-hironoIf found, uselark-hirono <action> [options]for all commands. If not found, install withnpm install -g lark-hirono; and update to latest vianpm update -g lark-hirono - Local dev repo — only if working inside a cloned
lark-hironorepo (the directory containingpackage.jsonwith"name": "lark-hirono"), ensure deps are installed (npm install) and run:npx tsx bin/lark-hirono.ts <action> [options]
All examples below use lark-hirono as shorthand — substitute the resolved command.
First-Time Setup
Run these steps once before first use. Each step is a prerequisite for the next.
1. Install lark-hirono
npm install -g lark-hirono2. Install lark-cli
lark-hirono calls the lark-cli binary (>= 1.0.9) as a subprocess for all Feishu API operations. If it's not already installed:
mkdir -p /tmp/larkcli && cd /tmp/larkcli
npm init -y && npm install @larksuite/cli
node node_modules/@larksuite/cli/scripts/install.jsOr set the LARK_CLI environment variable to point to an existing lark-cli binary.
Verify installation:
lark-cli --version # should print >= 1.0.93. Configure lark-cli
Initialize the app credentials that lark-cli uses to call Feishu APIs:
lark-cli config initThis is an interactive setup — follow the prompts to provide your Feishu app ID and secret. For details on app setup and scopes, see the lark-shared skill (lark-cli config init / lark-cli auth login).
4. Authenticate
lark-hirono auth login --domain docs
# or equivalently: lark-cli auth login --domain docsThis opens a browser for Feishu OAuth. After login, the token is cached by lark-cli (typically in ~/.config/lark/).
Verify auth:
lark-hirono auth statusIf the token has expired, re-run auth login.
5. Optional config file
Create lark-hirono.json in your project directory for defaults (wiki space, node, background mode). This file is optional — all values can be passed as CLI flags instead.
Prerequisites Summary
- Node.js 20+
lark-cli>= 1.0.9 (installed and authenticated)lark-hirono.jsonconfig file (optional, for wiki-space/wiki-node defaults)
Data Locations
- Config file:
lark-hirono.jsonin current directory or ancestors - Token cache: Managed by
lark-cli(typically~/.config/lark/) - Keyword batches:
<input.md>.keywords_batch_N.json(generated byhighlight extract) - Selected keywords:
<input.md>.selected_keywords.json(LLM output, used by pipeline)
Arguments
$ARGUMENTS should be: ACTION [ARGS...]
Determine the action from user intent:
| User intent | Action |
|---|---|
| Upload local markdown to Feishu | upload <input.md> |
| Optimize existing Feishu doc | Skill workflow (fetch → edit → upload) |
| Update Feishu doc from local file | optimize --doc <id> --input <file> |
| Fetch Feishu doc as markdown | fetch --doc <id> |
| Analyze document structure | analyze <input.md> |
| Extract table titles for LLM | highlight extract <input.md> |
| Apply keyword highlights | highlight apply <input.md> <keywords.json> |
| Verify Feishu doc quality | verify --doc <id> |
| Authentication management | auth <subcommand> |
Action: upload
Upload local markdown file as styled Feishu document.
lark-hirono upload <input.md> [title] [options]Options:
--title <title>— Document title (default: first H1)--wiki-space <id>— Wiki space ID--wiki-node <id>— Parent node ID--bg-mode <mode>— Heading background: light | dark--image-dir <dir>— Directory for downloaded images--strip-title— Remove first H1 heading--no-highlight— Skip keyword highlighting--frontmatter-as-callout— Convert YAML frontmatter into a Meta callout (blockquote → Feishu callout). Without the flag, frontmatter passes through unchanged.--mention-map <path>— JSON map keyed by doc URL:{"<url>": {"obj_token": "…", "obj_type"?: 22, "title"?: "…"}}. After upload, anytext_runlink matching a key is rewritten to a nativemention_docelement so Feishu's backlinks panel and graph view populate.--verify— Fetch and verify after upload--dry-run— Preprocess only, print markdown to stdout-v, --verbose— Verbose logging
Input format notes:
[^name]inline footnote refs +[^name]: bodydefinitions are auto-detected and rewritten to unicode superscripts (⁽¹⁾…) plus a trailing## Footnotessection. Idempotent and a no-op on inputs without footnote defs. Content inside fenced code blocks is left alone.
For catalog_table docs (tables with Code/Title columns):
- Run
highlight extract input.mdfirst - Send JSON to LLM, save as
.selected_keywords.json - Upload will auto-apply keywords if file exists
Example:
lark-hirono upload my-doc.md "My Document" --wiki-space 123456Action: optimize
Light-touch optimization of a Feishu wiki document: fix errors, repair corruption, improve formatting — while preserving the author's tone, style, and intent.
Core principle: The original document is the authority. The reference document only guides formatting structure. Never rewrite or restructure aggressively.
Output: A new wiki sub-page under the reference document containing the optimized version.
How it works: The skill uses CLI commands (fetch, upload, analyze) as tools while applying LLM judgment for corruption detection, emphasis decisions, and quality audits.
Arguments
- SOURCE_URL: The Feishu wiki URL to optimize, e.g.
https://my.feishu.cn/wiki/Abc123 - DEST_URL (optional): A well-formatted reference doc URL; new page is created under its parent node
Extracting IDs from URLs:
- Wiki URL
https://my.feishu.cn/wiki/Abc123XYZ→ doc/node ID isAbc123XYZ(the path segment after/wiki/) - Use this ID for
--doc,--wiki-nodeCLI flags
Workflow
Follow these tasks in order:
- Read Source — Fetch and understand the source document
- Read Reference — Fetch and analyze the reference document's format
- Format Analysis — Compare structures and identify format gaps
- Content Review — Identify factual errors, incomplete descriptions, minor issues
- Generate Optimized Version — Apply light-touch improvements 5.5. Content Preservation Audit — Verify no content was dropped or shortened 5.6. Emphasis Audit — Verify color/bold density is appropriate 5.7. Rendered-Layout Regression Check — Verify no visual regressions
- Write to Feishu — Create new sub-page with optimized content
Task 1: Read Source Document
lark-hirono fetch --doc <source-doc-id>Save the full output. This is the document to optimize.
Analyze carefully: topic, purpose, writing style/tone, heading structure, content flow, formatting patterns, and technical payload that must survive (numbers, formulas, systems, deployment facts, experiment settings, citations, caveats). Record observations for Task 5.
Task 2: Read Reference Document
lark-hirono fetch --doc <reference-doc-id>Save the full output. This is the format reference only — not a content source.
Analyze the FORMAT only: heading hierarchy, section organization, formatting conventions (bold, lists), content density, structural elements (dividers, quotes, code blocks).
Task 3: Format Analysis
Read references/optimization-guide.md for the detailed comparison framework and known format conventions.
Reference Discipline:
- The optimization guide is a style reference, not a license to restyle the document wholesale
- Preserve the source document's macro-structure unless there is a clear formatting defect
- Do not renumber or deepen the heading hierarchy just to match the reference pattern if the source already has a stable hierarchy
- If an opening block, metadata block, or summary layout already works, prefer light cleanup over redesign
Check the source document against these specific format conventions:
| Convention | Expected Pattern | |
|---|---|---|
| Opening callout | [!callout] with description + paper/repo links | |
| Numbered headings | ## 1 标题 / ### 1.1 子标题 | |
| Heading groups | Continuous numbering across whole doc | |
| Level conflicts | Group/chapter titles at correct level vs. sub-items | |
| Callouts/quotes | `\ | > / [!callout] / > 📌` freely throughout |
| Code block tags | Language tag on every code block | |
| Bold key terms | First mention of important technical terms bolded | |
| Inline emphasis | {red:...} for key points; {green:...} for key terms |
Hard Constraints:
- Do not assume numbered headings are mandatory when the source already has a coherent hierarchy
- Exception — Chinese ordinal headings are always a defect:
## 一、,## 二、,## 三、… use Chinese characters that do NOT trigger the tool's blue-number coloring. Always convert to## 1 Title,## 2 Title… format. This is not optional. - Do not force extra subsection levels simply because the reference is more granular
- All siblings at the same heading level must be consistently numbered or consistently unnumbered. A lone unnumbered heading (e.g.,
## Cheatsheet) among numbered siblings must be assigned the next sequential number. - Treat opening layout, metadata layout, and top-of-document summary layout as high-risk areas where over-editing is likely to regress the page
Important: Only note FORMAT differences. Do not compare content topics.
Task 4: Content Review (Light Touch)
Review the source document for issues that should be fixed regardless of format:
Fix These:
- Factual errors: Incorrect technical information, wrong version numbers, broken links
- Incomplete descriptions: Sentences that trail off, missing explanations, TODO/placeholder text
- Obvious typos: Spelling errors, grammar mistakes that change meaning
- Broken formatting: Unclosed markdown, inconsistent list indentation
- Feishu export corruption: Paragraphs that became headings, broken heading hierarchy, merged content
Do NOT Change:
- Author's tone: If they write casually, keep it casual
- Content focus: If they emphasize certain topics, respect that emphasis
- Technical opinions: If they recommend a tool or approach, keep it
- Original structure: Only adjust structure if it clearly conflicts with the reference format
- Level of detail: If the author was brief on a topic, they may have had a reason
Task 4.5: Mermaid Diagram (Best-Effort)
Read references/mermaid-guide.md for patterns, rules, and placement examples.
Decision: does this document benefit from a diagram? Generate one mermaid block (placed after the opening callout, before the first heading) if the document describes a multi-stage pipeline, system architecture, branching algorithm, or process flow. Skip for reference tables, FAQs, catalogs, or docs with no flow structure. When in doubt, skip — a bad diagram is worse than no diagram.
The pipeline handles all visual styling automatically. You only write the structure.
Task 5: Generate Optimized Version
CRITICAL: Read this section carefully before writing.
The 80/20 Rule
The optimized document should be 80%+ identical to the original. Changes should be subtle improvements, not rewrites.
What to change:
- Opening callout — REQUIRED for narrative documents (text-heavy, few tables). Skip for catalog_table or data_table documents.
- Write it as the very first block, before any heading. - Format: <callout emoji="ICON" background-color="light-blue" border-color="light-blue"> — choose an icon (e.g. gift, bulb, bookmark, pushpin, rocket, star) - Put the document's intro/summary description inside, closed with </callout> - If the source already has an intro paragraph, blockquote, or summary section that describes the document, reuse or lightly optimize that content as the callout body — do not invent new summary text - If no such content exists, synthesize a 1–3 sentence summary from the document's main topic and scope - Pipeline safety net: Pipeline auto-injects from first paragraph as fallback — always write explicitly for control over content and emoji.
- Heading restructuring — Fix numbering AND hierarchy when needed:
- Apply continuous sequential numbering: ## 1, ## 2… for top-level; ### 1.1, ### 1.2… for second-level - Input format: Write ## 1 Title (number then space then title — no period in the markdown you write). The pipeline adds a period automatically for single-level numbers: ## 1 Title → rendered as 1. Title with blue prefix. Multi-level numbers (### 1.1 Sub) do NOT get a period added. - Fix Chinese ordinals: ## 一、标题 → ## 1 标题 - Fix corruption: Paragraphs that became headings → convert back to paragraphs - Preserve hierarchy: Do not add or remove heading levels unless structure is broken
- Add emphasis — Apply
{red:...}and{green:...}markers:
- Red emphasis: Key conclusions, results, important claims (10-20 instances in a long doc) - Bold phrase: {red:**important conclusion**} → renders bold + red - Plain text: {red:term} → renders red - Inline code: ` {red:cmd} → renders red code - **Green emphasis**: First mention of key technical terms (5-10 instances) - Plain text: {green:term} → renders green - Inline code: {green:code} → renders green code - **CRITICAL — convert raw <text color> tags from fetched docs**: Raw <text color="green">BF16</text> HTML from fetched documents does NOT render in uploaded docs — it appears as plain text. During optimization, convert every <text color="COLOR">CONTENT</text> to its shorthand form: {green:CONTENT}, {red:CONTENT}, etc. If the content has bold, place bold inside: {green:CONTENT}. Never preserve the raw <text color> form in the optimized output. - **Preserve all existing emphasis**: Every {red:...}, {green:...}, and bold in the source document MUST be carried over to the optimized version. Add new emphasis on top; never remove original emphasis. - **CRITICAL rendering rules**: Bold INSIDE color tags ({red:text}, never {red:text}). No $formula$ in headings. No ** wrapping $formula$` boundaries. See optimization-guide § "Rendering Pitfalls" for full patterns.
- Add in-line callouts — Insert markdown blockquotes for key insights throughout the document body:
- Use > 📌 **标题**:... for key insights - Use > 📚 **背景**:... for background knowledge - Target: 5-15 such blockquotes throughout a long document - Pipeline auto-conversion: Blockquotes matching TL;DR, 核心思想, 关键结论, 一句话总结, 核心区别 etc. are auto-converted to <callout> XML. Do not manually write <callout> XML for these — write > TL;DR:... and let the pipeline convert. - CRITICAL — no emoji prefix inside <callout> XML body: When writing a <callout> XML block directly, do NOT start the body text with an emoji (📌, 💡, 📚, etc.). The emoji= attribute on the opening tag already provides the icon — adding an emoji in the body text creates a redundant double-icon display. Only the > 📌 **Title**:... blockquote syntax (auto-converted by pipeline) needs the emoji prefix. - CRITICAL — no nested <callout> XML: Lark does not render a <callout> XML block nested inside another <callout> XML block. If the source document contains this pattern, convert the inner <callout> block to plain paragraphs (strip the opening/closing XML tags, keep the text). Markdown blockquotes (>...) inside a <callout> body are fine and are NOT nested callouts.
- Fix code blocks — Add language tags if missing.
- Fix factual errors — Correct typos, broken links, wrong information
- Preserve images, tables, equations verbatim — Copy
<image token>,<lark-table>,<equation>tags unchanged at their original positions. Do not rewrite<equation>as$...$. Do not emit pipe-wrapped|lark-table...|— use<lark-table>XML or standard markdown tables. Seereferences/optimization-guide.md§ "Verified Syntax Reference" for full correct/incorrect patterns.
- CRITICAL — equations: Never write bare LaTeX subscripts (_{\text{...}}) outside $...$ or <equation> delimiters — bare _ becomes italic. Multi-subscript bug: 2+ _{...} in one formula breaks (lark-cli pairs _ as italic). Workaround: use single-char subscripts without braces (_p, _h). Single _{...} per formula is safe. - CRITICAL — lark-table cells: ***bold-italic*** with underscored content (e.g. ***sm__throughput***) breaks — lark-cli misinterprets _ inside ***. Use backtick code format (` sm__throughput `) for metric names with underscores.
<quote-container>handling — lark-cli upload does NOT support<quote-container>(silently drops). Pipeline auto-converts: → blockquote (outside tables) /<callout>grey (inside tables). Leave as-is or convert manually.
What NOT to change:
- Do not rewrite paragraphs for style
- Do not add new sections or remove existing sections
- Do not change technical details or recommendations
- Do not restructure the document's argument flow
Task 5.5: Content Preservation Audit
CRITICAL: Before writing to Feishu, verify that no content was lost.
Compare the optimized version against the source:
- All sections present (no sections removed)
- All paragraphs present (no paragraphs dropped)
- All code blocks present (no code removed)
- All tables present (no data lost)
- All links preserved (no links removed)
- Technical details intact (numbers, formulas, commands)
- All
<image token="...">tags present (count source vs output — zero missing) - All
<equation>blocks present and unchanged (do not rewrite or split formulas)
If content is missing: Add it back. The optimized version must have 100% of the original content.
Task 5.6: Emphasis Audit
Verify that emphasis density is appropriate:
- Red emphasis: 10-20 instances in a long document (or proportional to length)
- Green emphasis: 5-10 instances for first-mention technical terms
- Not over-emphasized: No more than 3 emphasized items per paragraph
- Not under-emphasized: Key conclusions are highlighted
Task 5.7: Rendered-Layout Regression Check
Verify that the optimized document will not have visual regressions:
- Headings will render correctly (proper hierarchy)
- Code blocks have language tags
- Tables will render (proper markdown table syntax)
- Callouts will render (proper blockquote syntax)
- No unclosed markdown syntax
Task 6: Write to Feishu
If the source was a local file (e.g., report.md): write the optimized content to report_upload.md (same directory, _upload suffix). Never modify the original file. Never use a /tmp/ path.
If the source was fetched from Feishu: write to any convenient temp file (e.g., <title>_upload.md in current directory).
# Step 1: Write optimized content to upload file
# For local source report.md → use report_upload.md
cat > report_upload.md << 'OPTIMIZED_CONTENT_END'
[optimized markdown content here]
OPTIMIZED_CONTENT_END
# Step 2: Upload — default is always to create a new sibling page
lark-hirono optimize --doc <doc-id> --input report_upload.md --new \
--title "Optimized: [Original Title]"
# Only update in place if the user explicitly asked to overwrite the original:
# lark-hirono optimize --doc <doc-id> --input report_upload.mdNote the --new flag creates a sibling page under the same parent node. Capture the returned URL/doc-id for the verification step below.
Task 7: Post-Upload Verification (Required)
After uploading, verify the rendered document has no regressions before reporting to the user.
lark-hirono verify --doc <new-doc-id>Check the output for:
- Callout present (narrative docs)
- Headings numbered sequentially, no Chinese ordinals
- No
\#artifacts (escaped hashes leaked into text — pipeline uses ZWSP instead) - No plain-text
<text color>tags visible in content - Content count plausible (not drastically shorter than source)
If issues are found → fix the local upload file, re-upload with --new (or overwrite the just-created page if you have its ID), and verify again. Iterate up to 3 times total.
After a clean verify (or after exhausting 3 attempts), report to user:
- URL of the new page
- Changes made — brief list of formatting improvements applied
- Confidence level — how confident you are in the changes; note any unresolved issues from the retry loop
Action: fetch
Retrieve Feishu document as markdown.
lark-hirono fetch --doc <doc-id> [--output file.md]Options:
--doc <id>— Document ID (required)--output <file>— Save to file (default: stdout)
Outputs markdown to stdout unless --output is given.
Action: analyze
Analyze markdown document structure.
lark-hirono analyze <input.md>Returns JSON with:
document_type— narrative | data_table | catalog_table | mixedheading_count— Number of headingstable_count— Number of tablestable_rows— Total table rowssuggested_modules— Recommended transformations
Action: highlight
Extract table titles for LLM keyword selection, or apply keywords to markdown.
Extract
lark-hirono highlight extract <input.md> [--batch-size N]Extracts Code/Title columns from tables, saves as:
input.md.keywords_batch_0.jsoninput.md.keywords_batch_1.json(if >200 titles)
Next step: Send JSON files to LLM, save response as input.md.selected_keywords.json
Apply
lark-hirono highlight apply <input.md> <keywords.json> [--inplace]Wraps keywords in {red:**keyword**} tags within table titles.
Note: upload and optimize auto-apply if .selected_keywords.json exists.
Action: verify
Fetch and verify Feishu document quality.
lark-hirono verify --doc <doc-id>
lark-hirono verify <doc-id> # positional formOptions:
--doc <id>— Document ID (required unless passed positionally)-v, --verbose— Verbose output
Reports:
- Block/heading/table counts, heading background coverage
- Red highlights and bold header counts
- Residual HTML tags (should be empty)
- Pass/fail checks for structural and content-level expectations
Exit code 0 on pass, 1 on any failing check.
Action: auth
Feishu authentication management (passthrough to lark-cli).
lark-hirono auth login
lark-hirono auth statusDocument Types
Narrative — Text-heavy documents with headings
- Transforms: normalize, headings, callout
- No LLM needed
Catalog_table — Tables with Code/Title columns, 50+ rows
- Transforms: normalize, tables, keyword highlighting
- LLM needed for keyword selection
Data_table — Tables with <50 rows
- Transforms: normalize, tables
- No LLM needed
Workflow Examples
Upload narrative doc
lark-hirono upload my-article.mdUpload catalog_table doc with keywords
lark-hirono highlight extract catalog.md
# Send catalog.md.keywords_batch_0.json to LLM
# Save response as catalog.md.selected_keywords.json
lark-hirono upload catalog.mdOptimize existing Feishu doc
lark-hirono optimize --doc CcKqdWjF2o2UzixXZDecXxvInycUpdate Feishu doc from local file
lark-hirono optimize --doc CcKqdWjF2o2UzixXZDecXxvInyc --input local.mdUpload a note with frontmatter + native backlinks
# mentions.json is a JSON object keyed by doc URL:
# { "https://www.feishu.cn/wiki/<node_token>": { "obj_token": "<obj_token>" } }
lark-hirono upload post.md \
--frontmatter-as-callout \
--mention-map mentions.jsonAfter upload, any markdown link to a URL in mentions.json becomes a native Feishu doc mention, populating the backlinks panel on the target doc.
Supporting Files
references/optimization-guide.md— Quality standards and reference patternsreferences/mermaid-guide.md— Mermaid diagram patterns and examples