Claude Skill

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

LLM Mart · 0 points · 1 views 22 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download deckflow-deckprobe-skills_deckprobe-2e5d064.zip · 17 KB

Install

skills CLI npx skills add https://github.com/deckflow/deckprobe/tree/main/skills/deckprobe
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install deckflow-deckprobe@llmmart
Git 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 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

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 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

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/-T overrides, -O format options, and how to diagnose an exit 4.
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.

No comments yet.

Reviews (0)

No reviews yet.

Related