Metadata-Version: 2.4
Name: agent-wiki-workspace
Version: 0.4.0
Summary: Contract-driven Markdown knowledge bases for PDF-aware agents
Author-email: Dark Light <darklight@noreply.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: agents,knowledge-base,markdown,obsidian,pdf
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <3.15,>=3.11
Requires-Dist: agent-codinglanguage-mapper<1.2,>=1.1.4
Requires-Dist: agent-pdf-workspace<0.2,>=0.1.1
Requires-Dist: httpx<1,>=0.28
Requires-Dist: mcp<2,>=1.27
Requires-Dist: pydantic<3,>=2.11
Requires-Dist: pyjwt[crypto]<3,>=2.10
Requires-Dist: python-delphi-lsp<2.1,>=2.0.3
Requires-Dist: pyyaml<7,>=6
Requires-Dist: textual<7,>=6
Requires-Dist: typer<1,>=0.16
Requires-Dist: uvicorn<1,>=0.38
Description-Content-Type: text/markdown

# agent-wiki-workspace

`agent-wiki-workspace` builds persistent, searchable Markdown knowledge bases from local PDFs and
source-code repositories. It uses
[`agent-pdf-workspace`](https://pypi.org/project/agent-pdf-workspace/) for local extraction and OCR,
and semantic code providers for evidence-bound architecture wikis. Source-code generation can use
the user's installed OpenCode runtime or the package's standalone OpenAI-compatible model harness.

## What it creates

An existing Markdown or Obsidian vault can be adopted in place, or a new empty directory can be
initialized:

```text
wiki/
├── Sources/ Concepts/ Entities/ Syntheses/
├── raw/<source-id>/                  # complete agent-pdf-workspace output
└── .agent-wiki/
    ├── manifest.json
    ├── profile.{json,md}
    ├── jobs/{curation,query,visual}/
    ├── changesets/
    └── cache/search.sqlite
```

Existing notes, `.obsidian/`, `AGENTS.md`, and symlinks are not rewritten or followed. Curated
claims retain PDF page/block evidence or exact code revision/path/line/target evidence. Agent
changes are staged first and are applied only by an explicit host/user action.

## Install

Python 3.11 through 3.14 is supported. A permanent tool environment is recommended because the
generated OpenCode tool records its exact Python virtual-environment launcher:

```console
uv tool install agent-wiki-workspace
wikiws --help
```

The same installation includes the semantic providers required for source-code wikis; no package
extra or second install command is needed.

For repository development, use the pinned pyenv version and lockfile:

```console
pyenv install -s 3.11.14
uv sync --frozen --group dev
```

## Quick start

```console
# A new wiki must be new or empty.
wikiws init /absolute/path/to/wiki --mode create --json

# Optional Obsidian vault output; standard Markdown remains the default.
wikiws init /absolute/path/to/vault --mode create --wiki-format obsidian --json

# Existing Markdown/Obsidian content is adopted without modifying existing files.
wikiws init /absolute/path/to/vault --mode adopt --json

# Extraction and OCR are local. The original PDF stays under raw/<source-id>/.
wikiws source add /absolute/path/to/wiki report.pdf --ocr auto --language deu+eng --json

# Explore locally.
wikiws search /absolute/path/to/wiki "specific phrase" --json
wikiws read /absolute/path/to/wiki --note "Sources/report.md" --json

# Agent-authored changes remain pending until explicitly approved.
wikiws curate prepare /absolute/path/to/wiki --source-id src-... --json
wikiws curate submit /absolute/path/to/wiki --input changeset.json --json
wikiws changes apply /absolute/path/to/wiki --changeset-id chg-... --json

wikiws lint /absolute/path/to/wiki --json
wikiws verify /absolute/path/to/wiki --json

# Install the Wiki's OpenCode agent. Its name is <wiki-name>-curator.
wikiws agent install /absolute/path/to/wiki
```

## Standalone model configuration

OpenCode remains the default code-build engine. To build and chat without OpenCode, create one
package-owned provider profile. The default location is `~/.config/wikiws/config.toml` on Unix and
`%USERPROFILE%\.config\wikiws\config.toml` on Windows. `--config-dir` overrides it for one command;
`WIKIWS_CONFIG_DIR` provides a process-wide override.

Reference the API key from an environment variable (recommended):

```console
wikiws config init \
  --endpoint https://openrouter.ai/api/v1 \
  --model google/gemma-4-31b-it \
  --api-key-env OPENROUTER_API_KEY
wikiws config doctor --json
```

The endpoint may also be supplied indirectly with `--endpoint-env WIKIWS_MODEL_ENDPOINT`, so a
machine-specific local URL does not need to be duplicated in the config file. Exactly one of
`--endpoint`/`--endpoint-env` and exactly one of `--api-key`/`--api-key-env` is required.

The equivalent recommended TOML profile keeps both values in environment variables:

```toml
default_profile = "default"

[profiles.default]
allow_insecure_remote_http = false
api_key_env = "OPENROUTER_API_KEY"
endpoint_env = "WIKIWS_MODEL_ENDPOINT"
model = "google/gemma-4-31b-it"
timeout_seconds = 120
```

A literal profile uses `endpoint = "https://..."` and `api_key = "..."` instead. Do not configure
both forms of the same value.

For a trusted local or dedicated endpoint, a literal key can be stored with `--api-key`. The TOML
file is written atomically with user-only POSIX permissions. `wikiws config show --json` always
redacts literal keys; logs and model errors redact them as well. Profiles can be selected with
`--profile`. Only HTTPS endpoints are accepted remotely; HTTP is limited to `localhost` and
loopback addresses by default. A trusted private-network endpoint can opt out per profile:

```console
wikiws config init \
  --endpoint http://10.0.0.20:1234/v1 \
  --model local/gemma \
  --api-key-env LOCAL_MODEL_KEY \
  --allow-insecure-remote-http
```

This stores `allow_insecure_remote_http = true` and prints a warning whenever the profile is used.
Use `--require-https` when recreating a profile to restore the secure default. Plain HTTP can expose
the API key, source-derived prompts, and model responses to network observers.

The doctor performs a real, bounded structured-output request and therefore distinguishes a
missing config, missing key environment variable, unreachable endpoint, rejected credential,
unknown model, and invalid model response before a large build starts.

## Source-code architecture wikis

Code-wiki orchestration belongs to this package because it owns the Markdown layout, evidence
contracts, staged changes, OpenCode agents, and quality evaluation. `agent-codinglanguage-mapper`
remains the cross-language semantic provider; `python-delphi-lsp` adds Delphi-specific semantics
and metrics.

Build a coherent wiki directly from a repository without copying source text into the wiki. The
command recursively inventories supported source files, including tests, while excluding VCS,
cache, generated, vendored, third-party, build, OpenCode, and agent-integration directories:

```console
wikiws code build /absolute/path/to/repository \
  --wiki /absolute/path/to/wiki \
  --model openrouter/google/gemma-4-31b-it \
  --apply --json
```

Use the standalone harness instead of OpenCode with:

```console
wikiws code build /absolute/path/to/repository \
  --wiki /absolute/path/to/wiki \
  --engine direct --config-dir /optional/config/directory \
  --apply --json
```

The direct engine reads the model and endpoint from the selected wikiws profile. It performs a
model-access preflight before mapper indexing, sends only bounded mapper packets, validates every
response against the same Pydantic evidence contracts, and uses the existing deterministic
coverage, incremental cache, quality gate, and atomic ChangeSet apply path. Cache identities include
the endpoint, profile, and model, so results from two local or hosted endpoints cannot be mixed.
`--wiki-format obsidian` creates a fresh build target as an Obsidian vault; an existing wiki keeps
the format persisted in its manifest.

Uncached semantic packets are analyzed serially with one OpenCode worker by default. Any positive
worker count can be selected explicitly; provider concurrency, memory, and cost limits remain the
user's responsibility.
Apply builds create `Agent Wiki Build Status.md` immediately and update it after every completed
packet, so multi-million-line repositories expose file/symbol counts and durable progress while
the final pages are still being synthesized. `Sources/inventory-in-progress.md` appears after the
deterministic inventory. After every validated packet, evidence-backed pages are refreshed under
`Entities/`, `Concepts/`, and `Syntheses/`; their frontmatter and warning callout identify them as
provisional. Successful packet checkpoints and provisional pages survive interruption. Global
synthesis and the quality gate atomically replace them with final pages. A package-owned SHA-256
manifest prevents overwriting user notes or silently accepting manually changed provisional pages.

The command streams build phases and packet counts to standard error, leaving `--json` output on
standard output machine-readable. From command start it also appends a diagnostic transcript to
`<wiki>/.agent-wiki/code-wiki/build.log`; Python failures include their traceback there. OpenCode
packet transcripts and provider errors remain under
`<wiki>/.agent-wiki/code-wiki/opencode/<packet>/`.

Before scanning the repository, the command runs a bounded OpenCode preflight with the same config
and restricted environment used for analysis. The config source (`--config-dir`,
`OPENCODE_CONFIG_DIR`, or `opencode debug paths`), absolute path, OpenCode version, provider, and
exact model match are written to stderr and `build.log`. A missing/malformed config, unknown
provider, or unknown `provider/model` therefore fails before expensive mapper indexing and reports
safe available identifiers without persisting the resolved config or its secrets.

The wiki target may be absent, empty, or an existing managed agent wiki. Every source file and
symbol remains in a deterministic coverage ledger outside the model context. The language mapper
supplies bounded semantic packets and progressive focus/inspect/trace calls, so the model sees only
useful working context without losing whole-codebase completeness. Each packet has its own evidence
frontier: a model claim may cite only target IDs actually rendered in that packet or returned by a
verified mapper tool call. Coverage and model context are therefore deliberately separate. Pages
are organized by architecture, components, concepts, workflows, test-observed behavior, and
significant entities. Small helpers are grouped; large or conceptually important helpers receive
their own pages.

`--apply` is explicit. Without it, the command stages a dry-run report and writes no wiki pages.
Run the same command after source changes to reuse unchanged packets and update/create/delete only
managed pages. A no-change run performs zero model calls and zero page operations. Source IDs remain
stable across revisions, and exact source revision/path/line/target evidence is verified before
application.

Re-evaluate an applied full code wiki from its persisted mapper inventory, packet ledgers, current
page contents, and page hashes with:

```console
wikiws code evaluate /absolute/path/to/wiki \
  --changeset-id changeset-full-code-... \
  --minimum-score 90 --json
```

OpenCode model identifiers always use `provider/model`. For a custom provider named `D`, use
`--model D/gemma4`. `--vision-model` configures the separate vision agent and is not the model
selector for this code-wiki build.

The earlier anchor-based workflow remains available for intentionally bounded, entity-oriented
wikis. Pass one important semantic anchor per detected language:

```console
wikiws code generate /absolute/path/to/wiki \
  --source-id src-... \
  --anchor python=BaseEventLoop \
  --anchor rust=OpenOptions \
  --anchor csharp=LanguageParser \
  --anchor delphi=TSynLogVoid \
  --anchor cpp=getClangFormatStyle \
  --model openrouter/google/gemma-4-31b-it \
  --apply --json
```

Both builders deny raw file, shell, write, web, task, and generic LSP tools. They use
`agent-codinglanguage-mapper` for every language and additionally use `python-delphi-lsp` for
Delphi. Code evidence is rejected if the source changes, a path escapes the repository, a line is
out of range, or the exact quote is absent. Evaluation happens before any page is applied, and the
full builder requires complete deterministic file/symbol coverage plus resolvable navigation.

Provider skills, plugins, and agents are installed transactionally into the selected trusted
OpenCode configuration directory, never into the repository. Generation rejects repositories that
contain project-local OpenCode, Claude, or agent instruction controls because OpenCode could load
those files before tool permissions take effect. The provider Python processes also run in
isolated mode, and the OpenCode child receives only an allowlisted environment.

Machine-readable mode writes JSON only to stdout and diagnostics to stderr. Exit codes are `0`
(success), `2` (input/contract error), `3` (incomplete state), `4` (resource limit), and `5`
(integrity/security failure).

## Standalone wiki chat and content approval

Start the terminal UI with the same standalone profile:

```console
wikiws chat /absolute/path/to/wiki --profile default
```

The Chat, Content Inbox, Questions, and Queue tabs remain usable without OpenCode. Chat answers are
grounded in bounded relevant wiki notes and cite only supplied paths. If a chat requests durable
new knowledge or a correction, that portion is saved under `.agent-wiki/inbox`; the model never
writes a page directly.

Every proposal follows a durable state machine: `queued -> evaluating -> needs_answer` or
`ready_for_approval`. Answers requeue reconciliation against current content. A ready item still
requires an explicit preview and human confirmation before `approved -> applied`; rejection is
terminal. If wiki content changed after evaluation, approval is cleared and the item is requeued.
Interrupted worker claims can be recovered after a lease. Model failures remain visible and
retryable.

The same workflow is available headlessly:

```console
wikiws inbox propose WIKI --content "Document the verified timeout policy" --json
wikiws inbox list WIKI --json
wikiws queue process WIKI --profile default --json
wikiws questions list WIKI --status open --json
wikiws questions answer WIKI question-... --answer "Keep the documented limit" --json
wikiws inbox approve WIKI content-... --actor reviewer --apply --json
wikiws queue recover WIKI --max-age 900 --json
```

Reconciliation compares the proposal with relevant current notes. Contradictions, ambiguity, and
unsupported assertions become durable questions instead of edits. Approved creates/updates are
restricted to managed Markdown roots, carry exact SHA-256 preconditions, reject symlinks and stale
revisions, and roll back if a multi-file apply fails.

## MCP access to existing Wikis

The MCP server queries existing Wikis and never scans or rebuilds their source repositories. A
successful code build can add its Wiki to the package-owned multi-Wiki catalog:

```console
wikiws code build /absolute/path/to/repository \
  --wiki /absolute/path/to/wiki --apply --register-mcp

# Existing Wikis can be registered independently.
wikiws mcp register /absolute/path/to/wiki
wikiws mcp list --json
```

Install one local stdio server in OpenCode. Its `list_wikis` tool provides the Wiki selection; the
remaining tools require a `wiki_id` and use bounded search/read operations:

```console
wikiws mcp opencode-install
```

The default local client is a read-only `viewer`. A client that may submit untrusted improvement
proposals uses `contributor`; proposals go only to the durable inbox. Curator tools require both the
`curator` role and the separate server-side switch:

```console
wikiws mcp opencode-install --role contributor
wikiws mcp opencode-install --role curator --curator-enabled
```

Loopback Streamable HTTP requires a bearer token and may use plain HTTP. Keep the token in an
environment variable and configure the corresponding remote OpenCode entry without copying it into
the JSON file:

```console
export WIKIWS_MCP_TOKEN='replace-with-a-long-random-token'
wikiws mcp serve --transport http --host 127.0.0.1 --port 8765 \
  --token-env WIKIWS_MCP_TOKEN --role contributor
wikiws mcp opencode-install --url http://127.0.0.1:8765/mcp \
  --token-env WIKIWS_MCP_TOKEN
```

Non-loopback listeners require a certificate/key pair and therefore serve HTTPS. Enterprise mode
validates JWT signature, issuer, audience, expiry and subject against an HTTPS JWKS endpoint, then
maps IdP groups to the three internal roles. An optional `wiki_ids` claim restricts each principal
to specific catalog entries:

```console
wikiws mcp serve --transport http --host wiki-mcp.corp.example --port 8443 \
  --tls-certificate /etc/wikiws/tls/server.crt \
  --tls-key /etc/wikiws/tls/server.key \
  --allowed-host wiki-mcp.corp.example:8443 \
  --allowed-origin https://opencode.corp.example \
  --issuer https://idp.corp.example/ \
  --audience https://wiki-mcp.corp.example:8443/mcp \
  --jwks-url https://idp.corp.example/.well-known/jwks.json \
  --role-map wiki-readers=viewer \
  --role-map wiki-authors=contributor \
  --role-map wiki-curators=curator \
  --curator-enabled
```

OpenCode configuration is merged as strict JSON while preserving foreign fields and MCP entries.
The package records a hash for its single owned entry and refuses to overwrite user modifications.
If only `opencode.jsonc` exists, automatic installation fails closed so comments are not destroyed.

## OpenCode integration

Install an existing managed Wiki as an OpenCode agent with the default command:

```console
wikiws agent install /absolute/path/to/wiki
```

The selectable agent is named after the Wiki directory with only `-curator` appended, for example
`payments-platform-curator`. The command does not pin a model in the generated curator or code
builder. Select any available model in the OpenCode interface; otherwise OpenCode uses its
configured default. For a Wiki with one registered source-code repository, the semantic mapper,
Delphi provider, and model-selectable code builder are installed automatically.

Register or refresh all known Wikis, the router, shared vision agent, packaged skill, and native
tools with the advanced command:

```console
wikiws opencode sync
```

OpenCode also receives the primary agent `agent-wiki-manager`. Select it and ask it to initialize
a Wiki at an absolute path; the directory must be new or empty. The manager can only call the
package's create tool. It cannot adopt existing content, use shell or web tools, or apply curated
changes.

The router and shared vision agent are installed as primary agents. Per-Wiki curators are
restricted to their bound tools, can be selected directly for auditable E2E queries, and
communicate through persisted versioned contracts.

The command discovers the configuration through `OPENCODE_CONFIG_DIR` or `opencode debug paths`.
It never edits `opencode.json` and refuses collisions with files it does not own. Interactive sync
asks which vision-capable `provider/model` to use for the separate vision agent. The default is
`openrouter/google/gemma-4-31b-it`; automation can choose any valid OpenCode model identifier:

```console
wikiws opencode sync --vision-model openrouter/google/gemma-4-31b-it --json
wikiws doctor --json
```

The generated TypeScript tool starts exactly
`[absolute-venv-python, "-m", "agent_wiki_workspace", ...]` without a shell or `PATH` lookup.
Running sync again updates that binding after moving or reinstalling the environment.

Visual jobs remain pending if the selected agent/model cannot inspect images. Text curation and
search continue with `visual_coverage: pending`; no description is fabricated. A capable external
agent reads only a registered crop, then persists a matching `VisualResult`. Whether an image may
be sent to a remote model is the calling system's privacy decision.

## Python API

```python
from agent_wiki_workspace import KnowledgeBase, KnowledgeRegistry

kb = KnowledgeBase.init("/absolute/path/to/wiki", mode="create")
source = kb.add_pdf("report.pdf", ocr="auto", languages=("deu", "eng"))
job = kb.prepare_curation(source.source_id)
hits = kb.search("specific phrase")

registry = KnowledgeRegistry.open()
registry.register(kb)
registry.install_opencode(vision_model="openrouter/google/gemma-4-31b-it")
```

All public agent contracts are Pydantic models using schema `1.0`, reject unknown fields, and fail
closed on wrong wiki/job IDs, expired jobs, unsafe paths, stale updates, or unverifiable evidence.
A source changeset copies `job.job_id` and cites every claim with a `block_id` or page-bounded
`bbox` from the PDF layout sidecar. Block quotes are normalized and checked against block text.

## Trust boundary

PDF text, source code/comments, OCR, Markdown, metadata, links, attachments, QR codes, and images
are untrusted content. They are never executed or fetched. Package-generated agents deny web and
shell tools and receive only role-specific wiki tools. PDF passwords are accepted through Python
parameters or stdin and are never persisted.

See [the release procedure](docs/release.md) and [verification status](docs/verification.md) for
reproducible builds, critical coverage, neutral clean-environment evidence, and the explicitly
networked OpenRouter/OpenCode multi-wiki release gate.
