The @karmaniverous/jeeves-watcher-openclaw plugin gives your OpenClaw agent access to jeeves-watcher's semantic search, metadata enrichment, and management capabilities.
The plugin is a standard OpenClaw plugin with no installer of its own. On a Jeeves box, jeeves install (from @karmaniverous/jeeves 0.6+) installs it and writes its config:
jeeves install watcher --config-root /srv/jeeves/config
Or install it directly with the OpenClaw CLI and set the config yourself:
openclaw plugins install npm:@karmaniverous/jeeves-watcher-openclaw@<version> --pin --accept-capabilities
The plugin registers an always-in-context rule set through OpenClaw's before_prompt_build hook, so it needs plugins.entries.jeeves-watcher-openclaw.hooks.allowConversationAccess: true. jeeves install / jeeves update grant this automatically (the hook is declared in package.json under jeeves.conversationHooks).
Restart the OpenClaw gateway to apply changes.
Plugin config lives in openclaw.json under plugins.entries.jeeves-watcher-openclaw.config:
{
"apiUrl": "http://127.0.0.1:1936",
"apiKey": "<the service api.apiKey>",
"configRoot": "/srv/jeeves/config"
}
apiUrl: jeeves-watcher API base URL (default: http://127.0.0.1:1936; env fallback JEEVES_WATCHER_URL).apiKey (optional): bearer key sent as Authorization: Bearer <key> on every watcher API request; it must match the service's api.apiKey. Env fallback JEEVES_WATCHER_API_KEY. Resolved on every call and never logged. Unset (the default): no header is sent, exactly as before. If the service requires a key and it is missing or wrong, tools return an HTTP 401 unauthorized error naming both ways to set it (see Errors). Only watcher_status (GET /status is always open) and watcher_service (no HTTP) do not use it.configRoot: platform config root path, used by @karmaniverous/jeeves core to derive {configRoot}/jeeves-watcher/. No default. Set it in plugin config or via the JEEVES_CONFIG_ROOT env var.configRoot is resolved lazily. The plugin always registers, even before its config is written (openclaw plugins install activates a plugin before jeeves install writes plugins.entries.<id>.config). While configRoot is unset the plugin logs one warning at registration. Gating is per call: only invocations that actually read configRoot are gated. Today that is exactly watcher_service with action: "install" (it derives the service config path from configRoot); it returns an error naming both ways to set it. Other watcher_service actions (uninstall, start, stop, restart, status) address the OS service by name, and every other tool only calls the watcher HTTP API, so they keep working without configRoot. Core is initialized on the first gated call after it resolves.
The plugin registers 18 tools (contracts.tools in openclaw.plugin.json). watcher_status and watcher_service come from the shared @karmaniverous/jeeves core toolset; every other tool is a thin wrapper over one watcher HTTP endpoint and sends the API key when one is configured.
A non-2xx response from the watcher API becomes a tool error reading HTTP <status> <code>: <message> (prefixed Error: ), where <code> is the server's machine-readable error code, e.g. HTTP 400 invalid_request: Config validation failed. A 401 keeps its key guidance: HTTP 401 unauthorized: the watcher API requires an API key. Provide it via plugins.entries.jeeves-watcher-openclaw.config.apiKey or the JEEVES_WATCHER_API_KEY env var. (or, when a key was sent, ... rejected the API key. Check ... against the service's api.apiKey.). Structured details (Zod issues, per-property validation failures) stay in the HTTP response body. A refused, unresolvable or timed-out connection reads Service not reachable at <apiUrl>. with a hint to set apiUrl.
watcher_statusReturns service health, uptime, and Qdrant collection statistics (GET /status, which never requires the API key). No parameters.
watcher_searchSemantic search across all indexed documents (POST /search). Pass a natural-language query and optional filters. Returns { id, score, payload } hits ordered by score; a source document's payload carries source and no file_path.
Parameters:
query (string, required) — search textlimit (number) — max results (default: 10)offset (number) — skip N results for paginationfilter (object) — Qdrant filter conditionswatcher_enrichSet or update metadata on a document by file path (POST /metadata). Metadata is validated against the schemas of the rules matching the file and merged into its points without re-embedding. Returns { ok: true, matched_rules }. Source documents are enriched through POST /metadata with source directly; the tool sends path only.
Parameters:
path (string, required) — file path of the documentmetadata (object, required) — key-value metadata to set on the documentwatcher_configQuery the effective runtime config via JSONPath (GET /config). Returns the full resolved merged document when no path is provided. Useful for discovering available inference rules, schemas, and runtime values. Only the allow-listed keys (description, watch, configWatch, search, schemas, inferenceRules, mapHelpers, templateHelpers, maps, templates) plus issues are included; api, sources and provider keys never are. Implemented by the plugin (not the core toolset) so the request carries the API key.
Parameters:
path (string, optional) — JSONPath expressionwatcher_validateValidate a candidate config (or the current config if omitted), optionally testing file paths against it (POST /config/validate). Returns { valid: false, errors }, or { valid: true, warnings?, testResults? } with { path, matchedRules, metadata, error? } per test path.
Parameters:
config (object) — candidate config (partial or full); merged into the running config (top-level keys replace, inferenceRules merge by name). Omit to validate the current config.testPaths (string[]) — file paths to test against the config for a dry-run previewwatcher_config_applyApply a config patch to the running watcher service (POST /config/apply, body { patch }). The patch is deep-merged into the service's config file (inferenceRules by rule name), validated and written; the configWatch.reindex scope (default issues, files only) is then triggered. Implemented by the plugin so the request carries the API key.
Parameters:
config (object, required) — config patch to applyReturns { applied: true, config }. The echoed config is redacted through the same allow-list as GET /config (api, sources and provider keys are never echoed). When the config was written but the post-write apply step failed, the response also carries warning (a string). An invalid merged config is HTTP 400 invalid_request: Config validation failed; use watcher_validate to see the individual errors. A server-side failure (config file unreadable or unwritable) comes back as a generic internal_error, with the cause in the service log.
watcher_reindexTrigger a scoped reindex (POST /reindex). Supports rules, full, issues, path, and prune scopes; the tool sends rules when scope is omitted (the HTTP API defaults to issues). Non-prune scopes return a blast area plan (files plus source documents). Live prune returns immediately without a plan. Explicit reindexes also cover source documents in full, rules (without path) and issues; path and prune cover files only (prune never deletes source points). Progress is reported by watcher_status under health.reindex. To reindex specific source references, call POST /reindex with { "scope": "path", "source": [...] } directly; the tool has no source parameter.
Parameters:
scope (string) — reindex scope (default: rules)path (string | string[]) — target file(s) or directories for path scope (required there); globs narrowing rules scopedryRun (boolean) — compute plan without executingwatcher_walkList watched files matching globs (POST /walk), from the filesystem watcher's in-memory file list across all configured watch roots, with watch.ignored and gitignore filtering applied. Returns { paths, matchedCount, scannedRoots }; HTTP 503 scan_in_progress until the initial scan completes.
Parameters:
globs (string[], required) — glob patterns to intersect with watch pathswatcher_scanFilter-only point query without vector search (POST /scan). Returns metadata for points matching a Qdrant filter. Use for structural queries: file enumeration, staleness checks, domain listing, counts.
Parameters:
filter (object, required) — Qdrant filter objectlimit (number) — page size (default: 100, max: 1000)cursor (string) — opaque cursor from previous response for paginationfields (string[]) — payload fields to return (projection)countOnly (boolean) — if true, return { count } instead of pointswatcher_issuesList current processing issues (GET /issues): files and source documents with template or rule errors, or failed extraction, embedding, upsert, rule matching or source retrieval. Returns { count, issues }, where issues maps each file path or source reference to its issue records (type, message, timestamp, and context fields). No parameters.
watcher_serviceManage the watcher background service (install, uninstall, start, stop, restart, status). No HTTP call.
Parameters:
action (string, required) — one of: install, uninstall, start, stop, restart, statuswatcher_vcs_statusGet version tracking health: enabled state, tracked roots, remote status, last activity (GET /vcs/status). No parameters.
watcher_vcs_historyQuery change history by path or glob with optional date range (GET /vcs/history).
Parameters:
glob (string, required) — path or glob pattern to query history forsince (string) — start date (ISO 8601 or git date string)until (string) — end date (ISO 8601 or git date string)limit (number) — maximum number of history entries to returnwatcher_vcs_showRetrieve file content at a specific version (GET /vcs/show).
Parameters:
path (string, required) — file path to retrievecommit (string, required) — version identifierwatcher_vcs_diffShow what changed between two versions, or between a version and current (GET /vcs/diff).
Parameters:
glob (string, required) — path or glob pattern to diffcommit (string, required) — start version identifiercommitEnd (string) — end version identifier (defaults to current if omitted)watcher_vcs_revertUndo changes by restoring files to a specific version (POST /vcs/revert).
Parameters:
glob (string, required) — path or glob pattern to revertcommit (string, required) — version to restore files toexistingOnly (boolean) — when true, only revert files that currently exist (skip deleted files)watcher_vcs_excludeExclude or re-include paths from version tracking (POST /vcs/exclude).
Parameters:
glob (string, required) — glob pattern to exclude or re-includeroot (string) — tracked root to target (defaults to auto-detect)remove (boolean) — when true, remove the exclusion rule (re-include the path)watcher_vcs_checkCheck whether a path is excluded from version tracking and why (GET /vcs/check-exclusion).
Parameters:
path (string, required) — file path to check exclusion status for
The plugin builds on @karmaniverous/jeeves 0.7 (static-content core since 0.6). It writes no workspace files and starts no timers:
before_prompt_build (registerPromptContext). They replace the v0.x TOOLS.md ## Watcher section.watcher_status, watcher_config) rather than a refreshed file.jeeves-watcher ships in the package and is declared in openclaw.plugin.json (skills).jeeves install."Search the watcher for documents about authentication configuration"
The agent calls watcher_search with the query and returns matching document chunks with their source paths and metadata.
"Is the watcher service running? How many documents are indexed?"
The agent calls watcher_status and reports uptime, health, and collection point count.
"Are there any files that failed to index?"
The agent calls watcher_issues and summarizes any errors or warnings.
"I updated the watcher config — please reindex everything"
The agent calls watcher_config_apply (if a new config is provided), then watcher_reindex with scope: "full", previewing the blast area with dryRun: true first.