A PII anonymizer with consistent, reversible labels (<人名_1> /
<Name_1>, <Nombre_1>, <Tên_1>, …). Two cores that must stay in behavioural parity:
- Python:
src/prompt_anonymizer/(Presidio + spaCy) — PyPI package + CLI - TypeScript:
web/packages/core/(regex + transformers.js NER) — browser app, Chrome extension, Tauri desktop, Node CLI (web/packages/cli), embeddable web component (web/packages/element), React/Vue bindings (web/packages/{react,vue}), OpenAI-compatible anonymizing proxy + admin GUI (web/packages/proxy,web/apps/proxy-admin), MCP server (web/packages/mcp; the mapping stays server-side behind amapping_id— never returned to the model unless explicitly requested, P0)
Both CLIs also expose a commit-time / CI gate (scan subcommand; exit
codes 0 = clean, 1 = PII found, 2 = error) consumed via
.pre-commit-hooks.yaml. scan must never print the matched text —
only file:line:col and the entity type (P0).
The parity contract is the golden set in tests/golden/ (regenerated by
uv run python -m prompt_anonymizer.evals).
Supported languages have a single source of truth per core
(src/prompt_anonymizer/languages.py / web/packages/core/src/languages.ts);
never hardcode language lists elsewhere. To add a language, follow
docs/ADDING_A_LANGUAGE.md — CI consistency tests fail on half-added states.
- P0 — PII leak risk. Any change that could cause PII to leave the device (new network calls, telemetry, logging of input text or mappings, persisting mappings) or that weakens detection (score threshold changes, recognizer removal) must be treated as blocking and called out explicitly.
- P1 — ReDoS. Recognizer regexes run on untrusted input. Flag patterns with nested quantifiers or overlapping alternations. Prefer bounded quantifiers and lookarounds as used in the existing recognizers.
- Offset handling requires tests. Any change to span merging, label
assignment, or replacement (
labeling.py/labeling.ts) must come with matching tests in BOTH cores, plus a round-trip (anonymize → deanonymize identity) assertion. - Label format changes are breaking changes: they invalidate stored mappings and demo assets. Require a CHANGELOG entry and demo regeneration.
Limit review comments to the priorities above plus clear correctness bugs; avoid style nits (ruff/tsc enforce style mechanically).
- Python:
uv run pytest -m "not slow",uv run ruff check .,uv run mypy src - Web:
cd web && pnpm test && pnpm lint && pnpm build - Docs (README etc.):
pnpm -C web docs:checkvalidates relative links and#anchorsin every tracked*.md(github-slugger, same algorithm as GitHub) and guards user-facing docs against release drift (registry SSOT, install paths, MCP coverage, pre-commit rev pins); runs in PR CI - Web e2e (after build):
cd web && pnpm e2e(offline, PR CI);pnpm e2e:nerruns the full NER pipeline (model download, weekly CI) - Demo assets are script-generated only:
demo/README.md
Dependencies are refreshed automatically on startup (Python via uv sync --all-extras --group models, web via pnpm -C web install --frozen-lockfile).
Standard lint/test/build/run commands live in ## Commands above and
CONTRIBUTING.md. Non-obvious notes:
uvinstalls to~/.local/bin; it is sourced for login shells via~/.bashrc.uvauto-creates a Python 3.13 venv from.python-version, sopytestruns on 3.13 even though the base image ships 3.12.- The web workspace uses pnpm
11.10.0(pinned via corepack + thepackageManagerfield); the base image's global pnpm is older, so run pnpm from insideweb/(or viapnpm -C web) to get the pinned version. - Before running the web app / extension / desktop, the shared core must be
built (
pnpm --filter @prompt-anonymizer/core build) because itsmainpoints atdist/index.js;dist/is not committed.pnpm lintand the fullpnpm buildalready build core first. - Browser dev server:
pnpm --filter @prompt-anonymizer/web devon port5173. The first "Anonymize" click downloads a transformers.js NER model from the Hugging Face CDN (needs network); untick the "NER model" checkbox for a fully offline regex-only run (masks emails/phones but not names). - Chrome is preinstalled at
google-chrome; there is no extension dev server — build it and load unpacked fromweb/apps/extension/dist/. - Desktop (
pnpm --filter @prompt-anonymizer/desktop dev) additionally needs the Rust/Tauri system libraries (see.github/workflows/release-apps.yml) which are not installed by default.