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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: ["main"]
pull_request:

# Least-privilege token: this workflow only reads the repo. Arbitrary
# dependency build code runs during install, so never expose a writable
# token to it (see the supply-chain notes in pyproject.toml).
permissions:
contents: read

# Cancel superseded runs on the same ref (rapid PR pushes) instead of
# letting them pile up and post stale statuses.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
version: "0.10.2"
enable-cache: true

# `uv sync --locked` installs the exact uv.lock resolution (direct AND
# transitive deps) and fails if the lock is stale — a bare `pip install`
# would ignore the lockfile and let transitive versions float, defeating
# the repo's exact-pin supply-chain policy. Run `uv lock` and commit the
# lockfile whenever pyproject dependencies change.
- name: Install (locked)
run: uv sync --locked --extra dev --python 3.12

# --no-sync: don't let `uv run` re-sync without the dev extra and
# uninstall the tools it is about to run.
- name: Ruff lint
run: uv run --no-sync ruff check .

- name: Ruff format check
run: uv run --no-sync ruff format --check .

- name: Mypy
run: uv run --no-sync mypy openkb

- name: Pytest
run: uv run --no-sync pytest
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,7 +29,7 @@ jobs:
id-token: write # OIDC trusted publishing to PyPI
contents: write # Create GitHub Release
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.2.2
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # hatch-vcs needs full history + tags

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,5 +16,11 @@ wiki/
output/

# Local only
docs/
docs/internal/
.claude/

# Heavy test-input documents for the examples (the old blanket `docs/` rule
# used to catch this dir at any depth; the anchored `docs/internal/` above
# does not). The PDFs already tracked on main stay tracked — this only stops
# new drops from being swept into commits.
examples/docs/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
# AGENTS.md — OpenKB map for coding agents

OpenKB compiles raw documents into an interlinked wiki knowledge base using
LLMs (vectorless retrieval via PageIndex). This repo is developed **agent-first**:
humans steer, agents execute. Optimize changes for agent legibility.

## Read next
- `docs/golden-principles.md` — mechanical rules to follow (enforced where possible).
- `docs/internal/superpowers/{specs,plans}/` — design history & plans *(maintainer-local, not in git)*.
- `README.md` — user-facing overview and commands.

## Dev commands
- Install: `pip install -e ".[dev]"` (or `uv sync --extra dev` — plain `uv sync` skips the dev tools)
- Run CLI: `openkb <command>` (entry point: `openkb.cli:cli`)
- Test: `pytest`
- Lint/format/types: `ruff check .` · `ruff format .` · `mypy openkb`

## Module map (openkb/)
- `cli.py` — Click CLI entry point & command wiring *(large; see tech-debt)*.
- `config.py` — config loading/validation (LiteLLM passthrough, env).
- `converter.py` — document → markdown conversion (markitdown).
- `url_ingest.py` — fetch & ingest URLs (trafilatura).
- `images.py` — figure/image extraction & handling.
- `indexer.py` — PageIndex tree indexing for long docs.
- `mutation.py` — crash-safe, serial KB mutations.
- `locks.py` — atomic writes / file locking (`atomic_write_text`, portalocker).
- `state.py` — run/session state tracking.
- `frontmatter.py` — YAML frontmatter round-trip (OKF).
- `schema.py` — page/content schema constants & helpers.
- `lint.py` — structural wiki lint (broken links, orphans, index sync).
- `tree_renderer.py`, `visualize.py`, `watcher.py` — rendering / graph / file watch.
- `agent/compiler.py` — LLM wiki compiler *(large; see tech-debt)*.
- `agent/linter.py` — semantic (LLM) wiki lint (contradictions, gaps, staleness).
- `agent/chat.py`, `agent/chat_session.py` — chat over the wiki *(chat.py large)*.
- `agent/query.py` — one-off query generator.
- `agent/tools.py` — shared wiki read/write tool functions used by query/linter (and by chat indirectly via `query.build_chat_agent`).
- `agent/skills.py`, `agent/skill_runner.py`, `skill/` — Skill Factory.
- `deck/`, `templates/`, `prompts/` — deck output, templates, prompt assets.

## Hard invariants
- Deps are pinned **exactly** (supply-chain caution). Vet before bumping.
- Wiki writes go through `locks.py` / `mutation.py` (never ad-hoc).
- Modules stay < 800 lines (`tests/test_file_size.py`); grandfathered files are in tech-debt.
- Keep this file a short map — put depth in `docs/`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
# Default-closed: docs/ content stays out of git unless explicitly
# allowlisted below. This repo publishes code, not design/spec docs — the
# allowlist restores the safety net the old blanket `docs/` ignore provided,
# so a doc accidentally written outside docs/internal/ can't be swept into a
# commit by `git add -A`. To publish a new doc, add a `!its-name.md` line.
*
!.gitignore
!golden-principles.md
32 changes: 32 additions & 0 deletions docs/golden-principles.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
# Golden Principles

Opinionated, mechanical rules that keep this agent-generated codebase legible
and consistent for future agent runs. Enforced by CI where possible; the rest
are honored by convention and checked in review. When a rule proves valuable,
promote it into a lint (see `tests/test_file_size.py` for the pattern).

## Boundaries
- **Validate data shapes at boundaries.** Parse/validate inputs (frontmatter via
`openkb/frontmatter.py`, config via `openkb/config.py`) at the edge. Never build
on guessed shapes.

## Reuse
- **Prefer shared utilities over hand-rolled helpers** so invariants stay
centralized. Check `openkb/` for an existing helper before writing a new one.

## I/O and state
- **All wiki file writes go through `openkb/locks.py` / `openkb/mutation.py`**
(atomic, crash-safe). No ad-hoc writes to the wiki tree.
- **Log through `openkb/log.py`**, not bare `print`, for anything diagnostic.

## Size and shape
<a id="file-size"></a>
- **Keep modules focused and under 800 lines** (enforced by
`tests/test_file_size.py`). Split large modules into focused units by
responsibility. Existing over-limit files are grandfathered (with reasons)
in the test's `_GRANDFATHERED` set and additionally tracked in
`docs/internal/tech-debt.md` *(maintainer-local, not in git)*.

## Docs
- **`AGENTS.md` is a map, not a manual.** Keep it short; deep/local docs live
under `docs/` (public) and `docs/internal/` (maintainer-local, not in git).
4 changes: 3 additions & 1 deletion openkb/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
"""OpenKB package."""
from importlib.metadata import PackageNotFoundError, version as _version

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _version

try:
__version__ = _version("openkb")
Expand Down
1 change: 1 addition & 0 deletions openkb/__main__.py
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
"""Allow running OpenKB as ``python -m openkb``."""

from openkb.cli import cli

cli()
1 change: 0 additions & 1 deletion openkb/agent/_markdown.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,6 @@
from rich.syntax import Syntax
from rich.text import Text


INLINE_CODE_STYLE = "blue"
BLOCKQUOTE_BAR = "\u258e"

Expand Down
Loading
Loading