对 .docx 格式的合同内容进行智能审核,快速识别潜在的法律与商业风险。审核完成后,系统将高亮标注出问题条款,并针对每项问题提供具体的修改建议,辅助您高效完成合同审查。
将以下命令发送给 AI 助手,AI 将获取安装索引后完成安装:
curl …/skills/contract-redline/download,然后按照返回的 Markdown 文件清单完成 contract-redline 的安装Produces three outputs:
1. Redlined `.docx` — original document with tracked changes (visible del/ins in Word) and margin comments explaining each change
2. Summary document — clean Word file listing all issues and recommended changes, organized by risk level
3. Brief explanation — a short message to the user describing what was changed and why
This skill builds on the docx skill. Use the docx skill's unpack.py, pack.py, and comment.py scripts.
---
Unpack and read the full document:
python .claude/skills/docx/scripts/office/unpack.py contract.docx unpacked/Then read unpacked/word/document.xml. Use pandoc for a cleaner text view if the XML is hard to parse:
pandoc --track-changes=all contract.docx -o contract.mdNote any existing tracked changes by other authors — you'll need to avoid conflicting with their IDs.
See references/analysis-framework.md for a comprehensive checklist of what to look for by contract type. Key questions:
Aim for 5–15 targeted changes. More than 15 starts to overwhelm the document.
Before touching the XML, list all planned changes in a structured way:
| # | Section | Old text (short) | Change type | New text (short) | Risk level |
|---|---------|-----------------|-------------|-----------------|------------|
| C1 | §3.3 | "30 days prepayment" | del+ins | "net 30 from invoice" | Medium |
| C2 | §5.1 | "ML training" | del | — | High |
...
This planning step prevents half-applied changes and makes it easy to write assertions.
Before assigning IDs to your changes, scan the document for existing ones to avoid collisions:
python -c "
import re
with open('unpacked/word/document.xml', encoding='utf-8') as f:
doc = f.read()
ids = [int(x) for x in re.findall(r'w:id=\"(\d+)\"', doc)]
print('Max existing ID:', max(ids) if ids else 0)
"Start your IDs at max_id + 1000 (e.g., if max is 8527, use 9001, 9002, ...). This leaves room for the original document's IDs to remain stable.
When there are 5 or more changes, use a Python script (not the Edit tool) for reliability. The Edit tool is fine for 1–3 simple edits; scripts are safer for bulk changes.
Use scripts/apply_redlines.py as your template — it handles the assertion/uniqueness pattern. Write your changes as a list of (label, old_string, new_string) tuples.
The cardinal rules of OOXML tracked changes:
NEVER put <w:del> or <w:ins> inside <w:t>.
<w:t> contains only text. Period.
<w:del> and <w:ins> are SIBLINGS of <w:r>, never children of <w:t>.Correct structure:
<!-- Split a run to change "thirty" to "sixty" -->
<w:r><w:t xml:space="preserve">Pay within </w:t></w:r>
<w:del w:id="9001" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:delText xml:space="preserve">thirty</w:delText></w:r>
</w:del>
<w:ins w:id="9002" w:author="Claude" w:date="2025-01-01T00:00:00Z">
<w:r><w:t xml:space="preserve">sixty</w:t></w:r>
</w:ins>
<w:r><w:t xml:space="preserve"> days.</w:t></w:r>Wrong (crashes Word):
<!-- WRONG: del/ins inside w:t -->
<w:t>Pay within <w:del...>thirty</w:del><w:ins...>sixty</w:ins> days.</w:t>Splitting a run: When your target text sits in the middle of a longer run, split the surrounding <w:r> into parts. The prefix and suffix stay as normal runs; the changed part becomes del/ins siblings:
<!-- Old: single run "Pay within thirty days of invoice." -->
<!-- New: split into prefix + del/ins + suffix -->
<w:r><w:rPr>...same rPr...</w:rPr><w:t xml:space="preserve">Pay within </w:t></w:r>
<w:del w:id="9001" w:author="Claude" w:date="...">
<w:r><w:rPr>...same rPr...</w:rPr><w:delText>thirty</w:delText></w:r>
</w:del>
<w:ins w:id="9002" w:author="Claude" w:date="...">
<w:r><w:rPr>...same rPr...</w:rPr><w:t>sixty</w:t></w:r>
</w:ins>
<w:r><w:rPr>...same rPr...</w:rPr><w:t xml:space="preserve"> days of invoice.</w:t></w:r>Copy the <w:rPr> from the original run into each split piece to preserve formatting (bold, font, color).
Modifying another author's insertion: When you need to change text inside an existing <w:ins> by someone else, split their <w:ins> into multiple pieces (the unchanged parts stay inside their <w:ins>, giving them different IDs; your changes go between them):
<!-- Original: Kimi's ins with "thirty days" -->
<w:ins w:author="Kimi" w:id="500">
<w:r><w:t>Pay within thirty days.</w:t></w:r>
</w:ins>
<!-- Revised: split Kimi's ins, add Claude's del/ins between -->
<w:ins w:author="Kimi" w:id="500">
<w:r><w:t xml:space="preserve">Pay within </w:t></w:r>
</w:ins>
<w:del w:id="9001" w:author="Claude" w:date="...">
<w:r><w:delText>thirty</w:delText></w:r>
</w:del>
<w:ins w:id="9002" w:author="Claude" w:date="...">
<w:r><w:t>sixty</w:t></w:r>
</w:ins>
<w:ins w:author="Kimi" w:id="9101"> <!-- new unique ID for the suffix -->
<w:r><w:t xml:space="preserve"> days.</w:t></w:r>
</w:ins>Assertion pattern for every replacement:
assert old in doc, f"C1: anchor text not found — check XML after unpack"
assert doc.count(old) == 1, f"C1: {doc.count(old)} matches — make anchor more specific"
doc = doc.replace(old, new, 1)If an assertion fails, your anchor string is either wrong or not unique. Widen the anchor (include more surrounding XML) to make it unique.
Use comment.py for each change, starting from max_existing_comment_id + 1:
python .claude/skills/docx/scripts/comment.py unpacked/ 31 "Risk: auto-renewal trap — vendor can raise price unilaterally on renewal. Change: require 90-day written notice and mutual written agreement on new price." --author "Claude"Then add markers to document.xml around the changed text. Markers are direct children of `<w:p>` — never inside <w:r>:
<w:commentRangeStart w:id="31"/>
<w:del w:id="9001" w:author="Claude" w:date="...">
<w:r><w:delText>auto-renews at vendor's current rates</w:delText></w:r>
</w:del>
<w:ins w:id="9002" w:author="Claude" w:date="...">
<w:r><w:t>renewed only upon mutual written agreement on new price</w:t></w:r>
</w:ins>
<w:commentRangeEnd w:id="31"/>
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="31"/></w:r>python .claude/skills/docx/scripts/office/pack.py unpacked/ contract_redlined.docx --original contract.docxOn Windows, if the validator fails with a codec error (GBK), use --validate false:
python .claude/skills/docx/scripts/office/pack.py unpacked/ contract_redlined.docx --original contract.docx --validate falseThen verify the result opens correctly:
import zipfile, xml.etree.ElementTree as ET
with zipfile.ZipFile('contract_redlined.docx') as z:
ET.fromstring(z.read('word/document.xml'))
ET.fromstring(z.read('word/comments.xml'))
print("Valid XML — document should open in Word")Create a clean summary .docx using docx-js (see the docx skill). Structure:
CONTRACT REVIEW SUMMARY
[Date] | [Contract title] | [Parties]
EXECUTIVE SUMMARY
[2–3 sentences: overall risk level, key concerns, recommendation]
HIGH-RISK ISSUES (🔴)
[Issue name]
Risk: [what could go wrong]
Location: [clause number]
Change: [what was modified]
MEDIUM-RISK ISSUES (🟡)
...
LOW-RISK / NOTES (🟢)
...
RECOMMENDED NEXT STEPS
...---
| Symptom | Cause | Fix |
|---------|-------|-----|
| Word says "content issues" on open | XML element inside <w:t> | Find <w:del/<w:ins inside <w:t> and restructure at <w:r> level |
| Assertion fails: "anchor not found" | Whitespace/newlines differ post-unpack | Use sed -n 'LINE,LINEp' document.xml to see exact content |
| Assertion fails: "N matches" | Anchor string appears multiple times | Extend old string to include more surrounding context |
| Comment doesn't appear | Markers inside <w:r> instead of as siblings | Move commentRangeStart/End outside any <w:r> block |
| ID collision (del/ins attributed to wrong author) | New IDs overlap existing | Re-scan IDs, pick new starting point |
| Pack fails with "gbk codec" error on Windows | Validator uses wrong encoding | Add --validate false flag |
---
references/analysis-framework.md — Legal analysis checklist by contract type (SaaS, NDA, employment, services, IP licensing)在进行任何创造性工作(如创建功能、构建组件、添加功能或修改行为)之前,先探究用户意图、需求和设计。它强制你在动手写代码之前先做设计。它的核心理念是:任何项目,不管多简单,都必须先经过设计讨论,获得你认可后才能开始实现。整个过程分几步:先了解项目上下文,看看文件、文档、最近的提交。然后一个一个问题问清楚,搞明白目的、约束和成功标准。 接下来提出 2-3 个方案,说明各自的优缺点,给出你的推荐理由。 最后呈现设计,按模块逐步展示,每个模块确认没问题再往下走。设计通过后,写一份设计文档保存到 docs/plans/ 目录,然后才能调用实现相关的 Skill。 有个硬性规定:在用户批准设计之前,禁止调用任何实现类 Skill,禁止写代码,禁止搭建项目。 听起来有点繁琐,但实际上能避免很多返工。很多时候我们觉得简单的项目,做着做着就发现各种问题,还不如一开始就把事情想清楚。
Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.