[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XDFXTG73YRT9F0NM27ZX2Q":3,"skill-same:01M6XDFXTG73YRT9F0NM27ZX2Q":58},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":32,"linkOnly":33,"markdownContent":34,"markdownBody":35,"frontMatter":36,"refName":37,"commitSha":38,"skillDirectory":39,"forksCount":13,"repositoryHtmlUrl":40,"repositoryHomepageUrl":6,"ownerHtmlUrl":41,"ownerBlogUrl":6,"ownerDescription":42,"updatedAt":43,"install":44,"resources":57},"01M6XDFXTG73YRT9F0NM27ZX2Q","academic-pdf-redaction",null,"Redact text from PDF documents for blind review anonymization","Waaaalnut\u002Fskillsbench","Waaaalnut","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F103995656?v=4","01M6XCS9K1V51EY3JMB7DGMX24","01M6XCS9Q4WPW88W62C55G1H44",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks-no-skills\u002Fpaper-anonymizer\u002Fenvironment\u002Fskills\u002Facademic-pdf-redaction\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31],"PDF","redaction","Academic","academic pdf redaction","review","documents","text","blind review","for","2026-11-10 10:12:30",false,"---\nname: academic-pdf-redaction\ndescription: Redact text from PDF documents for blind review anonymization\n---\n\n# PDF Redaction for Blind Review\n\nRedact identifying information from academic papers for blind review.\n\n## CRITICAL RULES\n\n1. **PRESERVE References section** - Self-citations MUST remain intact\n2. **ONLY redact specific text matches** - Never redact entire pages\u002Fregions\n3. **VERIFY output** - Check that 80%+ of original text remains\n\n## Common Pitfalls to AVOID\n\n```python\n# ❌ WRONG - This removes ALL text from the page:\nfor block in page.get_text(\"blocks\"):\n    page.add_redact_annot(fitz.Rect(block[:4]))\n\n# ❌ WRONG - Drawing rectangles over text:\npage.draw_rect(fitz.Rect(0, 0, 600, 100), fill=(0,0,0))\n\n# ✅ CORRECT - Only redact specific search matches:\nfor rect in page.search_for(\"John Smith\"):\n    page.add_redact_annot(rect)\n```\n\n## Patterns to Redact (Before References Only)\n\n**IMPORTANT: Use FULL names\u002Fphrases, not partial matches!**\n- ✅ \"John Smith\" (full name)\n- ❌ \"Smith\" (partial - would incorrectly match \"Smith et al.\" citations in References)\n\n1. **Author names** - FULL names only (e.g., \"John Smith\", not just \"Smith\")\n2. **Affiliations** - Universities, companies (e.g., \"Duke University\")\n3. **Email addresses** - Pattern: `*@*.edu`, `*@*.com`\n4. **Venue names** - Conference\u002Fworkshop names (e.g., \"ICML 2024\", \"ICML Workshop\")\n5. **arXiv identifiers** - Pattern: `arXiv:XXXX.XXXXX`\n6. **DOIs** - Pattern: `10.XXXX\u002F...`\n7. **Acknowledgement names** - Names in \"Acknowledgements\" section\n8. **Equal contribution footnotes** - e.g., \"Equal contribution\", \"* Equal contribution\"\n\n## PyMuPDF (fitz) - Recommended Approach\n\n```python\nimport fitz\nimport os\n\ndef redact_with_pymupdf(input_path: str, output_path: str, patterns: list[str]):\n    \"\"\"Redact specific patterns from PDF using PyMuPDF.\"\"\"\n    doc = fitz.open(input_path)\n    original_len = sum(len(p.get_text()) for p in doc)\n\n    # Find References page - stop redacting there\n    references_page = None\n    for i, page in enumerate(doc):\n        if \"references\" in page.get_text().lower():\n            references_page = i\n            break\n\n    for page_num, page in enumerate(doc):\n        if references_page is not None and page_num >= references_page:\n            continue  # Skip References section\n\n        for pattern in patterns:\n            # ONLY redact exact search matches\n            for rect in page.search_for(pattern):\n                page.add_redact_annot(rect, fill=(0, 0, 0))\n        page.apply_redactions()\n\n    os.makedirs(os.path.dirname(output_path), exist_ok=True)\n    doc.save(output_path)\n    doc.close()\n\n    # MUST verify after saving\n    verify_redaction(input_path, output_path)\n```\n\n## REQUIRED: Verification Function\n\n**Always run this after ANY redaction to catch errors early:**\n\n```python\nimport fitz\n\ndef verify_redaction(original_path, output_path):\n    \"\"\"Verify redaction didn't corrupt the PDF.\"\"\"\n    orig = fitz.open(original_path)\n    redc = fitz.open(output_path)\n\n    orig_len = sum(len(p.get_text()) for p in orig)\n    redc_len = sum(len(p.get_text()) for p in redc)\n\n    print(f\"Original: {len(orig)} pages, {orig_len} chars\")\n    print(f\"Redacted: {len(redc)} pages, {redc_len} chars\")\n    print(f\"Retained: {redc_len\u002Forig_len:.1%}\")\n\n    # DEFENSIVE CHECKS - fail fast if something went wrong\n    if len(redc) != len(orig):\n        raise ValueError(f\"Page count changed: {len(orig)} -> {len(redc)}\")\n    if redc_len \u003C 1000:\n        raise ValueError(f\"PDF corrupted: only {redc_len} chars remain!\")\n    if redc_len \u003C orig_len * 0.7:\n        raise ValueError(f\"Too much removed: kept only {redc_len\u002Forig_len:.0%}\")\n\n    orig.close()\n    redc.close()\n    print(\"✓ Verification passed\")\n```\n","# PDF Redaction for Blind Review\n\nRedact identifying information from academic papers for blind review.\n\n## CRITICAL RULES\n\n1. **PRESERVE References section** - Self-citations MUST remain intact\n2. **ONLY redact specific text matches** - Never redact entire pages\u002Fregions\n3. **VERIFY output** - Check that 80%+ of original text remains\n\n## Common Pitfalls to AVOID\n\n```python\n# ❌ WRONG - This removes ALL text from the page:\nfor block in page.get_text(\"blocks\"):\n    page.add_redact_annot(fitz.Rect(block[:4]))\n\n# ❌ WRONG - Drawing rectangles over text:\npage.draw_rect(fitz.Rect(0, 0, 600, 100), fill=(0,0,0))\n\n# ✅ CORRECT - Only redact specific search matches:\nfor rect in page.search_for(\"John Smith\"):\n    page.add_redact_annot(rect)\n```\n\n## Patterns to Redact (Before References Only)\n\n**IMPORTANT: Use FULL names\u002Fphrases, not partial matches!**\n- ✅ \"John Smith\" (full name)\n- ❌ \"Smith\" (partial - would incorrectly match \"Smith et al.\" citations in References)\n\n1. **Author names** - FULL names only (e.g., \"John Smith\", not just \"Smith\")\n2. **Affiliations** - Universities, companies (e.g., \"Duke University\")\n3. **Email addresses** - Pattern: `*@*.edu`, `*@*.com`\n4. **Venue names** - Conference\u002Fworkshop names (e.g., \"ICML 2024\", \"ICML Workshop\")\n5. **arXiv identifiers** - Pattern: `arXiv:XXXX.XXXXX`\n6. **DOIs** - Pattern: `10.XXXX\u002F...`\n7. **Acknowledgement names** - Names in \"Acknowledgements\" section\n8. **Equal contribution footnotes** - e.g., \"Equal contribution\", \"* Equal contribution\"\n\n## PyMuPDF (fitz) - Recommended Approach\n\n```python\nimport fitz\nimport os\n\ndef redact_with_pymupdf(input_path: str, output_path: str, patterns: list[str]):\n    \"\"\"Redact specific patterns from PDF using PyMuPDF.\"\"\"\n    doc = fitz.open(input_path)\n    original_len = sum(len(p.get_text()) for p in doc)\n\n    # Find References page - stop redacting there\n    references_page = None\n    for i, page in enumerate(doc):\n        if \"references\" in page.get_text().lower():\n            references_page = i\n            break\n\n    for page_num, page in enumerate(doc):\n        if references_page is not None and page_num >= references_page:\n            continue  # Skip References section\n\n        for pattern in patterns:\n            # ONLY redact exact search matches\n            for rect in page.search_for(pattern):\n                page.add_redact_annot(rect, fill=(0, 0, 0))\n        page.apply_redactions()\n\n    os.makedirs(os.path.dirname(output_path), exist_ok=True)\n    doc.save(output_path)\n    doc.close()\n\n    # MUST verify after saving\n    verify_redaction(input_path, output_path)\n```\n\n## REQUIRED: Verification Function\n\n**Always run this after ANY redaction to catch errors early:**\n\n```python\nimport fitz\n\ndef verify_redaction(original_path, output_path):\n    \"\"\"Verify redaction didn't corrupt the PDF.\"\"\"\n    orig = fitz.open(original_path)\n    redc = fitz.open(output_path)\n\n    orig_len = sum(len(p.get_text()) for p in orig)\n    redc_len = sum(len(p.get_text()) for p in redc)\n\n    print(f\"Original: {len(orig)} pages, {orig_len} chars\")\n    print(f\"Redacted: {len(redc)} pages, {redc_len} chars\")\n    print(f\"Retained: {redc_len\u002Forig_len:.1%}\")\n\n    # DEFENSIVE CHECKS - fail fast if something went wrong\n    if len(redc) != len(orig):\n        raise ValueError(f\"Page count changed: {len(orig)} -> {len(redc)}\")\n    if redc_len \u003C 1000:\n        raise ValueError(f\"PDF corrupted: only {redc_len} chars remain!\")\n    if redc_len \u003C orig_len * 0.7:\n        raise ValueError(f\"Too much removed: kept only {redc_len\u002Forig_len:.0%}\")\n\n    orig.close()\n    redc.close()\n    print(\"✓ Verification passed\")\n```\n",{"name":5,"description":7},"main","7eeb258ada72b7f58119b0e7a2c00ea89d9beedd","tasks-no-skills\u002Fpaper-anonymizer\u002Fenvironment\u002Fskills\u002Facademic-pdf-redaction","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench","https:\u002F\u002Fgithub.com\u002FWaaaalnut","student of Zhejiang University","2026-04-21 23:16:23",{"source":8,"skillName":5,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add Waaaalnut\u002Fskillsbench --skill academic-pdf-redaction",{"tool":50,"command":51},"bunx","bunx skills add Waaaalnut\u002Fskillsbench --skill academic-pdf-redaction",{"tool":53,"command":54},"pnpm","pnpm dlx skills add Waaaalnut\u002Fskillsbench --skill academic-pdf-redaction","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks-no-skills\u002Fpaper-anonymizer\u002Fenvironment\u002Fskills\u002Facademic-pdf-redaction\u002FSKILL.md",true,[],[59,73,87,113,135,153],{"skillNo":60,"name":61,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":69,"publishedAt":32},"01M6XEWMJ6VY3ZC53MPDCPG3GH","data-reconciliation","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fxlsx-recover-data\u002Fenvironment\u002Fskills\u002Fdata-reconciliation\u002FSKILL.md",[64],{"categoryNo":65,"categoryName":66,"taxonomyType":67,"taxonomyTypeDesc":68},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[70,71,72],"data reconciliation","reconciliation","data",{"skillNo":74,"name":75,"nameZh":6,"description":76,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":79,"publishedAt":32},"01M6XEW8MNPMF1KRXV6ATGFRQF","speech-to-text","Transcribe video to timestamped text using Whisper tiny model (pre-installed).","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-tutorial-indexer\u002Fenvironment\u002Fskills\u002Fspeech-to-text\u002FSKILL.md",[],[80,29,81,82,83,84,85,86],"speech","speech to text","transcribe video","whisper","model","Video","transcribe",{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":96,"publishedAt":32},"01M6XEW3FQZP0T1RKQQJFXVY9T","video-processor","Process videos by removing segments and concatenating remaining parts. Use when you need to remove detected pauses\u002Fopenings from videos, create highlight reels, or batch process segment removals using ffmpeg filter_complex.","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Fvideo-processor\u002FSKILL.md",[93],{"categoryNo":94,"categoryName":95,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",[85,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112],"processor","video processor","batch","create","reels","when","parts","segments","process","videos","you","segment","filter","ffmpeg","use","remove",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":120,"publishedAt":32},"01M6XEVXJ5KZ0NTY3XZKRY4AS1","silence-detector","Detect initial silence segments in audio\u002Fvideo using energy-based analysis. Use when you need to find low-energy periods at the start of recordings (title slides, setup time, pre-roll silence).","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Fsilence-detector\u002FSKILL.md",[119],{"categoryNo":94,"categoryName":95,"taxonomyType":20,"taxonomyTypeDesc":21},[121,122,123,124,102,107,125,126,127,128,129,104,130,131,85,132,133,134,111],"detector","silence detector","title","slides","time","energy","at","recordings","detect","find","Audio","analysis","start","setup",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":141,"publishedAt":32},"01M6XEVT3NGRN1FT7JPMAPXS6M","segment-combiner","Combine multiple segment detection results into a unified list. Use when you need to merge segments from different detectors, prepare removal lists for video processing, or consolidate detection outputs.","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Fsegment-combiner\u002FSKILL.md",[],[108,142,85,143,144,145,146,31,102,147,148,104,107,149,150,111,151,152],"segment combiner","detection","prepare","merge","video processing","outputs","combine","processing","consolidate","results","list",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":157,"categories":158,"keywords":162,"publishedAt":32},"01M6XEVMPVASGS2KX86QK4FMT7","report-generator","Generate compression reports for video processing. Use when you need to create structured JSON reports with duration statistics, compression ratios, and segment details after video processing.","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Freport-generator\u002FSKILL.md",[159],{"categoryNo":160,"categoryName":161,"taxonomyType":67,"taxonomyTypeDesc":68},"01JBSEEDCAT000000000000034","行政与办公人员",[163,164,165,108,102,31,107,166,149,167,168,169,111,146,170,85,171,172,100],"generator","report generator","report","with","statistics","structured","reports","json","compression","generate"]