deckprobe
Inspect PDF, Microsoft Office (docx/xlsx/pptx and legacy doc/xls/ppt), and Apple iWork (key/numbers/pages) files without opening or rendering them. Use for page and slide and sheet counts, title/author/created metadata, encryption and macro and digital-signature and JavaScript ri
Install
npx skills add https://github.com/deckflow/deckprobe/tree/main/skills/deckprobe
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install deckflow-deckprobe@llmmart
git clone https://github.com/deckflow/deckprobe.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole deckflow/deckprobe collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
DeckProbe
ffprobe for documents. One shell command answers a specific question about a PDF, Office, or iWork
file; the answer comes back as JSON on stdout and the exit status carries the verdict.
Reach for it when a task needs facts about a document — how many slides, is it encrypted, does it
have macros, is it corrupt, is this really a .pptx — rather than the text inside it. It is safe on
untrusted input: nothing is rendered, no macro runs, no external reference is followed.
1. Make sure it runs
npx skills add installs this document only, not the binary. Check first:
deckprobe --version
- Prints a version → use
deckprobeas written throughout this document. - Command not found, but
nodeexists → the command isnpx -y @deckflow/deckprobe. Prefix every example below with it. First run downloads the package; later runs are cached. - Neither → ask the user to install one of:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/deckflow/deckprobe/releases/latest/download/deckprobe-installer.sh | shnpm install -g @deckflow/deckprobecargo install --git https://github.com/deckflow/deckprobe --locked deckprobe
If you cannot get it running, say so and stop. Do not fall back to unzipping the package, grepping XML, or parsing PDF bytes by hand — avoiding exactly that is the point of this tool.
2. The shape of every call
deckprobe [-t TARGETS] [-l LEVEL] [-c CONFIDENCE] [--view values] [--pretty] FILE
Start here when you do not yet know what you need:
deckprobe -t @summary --view values --pretty report.pdf
--view values returns a compact target -> value map. Drop it when you also need each value's
confidence, the path that produced it, and the measured cost.
Check the exit status before parsing stdout. A non-zero status still writes valid JSON, but it is an error envelope, not a report.
3. Choosing targets
-t accepts short names (slide_count), canonical names (powerpoint.slide_count), and selector
presets. It is repeatable and comma-separated: -t @header,title,page_count.
| Selector | Expands to |
|---|---|
@header |
Container identity only — format, size, extension match, encryption flag |
@default |
The driver's defaults for the active level. Used when -t is omitted |
@summary |
Identity, common metadata, and primary structure |
@security |
Encryption, macros, signatures, external references, active content |
@structure |
Format-owned counts, names, and dimensions |
@assets |
Images, media, previews, fonts, embedded objects |
@quality |
Integrity, repair, extension match, conformance |
@format |
Every format-specific target at the active level |
@all |
Everything available at the active level |
@summary deliberately omits a statistic the current driver can only get from a full-file read. PDF
page count is the notable case — ask for page_count or @structure explicitly.
Never guess a target name. The tool documents itself:
deckprobe targets --format pptx --pretty # ids, aliases, min level, value type, cost class
deckprobe formats --pretty # drivers, profiles, support boundaries
deckprobe schema --pretty # the authoritative report JSON Schema
references/targets.md has the full catalogue per format if you would rather read than run.
4. Probe level and confidence
-l / --level picks the budget and which paths are eligible. Accepts h/m/d too.
| Level | Use it for |
|---|---|
header |
Identity and container properties. ~500 ms budget |
metadata |
Default. Bounded metadata and common structural counts. ~500 ms |
deep |
Higher-cost paths. ~5 s — only when a target's min_level says deep |
-c / --confidence filters eligible paths: low < medium < high (default) < exact. Lower it
when a target comes back unresolved and an approximate answer is acceptable; raise it with
-C target=exact for a single target that must be authoritative.
5. Reading the report
{
"schema_version": 2,
"status": "ok", // "ok" | "partial" | "error"
"input": { "display_name": "deck.pptx", "source_kind": "local_file" },
"driver": { "id": "powerpoint", "profile": "pptx" },
"results": {
"powerpoint.slide_count": {
"status": "resolved", // see the eight values below
"value": 31,
"confidence": "high", // none | low | medium | high | exact
"path": "powerpoint.app_statistics",
"source": "docProps/app.xml saved statistic"
}
},
"execution": { "unresolved_targets": [], "actual_cost": { } },
"diagnostics": []
}
A result's status is one of resolved, estimated, planned, unknown, unsupported, invalid,
budget_exceeded, failed. Only resolved and estimated carry a value. unknown is the common
one: the path ran and the document simply does not record that fact.
- The report's own
statusisokorpartial.partialis not a failure — at least one requested target could not be resolved at the requested confidence, and the rest are still valid. Checkexecution.unresolved_targets. - Add
--strictwhen an unresolved target must fail the command. The full report is still written, and the exit status becomes5. resultsis keyed by the canonical target name even when you asked with a short alias.
6. Exit status — and what to do about it
| Status | Meaning | Your next move |
|---|---|---|
0 |
Success | Parse stdout. Unresolved targets are allowed unless --strict |
1 |
Invalid request or unsupported target | You named a target this format has no path for. Run deckprobe targets --format <fmt> |
2 |
CLI syntax error, or the file is missing/unreadable | Fix the flags or the path |
3 |
Unsupported or unrecognized format | Check the extension against deckprobe formats |
4 |
Malformed input or budget exceeded | Genuinely damaged file, or raise -b/-x/-e/-T. See references/limits.md |
5 |
--strict and a target was unresolved |
The report is on stdout; decide whether the missing target matters |
6 |
Internal failure | A bug. Report it |
7. Many files at once
printf '%s\n' '{"path":"a.pdf"}' '{"path":"b.pptx"}' | deckprobe --jsonl -t @summary
One compact JSON per non-empty input line. A per-record error does not stop the run; the process
exits with the highest per-record status. Each record is a JSON string path, {"path":"..."}, or
{"name":"report.pdf","data_base64":"..."}. Add an id to an object record when the caller
needs stable attribution; JSONL error envelopes echo that id and the original path under
input.
For bytes on stdin, -n supplies the logical filename that selects the format:
curl -sL "$url" | deckprobe -n download.pdf -t @security -
8. What DeckProbe will not do
It has no renderer, no OCR, no text extraction, and no macro interpreter. It never opens a network
connection and never resolves an external reference. Legacy XML iWork packages are rejected by
design — only modern IWA is supported. .xlsb is identity-only. PDF XMP is not merged into the
metadata targets.
Routing is by filename extension, then verified against the container. Renaming a .pptx to .docx
returns MALFORMED_INPUT, and -f/--input-format is an assertion, not an override — it cannot
force an unrelated parser onto a file.
Never parse stderr. Every result, including every error, is one JSON value on stdout.
References
Read these only when you need them:
references/targets.md— the full target catalogue per format, with aliases and minimum levels. Read before naming a target you have not verified.references/recipes.md— task → command lookup for the common questions.references/output.md— the complete report shape, error codes, and cost accounting, for when you are consuming the JSON programmatically.references/limits.md— budgets, the-b/-x/-e/-Toverrides,-Oformat options, and how to diagnose an exit4.
Files (deckprobe)
-
references
-
limits.md 3.5 KB
# Budgets, limits, and format options DeckProbe bounds its own work. The level profile supplies defaults; four flags override them. ## What the defaults are | Level | Wall clock | Notes | | --- | --- | --- | | `header` | 500 ms | Up to 4 MiB physical and expanded, 4,096 archive entries — enough to validate a realistic iWork central directory and `Document.iwa` | | `metadata` | 500 ms | The default level | | `deep` | 5 s | Only for an explicitly selected deep probe | These are tuned for a fast CLI response, not for the largest file you own. ## Overriding them | Flag | Limit | | --- | --- | | `-b` / `--probe-size BYTES` | Physical bytes read by probe paths (alias `--probesize`) | | `-x` / `--max-expanded-bytes BYTES` | Cumulative decompressed bytes | | `-e` / `--max-archive-entries COUNT` | ZIP/OPC entry count | | `-T` / `--timeout-ms MILLISECONDS` | Wall-clock probe budget | ```bash deckprobe -l d -T 30000 -x 500000000 -t @all huge.pptx ``` ## Diagnosing exit 4 Exit `4` covers both `MALFORMED_INPUT` and `BUDGET_EXCEEDED` — read `error.code` to tell them apart. - `BUDGET_EXCEEDED` → the file is fine, the budget was too small. Raise the specific limit named in the message. `execution.actual_cost` in a successful run on a similar file tells you what to ask for. - `MALFORMED_INPUT` → the container or its required internal part did not validate. Common causes: the extension does not match the content, the ZIP central directory is damaged, or a required OOXML or IWA part is missing. This is a real answer about the file, not a tuning problem. Estimate before you pay: ```bash deckprobe -P -p -l d -t @all huge.pdf ``` `--plan-only` reports the paths that would run and their `estimated_cost` without executing driver probe paths. ## These are cooperative bounds They limit DeckProbe's own I/O and parsing at checkpoints. They are not a process-level deadline and not a sandbox. A caller that needs a hard kill should still impose one around the CLI — `timeout 10s deckprobe ...` — and run untrusted input with the filesystem permissions it deserves. What the bounds *do* guarantee: no rendering, no macro execution, no network access, no external reference resolution, and no unbounded decompression. ## Format options `-O` / `--format-option` takes namespaced `KEY=VALUE`, repeatable. After the driver is detected an unambiguous local key may drop its namespace. Last value wins; unknown keys and values are rejected. | Option | Default | Values | | --- | --- | --- | | `pdf.repair_xref` | `safe` | `safe`, `none` — bounded xref recovery for damaged-but-readable PDFs | | `pdf.max_objects` | `100000` | u64 | | `excel.workbook_path` | `auto` | enum | ```bash deckprobe -O repair_xref=none -t page_count report.pdf ``` Set `repair_xref=none` when you want to know whether a PDF is clean *without* recovery. With the default `safe`, check the `repaired` target instead — `true` means recovery was needed. The live list, with allowed values and descriptions, is always: ```bash deckprobe targets --format pdf --pretty # -> "format_options" ``` ## Support boundaries `deckprobe formats --pretty` reports each driver's boundary. The current ones worth knowing: - `.xlsb` — identity only; structural targets stay unresolved. - iWork — modern IWA only. Legacy XML packages return `UNSUPPORTED_FORMAT`. - Legacy `.doc`/`.xls`/`.ppt` — metadata and format statistics only; check `content_probe_supported`. - PDF XMP is not merged into the `document.*` metadata targets; `has_xmp` reports its presence. -
output.md 4.4 KB
# The JSON contract Every invocation writes exactly one JSON value to stdout — success, partial, or error. JSONL mode writes one compact JSON value per non-empty input line. Nothing useful goes to stderr. The authoritative schema ships inside the binary: ```bash deckprobe schema --pretty ``` ## Probe report (`--view full`, the default) Top-level fields: ```text schema_version tool_version status input driver results execution diagnostics ``` | Field | Notes | | --- | --- | | `schema_version` | `2` | | `tool_version` | The binary's version | | `status` | `ok` or `partial` — never `error`; failures use the envelope below | | `input` | `display_name`, `source_kind` (`local_file` \| `stdin` \| `jsonl_bytes`), `file_size` | | `driver` | `id` (e.g. `powerpoint`) and `profile` (e.g. `pptx`) | | `results` | Keyed by **canonical** target name, even when requested by alias | | `execution` | Level, selected paths, estimated and actual cost, unresolved targets | | `diagnostics` | Structured warnings from planning and probing | ### A result entry ```json { "target": "powerpoint.slide_count", "status": "resolved", "value": 31, "confidence": "high", "confidence_score": 0.95, "path": "powerpoint.app_statistics", "source": "docProps/app.xml saved statistic" } ``` `status` is one of: | Status | Meaning | | --- | --- | | `resolved` | A value was obtained at or above the requested confidence | | `estimated` | A value was obtained, but it is an estimate | | `planned` | `--plan-only`: this path would have run | | `unknown` | The path ran; the document does not record this fact | | `unsupported` | This format has no path for this target | | `invalid` | The document records something that does not validate | | `budget_exceeded` | A limit stopped this target specifically | | `failed` | The path errored | Only `resolved` and `estimated` carry `value`. `confidence` is `none` \| `low` \| `medium` \| `high` \| `exact`, with `confidence_score` as the numeric form. Distinguish carefully: - `"value": null` on a `resolved` result — the field exists and is empty. That is an answer. - `status: "unknown"` — the probe could not answer. Not the same thing. ### `execution` ```json { "probe_level": "metadata", "paths": ["powerpoint.app_statistics", "ooxml.core_properties"], "estimated_cost": 12, "actual_cost": { "physical_bytes_read": 16730, "expanded_bytes": 15181, "random_reads": 10 }, "unresolved_targets": ["document.author"] } ``` `actual_cost` is deterministic by design — byte and seek counters only, no wall-clock. `--telemetry` opts into an `elapsed_ms` field, which makes output non-reproducible; leave it off when diffing reports. `piggyback_targets` lists optional (`-o`) targets that came back for free. ## Values report (`--view values`) Compact envelope for when evidence does not matter: ```json { "schema_version": 2, "tool_version": "2.3.1", "status": "ok", "view": "values", "input": { "display_name": "deck.pptx", "source_kind": "local_file", "file_size": 306716 }, "driver": { "id": "powerpoint", "profile": "pptx" }, "values": { "powerpoint.slide_count": 31 }, "unresolved_targets": [], "piggyback_targets": [], "diagnostics": [] } ``` Note the shape difference: `unresolved_targets` is **top-level** here, not under `execution`. ## Error envelope ```json { "schema_version": 2, "tool_version": "2.3.1", "status": "error", "input": {"id": "record-42", "path": "/input/report.docx"}, "error": { "code": "SOURCE_IO", "message": "source I/O error: No such file or directory (os error 2)", "exit_code": 2 } } ``` `input` is optional and appears on JSONL record failures when the original record supplied a path or `id`. It is omitted when no record context can be recovered, such as malformed JSON syntax. `code` is stable and one of: | Code | Exit | | --- | ---: | | `CLI_SYNTAX` | 2 | | `INVALID_REQUEST` | 1 | | `UNSUPPORTED_TARGET` | 1 | | `SOURCE_IO` | 2 | | `UNSUPPORTED_FORMAT` | 3 | | `MALFORMED_INPUT` | 4 | | `BUDGET_EXCEEDED` | 4 | | `PARSER_FAILURE` | 6 | Switch on `error.code`, not on the message text. `--strict` is the exception to "non-zero means envelope": it exits `5` while still writing the complete probe report, because the report is the useful part. ## Determinism Two runs over the same bytes produce byte-identical output as long as `--telemetry` is off. That makes reports safe to store as fixtures, diff in CI, and cache. -
recipes.md 4.6 KB
# Recipes Task → command. Every one of these writes JSON to stdout; check the exit status first. ## Identity and triage **What is this file, cheaply?** ```bash deckprobe -t @header --view values --pretty suspicious.bin ``` Header level only — no metadata paths run. Good first move on anything untrusted. **Is the extension lying?** ```bash deckprobe -t format,format_profile,extension_matches --view values file.docx ``` `extension_matches: false` means the container disagrees with the name. A renamed file usually exits `4` (`MALFORMED_INPUT`) before it gets this far, which is itself the answer. **Is it damaged?** ```bash deckprobe -l d -t @quality --view values report.pdf ``` Use the selector, not `-t corrupted`: `corrupted` and `missing_assets` are declared for every modern format but only the iWork drivers implement them, so naming them on a PDF or OOXML file exits `1` with `UNSUPPORTED_TARGET`. `@quality` gives you what the driver actually supports — `repaired` for PDF (`true` means the bounded xref recovery had to run, so the file is readable but not clean), `conformance` for OOXML, and `corrupted`/`missing_assets` for iWork: ```bash deckprobe -l d -t @quality --view values deck.key ``` ## Counts **How many slides / pages / sheets?** ```bash deckprobe -t slide_count --view values deck.pptx deckprobe -t page_count --view values report.pdf # not in @summary; ask by name deckprobe -t sheet_count,sheet_names --view values book.xlsx deckprobe -t slide_count --view values deck.key ``` **Everything structural in one pass** ```bash deckprobe -t @structure --view values --pretty deck.pptx ``` **Keynote slide state** — needs deep, unlike the plain count: ```bash deckprobe -l d -t hidden_slide_count,slides_with_notes_count,orientation --view values deck.key ``` ## Security review **One-shot risk sweep** ```bash deckprobe -l d -t @security --pretty untrusted.docx ``` **Just the cheap questions** ```bash deckprobe -t encrypted,has_macros,password_protected --view values book.xlsm ``` `encrypted` is `header` level, so this is close to free. **Does this PDF carry active content?** ```bash deckprobe -l d -t has_javascript,has_embedded_files,active_content_risk --view values doc.pdf ``` **Is it signed, and by how many?** ```bash deckprobe -l d -t has_digital_signature,signature_count --view values contract.pdf ``` ## Metadata **Who made it and when?** ```bash deckprobe -t title,author,application,created_at,modified_at --view values --pretty report.docx ``` A `null` value means the field is absent from the document, not that the probe failed. An *unresolved* target — listed in `execution.unresolved_targets` — is the "probe could not answer" case. ## Bulk work **Inventory a directory** ```bash find . -type f ! -name '~$*' \( -name '*.pdf' -o -name '*.pptx' -o -name '*.docx' \) \ | python3 -c 'import sys,json; [print(json.dumps({"path":l.strip()})) for l in sys.stdin]' \ | deckprobe --jsonl -t @summary ``` One compact JSON per line. Per-record errors do not stop the run; the process exits with the highest per-record status. The shell performs discovery here, so it also excludes Office `~$` lock files; DeckProbe itself only receives the paths emitted by `find`. **Same thing, values only, easy to aggregate** ```bash ... | deckprobe --jsonl -t slide_count,author --view values ``` ## Piping and non-file input **Probe a download without saving it** ```bash curl -sL "$url" | deckprobe -n download.pdf -t @security - ``` `-n` supplies the logical filename — the extension is what selects the format, so it is required. **Base64 payload through JSONL** ```bash printf '{"name":"report.pdf","data_base64":"%s"}\n' "$(base64 < report.pdf)" \ | deckprobe --jsonl -t page_count --view values ``` ## CI and scripting **Fail the build if a target cannot be answered** ```bash deckprobe -s -t format,page_count report.pdf ``` `--strict` exits `5` on an unresolved target and still writes the full report. **Estimate cost before paying it** ```bash deckprobe -P -p -l d -t @all huge.pdf ``` `--plan-only` reports the paths that *would* run and their estimated cost without executing driver probe paths. **Shell completion** ```bash deckprobe completion zsh > ~/.zfunc/_deckprobe ``` ## Reading the output in a script ```bash if ! report=$(deckprobe -t slide_count --view values deck.pptx); then echo "probe failed: $(printf '%s' "$report" | python3 -c 'import json,sys; print(json.load(sys.stdin)["error"]["message"])')" >&2 exit 1 fi printf '%s' "$report" | python3 -c 'import json,sys; print(json.load(sys.stdin)["values"]["powerpoint.slide_count"])' ``` Note `values` is keyed by the **canonical** target name even when the request used the short alias. -
targets.md 7.4 KB
# Target catalogue The authoritative list always comes from the binary: ```bash deckprobe targets --format pptx --pretty ``` That returns, for every target: canonical `id`, `aliases`, `min_level`, `value_type`, a JSON Schema fragment, `cost_class`, selector membership, plus the profile's `format_options` and the complete `selector_expansions` for `header`, `metadata`, and `deep`. This file is a readable mirror — when the two disagree, the command wins. Accepted format names: `pdf`, `word`/`docx`/`docm`, `excel`/`xlsx`/`xlsm`, `powerpoint`/`pptx`/`pptm`, `keynote`/`key`, `numbers`, `pages`, and `legacy`/`doc`/`xls`/`ppt`. Use the short alias on the command line; reports key `results` by the canonical id. ## Shared across every modern format These are available for PDF, OOXML, and iWork alike. `min_level` in parentheses. **Identity** — `format`, `format_profile` (alias `profile`), `mime_type` (alias `mime`), `file_size`, `extension`, `extension_matches`. All `header`, all cheap. **Metadata** (all `metadata`) — `title`, `subject`, `author`, `keywords`, `description`, `created_at`, `modified_at`, `application`, `application_version`, `language`, `locale`. Each is nullable; a null value means the field is genuinely absent, not that the probe failed. **Security** | Alias | Level | Type | Notes | | --- | --- | --- | --- | | `encrypted` | header | bool | Cheapest security signal there is | | `has_macros` (`macros`) | metadata | bool | | | `password_protected` | metadata | bool\|null | | | `has_digital_signature` | metadata | bool | | | `signature_count` | deep | u64\|null | | | `has_javascript` | deep | bool | | | `has_external_relationships` | metadata | bool | | | `has_embedded_files` | metadata | bool | | | `active_content_risk` | deep | string\|null | Rolled-up verdict | **Quality** — `corrupted` and `missing_assets` (both deep, bool|null) are declared for every modern format, but only the iWork drivers currently implement a path for them. On PDF and OOXML they return `UNSUPPORTED_TARGET` (exit `1`), so use the `@quality` selector instead, which resolves to whatever that driver really supports: | Format | `@quality` resolves to | | --- | --- | | PDF | `extension_matches`, `repaired` | | docx / xlsx / pptx | `extension_matches`, `conformance` | | key / numbers / pages | `extension_matches`, plus `corrupted` and `missing_assets` at `-l d` | This is the general rule, not a quirk: a target listed by `deckprobe targets` is declared for the format, not guaranteed to have an executable path. Prefer a selector when you want "whatever this driver can tell me", and name a target directly only when you need that specific fact and can handle exit `1`. ## PDF | Alias | Level | Type | | --- | --- | --- | | `version` | header | string | | `linearized` | header | bool | | `page_count` | metadata | u64\|null | | `object_count` | metadata | u64\|null | | `xref_type` | metadata | string | | `repaired` | metadata | bool | | `annotation_count` | metadata | u64\|null | | `form_field_count` | metadata | u64\|null | | `attachment_count` | metadata | u64\|null | | `has_xmp` | metadata | bool | `page_count` is **not** in `@summary` — the driver can only reach it through a path `@summary` excludes. Ask for it by name or use `@structure`. Format options: `pdf.repair_xref` (`safe` default, or `none`) and `pdf.max_objects` (default `100000`). ## OOXML — shared by docx / xlsx / pptx `document_kind` (header, string), `package_entry_count` (metadata, u64), `conformance` (metadata, string). ### Word (docx, docm, dotx, dotm) `page_count`, `word_count`, `character_count`, `paragraph_count`, `is_template`, `unique_image_asset_count`, `comment_part_count` — all `metadata`. `table_count` is `deep`. Counts come from the package's own statistics, so `page_count` reflects what the authoring application last recorded, not a re-layout. A missing saved statistic is a resolved `null`, not evidence of corruption. DeckProbe never guesses `page_count`; exact pagination needs a compatible layout engine. At `deep`, `word_count` and `character_count` can fall back to medium-confidence estimates from visible `word/document.xml` text when their per-target confidence allows `medium`. ### Excel (xlsx, xlsm, xltx, xltm, xlsb) `sheet_count`, `sheet_names`, `hidden_sheet_count`, `defined_name_count`, `table_count`, `is_template`, `binary_workbook`, `chart_part_count`, `pivot_table_part_count`, `unique_image_asset_count` — all `metadata`. `shared_string_count` is `deep`. `.xlsb` is identity-only: it routes and validates, but the structural targets stay unresolved. Format option: `excel.workbook_path` (default `auto`). ### PowerPoint (pptx, pptm, ppsx, ppsm, potx, potm) All `metadata`: `slide_count`, `hidden_slide_count`, `master_count`, `layout_count`, `notes_slide_count` (alias `notes_count`), `slide_size`, `aspect_ratio`, `orientation`, `presentation_kind`, `chart_part_count`, `unique_image_asset_count`, `unique_media_asset_count`, `comment_part_count`. ## iWork — shared by key / numbers / pages `document_kind` (header). At `metadata`: `file_format_version`, `producer_build`, `package_entry_count`, `iwa_entry_count`, `data_asset_count`, `data_asset_bytes`, `asset_type_counts`, `has_preview`, `preview_count`, `preview_dimensions`, `is_multi_page`, `has_external_or_missing_data`. At `deep`: `all_iwa_valid`, `archive_object_count`, `message_type_counts`, `object_type_counts`. Validation requires `Index/Document.iwa`, `Metadata/Properties.plist`, and the expected IWA root object family. Legacy XML iWork returns `UNSUPPORTED_FORMAT`. ### Keynote `metadata`: `slide_count`, `master_slide_count`, `table_component_count`. `deep`: `slide_size`, `aspect_ratio`, `orientation`, `hidden_slide_count`, `slides_with_notes_count`, `slides_with_builds_count`, `slides_with_transitions_count`, `table_count`. Note the split — a plain slide count is cheap, but anything about slide *state* needs `-l d`. ### Numbers `metadata`: `sheet_count`, `sheet_names`, `table_component_count`. `deep`: `table_count`, `table_dimensions`, `hidden_row_count`, `hidden_column_count`, `filtered_row_count`, `formula_definition_count`. ### Pages `metadata`: `table_component_count`. `deep`: `section_count`, `section_names`, `page_size`, `aspect_ratio`, `orientation`, `change_tracking_enabled`, `body_text_length`, `body_paragraph_break_count`, `cached_page_count`, `table_count`. ## Legacy Office (doc, xls, ppt) Metadata and format statistics only — there is no deep content path, and the shared `document.*`/`security.*`/`quality.*` families do not apply. `header`: `document_kind`, `legacy_kind`, `cfb_container`, `content_probe_supported`. `metadata`: `cfb_entry_count`. - `.doc` adds `page_count`, `word_count`, `character_count`, `paragraph_count`, `is_template`. - `.xls` adds `sheet_count`, `sheet_names`, `is_template`, `binary_workbook`. - `.ppt` adds `slide_count`, `notes_slide_count`, `presentation_kind`. Check `content_probe_supported` before trusting the absence of a structural value. ## Optional targets `-o` requests targets that are returned only when a path already selected for a required target produces them for free. They never add a path and never make the report `partial`: ```bash deckprobe -t page_count -o object_count report.pdf ``` `-N` / `--no-piggyback` disables that zero-cost collection. ## Per-target confidence `-C target=level` overrides `-c` for one target. A short alias works when unambiguous: ```bash deckprobe -t slide_count,orientation -C slide_count=exact deck.pptx ```
-
-
LICENSE 11.1 KB · in bundle
-
NOTICE 51 B · in bundle
-
SKILL.md 9 KB
--- name: deckprobe description: >- Inspect PDF, Microsoft Office (docx/xlsx/pptx and legacy doc/xls/ppt), and Apple iWork (key/numbers/pages) files without opening or rendering them. Use for page and slide and sheet counts, title/author/created metadata, encryption and macro and digital-signature and JavaScript risk signals, sheet and table structure, embedded assets and fonts, corruption checks, and verifying a file really is the format its extension claims. Also use to inventory many documents at once. Returns bounded, deterministic JSON on stdout. Never renders, never runs macros, never follows external references, never sends the file anywhere. license: Apache-2.0 compatibility: >- Needs the deckprobe CLI on PATH, or Node.js with network access to run it through npx. Needs a shell tool and local read access to the file. Probing itself needs no network. allowed-tools: Bash(deckprobe:*) metadata: tool: deckprobe homepage: https://github.com/deckflow/deckprobe deckprobe-skill-format: "1" --- # DeckProbe `ffprobe` for documents. One shell command answers a specific question about a PDF, Office, or iWork file; the answer comes back as JSON on stdout and the exit status carries the verdict. Reach for it when a task needs facts **about** a document — how many slides, is it encrypted, does it have macros, is it corrupt, is this really a `.pptx` — rather than the text inside it. It is safe on untrusted input: nothing is rendered, no macro runs, no external reference is followed. ## 1. Make sure it runs `npx skills add` installs this document only, not the binary. Check first: ```bash deckprobe --version ``` - Prints a version → use `deckprobe` as written throughout this document. - Command not found, but `node` exists → **the command is `npx -y @deckflow/deckprobe`**. Prefix every example below with it. First run downloads the package; later runs are cached. - Neither → ask the user to install one of: - `curl --proto '=https' --tlsv1.2 -LsSf https://github.com/deckflow/deckprobe/releases/latest/download/deckprobe-installer.sh | sh` - `npm install -g @deckflow/deckprobe` - `cargo install --git https://github.com/deckflow/deckprobe --locked deckprobe` If you cannot get it running, say so and stop. Do **not** fall back to unzipping the package, grepping XML, or parsing PDF bytes by hand — avoiding exactly that is the point of this tool. ## 2. The shape of every call ```bash deckprobe [-t TARGETS] [-l LEVEL] [-c CONFIDENCE] [--view values] [--pretty] FILE ``` Start here when you do not yet know what you need: ```bash deckprobe -t @summary --view values --pretty report.pdf ``` `--view values` returns a compact `target -> value` map. Drop it when you also need each value's confidence, the path that produced it, and the measured cost. **Check the exit status before parsing stdout.** A non-zero status still writes valid JSON, but it is an error envelope, not a report. ## 3. Choosing targets `-t` accepts short names (`slide_count`), canonical names (`powerpoint.slide_count`), and selector presets. It is repeatable and comma-separated: `-t @header,title,page_count`. | Selector | Expands to | | --- | --- | | `@header` | Container identity only — format, size, extension match, encryption flag | | `@default` | The driver's defaults for the active level. Used when `-t` is omitted | | `@summary` | Identity, common metadata, and primary structure | | `@security` | Encryption, macros, signatures, external references, active content | | `@structure` | Format-owned counts, names, and dimensions | | `@assets` | Images, media, previews, fonts, embedded objects | | `@quality` | Integrity, repair, extension match, conformance | | `@format` | Every format-specific target at the active level | | `@all` | Everything available at the active level | `@summary` deliberately omits a statistic the current driver can only get from a full-file read. PDF page count is the notable case — ask for `page_count` or `@structure` explicitly. **Never guess a target name.** The tool documents itself: ```bash deckprobe targets --format pptx --pretty # ids, aliases, min level, value type, cost class deckprobe formats --pretty # drivers, profiles, support boundaries deckprobe schema --pretty # the authoritative report JSON Schema ``` `references/targets.md` has the full catalogue per format if you would rather read than run. ## 4. Probe level and confidence `-l` / `--level` picks the budget and which paths are eligible. Accepts `h`/`m`/`d` too. | Level | Use it for | | --- | --- | | `header` | Identity and container properties. ~500 ms budget | | `metadata` | **Default.** Bounded metadata and common structural counts. ~500 ms | | `deep` | Higher-cost paths. ~5 s — only when a target's `min_level` says `deep` | `-c` / `--confidence` filters eligible paths: `low` < `medium` < `high` (default) < `exact`. Lower it when a target comes back unresolved and an approximate answer is acceptable; raise it with `-C target=exact` for a single target that must be authoritative. ## 5. Reading the report ```jsonc { "schema_version": 2, "status": "ok", // "ok" | "partial" | "error" "input": { "display_name": "deck.pptx", "source_kind": "local_file" }, "driver": { "id": "powerpoint", "profile": "pptx" }, "results": { "powerpoint.slide_count": { "status": "resolved", // see the eight values below "value": 31, "confidence": "high", // none | low | medium | high | exact "path": "powerpoint.app_statistics", "source": "docProps/app.xml saved statistic" } }, "execution": { "unresolved_targets": [], "actual_cost": { } }, "diagnostics": [] } ``` A result's `status` is one of `resolved`, `estimated`, `planned`, `unknown`, `unsupported`, `invalid`, `budget_exceeded`, `failed`. Only `resolved` and `estimated` carry a `value`. `unknown` is the common one: the path ran and the document simply does not record that fact. - The report's own `status` is `ok` or `partial`. `partial` is **not** a failure — at least one requested target could not be resolved at the requested confidence, and the rest are still valid. Check `execution.unresolved_targets`. - Add `--strict` when an unresolved target must fail the command. The full report is still written, and the exit status becomes `5`. - `results` is keyed by the canonical target name even when you asked with a short alias. ## 6. Exit status — and what to do about it | Status | Meaning | Your next move | | ---: | --- | --- | | `0` | Success | Parse stdout. Unresolved targets are allowed unless `--strict` | | `1` | Invalid request or unsupported target | You named a target this format has no path for. Run `deckprobe targets --format <fmt>` | | `2` | CLI syntax error, or the file is missing/unreadable | Fix the flags or the path | | `3` | Unsupported or unrecognized format | Check the extension against `deckprobe formats` | | `4` | Malformed input or budget exceeded | Genuinely damaged file, or raise `-b`/`-x`/`-e`/`-T`. See `references/limits.md` | | `5` | `--strict` and a target was unresolved | The report is on stdout; decide whether the missing target matters | | `6` | Internal failure | A bug. Report it | ## 7. Many files at once ```bash printf '%s\n' '{"path":"a.pdf"}' '{"path":"b.pptx"}' | deckprobe --jsonl -t @summary ``` One compact JSON per non-empty input line. A per-record error does not stop the run; the process exits with the highest per-record status. Each record is a JSON string path, `{"path":"..."}`, or `{"name":"report.pdf","data_base64":"..."}`. Add an `id` to an object record when the caller needs stable attribution; JSONL error envelopes echo that `id` and the original `path` under `input`. For bytes on stdin, `-n` supplies the logical filename that selects the format: ```bash curl -sL "$url" | deckprobe -n download.pdf -t @security - ``` ## 8. What DeckProbe will not do It has no renderer, no OCR, no text extraction, and no macro interpreter. It never opens a network connection and never resolves an external reference. Legacy XML iWork packages are rejected by design — only modern IWA is supported. `.xlsb` is identity-only. PDF XMP is not merged into the metadata targets. Routing is by filename extension, then verified against the container. Renaming a `.pptx` to `.docx` returns `MALFORMED_INPUT`, and `-f`/`--input-format` is an assertion, not an override — it cannot force an unrelated parser onto a file. Never parse stderr. Every result, including every error, is one JSON value on stdout. ## References Read these only when you need them: - **`references/targets.md`** — the full target catalogue per format, with aliases and minimum levels. Read before naming a target you have not verified. - **`references/recipes.md`** — task → command lookup for the common questions. - **`references/output.md`** — the complete report shape, error codes, and cost accounting, for when you are consuming the JSON programmatically. - **`references/limits.md`** — budgets, the `-b`/`-x`/`-e`/`-T` overrides, `-O` format options, and how to diagnose an exit `4`.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.