@intentic/claude-plugin 1.322.0 → 1.323.0

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "intentic",
3
3
  "displayName": "Intentic",
4
- "version": "1.322.0",
4
+ "version": "1.323.0",
5
5
  "description": "Cheaper, sharper Claude Code sessions: Bash output trimmed before Claude reads it, a project map and this project's field notes at session start, iq code search and fileq document reading taught, and /intentic:stats to show what each one saved. Every mechanism is a switch in /config.",
6
6
  "author": {
7
7
  "name": "intentic"
package/dist/fileq.mjs CHANGED
@@ -87118,8 +87118,8 @@ var require_body_reader = __commonJS({
87118
87118
  if (checkboxResult) {
87119
87119
  var checkboxElement = fldChar.firstOrEmpty("w:ffData").firstOrEmpty("w:checkBox");
87120
87120
  var checkedElement = checkboxElement.first("w:checked");
87121
- var checked = checkedElement == null ? readBooleanElement(checkboxElement.first("w:default")) : readBooleanElement(checkedElement);
87122
- return { type: "checkbox", checked };
87121
+ var checked2 = checkedElement == null ? readBooleanElement(checkboxElement.first("w:default")) : readBooleanElement(checkedElement);
87122
+ return { type: "checkbox", checked: checked2 };
87123
87123
  }
87124
87124
  return { type: "unknown" };
87125
87125
  }
@@ -105433,12 +105433,12 @@ var init_pdf_furniture = __esm({
105433
105433
  });
105434
105434
 
105435
105435
  // ../fileq/src/lib/derivers/pdf.ts
105436
- import { execFile as execFile2 } from "node:child_process";
105436
+ import { execFile as execFile2, spawnSync } from "node:child_process";
105437
105437
  import { mkdtemp, readdir, readFile as readFile10, rm } from "node:fs/promises";
105438
105438
  import { tmpdir } from "node:os";
105439
105439
  import { join as join3 } from "node:path";
105440
105440
  import { promisify as promisify2 } from "node:util";
105441
- var SCAN_THRESHOLD_CHARS_PER_PAGE, MAX_OCR_PAGES, OCR_DPI, run3, ocrAvailable, recognisePages, pagesToMarkdown, furnitureNotes, withoutFurniture, pdfDeriver;
105441
+ var SCAN_THRESHOLD_CHARS_PER_PAGE, MAX_OCR_PAGES, OCR_DPI, OCR_TIMEOUT_MS, run3, checked, ocrAvailable, readPages, recognisePages, pagesToMarkdown, furnitureNotes, withoutFurniture, pdfDeriver;
105442
105442
  var init_pdf = __esm({
105443
105443
  "../fileq/src/lib/derivers/pdf.ts"() {
105444
105444
  "use strict";
@@ -105448,8 +105448,27 @@ var init_pdf = __esm({
105448
105448
  SCAN_THRESHOLD_CHARS_PER_PAGE = 24;
105449
105449
  MAX_OCR_PAGES = 20;
105450
105450
  OCR_DPI = "200";
105451
+ OCR_TIMEOUT_MS = 6e4 + MAX_OCR_PAGES * 15e3;
105451
105452
  run3 = promisify2(execFile2);
105452
- ocrAvailable = () => onPath("tesseract") && onPath("pdftoppm");
105453
+ checked = /* @__PURE__ */ new Map();
105454
+ ocrAvailable = () => {
105455
+ const path = process.env["PATH"] ?? "";
105456
+ let available = checked.get(path);
105457
+ if (available === void 0) {
105458
+ available = onPath("pdftoppm") && onPath("ocr") && spawnSync("ocr", ["--check"], { stdio: "ignore", timeout: 1e4 }).status === 0;
105459
+ checked.set(path, available);
105460
+ }
105461
+ return available;
105462
+ };
105463
+ readPages = (images) => new Promise((resolve6, reject) => {
105464
+ execFile2("ocr", [...images], { timeout: OCR_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024 }, (error2, stdout) => {
105465
+ if (error2 === null || error2.code === 1) {
105466
+ resolve6(stdout);
105467
+ } else {
105468
+ reject(error2);
105469
+ }
105470
+ });
105471
+ });
105453
105472
  recognisePages = async (absPath, totalPages) => {
105454
105473
  const dir = await mkdtemp(join3(tmpdir(), "fileq-ocr-"));
105455
105474
  try {
@@ -105457,12 +105476,11 @@ var init_pdf = __esm({
105457
105476
  timeout: 12e4
105458
105477
  });
105459
105478
  const images = (await readdir(dir)).filter((name) => name.endsWith(".png")).toSorted();
105460
- const pages = [];
105461
- for (const image of images) {
105462
- const { stdout } = await run3("tesseract", [join3(dir, image), "stdout", "-l", "eng"], { timeout: 12e4, maxBuffer: 16 * 1024 * 1024 });
105463
- pages.push(stdout.replaceAll(/[ \t]+/g, " ").trim());
105479
+ if (images.length === 0) {
105480
+ return [];
105464
105481
  }
105465
- return pages;
105482
+ const pages = (await readPages(images.map((image) => join3(dir, image)))).split("\f");
105483
+ return images.map((_3, index) => (pages[index] ?? "").replaceAll(/[ \t]+/g, " ").trim());
105466
105484
  } finally {
105467
105485
  await rm(dir, { recursive: true, force: true });
105468
105486
  }
@@ -105478,11 +105496,11 @@ ${page === "" ? "(no text on this page)" : page}`).join("\n\n");
105478
105496
  return { markdown: pagesToMarkdown(kept), notes: furnitureNotes(removed, example) };
105479
105497
  };
105480
105498
  pdfDeriver = {
105481
- // The stamp names OCR capability, since a sidecar written before tesseract was available must read as stale now
105482
- // that it's here.
105483
- // A static name would leave a pre-OCR sidecar looking current forever.
105499
+ // The stamp names OCR capability, since a sidecar written before the reader was available must read as stale now
105500
+ // that it's here. A static name would leave a pre-OCR sidecar looking current forever. It names the reader too:
105501
+ // `pdf+ocr` was tesseract's, whose pages PaddleOCR reads again.
105484
105502
  get name() {
105485
- return ocrAvailable() ? "pdf+ocr" : "pdf";
105503
+ return ocrAvailable() ? "pdf+ppocr" : "pdf";
105486
105504
  },
105487
105505
  version: 2,
105488
105506
  derive: async (absPath) => {
@@ -105504,10 +105522,10 @@ ${page === "" ? "(no text on this page)" : page}`).join("\n\n");
105504
105522
  }
105505
105523
  const recognised = await recognisePages(absPath, totalPages);
105506
105524
  const notes = [
105507
- `scanned: text recognised by OCR (tesseract) on ${recognised.length} of ${totalPages} page${plural3} \u2014 recognised, not exact; check figures against the page image`
105525
+ `scanned: text recognised by OCR (PaddleOCR) on ${recognised.length} of ${totalPages} page${plural3} \u2014 recognised, not exact; check figures against the page image`
105508
105526
  ];
105509
105527
  if (totalPages > MAX_OCR_PAGES) {
105510
- notes.push(`OCR stops at ${MAX_OCR_PAGES} pages: run tesseract over the rest yourself if they matter`);
105528
+ notes.push(`OCR stops at ${MAX_OCR_PAGES} pages: draw the rest with pdftoppm and read them with \`ocr <page.png>...\` if they matter`);
105511
105529
  }
105512
105530
  const { markdown, notes: stripped } = withoutFurniture(recognised);
105513
105531
  return { markdown, title, notes: [...notes, ...stripped] };
@@ -13,7 +13,7 @@ cached by content, so a repeat read of unchanged content is instant.
13
13
  - Prints a capsule (format, token cost), the content up to the budget, and `saved:` — the markdown file
14
14
  carrying the whole thing. Over budget, the cut is announced with that exact path to Read.
15
15
  - Formats: docx and odt (headings, lists, tables), xlsx (capped tables), pptx (slides + speaker notes),
16
- pdf (text layer; a scan is OCR'd when `tesseract` is on PATH, and the note says the words are
16
+ pdf (text layer; a scan is OCR'd when an `ocr` command is on PATH, and the note says the words are
17
17
  recognised, not exact), epub (chapters in reading order), ipynb (cells, fenced code, capped outputs),
18
18
  png/jpg/gif/webp (dimensions + EXIF), mp3/wav/flac/mp4/… (duration + tags), html, and V8 profiles
19
19
  (`.cpuprofile` from `node --cpu-prof`, `.heapprofile` from `--heap-prof`) as functions ranked by self and total cost.
@@ -29,9 +29,10 @@ without parsing it again. Run it again rather than opening a sidecar under `.int
29
29
  checks the hash for you.
30
30
 
31
31
  ## What it refuses, and why
32
- - A scanned PDF with no `tesseract` on PATH answers "no usable text layer … OCR is not part of this tier"
32
+ - A scanned PDF with no `ocr` on PATH answers "no usable text layer … OCR is not part of this tier"
33
33
  rather than an empty page; images say "no visual description". Treat those notes as "not generated",
34
- never as "nothing there". Installing tesseract turns the first into OCR.
34
+ never as "nothing there". An `ocr` command that prints each image's text, a form feed between images (the
35
+ Intentic sandbox's PaddleOCR reader), turns the first into OCR.
35
36
  - Plain text (md, csv, txt, code) is not fileq's business: Read it directly — but see below for the big ones.
36
37
  - Web pages belong to `webq`; images for a vision model belong to the Read tool, which shows the pixels.
37
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentic/claude-plugin",
3
- "version": "1.322.0",
3
+ "version": "1.323.0",
4
4
  "description": "The intentic Claude Code plugin: trimmed Bash output, a project map and field notes at session start, iq search and fileq teaching, and a /intentic:stats report of what each saved",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -40,8 +40,8 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@intentic/agent-context": "0.0.0",
43
- "@intentic/fileq": "1.322.0",
44
- "@intentic/iq": "1.322.0",
43
+ "@intentic/fileq": "1.323.0",
44
+ "@intentic/iq": "1.323.0",
45
45
  "@intentic/output-cleaners": "0.0.0",
46
46
  "@intentic/testing": "0.0.0",
47
47
  "@intentic/tsconfig": "0.0.0",