CtrlK
BlogDocsLog inGet started
Tessl Logo

docx-skill

Word 文档(.docx)读/写/转/评审一体化。触发场景:总结 docx、读段落/大纲/表格/图片/评论/修订、替换正文、插入图片、改页面设置、重建目录、加评论或回复、加修订(insert/delete/replace)、接受/驳回修订、docx ↔ pdf/html/md/txt 互转。典型说法:"帮我总结这份 Word"、"第二段改成 XXX"、"把全部'A 公司'换成'B 公司'"、"加一条修订批注"、"接受所有修订"、"docx 转 pdf"、"从这些要点生成销售报告"。用户提到 .docx / .doc 文件或以 Word 文档为产出目标都加载本技能。

72

Quality

88%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

SKILL.md
Quality
Evals
Security

DOCX 技能

读用 WordInspect写 / 改 / 创建用 JsSandbox 跑 Node 脚本(库首选 docx);格式互转用 DocConvert

工具清单

工具做什么只读
WordInspect读:summary / outline / text / tables / images / comments / tracked-changes / xml / render
JsSandbox所有写:create / replace-text / add-image / comment / resolve-changes …… 用 docx 库 + adm-zip 代码实现
DocConvertdocx ↔ pdf / html / md / txt
CloudImageUnderstand扫描件 docx 的 OCR 入口

加载(两步)

  1. LoadSkill docx-skill
  2. ToolSearch(query: "select:WordInspect,JsSandbox,DocConvert")

Read / DocPreview 对 .docx 只返回 Markdown 级正文(丢 rPr / pPr / 表格合并 / 修订 / 评论);任何"分析/总结/改/建/评审 Word"都走 WordInspect + JsSandbox


1. 读:WordInspect(summary) 先行

WordInspect { action: "summary", filePath: "…" }

返回 pageCount / wordCount / headingCount / hasTrackedChanges / hasComments / isProtected / availableStyles,按分派:

特征下一步
isProtected: true写操作不支持,先 DocConvert 生成未保护副本
hasTrackedChangesWordInspect(tracked-changes) → 用下面 2.4 的脚本 resolve
hasCommentsWordInspect(comments) 拿 parent/reply 树
wordCount === 0 && pageCount >= 1可能扫描件,WordInspect(render) + CloudImageUnderstand

2. 写:JsSandbox + docx

docx npm 包(flumens/docx),编程式构建 docx OOXML。对样式 / 修订 / 评论覆盖最全。

2.1 Demo:生成中文会议纪要

import {
  Document, Packer, Paragraph, TextRun, HeadingLevel, Table, TableRow, TableCell,
  WidthType, AlignmentType, PageOrientation,
} from 'docx'
import fs from 'node:fs/promises'

const title = new Paragraph({
  heading: HeadingLevel.HEADING_1,
  alignment: AlignmentType.CENTER,
  children: [new TextRun({ text: 'Q2 产品规划讨论 — 会议纪要', bold: true, size: 36 })],
})

const meta = new Paragraph({
  children: [new TextRun({ text: '日期:2026-04-20   参会:张三 / 李四 / 王五', size: 22 })],
})

const decisions = [
  ['张三', 'Q2 功能排期', '2026-05-10'],
  ['李四', 'API 文档', '2026-04-30'],
]
const tbl = new Table({
  width: { size: 100, type: WidthType.PERCENTAGE },
  rows: [
    new TableRow({
      tableHeader: true,
      children: ['负责人', '事项', '截止日'].map(
        t => new TableCell({
          children: [new Paragraph({ children: [new TextRun({ text: t, bold: true })] })],
        }),
      ),
    }),
    ...decisions.map(row => new TableRow({
      children: row.map(v => new TableCell({ children: [new Paragraph(v)] })),
    })),
  ],
})

const doc = new Document({
  creator: 'OpenLoaf',
  styles: {
    default: {
      document: { run: { font: 'Microsoft YaHei' } }, // CJK 必设
    },
  },
  sections: [{
    properties: { page: { orientation: PageOrientation.PORTRAIT } },
    children: [title, meta, new Paragraph({ text: '' }), tbl],
  }],
})

await fs.writeFile('meeting_notes.docx', await Packer.toBuffer(doc))
console.log('meeting_notes.docx written')

CJK 字体:必设 styles.default.document.run.font = 'Microsoft YaHei' / 'SimSun' / 'Noto Sans CJK SC',否则 Word 里可能显示 □。

2.2 Demo:替换文字(simple find-replace)

docx 里 {{placeholder}} 可能被 Word 切到多个 w:r run;先用 WordInspect(xml) 看结构。若占位符在单个 run 里:

import AdmZip from 'adm-zip'
import fs from 'node:fs/promises'

const zip = new AdmZip(await fs.readFile('in.docx'))
let xml = zip.readAsText('word/document.xml')
xml = xml
  .replace(/\{\{date\}\}/g, '2026-04-20')
  .replace(/\{\{name\}\}/g, '张三')
zip.updateFile('word/document.xml', Buffer.from(xml, 'utf-8'))
await fs.writeFile('out.docx', zip.toBuffer())
console.log('replaced')

多 run 切分时,一次性 new Document({...}) 重写更可靠。

2.3 Demo:插图 + 页眉 logo

import {
  Document, Packer, Paragraph, ImageRun, Header, AlignmentType,
} from 'docx'
import fs from 'node:fs/promises'

const logo = await fs.readFile('logo.png')

const doc = new Document({
  sections: [{
    headers: {
      default: new Header({
        children: [new Paragraph({
          alignment: AlignmentType.RIGHT,
          children: [new ImageRun({
            data: logo,
            transformation: { width: 80, height: 24 },
          })],
        })],
      }),
    },
    children: [
      new Paragraph({ text: '报告正文...' }),
      new Paragraph({
        children: [new ImageRun({
          data: await fs.readFile('chart.png'),
          transformation: { width: 500, height: 300 },
        })],
      }),
    ],
  }],
})
await fs.writeFile('report.docx', await Packer.toBuffer(doc))

2.4 Demo:接受所有 tracked changes

没有直接 API,用 adm-zipdocument.xml

import AdmZip from 'adm-zip'
import fs from 'node:fs/promises'

const zip = new AdmZip(await fs.readFile('in.docx'))
let xml = zip.readAsText('word/document.xml')
// 1) 接受所有 w:ins(保留内容,去掉标签)
xml = xml.replace(/<w:ins [^>]*>([\s\S]*?)<\/w:ins>/g, '$1')
// 2) 接受所有 w:del(删掉被删除的内容)
xml = xml.replace(/<w:del [^>]*>[\s\S]*?<\/w:del>/g, '')
zip.updateFile('word/document.xml', Buffer.from(xml, 'utf-8'))
await fs.writeFile('out.docx', zip.toBuffer())
console.log('tracked changes accepted')

3. 格式互转用 DocConvert

DocConvert(from="docx", to="pdf",  sourcePath="…")
DocConvert(from="docx", to="md",   sourcePath="…")
DocConvert(from="docx", to="html", sourcePath="…")

底层 LibreOffice headless,排版保真度优于手搓。


4. 常见陷阱

症状原因处理
中文变方块未设 CJK 字体styles.default.document.run.font = 'Microsoft YaHei'
find-replace 失效占位符切到多个 runWordInspect(xml) 看结构,或 docx 重写
ImageRun 图片变形未给 transformation.width/height必传像素尺寸
docx 打开提示"修复文档"XML 不合法 / 手搓 XMLdocx 包生成别手改 OOXML

修脚本:JsSandbox(action="edit-and-run", scriptPath=<上次返回的>, edits=[{find,replace}]) 只传改动点。

Repository
itsablabla/openloaf-web
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.