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
132 changes: 59 additions & 73 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,109 +1,95 @@
# CLAUDE.md

Guidance for Claude Code (and other coding agents) working in the
`dikw-plugins` repo.
Guidance for Claude Code (and other coding agents) in the `dikw-plugins` repository.

## What this repo is

A **uv-workspace monorepo** of converter plugins that extend
[`dikw-core`][core]'s `dikw client import` to non-markdown formats.
Each `packages/dikw-converter-<format>/` is its own pypi package with
its own version + dependencies + tests. The repo as a whole has
shared tooling (ruff, mypy, pytest, CI) but per-package release
cadence.
A **uv-workspace monorepo** of converter plugins that extend [`dikw-core`][core]'s `dikw client import` to non-Markdown formats.

- Each `packages/dikw-converter-<format>/` is its own PyPI package, with its own version, dependencies, and tests.
- The repo shares tooling (ruff, mypy, pytest, CI). Each package has its own release cadence.

[core]: https://github.com/opendikw/dikw-core

## Before doing anything: read these in order

1. [`docs/architecture.md`](docs/architecture.md) — **start here**. The
full architecture story: why this lives in a sibling repo, the
plugin contract, output layout, idempotency rules. Self-contained;
no assumed prior context.
2. [`dikw-core/docs/converters.md`][spec] — the formal contract spec
(Protocol shape, entry-points, selection order). Source of truth
for what every plugin must implement.
3. [`dikw-core/docs/adr/0001-client-side-converter-plugins.md`][adr] —
why we picked this design over the alternatives (engine-side
SourceBackend, server-side plugin, pure upstream tool).
4. [`docs/plugin-author-guide.md`](docs/plugin-author-guide.md) —
tutorial when you're actually writing a new plugin.
## Read before you change things

Read the document that matches your change:

| change | read first |
|---|---|
| anything in the plugin contract, output layout, or idempotency | [`docs/architecture.md`](docs/architecture.md) — the full architecture story; self-contained |
| what every plugin must implement (Protocol shape, entry points, selection order) | [`dikw-core/docs/converters.md`][spec] — the formal contract and source of truth |
| why plugins live client-side in a sibling repo | [`dikw-core/docs/adr/0001-client-side-converter-plugins.md`][adr] |
| a new plugin | [`docs/plugin-author-guide.md`](docs/plugin-author-guide.md) — the tutorial |
| a release | [`docs/release-process.md`](docs/release-process.md) |

[spec]: https://github.com/opendikw/dikw-core/blob/main/docs/converters.md
[adr]: https://github.com/opendikw/dikw-core/blob/main/docs/adr/0001-client-side-converter-plugins.md

## Layering invariants (inherited from dikw-core)

- **No server dependencies.** Plugins run in the `dikw client` process.
Never import anything from `dikw_core.server.*`, `dikw_core.api`,
`dikw_core.storage`, or `dikw_core.providers`. The Converter Protocol
+ Path is all you need from dikw-core.
- **No engine-side imports.** `dikw_core.domains.*` is engine territory
— plugins don't touch it. The split is structural, not stylistic;
importing engine modules means your plugin can't be loaded by a
thin client.
- **Plugin deps are your problem.** Marker, MinerU, docling — whatever
your plugin pulls in stays scoped to your package's `pyproject.toml`.
Don't add deps to a sibling package "for convenience".
- **No server dependencies.** Plugins run in the `dikw client` process. Never import from `dikw_core.server.*`, `dikw_core.api`, `dikw_core.storage`, or `dikw_core.providers`. The Converter Protocol and `Path` are all you need from dikw-core.
- **No engine imports.** `dikw_core.domains.*` belongs to the engine. Plugins do not touch it. If a plugin imports engine modules, a thin client cannot load it.
- **Plugin dependencies stay in the plugin.** Marker, MinerU, docling, or any other dependency goes only in that package's `pyproject.toml`. Do not add a dependency to a sibling package "for convenience".

## Dev workflow

```bash
uv sync # workspace deps
uv sync --all-packages --all-extras # every workspace member and its deps (same as CI)
uv run pytest # all package tests
uv run pytest packages/dikw-converter-pdf/tests # one package
uv run pytest packages/dikw-converter-mineru/tests # one package
uv run ruff check .
uv run mypy packages/*/src

# Test a plugin against a locally checked-out dikw-core:
pip install -e ../dikw-core
pip install -e packages/dikw-converter-pdf
pip install -e packages/dikw-converter-mineru
dikw client import sample.pdf
```

## Conventions

- **Package names**: `dikw-converter-<format>` (one format per package
is the norm; multi-format packages allowed when formats share an
upstream tool, e.g. a hypothetical `dikw-converter-pandoc`).
- **Module names**: `dikw_converter_<format>` (Python identifier form).
- **Engine names**: the `Converter.name` attribute — short, unique
across plugins the user has installed (e.g. `marker`, `mineru`,
`docling`).
- **Output layout**: `<output_dir>/<stem>.md` + `<output_dir>/assets/*`,
with every asset image-referenced from the md (see
`docs/architecture.md` § "Asset reference rule").
- **Versioning**: each package is independently SemVer'd. Bump the
package's own `version` field AND add a matching `## [X.Y.Z]` block
at the top of that package's `CHANGELOG.md` in the same commit —
the release pipeline rejects a tag whose version is not present in
the changelog.
- **Releasing**: tagging `dikw-converter-<format>-vX.Y.Z` triggers
`.github/workflows/release.yml` (PyPI via OIDC + GitHub Release).
Before tagging, run `uv run python scripts/check-package.py
dikw-converter-<format>` locally — it executes the same artifact
gate (`tests/packaging/`) that CI runs, so red on your machine =
red on the runner. Full procedure in `docs/release-process.md`.
- **Package names:** `dikw-converter-<format>`. One format per package is the norm. A multi-format package is allowed only when the formats share an upstream tool (for example a hypothetical `dikw-converter-pandoc`).
- **Module names:** `dikw_converter_<format>` (the Python identifier form).
- **Engine names:** the `Converter.name` attribute. Keep it short and unique across the plugins a user installs (for example `marker`, `mineru`, `docling`).
- **Output layout:** `<output_dir>/<stem>.md` + `<output_dir>/assets/*`. The Markdown must image-reference every asset (see `docs/architecture.md` § 5 "Output layout convention").
- **Versioning:** each package has its own SemVer. In the same commit, bump the package's `version` field AND add a matching `## [X.Y.Z]` block at the top of that package's `CHANGELOG.md`. The release pipeline rejects a tag whose version is not in the changelog.
- **Releasing:** a tag `dikw-converter-<format>-vX.Y.Z` triggers `.github/workflows/release.yml` (PyPI through OIDC + GitHub Release).
Before you tag, run `uv run python scripts/check-package.py dikw-converter-<format>`. It runs the same artifact gate (`tests/packaging/`) as CI, so red on your machine means red on the runner.
Full procedure: `docs/release-process.md`.

## Tooling

- Python 3.12+, same as dikw-core.
- `uv` for workspace + venv + dep resolution.
- `ruff` rules + line length match dikw-core's `pyproject.toml` so
there's a single style across both repos.
- `mypy strict = true`. Plugins should be fully typed.
- Python 3.12+, the same as dikw-core.
- `uv` for the workspace, the venv, and dependency resolution.
- `ruff` rules and line length match dikw-core's `pyproject.toml`, so both repos share one style.
- `mypy strict = true`. Plugins are fully typed.
- `pytest` with the workspace's shared config.

## Things not to do

- Don't put the original PDF / EPUB into `output_dir/` top-level.
Put it under `output_dir/assets/` and image-ref it from the md.
- Don't hard-code paths inside `<base>/sources/` — plugins write to
the temp `output_dir` they were given; dikw-core handles staging
into the base.
- Don't reach into dikw-core internals to "speed things up" — the
Protocol contract is what we promise to keep stable. Internals
may move.
- Don't write a single mega-plugin handling many formats unless they
legitimately share an upstream tool. One pypi package per logical
unit makes user dep-pinning easier.
- Don't put the original PDF / EPUB into `output_dir/` top-level. Put it under `output_dir/assets/` and image-ref it from the md.
- Don't hard-code paths inside `<base>/sources/` — plugins write to the temp `output_dir` they were given; dikw-core handles staging into the base.
- Don't reach into dikw-core internals to "speed things up" — the Protocol contract is what we promise to keep stable. Internals may move.
- Don't write a single mega-plugin handling many formats unless they legitimately share an upstream tool. One pypi package per logical unit makes user dep-pinning easier.

## Working rules

- State your assumptions. If a decision blocks you, ask one question with the AskUserQuestion tool, and put your recommended answer first.
- Write the minimum code that solves the request. Change only what the request needs, and match the existing style.
- Test first: a bug fix starts with a failing test that reproduces it.

## Autonomy

- When a step does not need my input, continue. Put status notes in the same message as your next action.
- Stop and ask only when you cannot continue without my decision, or before a destructive action: delete data or files you did not create, push a release tag, force-push, or change anything outside this repository.
- Do not end a turn with a summary that announces the next step but does not take it, or with an offer to continue "unless you prefer otherwise".

## Finish line

A change is done when `uv run pytest`, `uv run ruff check .`, and `uv run mypy packages/*/src` are green locally, every PR check is green, the PR is merged, and local `main` is synced.
A release is done when `scripts/check-package.py` passes, the tag is pushed with my approval, and the release workflow is green.

## Report

End every run with three headings: **需要你决定** (decisions you wait for; "无" if none), **改动** (what changed, with PR links), **发现** (what you found; mark each claim you could not confirm, and say where you looked).
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ all lives in a sibling repo (rather than inside dikw-core) is in
```
dikw-plugins/
├── README.md (you are here)
├── CLAUDE.md agent guidance — start with docs/architecture.md
├── CLAUDE.md agent guidance — which doc to read for which change
├── CONTEXT.md local terms (defers to dikw-core's glossary)
├── pyproject.toml uv workspace root
├── uv.lock committed for reproducible installs
Expand Down
Loading