token-goat 2.9.12 → 2.9.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +20 -1
  2. package/dist/token-goat-chunk-2ESBO4IN.mjs +209 -0
  3. package/dist/token-goat-chunk-3BTK54F3.mjs +1733 -0
  4. package/dist/{token-goat-chunk-6NIGPPN6.mjs → token-goat-chunk-3XQPEJMV.mjs} +1 -1
  5. package/dist/token-goat-chunk-4SDX3QP3.mjs +122 -0
  6. package/dist/token-goat-chunk-7OGKZ7AP.mjs +142 -0
  7. package/dist/token-goat-chunk-AH6QILZM.mjs +26 -0
  8. package/dist/token-goat-chunk-AMYCQJX4.mjs +1548 -0
  9. package/dist/{token-goat-chunk-OTW7LC4Y.mjs → token-goat-chunk-ASVVF4JV.mjs} +13 -10
  10. package/dist/{token-goat-chunk-GCXX67HM.mjs → token-goat-chunk-DAMXYVIW.mjs} +12477 -12156
  11. package/dist/token-goat-chunk-DBNY4RLN.mjs +308 -0
  12. package/dist/token-goat-chunk-EG3663UT.mjs +226 -0
  13. package/dist/token-goat-chunk-EZNVAIR3.mjs +21 -0
  14. package/dist/token-goat-chunk-GMOUBOX4.mjs +386 -0
  15. package/dist/{token-goat-chunk-6F5TLJC7.mjs → token-goat-chunk-HF6H7RNK.mjs} +1 -1
  16. package/dist/{token-goat-chunk-GM6QZWCF.mjs → token-goat-chunk-IT6O3PNN.mjs} +2660 -6877
  17. package/dist/token-goat-chunk-NEI4NC54.mjs +424 -0
  18. package/dist/token-goat-chunk-O5WAMISC.mjs +167 -0
  19. package/dist/{token-goat-chunk-LHLQFGWQ.mjs → token-goat-chunk-OMNQUUIT.mjs} +9 -27
  20. package/dist/{token-goat-chunk-SF2CKCFQ.mjs → token-goat-chunk-PGGDW7DZ.mjs} +30 -13
  21. package/dist/token-goat-chunk-POBYR64E.mjs +1632 -0
  22. package/dist/token-goat-chunk-PRJVGIC5.mjs +58 -0
  23. package/dist/{token-goat-chunk-4CK445AW.mjs → token-goat-chunk-RUDOKYPJ.mjs} +10074 -9765
  24. package/dist/token-goat-chunk-SAQ5PG4L.mjs +2634 -0
  25. package/dist/token-goat-chunk-T2IWWTHB.mjs +3155 -0
  26. package/dist/{token-goat-chunk-TOGYS5A7.mjs → token-goat-chunk-VZYD4OZB.mjs} +1469 -5752
  27. package/dist/token-goat-chunk-XEH6KBWW.mjs +23 -0
  28. package/dist/token-goat-chunk-XQF5J25J.mjs +33 -0
  29. package/dist/token-goat-chunk-XTQAOTSO.mjs +89 -0
  30. package/dist/{token-goat-chunk-THLHC6QJ.mjs → token-goat-chunk-XVZ4MNQC.mjs} +614 -603
  31. package/dist/token-goat-chunk-Y4AFKTHK.mjs +22 -0
  32. package/dist/token-goat-chunk-YHGTGG6K.mjs +396 -0
  33. package/dist/{token-goat-chunk-SBYRP3X4.mjs → token-goat-chunk-YTQHZJXW.mjs} +61 -27
  34. package/dist/token-goat-chunk-Z6UXPYJA.mjs +62 -0
  35. package/dist/token-goat-chunk-ZD4EM4LR.mjs +30 -0
  36. package/dist/token-goat-chunk-ZFM4PWXL.mjs +585 -0
  37. package/dist/{token-goat-chunk-A6QTLAWO.mjs → token-goat-chunk-ZYNNQ36L.mjs} +128 -328
  38. package/dist/token-goat-hook.mjs +12 -6
  39. package/dist/token-goat.core.mjs +22 -7
  40. package/docs/cli.md +12 -8
  41. package/docs/security.md +1 -1
  42. package/package.json +4 -2
  43. package/dist/token-goat-chunk-HKFOH6JH.mjs +0 -28
  44. package/dist/token-goat-chunk-LOCOX2ML.mjs +0 -3564
  45. package/dist/token-goat-chunk-VRWX6QYW.mjs +0 -24
package/README.md CHANGED
@@ -117,7 +117,11 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
117
117
  | Subagent reads a ≥30 KB markdown file whole, on its first look at it | Off by default. Set `subagent_markdown_first_read_deny = true` under `[hints]` to block that read and answer with the heading tree instead. Only fires in a subagent, only on a first, un-ranged read of a `.md`/`.mdx`/`.markdown` file with at least three headings; a read that already asks for a line range, and every main-session read, pass through untouched |
118
118
  | A source file is read whole for the first time, with no earlier copy to compare it against | Keeps each function's declaration and its first eight lines, replacing the rest with one line naming the symbol, the exact range removed, and the command that returns it. Set `fold_code_bodies = false` under `[hints]` to turn it off. Comment blocks of twelve lines or more fold to their first two on the same terms. Imports, signatures, type declarations, classes and interfaces are never folded, and a file whose index no longer matches what is on disk keeps its bodies. Measured on this repository's own source: 229 of 256 files fold, and 43% of what a read of them delivers is removed. A read that already names an offset or a limit, and any file holding something that looks like a secret, pass through untouched |
119
119
  | PDF opened via Read | Full read denied; PDF shows page count and outline (`token-goat pdf-extract` pulls the actual text, optionally paged/sliced, when the outline isn't enough) |
120
- | Excel/PowerPoint/Word file (.xlsx/.pptx/.docx) opened via Read | Full read denied; redirects to the matching narrow-slice command family (`xlsx-sheets`/`xlsx-head`/`xlsx-range`/`xlsx-query`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `docx-outline`/`docx-text`) instead of extracting the whole document as text |
120
+ | Excel/PowerPoint/Word file (.xlsx/.pptx/.docx) opened via Read | Full read denied; redirects to the matching narrow-slice command family (`xlsx-sheets`/`xlsx-columns`/`xlsx-head`/`xlsx-range`/`xlsx-query`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `docx-outline`/`docx-tables`/`docx-text`) instead of extracting the whole document as text |
121
+ | SQLite database (.db, .sqlite, .sqlite3, .db3) opened via Read | Full read denied; redirects to the SQLite narrow-slice family (`sqlite-tables`/`sqlite-schema`/`sqlite-query`) instead of reading binary bytes into context |
122
+ | Parquet file (.parquet) opened via Read | Full read denied; redirects to DuckDB for SQL querying instead of reading binary columnar data |
123
+ | Large JSON or YAML file (≥20 KB) read in full | Top-level keys or array summary previewed; redirects to `json-outline`/`json-query` or `yaml-outline`/`yaml-query` instead of blowing context on multi-megabyte payloads |
124
+ | Large JSON Lines file (.jsonl ≥20 KB) read in full | Record count and first-record schema shown; suggests slicing records with offset/limit instead of loading millions of tokens |
121
125
  | Other Office binary (.odt, .ods, .ott, .odp) opened via Read | Full read denied; redirects to `pandoc` for text extraction (no dedicated reader for these formats yet) |
122
126
  | Large CSV or TSV file (≥10 KB) read in full | Column headers, row count, and 3 sample rows shown; `token-goat csv-query` projects columns and/or filters rows instead of a full read; `duckdb` query suggestion for very large tabular data |
123
127
  | WebFetch returns a page's full raw HTML | HTML-to-text extraction strips markup/scripts/styles before the model ever sees it — readable prose instead of a wall of tags |
@@ -294,6 +298,15 @@ Built-in output compression covers 150+ dev tool CLIs: `pytest`, `jest` / `vites
294
298
 
295
299
  A failing `pytest` / `jest` / `vitest` / `go test` / `cargo test` run (including bare `npm test`/`npm run test`/`yarn test`/`pnpm test`) also gets a one-line advisory naming the exact `token-goat bash-output <id> | token-goat failures` command to run instead of re-reading the raw dump. Silent on a passing run, a non-test command, or output too small to be worth reducing.
296
300
 
301
+ Give the command three ways: as positional arguments (`token-goat compress pytest -v tests/`), as one string with `-c` / `--cmd`, or base64-encoded with `--cmd-b64`. The base64 form preserves quotes, backslashes, and dollar signs that a harness or an intermediate shell would otherwise mangle; it takes precedence when more than one form is supplied. `--native` runs the command through the platform's own shell (`cmd.exe` on Windows) instead of bash, which keeps Windows path backslashes intact for tools that reject the forward-slash form.
302
+
303
+ `-q` / `--quiet-success` trades a clean run's output for one line. On exit code 0 the full output is stored for recall and only the summary prints; on any other exit code the compressed output is delivered as usual.
304
+
305
+ ```
306
+ $ token-goat compress -q --cmd "npm run lint"
307
+ [tg: ok] npm run lint (22.2s, recall: token-goat bash-output 326d9800e732eb9d)
308
+ ```
309
+
297
310
  ### 6. Context pressure
298
311
 
299
312
  Token-goat tracks how close a session is to the autocompact trigger and tightens its hints as the window fills. Surgical-read suggestions kick in on progressively smaller files as pressure builds (500 lines at cool, down to 50 at critical), so large reads get flagged before they tip the session over. The PreCompact manifest also shrinks: capped at 500 tokens once the window runs hot, 300 once critical, so it stops contributing to the pressure it measures. The denominator is always the fixed 660,000-token autocompact trigger budget, not the model's raw context window, so the same thresholds apply across models. Run `token-goat doctor --context` to see the current footprint.
@@ -474,6 +487,8 @@ The file-by-file table for each harness, and the path that file sits at: **[What
474
487
 
475
488
  Hooks fire automatically on every tool call once installed — nothing to start or restart there. The background worker is a separate, manual step: `token-goat worker start` launches it as a detached process, `token-goat worker status` checks it, `token-goat worker stop` kills it. It restarts itself automatically if it crashes or gets killed while the machine is running — an edit hook checks its liveness and respawns it, rate-limited to about once every 5 minutes. It does not survive a reboot or logout, though; re-run `worker start` after either. `token-goat uninstall` removes the hook entries, `CLAUDE.md` block, and skill directory, but does not touch a running worker — stop it separately with `token-goat worker stop` if you no longer want it running.
476
489
 
490
+ To move to a newer release, `token-goat upgrade` installs it from npm and then re-runs `token-goat install`, so your hooks and integration manifests point at the build that just landed. `token-goat upgrade --check` reports whether one is available without installing anything, and `--json` gives the same answer for a script. Both go quiet when `network.offline` is set, and `token-goat doctor` mentions an available update at the end of its report.
491
+
477
492
  ## Verify
478
493
 
479
494
  ```
@@ -519,6 +534,8 @@ The pipeline is written in TypeScript and decodes PNG, JPEG, GIF, and BMP itself
519
534
 
520
535
  The one thing the decoders will do is decline. A file whose header asks for more than 256 MB of pixel buffer, or that is in a variant token-goat cannot read exactly, is refused rather than guessed at, and the untouched original goes to the model. An untouched original is the right picture, so the only cost of a refusal is that the read is not shrunk.
521
536
 
537
+ When `tesseract.js` is installed, text-heavy images can also have their text extracted via OCR (`token-goat image-text <file>`) to replace raw pixel data with plain text. English (`eng`) is always enabled by default, and users can combine one or more additional languages (`token-goat image-text <file> --lang "fra,spa"`). Language preferences persist across version updates when saved globally (`token-goat config set image_shrink.ocr_lang "fra,spa"`), per-project in `.token-goat.toml` (`[image_shrink] ocr_lang = "fra+spa"`), or via `TOKEN_GOAT_OCR_LANG`. 16 languages are supported (`eng`, `fra`, `spa`, `deu`, `ita`, `por`, `nld`, `pol`, `rus`, `tur`, `swe`, `ara`, `chi_sim`, `chi_tra`, `jpn`, `kor`), each cryptographically verified against pinned package checksums to protect against supply-chain tampering.
538
+
522
539
  ## Stats display
523
540
 
524
541
  `token-goat stats` uses 24-bit ANSI color and Unicode block characters for gradient bars, sparklines, and the activity heatmap. In the right terminal it renders sharply. In the wrong one you get broken characters, flat gray blocks, or a "rich is not installed" error.
@@ -695,6 +712,8 @@ Bug reports go to the same place. The most useful ones include:
695
712
  - The matching log line from `%LOCALAPPDATA%\dfk-helper\token-goat\logs\` on Windows or `~/.local/share/token-goat/logs/` on Linux/WSL
696
713
  - What you expected and what actually happened
697
714
 
715
+ To audit an existing AI session for missed savings, hook friction, or optimization opportunities before opening an issue, see the [Session Mining Prompt](docs/PROMPT_SESSION_MINING.md).
716
+
698
717
  For private questions, commercial licensing, or anything you'd rather not post publicly, contact me at token-goat@dfkhelper.com.
699
718
 
700
719
  ## Disclaimer
@@ -0,0 +1,209 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ NotAnOfficeDocumentError,
5
+ assertOoxmlWithinDeadline,
6
+ collectElements,
7
+ collectTextRuns,
8
+ decodeZipEntry,
9
+ ooxmlPartBudget,
10
+ ooxmlWorkDeadline,
11
+ parseOoxmlPart,
12
+ readOoxmlZip,
13
+ sortNumberedParts
14
+ } from "./token-goat-chunk-YHGTGG6K.mjs";
15
+ import {
16
+ compileGuardedRegex
17
+ } from "./token-goat-chunk-GMOUBOX4.mjs";
18
+ import {
19
+ init_define_import_meta_env
20
+ } from "./token-goat-chunk-A37V4PBF.mjs";
21
+
22
+ // src/pptx_extract.ts
23
+ init_define_import_meta_env();
24
+ function shapePlaceholderType(shape) {
25
+ const sp = shape;
26
+ return sp["p:nvSpPr"]?.["p:nvPr"]?.["p:ph"]?.["@_type"];
27
+ }
28
+ function slideShapes(parsedSlide) {
29
+ return collectElements(parsedSlide, "p:sp");
30
+ }
31
+ function shapeText(shape) {
32
+ return collectTextRuns(shape, "a:t").join(" ").trim();
33
+ }
34
+ function tableRowBlocks(parsedSlide) {
35
+ const blocks = [];
36
+ for (const tbl of collectElements(parsedSlide, "a:tbl")) {
37
+ for (const row of collectElements(tbl, "a:tr")) {
38
+ const cellTexts = collectElements(row, "a:tc").map((cell) => collectTextRuns(cell, "a:t").join(" ").trim());
39
+ const rowText = cellTexts.join(" | ").trim();
40
+ if (rowText.length > 0) blocks.push(rowText);
41
+ }
42
+ }
43
+ return blocks;
44
+ }
45
+ async function slidePathsInPresentationOrder(entries, budget) {
46
+ const presXml = decodeZipEntry(entries, "ppt/presentation.xml", budget);
47
+ const relsXml = decodeZipEntry(entries, "ppt/_rels/presentation.xml.rels", budget);
48
+ if (presXml === null || relsXml === null) return null;
49
+ const presParsed = await parseOoxmlPart(presXml);
50
+ const relsParsed = await parseOoxmlPart(relsXml);
51
+ const ridToTarget = /* @__PURE__ */ new Map();
52
+ for (const rel of collectElements(relsParsed, "Relationship")) {
53
+ if (rel["@_Id"] !== void 0 && rel["@_Target"] !== void 0) {
54
+ ridToTarget.set(rel["@_Id"], rel["@_Target"]);
55
+ }
56
+ }
57
+ const ordered = [];
58
+ const seen = /* @__PURE__ */ new Set();
59
+ for (const sldId of collectElements(presParsed, "p:sldId")) {
60
+ const rid = sldId["@_r:id"];
61
+ if (rid === void 0) continue;
62
+ const target = ridToTarget.get(rid);
63
+ if (target === void 0) continue;
64
+ const normalized = target.startsWith("slides/") ? `ppt/${target}` : `ppt/slides/${target}`;
65
+ if (entries[normalized] === void 0 || seen.has(normalized)) continue;
66
+ seen.add(normalized);
67
+ ordered.push(normalized);
68
+ }
69
+ return ordered.length > 0 ? ordered : null;
70
+ }
71
+ async function listSlideParts(filePath) {
72
+ const entries = await readOoxmlZip(filePath, ".pptx");
73
+ const budget = ooxmlPartBudget();
74
+ const slidePaths = await slidePathsInPresentationOrder(entries, budget) ?? sortNumberedParts(
75
+ Object.keys(entries).filter((p) => /^ppt\/slides\/slide\d+\.xml$/.test(p)),
76
+ /slide(\d+)\.xml$/
77
+ );
78
+ if (slidePaths.length === 0) throw new NotAnOfficeDocumentError(`no slides found in ${filePath} (not a valid .pptx?)`);
79
+ return { entries, slidePaths, budget };
80
+ }
81
+ function relsPathFor(slidePath) {
82
+ const idx = slidePath.lastIndexOf("/");
83
+ return `${slidePath.slice(0, idx)}/_rels/${slidePath.slice(idx + 1)}.rels`;
84
+ }
85
+ function resolveRelativeTarget(basePath, target) {
86
+ const baseDir = basePath.slice(0, basePath.lastIndexOf("/"));
87
+ const segments = `${baseDir}/${target}`.split("/");
88
+ const resolved = [];
89
+ for (const segment of segments) {
90
+ if (segment === "" || segment === ".") continue;
91
+ if (segment === "..") resolved.pop();
92
+ else resolved.push(segment);
93
+ }
94
+ return resolved.join("/");
95
+ }
96
+ async function notesPathFor(entries, slidePath, budget) {
97
+ const relsXml = decodeZipEntry(entries, relsPathFor(slidePath), budget);
98
+ if (relsXml === null) return null;
99
+ const relsParsed = await parseOoxmlPart(relsXml);
100
+ for (const rel of collectElements(relsParsed, "Relationship")) {
101
+ if (rel["@_Target"] !== void 0 && rel["@_Type"]?.endsWith("/notesSlide") === true) {
102
+ return resolveRelativeTarget(slidePath, rel["@_Target"]);
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ async function parseSlide(entries, path, budget) {
108
+ const xml = decodeZipEntry(entries, path, budget);
109
+ if (xml === null) throw new Error(`missing part: ${path}`);
110
+ return parseOoxmlPart(xml);
111
+ }
112
+ async function notesTextFor(entries, notesPath, budget) {
113
+ if (notesPath === null) return "";
114
+ const xml = decodeZipEntry(entries, notesPath, budget);
115
+ if (xml === null) return "";
116
+ const parsed = await parseOoxmlPart(xml);
117
+ const shapes = collectElements(parsed, "p:sp");
118
+ const bodyShape = shapes.find((s) => shapePlaceholderType(s) === "body");
119
+ return bodyShape !== void 0 ? shapeText(bodyShape) : collectTextRuns(parsed, "a:t").join(" ").trim();
120
+ }
121
+ async function pptxOutline(filePath, deadline = ooxmlWorkDeadline()) {
122
+ const { entries, slidePaths, budget } = await listSlideParts(filePath);
123
+ const out = [];
124
+ for (let i = 0; i < slidePaths.length; i++) {
125
+ assertOoxmlWithinDeadline(deadline, "Narrow the read to specific slides with pptx-slide, or use a smaller deck.");
126
+ const path = slidePaths[i];
127
+ const parsed = await parseSlide(entries, path, budget);
128
+ const shapes = slideShapes(parsed);
129
+ const titleShape = shapes.find((s) => {
130
+ const t = shapePlaceholderType(s);
131
+ return t === "title" || t === "ctrTitle";
132
+ });
133
+ const title = titleShape !== void 0 ? shapeText(titleShape) : "";
134
+ const allText = collectTextRuns(parsed, "a:t").join(" ");
135
+ const bodyChars = Math.max(0, allText.length - title.length);
136
+ const hasNotes = (await notesTextFor(entries, await notesPathFor(entries, path, budget), budget)).length > 0;
137
+ out.push({ slide: i + 1, title, bodyChars, hasNotes });
138
+ }
139
+ return out;
140
+ }
141
+ async function slideTextFromParts(entries, slidePaths, slideNumber, includeNotes, budget) {
142
+ if (slideNumber < 1 || slideNumber > slidePaths.length) {
143
+ throw new Error(`slide ${slideNumber} out of range (this deck has ${slidePaths.length} slides)`);
144
+ }
145
+ const path = slidePaths[slideNumber - 1];
146
+ const parsed = await parseSlide(entries, path, budget);
147
+ const shapes = slideShapes(parsed);
148
+ const blocks = [...shapes.map(shapeText).filter((t) => t.length > 0), ...tableRowBlocks(parsed)];
149
+ const lines = [`# Slide ${slideNumber}`, ...blocks];
150
+ if (includeNotes) {
151
+ const notes = await notesTextFor(entries, await notesPathFor(entries, path, budget), budget);
152
+ if (notes.length > 0) lines.push("", "## Speaker notes", notes);
153
+ }
154
+ return lines.join("\n\n");
155
+ }
156
+ async function pptxSlideText(filePath, slideNumber, includeNotes) {
157
+ const { entries, slidePaths, budget } = await listSlideParts(filePath);
158
+ return slideTextFromParts(entries, slidePaths, slideNumber, includeNotes, budget);
159
+ }
160
+ async function pptxAllSlidesText(filePath, includeNotes, deadline = ooxmlWorkDeadline()) {
161
+ const { entries, slidePaths, budget } = await listSlideParts(filePath);
162
+ const slideTexts = [];
163
+ for (let i = 1; i <= slidePaths.length; i++) {
164
+ assertOoxmlWithinDeadline(deadline, "Narrow the read to specific slides with pptx-slide, or use a smaller deck.");
165
+ slideTexts.push(await slideTextFromParts(entries, slidePaths, i, includeNotes, budget));
166
+ }
167
+ return slideTexts.join("\n\n");
168
+ }
169
+ async function pptxNotesText(filePath, slideNumber, deadline = ooxmlWorkDeadline()) {
170
+ const { entries, slidePaths, budget } = await listSlideParts(filePath);
171
+ const targets = slideNumber !== void 0 ? [slideNumber] : slidePaths.map((_, i) => i + 1);
172
+ const sections = [];
173
+ for (const n of targets) {
174
+ assertOoxmlWithinDeadline(deadline, "Narrow the read to one slide with the slide-number argument, or use a smaller deck.");
175
+ if (n < 1 || n > slidePaths.length) throw new Error(`slide ${n} out of range (this deck has ${slidePaths.length} slides)`);
176
+ const notesPath = await notesPathFor(entries, slidePaths[n - 1], budget);
177
+ const text = await notesTextFor(entries, notesPath, budget);
178
+ if (text.length > 0) sections.push(`# Slide ${n} notes
179
+
180
+ ${text}`);
181
+ }
182
+ return sections.join("\n\n");
183
+ }
184
+ async function pptxTextGrep(filePath, pattern, deadline = ooxmlWorkDeadline()) {
185
+ const { entries, slidePaths, budget } = await listSlideParts(filePath);
186
+ const guarded = compileGuardedRegex(pattern, "i");
187
+ if (!guarded.ok) throw new Error(`invalid --grep pattern: ${pattern} -- the pattern ${guarded.reason}`);
188
+ const re = guarded.re;
189
+ const out = [];
190
+ for (let i = 0; i < slidePaths.length; i++) {
191
+ assertOoxmlWithinDeadline(deadline, "Narrow the read to specific slides with pptx-slide, or use a smaller deck.");
192
+ const parsed = await parseSlide(entries, slidePaths[i], budget);
193
+ const text = collectTextRuns(parsed, "a:t").join(" ");
194
+ if (re.test(text)) {
195
+ const idx = text.search(re);
196
+ const snippet = text.slice(Math.max(0, idx - 40), idx + 80).trim();
197
+ out.push({ slide: i + 1, snippet });
198
+ }
199
+ }
200
+ return out;
201
+ }
202
+
203
+ export {
204
+ pptxOutline,
205
+ pptxSlideText,
206
+ pptxAllSlidesText,
207
+ pptxNotesText,
208
+ pptxTextGrep
209
+ };