Skip to content

Releases: exbuf/peekdocs

v1.2.86

Choose a tag to compare

@github-actions github-actions released this 10 Jul 16:27

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 same peekdocs.api engine 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 DIR is required and confines every
      search to the folders you name; out-of-root and path-traversal
      requests are rejected. --max-results caps results per call with a
      truncation notice. Searches never write the on-disk index by
      default; opt in per call with allow_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
      FileInventoryItem dataclass) and list_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.
  • 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 a doc-links CI job
    verify that every internal Markdown link resolves — the target file
    and, for .md targets, the #anchor against 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 mypy job no longer fails on recent numpy's PEP 695 type
    statements in its bundled stubs — a numpy-scoped follow_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

Choose a tag to compare

@github-actions github-actions released this 07 Jul 12:53

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 in apply_context, variable reuse across scanner's
    per-format extraction branches, and a result variable in
    cli._main_inner that held an index-stats dict and a
    SearchResult at different points. Also removed a latent
    footgun: _dry_run_report's file-filter params claimed to
    accept a bare str, which discover_files would have
    iterated character-wise.
  • Asset sweep. Orphaned hero.gif (3.2 MB, replaced by
    hero2) and never-embedded screenshot-getting-started.png
    removed; RELEASE_CHECKLIST updated to track only embedded
    assets.

Full Changelog: v1.2.84...v1.2.85

v1.2.84

Choose a tag to compare

@github-actions github-actions released this 07 Jul 12:14

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

Choose a tag to compare

@github-actions github-actions released this 07 Jul 00:20

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, legacy docsearch_* artifacts) and four
    build directories deleted; .gitignore extended 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.find scope holds) — this is
    maintainer-workbench discipline, not a shipping fix.

Full Changelog: v1.2.82...v1.2.83

v1.2.82

Choose a tag to compare

@github-actions github-actions released this 06 Jul 21:08

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 .app bug, but different root cause. pipx installs
    place LICENSE in the sibling .dist-info/licenses/ directory
    (PEP 639's license-files mechanism via setuptools ≥ 77), not
    inside the peekdocs package itself. resource_path deliberately
    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

Choose a tag to compare

@github-actions github-actions released this 06 Jul 20:44

Fixed

  • About → View License now finds LICENSE in the macOS .app
    bundle.
    User reported on 1.2.80 that both the standalone macOS
    .app and Windows .exe showed "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._MEIPASS on a PyInstaller --onedir --windowed .app
    bundle points at Contents/Frameworks/ (runtime + libs), so
    the traditional os.path.join(sys._MEIPASS, "LICENSE") lookup
    missed the file entirely. Extended
    peekdocs.paths.resource_path() to search a small ordered
    candidate list — _MEIPASS/relative_path first (works on
    Windows, Linux, macOS CLI), then
    _MEIPASS/../Resources/relative_path (macOS .app fallback)
    — 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

Choose a tag to compare

@github-actions github-actions released this 06 Jul 20:18

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_cli redirected stderr
    into a plain io.StringIO() that was only readable after
    _cli_main returned, so every print("PHASE: ...", file=sys.stderr, flush=True) in cli.py got 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 to on_stderr_line synchronously —
    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

Choose a tag to compare

@github-actions github-actions released this 06 Jul 19:46

Added

  • peekdocs.errors — public exception hierarchy for library
    consumers.
    New module exports PeekdocsError (root),
    QueryError (bad search input — invalid mode combinations, empty
    terms, malformed regex, boolean-expression syntax), RangeError
    (malformed -R range spec), and NameNotFoundError (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-level peekdocs package
    for import convenience. Raise sites in api.py, range_query.py,
    and expr_parser.py (32 total) now raise the typed subclasses
    instead of raw stdlib exceptions.
  • peekdocs/commands/ package — extracted six self-contained
    CLI subcommand handlers from cli.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.py reduced
    by 215 LOC cumulative (2781 → 2566, -8%). Standard search +
    --suite + --regex-collection + --watch + the --index-*
    cluster remain in cli._main_inner because they share
    flag-parsing plumbing that spans several output-format branches
    — factoring that shared surface cleanly is its own larger
    refactor. See commands/__init__.py for the "adding a new
    subcommand" pattern + the circular-import defense (lazy imports
    of peekdocs.cli symbols inside handler bodies).
  • peekdocs.gui._error_guard — controlled exception swallowing.
    New module ships two context managers replacing the ~149
    ambient except Exception: pass sites across the GUI mixins:
    gui_guard(operation) swallows AND logs the exception name +
    traceback tail to peekdocs_errors.log with 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 find peekdocs.errors there).
  • 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 single peekdocs.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_size becomes a thin re-export
    for back-compat with peekdocs.reporter.fmt_size importers.
  • Type-check gate widened to 12 files. All six commands/
    handlers plus peekdocs.errors plus gui/_error_guard.py are
    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, and gui/_error_guard.py. Docs updated on
    four surfaces (README, USER_GUIDE, ARCHITECTURE, CLAUDE) to
    reflect the wider scope.
  • gui/_helpers.py split 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 (themed askstring
    • OS file-open shim, 105 LOC). _helpers.py shrinks from 850
      to 71 LOC as a re-export shim so existing imports through
      peekdocs.gui._helpers continue 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.

Fixed

  • Stale numeric claims across docs. Test count (~630 → 718
    in docs/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 in README.md, excluding
    auto-generated peekdocs report files); _mixin_tools.py LOC
    (~870 → 873 in docs/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]+ extensions would catch this whole
    class next time.
  • commands/runs.py int-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

Choose a tag to compare

@github-actions github-actions released this 06 Jul 18:35

Added

  • OCR integration test with real Tesseract on CI. New
    tests/test_ocr_integration.py generates a small PNG containing
    known text via Pillow, runs peekdocs.api.search() with
    use_ocr=True on that directory, and asserts the extracted
    text lands in SearchResult.matches. Exercises the full
    find_tesseract → cmd-pin → pytesseract.image_to_string → SearchResult contract with a real Tesseract binary — the
    gap that let the 1.2.77 detection-vs-execution bug slip past
    the mocked unit tests. A new ocr-integration job in
    .github/workflows/test.yml apt 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.py and reporter.py.
    The mypy CI gate introduced in 1.2.76 for api.py + paths.py
    now also enforces signatures on peekdocs/cli.py (the CLI
    entrypoint and its 22 top-level helpers) and
    peekdocs/reporter.py (18 report-writer functions consumed
    by cli.py and by anyone extending output formats). All four
    files are listed in pyproject.toml's [tool.mypy] block;
    signature drift on any of them fails the build. Docs updated
    in README.md, USER_GUIDE.md, and ARCHITECTURE.md to
    name the widened scope.

Full Changelog: v1.2.77...v1.2.78

v1.2.77

Choose a tag to compare

@github-actions github-actions released this 06 Jul 17:36

Fixed

  • Tesseract detection now checks well-known install locations
    in addition to PATH.
    macOS GUI launches (Finder / Dock /
    Spotlight) inherit a stripped PATH that omits Homebrew's
    /opt/homebrew/bin (Apple Silicon) and /usr/local/bin
    (Intel), so shutil.which("tesseract") used to return None
    even when Tesseract was correctly installed. Users would
    install Tesseract via brew 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/bin on Linux. The scanner's OCR call also pins
    pytesseract.pytesseract.tesseract_cmd to 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