Install
openclaw skills install @slearnai/iwork2mdConvert Apple iWork documents (Pages .pages, Numbers .numbers, Keynote .key) into Markdown. Use whenever the user wants to read, extract, or translate the content of an iWork file into text/markdown, for example 'convert this .pages file to markdown', 'extract text from a Numbers sheet', 'read a Keynote file', or 'open a .key/.numbers/.pages and turn it into markdown'. Handles the iWork '13+ format (bundle containing Index.zip with .iwa files that wrap Snappy-framed Protobuf) with no third-party dependencies.
openclaw skills install @slearnai/iwork2mdConvert Apple Pages / Numbers / Keynote documents to Markdown. The parser is in
scripts/iwa.py (pure stdlib); the converter CLI is scripts/iwork2md.py.
.pages, .numbers, or .key file and wants its text,
tables, or slides as Markdown (or just to read the content).# Write a .md next to the source (auto-named)
python3 scripts/iwork2md.py path/to/Doc.pages
# Explicit output path
python3 scripts/iwork2md.py Doc.numbers out.md
# Print to stdout
python3 scripts/iwork2md.py Doc.key --stdout
# Debug: dump every recovered text fragment
python3 scripts/iwork2md.py Doc.numbers --texts
# List embedded media (images/video)
python3 scripts/iwork2md.py Doc.pages --media
From inside a chat, invoke with exec (or tell the user to run it). The script
is dependency-free (Python 3.8+, stdlib only: zipfile, struct, io,
plistlib).
Index.zip (or .iwa files directly under
Index/)..iwa: removes the iWork Snappy framing (chunk type + 3-byte
LE length, no stream-id, no CRC), then raw-Snappy-decompresses the body.varint len + ArchiveInfo {identifier, message_infos[]} then payloads), and generically walks every message to
collect UTF-8 string fields — recovering ~100% of readable content without
needing the app-specific schema map (TSPRegistry).Metadata/Properties.plist or first
heading), an embedded-media list, reconstructed Numbers tables (rows
stored as "a | b | c" become proper markdown tables, deduped across
mirrored components), a body block (largest multi-line text), and remaining
text fragments..iwa Snappy framing is non-standard: type byte 0x00, 3-byte LE
length, then a raw Snappy block (NOT an official framed stream). No
stream-identifier chunk, no CRC. (iwa.iwa_unframe)iwa.snappy_decompress)type ids map to schemas inside the iWork binaries and vary by
app/version — Protobuf is not self-describing, so we decode generically by
string fields. See references/FORMAT.md for the full spec and limits."cell | cell | cell" string per
row → the CLI groups consecutive such rows into a markdown table.scripts/test_iwa.py round-trips a synthetic .iwa (encoder + parser) to prove
the Snappy framing, raw-Snappy copy path, and Protobuf container logic. Run:
python3 scripts/test_iwa.py.