The Story of Redesigning Claude Code Skills — Integrating xlsx/pptx/docx into ms-office-suite

The Story of Redesigning Claude Code Skills — Integrating xlsx/pptx/docx into ms-office-suite

I consolidated Excel, PowerPoint, and Word processing from three independent skills into one ms-office-suite skill in Claude Code custom skills. I will introduce a design pattern that makes SKILL.md a thin router and lazy-loads based on file extension.
2026.07.11

This page has been translated by machine translation. View original

Introduction

I created an ms-office-suite skill using Claude Code's custom skill feature, which bundles processing for Excel, PowerPoint, and Word files.

Originally it was split into 3 separate skills — xlsx, pptx, and docx — but while reviewing the design, the idea came up that "merging everything into one skill might be cleaner," so I reorganized the structure. This article introduces the design decisions behind that and the final structure.

Background: The Original Structure

At first, Office file operations were built as 3 independent skills:

.claude/skills/
├── xlsx/
│   ├── SKILL.md
│   └── scripts/
├── pptx/
│   ├── SKILL.md
│   └── scripts/
├── docx/
│   ├── SKILL.md
│   └── scripts/
└── shared/          ← Utilities shared by the 3 skills
    ├── pack.py
    ├── unpack.py
    ├── validate.py
    └── libreoffice.py

This worked well enough, but some concerns surfaced while using it.

The Awkwardness of shared/

The problem with the shared/ folder is that it isn't a skill at all. Since there's no SKILL.md, it can't be called as a /shared command — it just floats inside .claude/skills/. The SKILL.md files of the 3 skills each contained a path variable like SHARED_DIR = .claude/skills/shared, creating an implicit dependency.

Revisiting the Design

The first question was whether to merge the 3 skills into one.

Initially I considered keeping them separate due to concerns about "context window cost," but thinking it through, each skill's SKILL.md was around 50 lines, making all three together about 150 lines. Merging them wouldn't make much of a difference.

The real issue was routing. For tasks like "read an xlsx" or "convert a pptx," having all 3 skill descriptions loaded every time is inefficient.

The structure I arrived at is this:

claude-code-ms-office-suite-skill-structure-comparison

.claude/skills/ms-office-suite/
├── SKILL.md        ← Thin router (~30 lines)
├── xlsx.md         ← Detailed instructions for .xlsx (read when needed)
├── pptx.md         ← Detailed instructions for .pptx (read when needed)
├── docx.md         ← Detailed instructions for .docx (read when needed)
└── scripts/
    ├── xlsx/
    │   ├── read_xlsx.py
    │   └── write_xlsx.py
    ├── pptx/
    │   ├── read_pptx.py
    │   └── write_pptx.py
    ├── docx/
    │   ├── read_docx.py
    │   └── write_docx.py
    └── shared/
        ├── pack.py
        ├── unpack.py
        ├── validate.py
        └── libreoffice.py

Key Point: Making SKILL.md a Thin Router

SKILL.md contains only the routing logic per file extension:

---
name: ms-office-suite
description: Read, create, and edit Microsoft Office files (.xlsx, .pptx, .docx).
---

# Microsoft Office Suite Skill

Route by file extension:

| Extension | Instructions | Scripts | Package |
|-----------|-------------|---------|---------|
| `.xlsx`   | Read `SKILLS_DIR/xlsx.md` | `scripts/xlsx/` | openpyxl |
| `.pptx`   | Read `SKILLS_DIR/pptx.md` | `scripts/pptx/` | python-pptx |
| `.docx`   | Read `SKILLS_DIR/docx.md` | `scripts/docx/` | python-docx |

**After identifying the file type, read the corresponding .md file.**

This way, when handling a .xlsx file, only SKILL.md (~30 lines) + xlsx.md (~40 lines) are loaded. The details for pptx and docx are never read.

Key Point: Separating Format-Specific .md Files

Here's what xlsx.md looks like (excerpt):

# Excel (.xlsx)

## Available Operations

### Read / Extract
```bash
uv run --with openpyxl SKILLS_DIR/scripts/xlsx/read_xlsx.py "$ARGUMENTS"

Edit (Unpack → Edit → Repack)

  1. Unpack: uv run SKILLS_DIR/scripts/shared/unpack.py <file.xlsx>
  2. Edit: Modify XML files directly
  3. Repack: uv run SKILLS_DIR/scripts/shared/pack.py <dir> <output.xlsx>
  4. Validate: uv run SKILLS_DIR/scripts/shared/validate.py <output.xlsx>

Format-specific information (libraries to use, script paths, notes) is consolidated here.

### Key Point: Moving `shared/` to `scripts/shared/`

By moving `shared/` to `scripts/shared/`, the structure now communicates the intent — "this is a shared script library, not a skill" — without needing any explanation.

## Contents of the Shared Scripts

There are 4 utilities in `scripts/shared/`:

| Script | Role |
|-----------|------|
| `unpack.py` | Extracts an Office file (ZIP) into an XML tree |
| `pack.py` | Repacks edited XML back into an Office file (with auto-repair) |
| `validate.py` | Checks ZIP integrity and XML well-formedness |
| `libreoffice.py` | Format conversion and formula recalculation via headless LibreOffice |

Since Office files are ZIP + XML, unpacking and editing the XML directly allows finer control than libraries like python-pptx. pack.py also includes auto-repair for common XML breakage such as incorrect durableId values.

## How It Works in Practice

For example, to read an xlsx file:

```bash
uv run --with openpyxl .claude/skills/ms-office-suite/scripts/xlsx/read_xlsx.py "requirements.xlsx" --summary

If there are multiple sheets, you can specify one with --sheet:

uv run --with openpyxl .claude/skills/ms-office-suite/scripts/xlsx/read_xlsx.py "requirements.xlsx" --sheet "User Requirements"

To extract text from a pptx:

uv run --with python-pptx .claude/skills/ms-office-suite/scripts/pptx/read_pptx.py "review-meeting.pptx"

Summary

Here's a summary of the key points from this design change:

  • Benefits of merging: The skill list is cleaner, and the awkward shared/ entry is gone
  • Lazy-loading context: Making SKILL.md a thin router means format-specific instructions are only read when needed
  • Structure communicates intent: The scripts/shared/ location makes it clear that "this is a shared script library, not a skill"

Whether to go with "one skill per feature" or "group related features into one skill" in Claude Code skill design depends on the use case. This time, merging was natural because there was a common domain of "Office file operations." On the other hand, forcing entirely independent tools into one skill would just complicate routing, so that judgment call is still necessary.

I hope this is useful for anyone building Claude Code skills that work with Office files.


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

Share this article

Related articles