
An agency looks after fifteen WordPress sites. Nobody notices that site seven runs a plugin with a critical security hole until something breaks. And when site seven has the problem that site three had eight months ago, the only person who knows the fix is the colleague who answered that email. WP Monitor closes both gaps: it watches the sites, remembers every fix that ever landed in the support inbox, and drafts the ticket. A human clicks create.
It watchesThree checks run on their own schedule and turn everything they find into one kind of record: a finding, with a type, a severity and the raw evidence.
- Uptime, every 5 minutes. Fetches the homepage, retries once, flags timeouts, HTTP errors and half-rendered pages.
- PageSpeed, every 20 hours. A mobile Lighthouse run through Google PageSpeed Insights; a finding below a score of 80 or above 4 seconds LCP.
- Vulnerabilities, every hour. Installed plugin versions against the Wordfence vulnerability feed, matched by a fully tested version-range matcher. Severity follows the CVSS score.
Findings are de-duplicated per site, so a site that stays down produces one finding, not one per run. When the problem disappears, the finding resolves itself. There is one cron endpoint, and each check carries its own interval.
It remembersDrop a mailbox export on the Wissen page. The browser parses the .mbox file, groups messages into threads and sends them to the server in small packets, because a support mailbox can be hundreds of megabytes and a serverless request cannot. The server removes e-mail addresses, IBANs, phone numbers and known names, splits the text into chunks of 800 words with 100 words of overlap, embeds them with text-embedding-3-small and stores them in Postgres with pgvector. Importing the same file twice is safe: unchanged threads are skipped, failed ones are retried.
Every finding page then asks the knowledge base on its own: "Kennen wir das?" Do we know this? The query is not the CVE title but what a support inbox would actually contain: update plugin X from A to B, problems after the update.
The retrieval is measured, not guessed. The threshold of 0.55 cosine distance was calibrated on German support mail: real matches landed between 0.29 and 0.51, unrelated questions started at 0.58. An empty result is a valid answer and is shown as one.


The Brain is a chat panel that opens on every page. Underneath it is a LangChain agent (createAgent) over OpenRouter, streamed with the Vercel AI SDK. The model is selectable; Claude Haiku 4.5 is the default. It has five tools. Four read: list sites, list plugins, search the incident history, search the web. One writes: create a ClickUp task. That one never runs without a human.
When you click "Lösung vorschlagen" on a finding, the server runs the history search first and hands the result to the model as an already completed tool call. Inventing history is forbidden in the prompt: only what the search returned may be cited as a previous case.
The ticket is a schema, not free text: title, priority, two sentences of situation, steps, references. Every step carries its source: wordfence, standard, or the id of the mail thread it came from. Due date and assignee are not part of the schema, because language models are unreliable with calendars. Code derives the date from the priority, and a person picks the assignee.
Title meier-bau.de — update Elementor 3.21.4 to 3.21.5
Priority urgent
Steps
1. Create a backup of the site [standard]
2. Apply the update on staging, check the frontend [standard]
3. Update Elementor to 3.21.5 [wordfence]
4. Clear the WP Rocket cache after the update. Last
time the site went white without it. [mail:7f3a…]
5. Check the homepage and the contact form [mail:7f3a…]
Step 4 is the reason this project exists. That sentence sat in a mail thread from eight months ago, and without the tool nobody would have found it.

Why a language model never decides whether something is brokenThree rules hold the codebase together.
1. Detection is deterministic. Whether a plugin is vulnerable is decided by ordinary, tested code. The model explains, plans and writes. It never detects.
2. Everything becomes a finding. One table, one type field. Dashboard, filters, agent and approval only know Finding and never need to know which check produced it. Adding a check is one new file, one line in the registry and one enum value. The dashboard grows a new card by itself.
3. Nothing writes without approval. Every tool is either read-only or carries requiresApproval: true, and exactly one place in the code reads that flag. A middleware ends the agent run the moment the model calls such a tool; the tool function itself is never executed. The server claims the pending approval atomically, so a double click cannot create a second ticket.
The state of that approval is the chat history itself, stored in Postgres. A card that is no longer the last message is disabled, and a new message while a card is open counts as a rejection.

Next.js 15 (App Router) · React 19 · TypeScript · Drizzle ORM · Neon Postgres with pgvector · LangChain.js 1.x via OpenRouter · Vercel AI SDK and AI Elements · shadcn/ui · Tailwind CSS 4 · Zod 4 · vitest
Deliberately out: malware and file-integrity scans, probing open WordPress endpoints, automatic plugin updates, themes and core in the vulnerability check, e-mail or Slack notifications. The tool reads client sites through the REST API and never touches their files. A monitoring tool should observe, not roll out software on fifteen servers it does not own.
Not built yet: authentication, which is why there is no public demo link; a scheduled refresh of the plugin inventory; ClickUp tickets as a second knowledge source. One feature was built and deleted again: an LLM-extracted table of plugin conflicts, removed because it stored the same knowledge twice. The vector search already answers that question.
Redaction is regex plus a name list, not entity recognition, so the knowledge base should be treated as still containing personal data. All checks are read-only, and the tool only scans sites the agency is authorised to maintain.
3 checks · 5 agent tools, 4 read-only and 1 approval-gated · 12 tables · 70 unit tests, the version matcher fully covered · 29 commits in 12 days
The interface is German because the agency is. The screenshots show a fictional client, and the knowledge-base excerpts in them are the sample mailbox that ships with the repository.