Releases: exbuf/peekdocs
Releases · exbuf/peekdocs
Release list
v1.2.86
Added
- Optional read-only MCP server (
peekdocs-mcp). A Model Context
Protocol server that lets any
MCP-capable AI assistant — Claude Desktop, Claude Code, and
local-model hosts (LM Studio, Ollama-based clients) — search local
documents over the samepeekdocs.apiengine the CLI and GUI use. It
is a thin adapter, so an assistant's search returns the same matches
your own would. Install with the optional extra
(pip install "peekdocs[mcp]") and run over stdio. Eight read-only
tools:search_documents,get_document_context,inventory_folder,
list_supported_file_types,list_search_suites,run_search_suite,
list_regex_collections,run_regex_collection— with no write,
move, rename, delete, or report-generation surface (those code paths
are never imported by the server).- Guardrails.
--root DIRis required and confines every
search to the folders you name; out-of-root and path-traversal
requests are rejected.--max-resultscaps results per call with a
truncation notice. Searches never write the on-disk index by
default; opt in per call withallow_index_write. - Privacy. peekdocs stays local and initiates nothing (the
exchange is one-way — the assistant asks, peekdocs answers; the MCP
"sampling" capability is deliberately not implemented). A cloud
assistant still receives the returned snippets as part of the
conversation; pairing with a local downloadable model (Llama,
Qwen, Mistral) keeps everything on your machine. - New public API helpers
inventory_folder()(+ the
FileInventoryItemdataclass) andlist_supported_file_types(),
re-exported at the top level
(from peekdocs import inventory_folder, list_supported_file_types),
back the folder-listing tools. - Documented across the README (Feature Highlights, "How these
compose", "Why peekdocs?"), the User Guide (setup, "Who benefits"
with per-persona examples, one-way/sampling and data-locality notes,
and a fully-local downloadable-model setup), the API reference, and
the Glossary.
- Guardrails.
- Schedule Search dialog —
?help panel and Close button (GUI).
The Schedule Search dialog (Tools menu) gained a?help button, the
only Tools dialog that lacked one. Its panel explains what the feature
does and why peekdocs generates a scheduler command for you to paste
rather than registering the schedule itself (the OS scheduler owns the
job; peekdocs stays out of your system). The help popup also gained a
Close button, matching the other help panels. - Internal documentation link check in CI. New
scripts/check_doc_links.py(stdlib-only) plus adoc-linksCI job
verify that every internal Markdown link resolves — the target file
and, for.mdtargets, the#anchoragainst the target's real
heading slugs / explicit HTML anchors. A broken internal link (missing
file or renamed/removed anchor) now fails CI.
Fixed
- Fixed 5 broken internal documentation links (anchors left stale by
renamed / renumbered sections, plus a glossary term that needed an
explicit anchor). - CI
mypyjob no longer fails on recent numpy's PEP 695type
statements in its bundled stubs — a numpy-scopedfollow_imports
override skips the stub that mypy rejected under the
python_version = "3.10"target. (Pytest was never affected.)
Full Changelog: v1.2.85...v1.2.86
v1.2.85
Changed
- Type-check gate widened to
scanner.py+indexer.py— the
engine is now type-checked end to end. The two largest
untyped modules (scanner: file discovery + extraction for 100+
formats; indexer: SQLite FTS5 build / refresh / search) carry
full top-level signatures and sit in the mypy CI gate (14
files, was 12). The whole engine path — api → cli/commands →
scanner → indexer → reporter — is now covered; only GUI mixins
and small helpers remain deliberately out of scope. The
checker forced real internal fixes: a mixed tuple/list ranges
list inapply_context, variable reuse across scanner's
per-format extraction branches, and aresultvariable in
cli._main_innerthat held an index-stats dict and a
SearchResultat different points. Also removed a latent
footgun:_dry_run_report's file-filter params claimed to
accept a barestr, whichdiscover_fileswould have
iterated character-wise. - Asset sweep. Orphaned
hero.gif(3.2 MB, replaced by
hero2) and never-embeddedscreenshot-getting-started.png
removed; RELEASE_CHECKLIST updated to track only embedded
assets.
Full Changelog: v1.2.84...v1.2.85
v1.2.84
Changed
- License popup widened 560 → 700px. The MIT text's
~75-character lines soft-wrapped in Courier 10 at the old
width; 700px renders every line unwrapped. Height unchanged. - New hero clip. The top-of-README walkthrough replaced with
a re-recorded version (hero2.gif); caption updated to the new
run's stats (10,412 files in 2.98s) and retitled "A Standard
Search" to match the clip's name on the maintainer's site.
Full Changelog: v1.2.83...v1.2.84
v1.2.83
Changed
- README top-of-file restructure. Installation moved from line
434 to ~line 159; Quick Start follows immediately. The hero clip
(46-second walkthrough: 10,411 files searched in 3.17s, File
Types + Categories charts) is now the above-the-fold visual, and
the Contents (TOC) moved from line 556 — where it navigated
nothing — to right before Installation, rebuilt in document
order. Installation subsections reordered to Option A → Option B
→ Prerequisites → Upgrading → Uninstalling with a numbered nav
list matching numbered headings (anchor fragments rewritten
across README, USER_GUIDE, and TROUBLESHOOTING). A follow-up
cold-read audit fixed seven orphaned references the moves left
behind (stale "below" pointers, a stranded duplicate
Quick-install block, a footnote marker with no referent, and a
"(one-time, per platform)" claim its own section's body text
contradicted — now "(per download, per platform)"). - Repo-root hygiene. 18 stray local files (old sample docs,
logs, search outputs, legacydocsearch_*artifacts) and four
build directories deleted;.gitignoreextended with annotated
blocks for dev-scratch patterns, local WIP demo videos, and
personal sample corpora. None of it ever shipped (sdist audit
confirmed setuptools'packages.findscope holds) — this is
maintainer-workbench discipline, not a shipping fix.
Full Changelog: v1.2.82...v1.2.83
v1.2.82
Fixed
- About → View License now finds LICENSE in pipx / pip installs.
User reported on 1.2.81 that the pipx install on macOS showed
"LICENSE file not found in this build" — same fallback text as
the 1.2.80.appbug, but different root cause. pipx installs
place LICENSE in the sibling.dist-info/licenses/directory
(PEP 639'slicense-filesmechanism via setuptools ≥ 77), not
inside the peekdocs package itself.resource_pathdeliberately
doesn't chase into.dist-info/per its docstring, so the About
viewer hit the fallback. New universal
peekdocs.paths.read_bundled_text(name)helper tries three
sources in order —resource_path, PEP 639
.dist-info/licenses/<name>, legacy pre-PEP-639
.dist-info/<name>— covering LICENSE, NOTICE, and
THIRD_PARTY_NOTICES.md across every install method (pipx, pip,
editable, PyInstaller.exe,.app, source checkout).
Full Changelog: v1.2.81...v1.2.82
v1.2.81
Fixed
- About → View License now finds LICENSE in the macOS
.app
bundle. User reported on 1.2.80 that both the standalone macOS
.appand Windows.exeshowed "LICENSE file not found in this
build" instead of the actual MIT text. Diagnosed by inspecting
the released binaries: LICENSE ships correctly at
peekdocs-gui.app/Contents/Resources/LICENSE, but
sys._MEIPASSon a PyInstaller--onedir --windowed.app
bundle points atContents/Frameworks/(runtime + libs), so
the traditionalos.path.join(sys._MEIPASS, "LICENSE")lookup
missed the file entirely. Extended
peekdocs.paths.resource_path()to search a small ordered
candidate list —_MEIPASS/relative_pathfirst (works on
Windows, Linux, macOS CLI), then
_MEIPASS/../Resources/relative_path(macOS.appfallback)
— and return the first candidate that exists on disk. If
neither exists, returns the traditional path so the caller's
own "not found" fallback still runs cleanly. 4 new tests cover
the multi-candidate lookup logic.
Full Changelog: v1.2.80...v1.2.81
v1.2.80
Fixed
- PyInstaller-frozen GUI now streams stderr phase markers in
real time. User reported on 1.2.79 that the standalone.exe
and the pipx install find identical results, but the.exe
status line skipped the report-writing progression
("building txt report", "building docx report") that the pipx
install showed. Root cause: the frozen path in
peekdocs.gui._cli_runner._run_peekdocs_cliredirected stderr
into a plainio.StringIO()that was only readable after
_cli_mainreturned, so everyprint("PHASE: ...", file=sys.stderr, flush=True)incli.pygot buffered and the
GUI never saw intermediate markers. Fix: new
_StderrLineStreamer(io.StringIO)subclass that intercepts
writes, buffers partial lines, and forwards completed
newline-terminated lines toon_stderr_linesynchronously —
matches the subprocess path's background-reader behavior.
getvalue()at the end still returns the full transcript, so
any completion-time consumer of the buffered stderr keeps
working. 7 new tests cover the streaming logic including CRLF
handling and callback-exception swallowing.
Full Changelog: v1.2.79...v1.2.80
v1.2.79
Added
peekdocs.errors— public exception hierarchy for library
consumers. New module exportsPeekdocsError(root),
QueryError(bad search input — invalid mode combinations, empty
terms, malformed regex, boolean-expression syntax),RangeError
(malformed-Rrange spec), andNameNotFoundError(missing
suite or regex collection). Each subclass inherits from the
closest stdlib exception (ValueError/KeyError) so existing
consumer code that catches those types keeps working — this is a
non-breaking upgrade for anyone already handling errors from
peekdocs.api. Re-exported from the top-levelpeekdocspackage
for import convenience. Raise sites inapi.py,range_query.py,
andexpr_parser.py(32 total) now raise the typed subclasses
instead of raw stdlib exceptions.peekdocs/commands/package — extracted six self-contained
CLI subcommand handlers fromcli.py's_main_inner
mega-dispatcher into focused per-subcommand modules. Phase 1:
--check,--diff,--runs. Phase 2:--list-files,
--list-suites,--clear/--clear-all. Establishes the
extraction pattern for future subcommand splits;cli.pyreduced
by 215 LOC cumulative (2781 → 2566, -8%). Standard search +
--suite+--regex-collection+--watch+ the--index-*
cluster remain incli._main_innerbecause they share
flag-parsing plumbing that spans several output-format branches
— factoring that shared surface cleanly is its own larger
refactor. Seecommands/__init__.pyfor the "adding a new
subcommand" pattern + the circular-import defense (lazy imports
ofpeekdocs.clisymbols inside handler bodies).peekdocs.gui._error_guard— controlled exception swallowing.
New module ships two context managers replacing the ~149
ambientexcept Exception: passsites across the GUI mixins:
gui_guard(operation)swallows AND logs the exception name +
traceback tail topeekdocs_errors.logwith the operation
label;gui_race_guard()swallows silently for known Tk
timing races (grab_set-on-not-yet-viewable, focus_set on
destroyed widget) where a companion retry-with-after()
handles correctness. Four persistence sites in_mixin_data.py
(factory-reset rc-file remove, config write, history-file
clear, bookmarks save) converted as pattern demonstration; the
remaining conversion is one-at-a-time future work.- API reference row for public exceptions in
docs/API.md
quick-reference table (was previously implicit; scanners of the
top-of-page table now findpeekdocs.errorsthere). - GLOSSARY.md entries for
PeekdocsError,QueryError,
RangeError,NameNotFoundError— each row names the module,
the raise sites, and the stdlib back-compat inheritance.
Changed
- Byte-formatter consolidation. Three drifting implementations
(SI 1000-based in reports + CLI, IEC 1024-based in the GUI file-
analysis dialogs) collapsed to a singlepeekdocs.paths.format_bytes
helper — SI decimal is now the peekdocs convention across every
user-visible surface. A 2 100 000-byte file used to show as
"2.10 MB" in reports and "2.00 MiB" in the dupe finder; both now
render identically.reporter.fmt_sizebecomes a thin re-export
for back-compat withpeekdocs.reporter.fmt_sizeimporters. - Type-check gate widened to 12 files. All six
commands/
handlers pluspeekdocs.errorsplusgui/_error_guard.pyare
now in mypy scope, bringing the CI-typed public surface to
api.py,paths.py,reporter.py,cli.py,errors.py,
commands/check.py,commands/diff.py,commands/runs.py,
commands/list_files.py,commands/list_suites.py,
commands/clear.py, andgui/_error_guard.py. Docs updated on
four surfaces (README, USER_GUIDE, ARCHITECTURE, CLAUDE) to
reflect the wider scope. gui/_helpers.pysplit into three focused modules. The
former 850-LOC grab-bag identified in the code-health review
became_cli_runner.py(subprocess + command build + result
parsing, 478 LOC),_cloud_guard.py(cloud-folder detection +
policy guard, 267 LOC), and_dialogs.py(themedaskstring- OS file-open shim, 105 LOC).
_helpers.pyshrinks from 850
to 71 LOC as a re-export shim so existing imports through
peekdocs.gui._helperscontinue to work — the ~30 import sites
across the CLI and GUI mixins don't need to change. New code
should import from the specific submodule.
- OS file-open shim, 105 LOC).
Fixed
- Stale numeric claims across docs. Test count (
~630→ 718
indocs/SMOKE_TEST.md,CLAUDE.md,docs/ARCHITECTURE.md;
the count moved from 695 → 703 → 710 → 718 across three
sync-and-add cycles as new test files landed); sample-corpus
extension count (41→ 38 inREADME.md, excluding
auto-generated peekdocs report files);_mixin_tools.pyLOC
(~870→ 873 indocs/ARCHITECTURE.md); test-file count
(~15→ 23). Discovered in the docs-vs-code audit agent pass;
a five-minute pre-release grep over[0-9]{3,4} test,
~[0-9]{3} LOC, and[0-9]+ extensionswould catch this whole
class next time. commands/runs.pyint-parse error routes to stderr. Was
going to stdout, carried forward verbatim from the pre-refactor
code. Now consistent with--diff's error-message convention.
Full Changelog: v1.2.78...v1.2.79
v1.2.78
Added
- OCR integration test with real Tesseract on CI. New
tests/test_ocr_integration.pygenerates a small PNG containing
known text via Pillow, runspeekdocs.api.search()with
use_ocr=Trueon that directory, and asserts the extracted
text lands inSearchResult.matches. Exercises the full
find_tesseract → cmd-pin → pytesseract.image_to_string → SearchResultcontract with a real Tesseract binary — the
gap that let the 1.2.77 detection-vs-execution bug slip past
the mocked unit tests. A newocr-integrationjob in
.github/workflows/test.ymlapt installs Tesseract on
Ubuntu and runs just this file. Skipped locally when Tesseract
isn't installed, so no dev-machine friction.
Changed
- Type hint coverage widened to
cli.pyandreporter.py.
The mypy CI gate introduced in 1.2.76 forapi.py+paths.py
now also enforces signatures onpeekdocs/cli.py(the CLI
entrypoint and its 22 top-level helpers) and
peekdocs/reporter.py(18 report-writer functions consumed
bycli.pyand by anyone extending output formats). All four
files are listed inpyproject.toml's[tool.mypy]block;
signature drift on any of them fails the build. Docs updated
inREADME.md,USER_GUIDE.md, andARCHITECTURE.mdto
name the widened scope.
Full Changelog: v1.2.77...v1.2.78
v1.2.77
Fixed
- Tesseract detection now checks well-known install locations
in addition toPATH. macOS GUI launches (Finder / Dock /
Spotlight) inherit a strippedPATHthat omits Homebrew's
/opt/homebrew/bin(Apple Silicon) and/usr/local/bin
(Intel), soshutil.which("tesseract")used to returnNone
even when Tesseract was correctly installed. Users would
install Tesseract viabrew install tesseract, see the
"Tesseract not detected" modal anyway, and have no path
forward without launching peekdocs from a terminal. The new
peekdocs.paths.find_tesseract()helper falls back to the
standard Homebrew and MacPorts locations on macOS, standard
Program Files locations on Windows, and standard/usr/bin/
/usr/local/binon Linux. The scanner's OCR call also pins
pytesseract.pytesseract.tesseract_cmdto the located
absolute path so the actual OCR execution doesn't fall back
to PATH lookup. The four detection sites (parser, CLI
--check, GUI OCR-toggle modal, scanner) share the single
helper.
Full Changelog: v1.2.76...v1.2.77