Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,89 @@ jobs:
run: |
python -m KnowledgeDigest --help
knowledgedigest --help

package-artifacts:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install Qt runtime libraries
run: |
sudo apt-get update
sudo apt-get install --yes libegl1 libgl1 libxkbcommon-x11-0 libdbus-1-3

- name: Build wheel and source distribution
run: |
python -m pip install --upgrade pip
python -m pip install build PySide6
python -m build --wheel --sdist --outdir "$RUNNER_TEMP/dist"

- name: Verify package icon and source documentation links
run: |
python - <<'PY'
from pathlib import Path
import os
import re
import tarfile
from urllib.parse import unquote, urlsplit
import zipfile

dist = Path(os.environ["RUNNER_TEMP"]) / "dist"
wheel = next(dist.glob("*.whl"))
sdist = next(dist.glob("*.tar.gz"))
with zipfile.ZipFile(wheel) as archive:
assert any(name.endswith("/KnowledgeDigest.ico") for name in archive.namelist())
with tarfile.open(sdist, "r:gz") as archive:
names = set(archive.getnames())
root = next(name.split("/", 1)[0] for name in names if name.endswith("/PKG-INFO"))
required = [
"ARCHITECTURE.md", "CHANGELOG.md", "LICENSE", "MARKETING-LOG.txt", "NOTICE",
"README.md", "README_de.md", "SECURITY.md", "SMOKE-CHECKS.md",
"THIRD_PARTY_LICENSES.md", "llms.txt", "assets/banner.png", "assets/banner.svg",
".github/workflows/tests.yml", "tests/package_smoke.py", "tests/test_metadata.py",
"tests/test_no_agpl.py", "tests/test_packaging.py", "KnowledgeDigest.ico",
]
missing = [path for path in required if f"{root}/{path}" not in names]
assert not missing, f"source archive is missing documented files: {missing}"

link_pattern = re.compile(r"!?\[[^\]]*\]\(([^\s)]+)|<img\b[^>]*?\bsrc=[\"']([^\"']+)", re.I)
for readme in ("README.md", "README_de.md"):
content = archive.extractfile(f"{root}/{readme}").read().decode("utf-8")
for markdown_link, image_src in link_pattern.findall(content):
link = markdown_link or image_src
parsed = urlsplit(link)
if parsed.scheme or parsed.netloc or not parsed.path:
continue
target = Path(unquote(parsed.path).replace("/", os.sep))
member = f"{root}/{target.as_posix()}"
assert member in names, f"{readme} links to missing source archive member: {link}"
PY

- name: Install and smoke-test wheel
env:
QT_QPA_PLATFORM: offscreen
run: |
mkdir -p "$RUNNER_TEMP/wheel-target"
wheel="$(find "$RUNNER_TEMP/dist" -maxdepth 1 -name '*.whl' -print -quit)"
python -m pip install --no-deps --target "$RUNNER_TEMP/wheel-target" "$wheel"
cd "$RUNNER_TEMP"
PYTHONPATH="$RUNNER_TEMP/wheel-target" python -m KnowledgeDigest --help
PYTHONPATH="$RUNNER_TEMP/wheel-target" python "$GITHUB_WORKSPACE/tests/package_smoke.py"

- name: Install and smoke-test source distribution
env:
QT_QPA_PLATFORM: offscreen
run: |
mkdir -p "$RUNNER_TEMP/sdist-target"
sdist="$(find "$RUNNER_TEMP/dist" -maxdepth 1 -name '*.tar.gz' -print -quit)"
python -m pip install --no-deps --target "$RUNNER_TEMP/sdist-target" "$sdist"
cd "$RUNNER_TEMP"
PYTHONPATH="$RUNNER_TEMP/sdist-target" python -m KnowledgeDigest --help
PYTHONPATH="$RUNNER_TEMP/sdist-target" python "$GITHUB_WORKSPACE/tests/package_smoke.py"
29 changes: 18 additions & 11 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

## Overview

KnowledgeDigest is a portable, self-contained knowledge database. It indexes
documents from local directories, chunks them into searchable segments, and
provides FTS5 full-text search via three interfaces: Desktop GUI, Web Viewer,
and Python API.
KnowledgeDigest indexes user-selected documents into a local SQLite database and provides FTS5 search through its desktop GUI, web viewer, CLI, and Python API. Optional summarization sends selected chunks and prompts to a configured provider; a custom API endpoint may be remote.

## Module Structure

Expand Down Expand Up @@ -88,7 +85,7 @@ sequenceDiagram
participant Core as "Digest Core"
participant Ingest as "Ingestor & Chunker"
participant DB as "SQLite (FTS5 & Triggers)"
participant LLM as "LLM Summarizer"
participant LLM as "Configured LLM endpoint"

Note over User,DB: Phase 1: Ingestion & Indexing
User->>CLI: "Ingest directory (/path/to/docs)"
Expand All @@ -97,8 +94,9 @@ sequenceDiagram
Ingest->>DB: "Store metadata, chunks & keywords"
DB-->>DB: "Auto-sync BM25 FTS5 index via triggers"
opt "Optional Summarization"
Core->>LLM: "Queue chunks (Gemini Flash / Claude Haiku)"
LLM-->>DB: "Store structured summaries & domain tags"
Core->>LLM: "Send selected chunks + prompt when invoked"
LLM-->>Core: "Return summary"
Core->>DB: "Store summary locally"
end
Core-->>CLI: "Ingestion complete & indexed"

Expand Down Expand Up @@ -132,8 +130,9 @@ Normalized multi-table schema with FTS5 full-text search:

### Portable JSON Config
- No hardcoded paths; everything configurable via `knowledgedigest.json`
- Config searched in: CWD → data/ → %APPDATA% → defaults
- Copy the folder to another machine and it works
- Config search order: CWD → package data/ → %APPDATA% (Windows, when set) → XDG_CONFIG_HOME or ~/.config/knowledgedigest/.
- If no file exists, defaults and package data/ paths are used; an installed package directory may not be writable.
- There is no --config CLI option. Python callers may pass an explicit path to get_config(config_path).

### FTS5 over Keyword Search
- SQLite FTS5 provides BM25 ranking, snippet highlighting, prefix search
Expand All @@ -154,7 +153,7 @@ Normalized multi-table schema with FTS5 full-text search:

### Two Frontends
- **PySide6 GUI**: Full desktop app with 3-panel layout, dark theme
- **Web Viewer**: Python stdlib only (no dependencies), runs on localhost
- **Web Viewer**: Python stdlib only; current HTTP server binds to 127.0.0.1 and is not an authenticated remote service

## API

Expand All @@ -178,9 +177,17 @@ python -m KnowledgeDigest status # Index statistics
python -m KnowledgeDigest search "query" # FTS5 search
```

## Provider and Data Boundaries

- Local parsing, indexing, and FTS5 search do not require an LLM provider.
- CLI summarize uses Anthropic by default; --flash selects Google GenAI. The Python API supports Ollama/custom base URLs.
- Provider requests include selected document content and prompts. Custom endpoints may be remote.
- Optional SQLite Transit synchronization can move configured snapshots between nodes.
- The current desktop GUI has German-only visible strings and no language selector.

## Technical Constraints

- Python 3.10+ with sqlite3 FTS5 support
- `PYTHONIOENCODING=utf-8` required on Windows
- Windows-compatible paths and encoding
- PySide6 (LGPL) for GUI, no PyQt dependency
- PySide6 for GUI, no PyQt dependency; upstream licensing options vary, so inspect the selected distribution's license files
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased]

- **Documentation and package boundary corrections (2026-10-12)**: Clarified that core indexing/search use the local database while optional provider and synchronization paths may transfer configured data; corrected CLI/configuration documentation; bounded dependency/license and security statements; added a wheel/sdist smoke gate for the packaged GUI icon. Version remains 0.4.0.

- **Bugfix-Sweep (2026-10-06)**:
- **FTS5-Delete-Trigger repariert (Schema v5)**: Die Trigger nutzten den FTS5-`'delete'`-Befehl auf regulären FTS5-Tabellen → `SQL logic error` bei jedem Löschen von Chunks (Re-Ingest, `index --force`, Web-Viewer-Löschen, `deduplicate`). Bestehende Datenbanken werden beim Öffnen migriert.
- **Löschen konsistent**: Web-Viewer und `deduplicate` entfernen zuerst die DB-Einträge (Transaktion) und verschieben danach die Datei in `_Papierkorb`; Fehler pro Eintrag brechen den Lauf nicht mehr ab.
Expand Down
18 changes: 18 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
include ARCHITECTURE.md
include CHANGELOG.md
include LICENSE
include MARKETING-LOG.txt
include NOTICE
include README.md
include README_de.md
include SECURITY.md
include SMOKE-CHECKS.md
include THIRD_PARTY_LICENSES.md
include llms.txt
include assets/banner.png
include assets/banner.svg
include .github/workflows/tests.yml
include tests/package_smoke.py
include tests/test_metadata.py
include tests/test_no_agpl.py
include tests/test_packaging.py
10 changes: 10 additions & 0 deletions MARKETING-LOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# Supersession note — 2026-10-12

Entries below record historical work and do not verify current security, privacy, licensing, test-count, service-level, or compliance guarantees. Current public descriptions are limited to reviewed source paths and declared direct dependencies:

- Core indexing and search use the configured local SQLite database. Optional summarization sends selected text and prompts to the configured provider when invoked; optional synchronization can move configured snapshots.
- The dependency note is manually maintained and is not a generated SBOM or complete transitive-dependency audit.
- The GUI currently has German-only strings. README validation checks 18 matching navigation anchors, not full translation parity.
- Response times are maintainer goals, not guaranteed SLAs. GitHub Private Vulnerability Reporting availability depends on repository settings.
- Version 0.4.0 remains frozen; this documentation update is not a release or store submission.

# MARKETING-LOG.txt — KnowledgeDigest
# Chronologisches Log fuer Repository-Pflege, Hygiene (Pfad A) und Sichtbarkeit/Marketing (Pfad B)

Expand Down
Loading
Loading