dsh-book-reader 0.0.0-stage → 0.1.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.
package/SKILL.md ADDED
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: pdf-reader
3
+ description: 'Read a PDF (book, paper, manual) cheaply and accurately with the dsh-book-reader tools: page map/TOC, literal full-text search, then selective page reads (text first, images only when typography matters). Use whenever the user asks about specific pages, a section, a theorem, a figure, or "this page" in a PDF.'
4
+ ---
5
+
6
+ # Reading a PDF with `dsh-book-reader`
7
+
8
+ Four tools, three cost tiers. **The default is text.** Images are a deliberate
9
+ expense, not the first move.
10
+
11
+ | Tool | Cost | Use for |
12
+ | --- | --- | --- |
13
+ | `pdf_info` | ~0 (text) | page count, printed↔physical offset, outline/TOC |
14
+ | `pdf_search` | ~0 (text) | locating a phrase, theorem number, or figure caption |
15
+ | `pdf_read_pages` + `include_image:false` | ~0 (text, per page) | prose, theorem statements, exercise text |
16
+ | `pdf_read_pages` (images on) | **~350–1024 tokens/page** | formulas, diagrams, tables, layout |
17
+ | `pdf_current_page` | ~0 | which page the user is looking at |
18
+
19
+ ## Default loop (text-first)
20
+
21
+ 1. `pdf_info` once per document → note `printedOffset` and the outline. Never
22
+ hardcode an offset; it is detected (`printed = pdf − printedOffset`).
23
+ 2. `pdf_search` for a distinctive phrase → page numbers.
24
+ 3. `pdf_read_pages` with **`include_image:false`** to read those pages as text.
25
+ 4. Only if the text is ambiguous or the question is about typography (a formula,
26
+ a diagram, a table) → call again with images on, and crop.
27
+
28
+ ## When images are worth it
29
+
30
+ - The question is about a **formula's exact form**, a **matrix layout**, a
31
+ **diagram**, or **which theorem box** a sentence is in.
32
+ - Extracted text is known to garble math: `P_{n−1}(C)` becomes `𝒫 𝑛 − 1(𝐂)`,
33
+ subscripts flatten, fractions collapse, theorem numbers split across lines.
34
+ - Prefer `crop` (`"0,0,1,0.55"` = top 55%) and `image_quality:"cheap"` (1.5×,
35
+ ~350 tokens) unless dense subscripts need `scale:3`.
36
+
37
+ ## Token economics (DeepSeek vision billing)
38
+
39
+ Each image is projected onto a 14 px patch grid, downsampled 3:1, and **capped at
40
+ 1024 tokens**. For a 6×9 in book page:
41
+
42
+ | Render | Pixels | Tokens |
43
+ | --- | --- | --- |
44
+ | 1.5× (`image_quality:"cheap"`) | 563×954 | **347** |
45
+ | **2.5× (default)** | 938×1590 | **914** |
46
+ | 3× | 1125×1908 | 1002 (capped — no gain above ~2.7×) |
47
+
48
+ - Hard cap `maxPagesPerCall` (default **4**) is enforced before rendering.
49
+ - `pdf_read_pages` returns `imageTokens` and per-page `tokens`; check them.
50
+ - Images stay in the conversation and are re-billed on later turns. **Read once,
51
+ then answer in text.**
52
+ - Never request `scale` above 3: the provider downscales to the same grid anyway.
53
+
54
+ ## Page numbering
55
+
56
+ `pdf_info` returns `printedOffset` with `physical = printed + printedOffset`.
57
+ Tools accept either numbering; **printed is the default when detectable**.
58
+ Roman-numeral front matter has no numeric printed page — use
59
+ `page_numbers:"pdf"`. Say which numbering you mean when it is ambiguous.
60
+
61
+ ## The injected reading position
62
+
63
+ At most one short line per turn may arrive as a user message, e.g.
64
+
65
+ ```
66
+ [dsh-pdf-reader] 正在阅读《book》printed 132 (PDF 152) · Chapter 5 › 5B
67
+ ```
68
+
69
+ It is a **position marker only**. The model decides whether to read anything.
70
+ Do not read a page just because the marker exists; read it when the question
71
+ needs it, and prefer `include_image:false` first.
72
+
73
+ ## Pitfalls
74
+
75
+ - **Scanned PDFs** have no text layer: `pdf_search` finds nothing and
76
+ `include_image:false` returns `textUnavailable`. Only images help.
77
+ - Errors are coded: `PDF_NOT_FOUND`, `PDF_NOT_FILE`, `PDF_ENCRYPTED`,
78
+ `PDF_CORRUPT`, `PDF_READER_INVALID_PAGE`, `PDF_READER_INVALID_SCALE`,
79
+ `PDF_RENDER_TIMEOUT`, `PDF_PAGE_TOO_LARGE`, `PDF_OUTSIDE_WORKSPACE`.
80
+ - **Tools are workspace-scoped by default.** Pass a path inside the session
81
+ working directory; anything outside fails with `PDF_OUTSIDE_WORKSPACE` unless
82
+ the user set `allowOutsideWorkspace: true`.
83
+ - `pdf_info` also reports `title`, `author`, `fileSizeBytes`, `pageSize` and
84
+ `textCoverage` (`isLikelyScanned`). Check `textCoverage.isLikelyScanned`
85
+ *before* searching: a scanned PDF has no text layer, so go straight to images.
86
+ - Search is literal: it does not unfold hyphenation and is not a regex engine.
87
+ - Page numbers are 1-based; relative paths resolve against the session directory.
88
+ - No annotation/note features: this plugin reads and renders only.
89
+
90
+ ## Fallback (plugin disabled)
91
+
92
+ ```sh
93
+ node -e "import('./lib/core/render.js').then(async m=>{const r=await m.renderPageToPng('<abs pdf>',344,{scale:2.5});require('fs').writeFileSync('/tmp/p344.png',r.buffer);console.log(r.width+'x'+r.height)})"
94
+ ```
95
+
96
+ then `read_image('/tmp/p344.png')`. Slower, bypasses the attachment cache —
97
+ prefer the tools.
@@ -0,0 +1,31 @@
1
+ # dsh-book-reader bundle patch: mount the plugin into the active profile.
2
+ # Host half: pdf_info / pdf_search / pdf_read_pages / pdf_current_page tools,
3
+ # /dsh-book-reader/* routes, and a minimal per-turn reading-position
4
+ # line injected through agent/pre-step.
5
+ # Browser half: page readout, jump, search panel, and share-to-chat on top of
6
+ # the official PDF preview's document-header slot.
7
+ #
8
+ # All config keys are optional. The defaults shown are the shipped behavior.
9
+ - insert:
10
+ - id: pdf-reader
11
+ name: 'dsh-book-reader'
12
+ # config:
13
+ # enabled: true
14
+ # autoShare: true # false stops position injection
15
+ # autoShareSection: true # include the outline section in the line
16
+ # autoShareMinIntervalMs: 180000 # at most once every 3 minutes
17
+ # autoSharePageDelta: 1 # require a page change
18
+ # autoShareMinSteps: 1 # at most once per step
19
+ # autoShareMaxAgeMs: 0 # 0 = no age limit on a reported position
20
+ # allowOutsideWorkspace: false # tools may only read inside the session cwd
21
+ # routeAllowedRoots: [] # extra dirs the HTTP routes may serve; [] = any
22
+ # maxPagesPerCall: 4 # hard cap per pdf_read_pages call
23
+ # defaultScale: 2.5 # ~180 DPI; never useful above ~2.7
24
+ # maxTextCharsPerPage: 1200 # text kept per page (0 disables)
25
+ # maxSearchResults: 100
26
+ # maxOutlineEntries: 500
27
+ # maxLabels: 0 # 0 = omit page labels (cheapest)
28
+ # outlineDepth: 1 # 0=chapters, 1=sections, 2=subsections
29
+ # debugCost: false # append JSON cost lines to .debug-cost.log
30
+ # debug: false # include error detail in HTTP 500 bodies
31
+ # maxFileBytes: 268435456 # 256 MiB
package/icon.svg ADDED
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Book Reader">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#4c6ef5"/>
5
+ <stop offset="1" stop-color="#364fc7"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect x="3" y="3" width="58" height="58" rx="14" fill="url(#bg)"/>
9
+ <path d="M12 21.5c6.5-3.6 13-3.6 20 0v23c-7-3.2-13.5-3.2-20 0z" fill="#f8f9fa"/>
10
+ <path d="M52 21.5c-6.5-3.6-13-3.6-20 0v23c7-3.2 13.5-3.2 20 0z" fill="#e9ecef"/>
11
+ <path d="M32 21.5v23" fill="none" stroke="#adb5bd" stroke-width="1.6" stroke-linecap="round"/>
12
+ <path d="M38.5 17.5h6v13l-3-2.4-3 2.4z" fill="#ffd43b"/>
13
+ </svg>