@karmaniverous/jeeves-watcher-openclaw 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * @module scripts/build-skills
3
- * Copies skill files to dist/skills/ for plugin packaging.
3
+ * Writes the watcher skill (watcher-core's `WATCHER_SKILL`, the descriptor's `skill`) to `dist/skills/jeeves-watcher/SKILL.md` for plugin packaging.
4
4
  */
5
5
  export {};
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: jeeves-watcher
3
3
  description: >
4
- Semantic search across a structured document archive. Use when you need to recall prior context, find documents, answer questions that require searching across indexed domains, enrich document metadata, manage watcher config, or diagnose indexing issues.
4
+ Semantic search of the indexed archive: recall prior context, find documents, scan, enrich metadata, watcher config, reindex, indexing issues, file history.
5
5
  ---
6
6
 
7
7
  # jeeves-watcher — Search, Discovery & Administration
@@ -12,11 +12,11 @@ The watcher is an HTTP API running as a background service (typically NSSM on Wi
12
12
 
13
13
  **Default port:** 1936 (configurable via `api.port` in watcher config) **Non-default port:** If the watcher runs on a different port, the user must set `plugins.entries.jeeves-watcher-openclaw.config.apiUrl` in `openclaw.json`. The plugin cannot auto-discover a non-default port.
14
14
 
15
- **API key:** If the watcher sets `api.apiKey`, every route except `GET /status` needs `Authorization: Bearer <key>`. The tools send it from `plugins.entries.jeeves-watcher-openclaw.config.apiKey` (or the `JEEVES_WATCHER_API_KEY` env var). A missing or wrong key comes back as `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.`). When calling the API directly, add the header yourself. Never echo the key.
15
+ **API key:** If the watcher sets `api.apiKey`, every route except `GET /status` and `GET /openapi.json` needs `Authorization: Bearer <key>`. The tools send it from `plugins.entries.jeeves-watcher-openclaw.config.apiKey` (or the `JEEVES_WATCHER_API_KEY` env var). From `watcher_config`, `watcher_config_apply`, `watcher_search`, `watcher_scan` and `watcher_show_vcs_file`, a missing or wrong key comes back as `HTTP 401: {"error":"Unauthorized","code":"unauthorized"}` (see [Error Handling](#error-handling) for the fix). From the other tools it reads `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.`). When calling the API directly, add the header yourself. Never echo the key.
16
16
 
17
- **Tool errors:** Tool error text is prefixed `Error: `. Any other non-2xx response reads `HTTP <status> <code>: <message>`, where `<code>` is the server's machine-readable error code (e.g. `HTTP 400 invalid_request: Validation failed`, `HTTP 503 scan_in_progress: Scan in progress: ...`). Branch on the code, not the message. Structured `details` (Zod issues, per-property validation failures) are in the HTTP response body only; call the API directly when you need them. An unreachable service reads `Service not reachable at <url>.` with a hint to set `apiUrl`. (`watcher_status` and `watcher_service` come from the shared Jeeves core toolset and word their own errors.)
17
+ **Tool errors:** Tool error text is prefixed `Error: `. Every `watcher_*` tool is built from the watcher's endpoint catalog and reports a non-2xx response as `HTTP <status>: <body>`, with the server's JSON body verbatim: read its `code` (and `details` or `issues`) there, e.g. `HTTP 400: {"error":"Validation failed","code":"invalid_request",...}` or `HTTP 503: {"error":"Scan in progress","code":"scan_in_progress"}`. Branch on the code, not the message. The tools reject parameters that don't match their schema before sending anything. Each call waits up to 120 s; a timeout does not cancel a state change on the service, so check the outcome (e.g. `watcher_check_vcs_exclusion` after `watcher_exclude_vcs_path`) before retrying. An unreachable service reads `Service not reachable at <url>.` with a hint to set `apiUrl`. (`watcher_service` comes from the shared Jeeves core toolset and words its own errors.)
18
18
 
19
- **Health check:** `GET /status` returns `name`, `version`, `uptime`, `status` (`healthy`/`degraded`/`unhealthy`), and a `health` object containing `collection` (`name`, `pointCount`, `dimensions`), `reindex` progress, and `initialScan` progress.
19
+ **Health check:** `GET /status` returns `name`, `version`, `uptime`, `status` (`healthy`/`degraded`/`unhealthy`), and a `health` object containing `collection` (`name`, `pointCount`, `dimensions`, `systemIndexes`), `reindex` progress, `initialScan` progress, `embedding` (provider health), and (when version tracking is on) `vcs` (`roots`, `pending`, `trippedRoots`, `oldestTrippedAt`). `status` is `degraded` (still HTTP 200) when embeddings are failing or a version-control root has tripped.
20
20
 
21
21
  **Mental model:** The `watcher_*` tools are thin HTTP wrappers. Each tool call translates to an HTTP request to the watcher API. When tools are available, use them. When they're not (e.g., different session, plugin not loaded), you can hit the API directly. Replace `<PORT>` below with the configured port (default 1936; check `plugins.entries.jeeves-watcher-openclaw.config.apiUrl` in `openclaw.json` if overridden), and add `-H "Authorization: Bearer <key>"` when the service sets `api.apiKey`:
22
22
 
@@ -38,6 +38,7 @@ curl http://127.0.0.1:<PORT>/config
38
38
  | Endpoint | Method | Purpose |
39
39
  | --- | --- | --- |
40
40
  | `/status` | GET | Health check, uptime, collection stats |
41
+ | `/openapi.json` | GET | OpenAPI 3.1 document of the catalog routes (no key needed) |
41
42
  | `/search` | POST | Semantic search (main query interface) |
42
43
  | `/config` | GET | Public resolved config (allow-listed keys plus `issues`); optional `?path=<jsonpath>` filter |
43
44
  | `/config/validate` | POST | Validate candidate config |
@@ -55,7 +56,8 @@ curl http://127.0.0.1:<PORT>/config
55
56
  | `/rules/reapply` | POST | Re-apply rules to indexed files matching `globs` |
56
57
  | `/points/delete` | POST | Delete points matching a Qdrant filter |
57
58
  | `/render` | POST | Render a document (file path or source reference) through its rule |
58
- | `/search/facets` | GET | Get search facet values |
59
+ | `/search/facets` | GET | Get search facet definitions and values (`?rules=a,b` scopes them to those rules) |
60
+ | `/search/facets/counts` | POST | Count values of indexed fields over a filter, each document once; counts are approximate (`{ fields, filter?, rules?, limit? }`); `{ min, max }` for number/date fields; `503 not_available` while its system indexes build or past its 10 s budget |
59
61
  | `/rebuild-metadata` | POST | Rebuild the enrichment store from Qdrant file point payloads |
60
62
  | `/config/schema` | GET | Get config JSON schema |
61
63
  | `/config/match` | POST | Test file paths against inference rules and watch scope |
@@ -89,7 +91,7 @@ You have access to a **semantic archive** of your human's working world. Documen
89
91
 
90
92
  **STOP and ask for a human decision before:**
91
93
 
92
- - **Renaming, moving, or reorganizing watched directories.** The watcher has **move detection** enabled by default (`watch.moveDetection.enabled: true`). When a file is moved, chokidar emits unlink + add events; the `MoveCorrelator` buffers the unlink, matches it with the add via content hash, and processes it as a zero-embedding move (point ID remap, metadata re-inference, enrichment migration). **No re-embedding cost for moves.** However, if a rename is blocked (e.g., by a file lock from the watcher or another service), **do NOT work around it by creating new directories and copying/moving files**. That creates duplicate embeddings at the new paths while the old paths still exist. Instead: stop the blocking service, perform the rename, restart.
94
+ - **Renaming, moving, or reorganizing watched directories.** The watcher has **move detection** enabled by default (`watch.moveDetection.enabled: true`). When a file is moved, the change source reports an unlink and an add; the `MoveCorrelator` buffers the unlink, matches it with the add via content hash, and processes it as a zero-embedding move (point ID remap, metadata re-inference, enrichment migration). Under the default parcel source, a renamed or moved directory is paired as a whole (at least 90% of its files at the same relative path and size) and each file becomes such a move. **No re-embedding cost for moves.** However, if a rename is blocked (e.g., by a file lock from the watcher or another service), **do NOT work around it by creating new directories and copying/moving files**. That creates duplicate embeddings at the new paths while the old paths still exist. Instead: stop the blocking service, perform the rename, restart.
93
95
  - **Changing `watch.paths` to add large directory trees.** Adding a new watch root triggers initial indexing of every matching file under it.
94
96
  - **Running `scope: "full"` reindex.** Every watched file is re-extracted (a file whose text to embed changed, or that was never indexed, is re-embedded) and every indexed source document is retrieved through its callback and always re-embedded. Use `scope: "rules"` for inference rule changes (no file re-embedding; only metadata reapplication).
95
97
  - **Any bulk file operation** (mass copy, mass move, template-based file generation) under watched paths.
@@ -100,7 +102,7 @@ You have access to a **semantic archive** of your human's working world. Documen
100
102
 
101
103
  ## Plugin Installation
102
104
 
103
- The plugin is a standard OpenClaw plugin. On a Jeeves box, `jeeves install watcher --config-root <path>` installs it and writes its config; otherwise use `openclaw plugins install npm:@karmaniverous/jeeves-watcher-openclaw@<version> --pin --accept-capabilities` and set `plugins.entries.jeeves-watcher-openclaw.config` yourself. Restart the gateway to load it.
105
+ The plugin is a standard OpenClaw plugin. On a Jeeves box, `jeeves install --workspace <workspace> --config-root <path> watcher` installs it and writes its config (without `--workspace` it takes the current folder as the workspace and writes platform files there); otherwise use `openclaw plugins install npm:@karmaniverous/jeeves-watcher-openclaw@<version> --pin --accept-capabilities --force` and set `plugins.entries.jeeves-watcher-openclaw.config` yourself. `--force` confirms the npm source and replaces an installed version; without it a non-interactive install is cancelled. To upgrade or roll back, run the same command with the other version. Restart the gateway to load it.
104
106
 
105
107
  `configRoot` has no default. Only `watcher_service` with `action: "install"` needs it; other `watcher_service` actions and all HTTP API tools work without it. If `watcher_service install` returns "configRoot not configured", set `configRoot` in the plugin config or the `JEEVES_CONFIG_ROOT` env var.
106
108
 
@@ -119,13 +121,13 @@ When the plugin loads and the watcher service is NOT yet set up, drive the entir
119
121
 
120
122
  ### Step 1: Check Node.js
121
123
 
122
- Verify Node.js is installed and version ≥ 22.13:
124
+ Verify Node.js is installed and version ≥ 24.22.2:
123
125
 
124
126
  ```bash
125
127
  node --version
126
128
  ```
127
129
 
128
- If missing or too old, guide the user to install Node.js 22.13+ from https://nodejs.org or via their package manager.
130
+ If missing or too old, guide the user to install Node.js 24.22.2 or later from https://nodejs.org or via their package manager.
129
131
 
130
132
  ### Step 2: Install Qdrant
131
133
 
@@ -344,7 +346,7 @@ Call `watcher_status` (or `curl http://127.0.0.1:1936/status`). Confirm:
344
346
  - Qdrant collection exists with expected dimensions (3072)
345
347
  - Point count is increasing (initial indexing in progress)
346
348
 
347
- If the point count is 0 after a minute, check `watcher_issues` for processing failures.
349
+ If the point count is 0 after a minute, check `watcher_list_issues` for processing failures.
348
350
 
349
351
  ### Step 8: Orientation
350
352
 
@@ -366,7 +368,7 @@ On sessions after bootstrap is complete:
366
368
 
367
369
  ## Tools
368
370
 
369
- The plugin registers 18 tools: `watcher_status`, `watcher_config`, `watcher_config_apply`, `watcher_service`, `watcher_search`, `watcher_enrich`, `watcher_validate`, `watcher_reindex`, `watcher_scan`, `watcher_issues`, `watcher_walk`, and the seven `watcher_vcs_*` tools (see [Version Tracking](#version-tracking)).
371
+ The plugin registers 18 tools: `watcher_status`, `watcher_config`, `watcher_config_apply`, `watcher_service`, `watcher_search`, `watcher_set_metadata`, `watcher_validate_config`, `watcher_reindex`, `watcher_scan`, `watcher_list_issues`, `watcher_walk`, and the seven version-control tools (see [Version Tracking](#version-tracking)).
370
372
 
371
373
  ### `watcher_search`
372
374
 
@@ -379,22 +381,22 @@ Semantic search over indexed documents (`POST /search`).
379
381
 
380
382
  Returns an array of `{ id, score, payload }` ordered by score (see [Search Result Shape](#search-result-shape)). A filter Qdrant rejects is `400 invalid_request`; an exhausted embedding budget is `503 embedding_unavailable`.
381
383
 
382
- ### `watcher_enrich`
384
+ ### `watcher_set_metadata`
383
385
 
384
386
  Set or update metadata on a document (`POST /metadata`).
385
387
 
386
388
  - `path` (string, required) — file path of the document (absolute or watched path)
387
389
  - `metadata` (object, required) — key-value metadata to merge
388
390
 
389
- Returns `{ ok: true, matched_rules }`. The tool addresses files only; to enrich a source document, call `POST /metadata` directly with `{ "source": "<name>://<id>", "metadata": { ... } }` (exactly one of `path` or `source`).
391
+ Returns `{ ok: true, matched_rules }`. If the document is busy (being indexed) for 10 s, the tool reports `503 not_available` with `Retry-After: 10` and changes nothing; retry after 10 s. The tool addresses files only; to enrich a source document, call `POST /metadata` directly with `{ "source": "<name>://<id>", "metadata": { ... } }` (exactly one of `path` or `source`).
390
392
 
391
393
  ### `watcher_status`
392
394
 
393
- Service health check (`GET /status`, never needs the API key). Returns `name`, `version`, `uptime`, `status`, and a `health` object: `collection` (`name`, `pointCount`, `dimensions`), `reindex` (`active`; while active also `scope`, `startedAt`, `filesProcessed`, `totalFiles`), and `initialScan`.
395
+ Service health check (`GET /status`, never needs the API key). Returns `name`, `version`, `uptime`, `status`, and a `health` object: `collection` (`name`, `pointCount`, `dimensions`, and `systemIndexes`: the state of each index the service creates itself, e.g. `{ "chunk_index": "ready", "file_path": "ready", "matched_rules": "building" }`; `building`, `ready`, `conflict` or `failed`), `embedding` (`state` `ok` or `failing`, `lastSuccessAt`, `lastError` `{ at, code }`, `failingSince`, `backoffUntil`; codes only, never provider text), `reindex` (`active`; while active also `scope`, `startedAt`, `filesProcessed`, `totalFiles`), `initialScan`, and `vcs` when version tracking is on: `roots`, `pending` (changes not yet committed), `trippedRoots` (roots whose commits keep failing) and `oldestTrippedAt`.
394
396
 
395
397
  After a service restart, the `health.initialScan` field shows scan progress:
396
398
 
397
- - `active: true` — filesystem walk in progress; `filesMatched` and `filesEnqueued` grow until chokidar completes
399
+ - `active: true` — filesystem walk in progress; `filesMatched` and `filesEnqueued` grow until the initial walk (or chokidar's scan) completes
398
400
  - `active: false` with `completedAt`/`durationMs` — scan finished
399
401
 
400
402
  Use this to determine if the service is still initializing after a restart.
@@ -407,7 +409,7 @@ Query the effective runtime config via JSONPath (`GET /config`). Returns the ful
407
409
 
408
410
  The document holds only the allow-listed keys `description`, `watch`, `configWatch`, `search`, `schemas`, `inferenceRules`, `mapHelpers`, `templateHelpers`, `maps`, `templates`, plus `issues` (the current issues). `api`, `sources`, `embedding`, `vectorStore` (and their keys) are never included. Each rule carries `source` (`config`, or the registering source of a virtual rule) and `values` (the runtime values index). File references (`map`, `maps`, `templates`) and named schema references are always resolved. An invalid JSONPath is `400 invalid_request`.
409
411
 
410
- ### `watcher_validate`
412
+ ### `watcher_validate_config`
411
413
 
412
414
  Validate config and optionally test file paths (`POST /config/validate`).
413
415
 
@@ -421,10 +423,11 @@ Partial configs are merged into the running config shallowly: each top-level key
421
423
  Apply config changes (`POST /config/apply`, sent as `{ patch: config }`).
422
424
 
423
425
  - `config` (object, required) — full or partial config to apply
426
+ - `replace` (boolean, optional) — replace the whole config with `config` instead of merging
424
427
 
425
428
  Partial configs are deep-merged into the config file the service runs from (objects recursively, arrays replaced, `inferenceRules` by rule name), so `{}` or a single key is valid. The merged result is validated and written to disk, then the `configWatch.reindex` scope (default `issues`) runs; that reindex covers files only. With `configWatch.enabled` (default `true`) the service picks up the written file without a restart, including new/removed watch paths (the filesystem watcher is rebuilt), inference rule changes, move detection settings, and gitignore filter updates.
426
429
 
427
- Returns `{ applied: true, config }`. `config` is redacted through the same allow-list as `GET /config`: `api`, `sources` and provider keys are never echoed. When the file was written but the post-write apply step failed, the body also carries `warning` (a string describing the failure). An invalid merged config is a tool error reading `HTTP 400 invalid_request: Config validation failed` (the Zod `issues` are only in the HTTP body), so run `watcher_validate` first. A config file the service cannot read or write is a generic `HTTP 500 internal_error`, with the cause in the service log.
430
+ Returns `{ applied: true, config }`. `config` is redacted through the same allow-list as `GET /config`: `api`, `sources` and provider keys are never echoed. When the file was written but the post-write apply step failed, the body also carries `warning` (a string describing the failure). An invalid merged config is a tool error reading `HTTP 400: {"error":"Config validation failed","code":"invalid_request","issues":[...]}`, so run `watcher_validate_config` first. A config file the service cannot read or write is `HTTP 500` with the generic `internal_error` body, the cause in the service log.
428
431
 
429
432
  ### `watcher_reindex`
430
433
 
@@ -435,7 +438,7 @@ Trigger a reindex operation (`POST /reindex`). Non-prune scopes return a `plan`
435
438
  - `scope` (string, optional) — Reindex scope. The tool sends `"rules"` when omitted (the HTTP API's own default is `"issues"`). One of:
436
439
  - `"rules"` — Re-apply inference rules to every watched file (no re-embedding; changed values written, keys the rules no longer project removed). With `path`, only files matching those globs. Without `path`, every indexed source document is also retrieved through its callback and its stored rule re-applied: unchanged content gets a metadata-only update, and a document is re-embedded only if its rendered text changed.
437
440
  - `"full"` — Re-extract and re-process every watched file (a file is re-embedded when the text to embed changed or it was never indexed), and retrieve and always re-embed every indexed source document. Clears the runtime values index first.
438
- - `"issues"` — Re-process the documents in `watcher_issues`: failed files, and failed source references (with their stored rule, or the `rule` recorded on the issue when the document was never indexed).
441
+ - `"issues"` — Re-process the documents in `watcher_list_issues`: failed files, and failed source references (with their stored rule, or the `rule` recorded on the issue when the document was never indexed).
439
442
  - `"path"` — Re-process a specific file or all files under a directory. Requires `path`. Each target must be watched and not gitignored (`403 path_not_watched`) and must exist (`404 not_found`). Files only.
440
443
  - `"prune"` — Delete Qdrant points for files no longer in watch scope (removed paths, gitignored files, deleted files). No re-embedding. Pure cleanup. Never selects source points (`source` set, no `file_path`).
441
444
  - `path` (string | string[], optional) — Target file(s) or directories for `"path"` (required there), or globs narrowing `"rules"`. Ignored by other scopes.
@@ -495,7 +498,7 @@ Filter-only point query without vector search (`POST /scan`). Use for structural
495
498
 
496
499
  - `filter` (object, required) — Qdrant filter object. Required to prevent accidental full-collection scans.
497
500
  - `limit` (number, optional) — page size (integer 1 to 1000), default 100
498
- - `cursor` (string, optional) — opaque cursor from previous response for pagination
501
+ - `cursor` (string or number, optional) — opaque cursor from previous response for pagination
499
502
  - `fields` (string[], optional) — payload fields to return (projection)
500
503
  - `countOnly` (boolean, optional) — if true, return `{ count }` instead of points
501
504
 
@@ -509,7 +512,7 @@ Filter-only point query without vector search (`POST /scan`). Use for structural
509
512
  "payload": { "file_path": "/data/tickets/PROJ-123.json" }
510
513
  }
511
514
  ],
512
- "cursor": "opaque-string-or-null"
515
+ "cursor": "opaque string, number or null"
513
516
  }
514
517
  ```
515
518
 
@@ -545,7 +548,7 @@ Manage the watcher background service (install, uninstall, start, stop, restart,
545
548
 
546
549
  Returns the service manager's response. On Windows uses NSSM, on Linux systemd, on macOS launchd. Only `install` needs `configRoot` (see [Plugin Installation](#plugin-installation)).
547
550
 
548
- ### `watcher_issues`
551
+ ### `watcher_list_issues`
549
552
 
550
553
  Get runtime processing failures (`GET /issues`). Returns `{ count, issues: { [key]: IssueRecord[] } }`: `count` is the number of documents with issues, and each key is a file path or a source reference (`<name>://<id>`). Each `IssueRecord` has `type` (one of the [issue codes](#error-categories)), `message`, `timestamp`, and optionally `operation`, `property`, `rule`, `rules`, `types`, `details`.
551
554
 
@@ -818,10 +821,10 @@ Search gives you chunks; use `read` with `file_path` for the complete document.
818
821
 
819
822
  ## Path Testing
820
823
 
821
- When uncertain whether a file would be indexed, test it against the rules. `watcher_validate` with `testPaths` (and no `config`) runs the current rules against each path and returns `{ path, matchedRules, metadata }` per path (`error: "File not found"` when the file cannot be read):
824
+ When uncertain whether a file would be indexed, test it against the rules. `watcher_validate_config` with `testPaths` (and no `config`) runs the current rules against each path and returns `{ path, matchedRules, metadata }` per path (`error: "File not found"` when the file cannot be read):
822
825
 
823
826
  ```
824
- watcher_validate: testPaths=["/data/tickets/PROJ-123.json"]
827
+ watcher_validate_config: testPaths=["/data/tickets/PROJ-123.json"]
825
828
  ```
826
829
 
827
830
  Or call `POST /config/match` directly with `{ "paths": [...] }`. It returns `{ matches: [{ rules, watched }] }`, one entry per path, without reading the files:
@@ -846,12 +849,19 @@ Each inference rule has:
846
849
  - `render` (optional) — declarative frontmatter + Markdown renderer (mutually exclusive with `template`)
847
850
  - `renderAs` (optional) — output extension (1-10 lowercase alphanumerics); requires `template` or `render`
848
851
 
852
+ ### Authoring Notes
853
+
854
+ - **`maps` entries are bare strings or inline maps.** A string value is a file path, loaded and parsed as a JsonMap at startup. A `{ path, description }` object is kept as-is and never loaded from the file, so it silently does nothing useful. (`mapHelpers` and `templateHelpers` are different: `{ path, description }` objects are their documented form.)
855
+ - **Metadata is derived with `set`, not `attributes`.** Each schema property's `set` template produces its value. There is no `attributes` key in the config schema; older configs and examples that use one need rewriting.
856
+ - **`render.frontmatter` accepts picomatch globs and `!` exclusions** alongside explicit keys, e.g. `["meta_id", "*", "!_*", "!chunk_*"]`. Explicit names keep their listed order; keys matched by a glob follow, sorted alphabetically; a key matching an exclusion is dropped.
857
+ - **Virtual rules have a startup window.** Virtual rules are registered at runtime by other services (`POST /rules/register`, e.g. jeeves-meta), so they are absent during the initial file scan after a watcher start. Files indexed in that window lack virtual-rule metadata until the registration's automatic rules reindex runs, or until `POST /rules/reapply` covers them. If metadata from a virtual rule is missing right after a restart, check whether its service has registered yet.
858
+
849
859
  ### Config Workflow
850
860
 
851
861
  1. Edit config (or build partial config object)
852
- 2. Validate: `watcher_validate` with optional `testPaths` for dry-run preview
862
+ 2. Validate: `watcher_validate_config` with optional `testPaths` for dry-run preview
853
863
  3. Apply: `watcher_config_apply` — validates, writes, triggers the `configWatch.reindex` reindex
854
- 4. Monitor: `watcher_issues` for runtime processing failures
864
+ 4. Monitor: `watcher_list_issues` for runtime processing failures
855
865
 
856
866
  ### Reindex Concurrency
857
867
 
@@ -874,7 +884,7 @@ Progress is reported via `watcher_status` (`health.reindex.filesProcessed` / `he
874
884
  ### Escalation Path
875
885
 
876
886
  1. `watcher_status` — is the service healthy? Is a reindex running? Is the initial scan still active?
877
- 2. `watcher_issues` — which documents are failing and why?
887
+ 2. `watcher_list_issues` — which documents are failing and why?
878
888
  3. `watcher_config` with `$.issues` — the same issue records via JSONPath
879
889
  4. Check logs at the configured log path
880
890
 
@@ -884,7 +894,7 @@ Every issue record's `type` is one of these codes:
884
894
 
885
895
  - `type_collision` — conflicting metadata types across rules (includes `property`, `rules[]`, `types[]`)
886
896
  - `interpolation_error` — template or `set` expression rendering failed (includes `property`, `rule`; `details` holds per-step failures)
887
- - `extraction_error` — reading, rule application or chunking failed
897
+ - `extraction_error` — reading, rule application or chunking failed. A file that was indexed and whose content no longer extracts (e.g. front matter with `!!float 1`, refused since 0.21.0) has its points deleted, so it is out of search until fixed; its enrichment is kept. A system error reading it (a lock, a dropped share) or a Node error (an enrichment store or extractor that fails) keeps the points
888
898
  - `embedding_error` — the embedding provider failed
889
899
  - `upsert_error` — the vector store write failed
890
900
  - `match_failed` — a source document's designated rule did not match (`details` holds the AJV errors)
@@ -892,11 +902,32 @@ Every issue record's `type` is one of these codes:
892
902
  - `no_content` — nothing to embed
893
903
  - `source_unavailable` — the source callback failed
894
904
  - `unsupported_content_type` — the source callback returned a non-JSON body
905
+ - `too_large` — the document is over a `processing` ceiling (`maxFileBytes`, `maxChunksPerFile` or `maxTextBytes`); it is not embedded and its points are deleted. Raise the ceiling and run `reindex` with scope `issues` to restore it
895
906
 
896
907
  Source-document issues always carry `rule` (the designated rule of the failed run).
897
908
 
898
909
  **Issues are self-healing:** resolved on successful re-process. The issues file always represents the current set of unresolved problems: a live todo list.
899
910
 
911
+ ### Change Source
912
+
913
+ `watcher_status` reports the change source under `health.changeSource` (`@parcel/watcher` by default, chokidar on request or when polling):
914
+
915
+ - **Service refuses to start, log names `@parcel/watcher failed to load`:** the platform binary is missing or broken. Reinstall without `--omit=optional`, or set `watch.changeSource: chokidar` (the manual switch; a reload rebuilds the watcher with one full initial scan).
916
+ - **Rising `reconcileEvents`:** the source is missing events and reconciliation is finding them. **Rising `resubscribes`:** subscriptions keep failing (Linux). **Rising `renewFailures`:** a Linux renewal could not unsubscribe; restart the service. **`unavailableRoots` above 0:** a watch root is missing or unreadable (an unmounted share); its files keep their points until it returns. **High `pendingStability`, or a long `lastReconcileDurationMs` / `lastRenewDurationMs`:** a large write burst or a slow disk; a long renewal pauses event handling for every root.
917
+ - **Linux, a file in a moved-in or renamed folder is indexed late:** changes inside folders nested in a newly created or moved-in tree, or in a directory renamed within a root, are missed until the root's subscription is renewed at the next periodic reconcile, up to `watch.reconcileIntervalMs` (15 minutes by default) late. Expected; shorten the interval if the latency matters. The files moved in with the tree are indexed at once.
918
+ - **Linux, events lost under a write burst with nothing in the log:** parcel cannot detect an inotify queue overflow. Check `sysctl fs.inotify.max_queued_events` (recommended 1048576) and `fs.inotify.max_user_watches` (the start log warns at 90%); the periodic reconcile finds what was lost (`reconcileEvents` rises).
919
+ - **Windows, files in deep folders indexed only by reconciliation:** expected. Under parcel, creating or changing a file whose path is 260 characters or more produces no change event at either `LongPathsEnabled` setting, so it is indexed at the next periodic reconcile, up to `watch.reconcileIntervalMs` late. Nothing to fix; check that `watch.reconcileIntervalMs` is not 0. (`LongPathsEnabled = 1` is still recommended for other tools; it is a host change for the owner.)
920
+ - **Windows, `lastRestartCause` set:** under parcel the service never unsubscribes on Windows (`@parcel/watcher` 2.6.0 crashes the process there), so a reload that rebuilds the watcher, a subscription error or a vanished watch root exits with code 75 and the service manager restarts it. `watch-config-changed`, `subscription-error` or `root-gone` says why, `lastRestartAt` when. If the service stays down after such an exit, its service manager is not restarting it on exit (NSSM: `AppExit Default Restart`).
921
+
922
+ ### Index Check at Start
923
+
924
+ After the start walk the service deletes the indexed documents of files gone from disk (deleted or moved while it was down). `watcher_status` reports it under `health.indexCheck` (`state`, `at`, `durationMs`, `checked`, `removed`, `skippedRoots`, `capped`):
925
+
926
+ - **`capped` above 0:** under some watch root more than 10% of the indexed documents (once more than 20 are missing) or more than 1,000 were missing from disk or filters, so nothing was deleted there; the log names the root. Run `watcher_reindex` with scope `prune` and `dryRun: true`, check the plan, then run it.
927
+ - **`state: failed`:** the index could not be listed (Qdrant down), or the service stopped first; it runs again at the next start.
928
+ - **`skippedRoots` above 0:** a root was unavailable, unwatched, gone, or its start walk failed; its deletions wait for the next start or a `prune`.
929
+ - **Files that still exist but are now ignored** keep their points: that is `prune`'s job. `watch.indexCheckOnStart: false` turns the check off.
930
+
900
931
  ---
901
932
 
902
933
  ## Helper Management
@@ -942,7 +973,7 @@ Retrieve extracted text from neighboring files in the same directory with the sa
942
973
 
943
974
  ## Enrichment
944
975
 
945
- Use `watcher_enrich` to tag documents after analysis (e.g., `reviewed: true`, project labels).
976
+ Use `watcher_set_metadata` to tag documents after analysis (e.g., `reviewed: true`, project labels).
946
977
 
947
978
  **Enrichments are durable.** Stored in a SQLite database (`<stateDir>/enrichments.sqlite`, default `stateDir` `.jeeves-metadata`), enrichments survive full reindexes. Enriching a file that is not indexed yet stores the enrichment, which applies once the file is indexed. When the watcher re-processes a file, enrichments are merged with inference rule output using composable semantics: scalar fields overwrite, array fields union+deduplicate.
948
979
 
@@ -968,56 +999,56 @@ Use `watcher_enrich` to tag documents after analysis (e.g., `reviewed: true`, pr
968
999
 
969
1000
  ## Version Tracking
970
1001
 
971
- The watcher includes built-in history and undo for your watched files. When enabled, every change to a watched file is automatically versioned. You can view the history of any file or folder, see exactly what changed between versions, retrieve old versions, and undo changes — all through the `watcher_vcs_*` tools.
1002
+ The watcher includes built-in history and undo for your watched files. When enabled, every change to a watched file is automatically versioned. You can view the history of any file or folder, see exactly what changed between versions, retrieve old versions, and undo changes — all through the version-control tools below.
972
1003
 
973
1004
  ### Tools
974
1005
 
975
1006
  | Tool | Purpose |
976
1007
  | --- | --- |
977
- | `watcher_vcs_status` | Check version tracking health: enabled state, tracked roots, remote sync status |
978
- | `watcher_vcs_history` | View change history for a file or folder, with optional date range and limit |
979
- | `watcher_vcs_show` | Retrieve the content of a file at a specific past version |
980
- | `watcher_vcs_diff` | Show what changed between two versions, or between a past version and now |
981
- | `watcher_vcs_revert` | Undo changes by restoring files to a specific past version |
982
- | `watcher_vcs_exclude` | Exclude (or re-include) paths from version tracking |
983
- | `watcher_vcs_check` | Check whether a path is excluded from version tracking and why |
1008
+ | `watcher_get_vcs_status` | Check version tracking health: enabled state, tracked roots, remote sync status |
1009
+ | `watcher_list_vcs_history` | View change history for a file or folder, with optional date range and limit |
1010
+ | `watcher_show_vcs_file` | Retrieve the content of a file at a specific past version |
1011
+ | `watcher_diff_vcs` | Show what changed between two versions, or between a past version and now |
1012
+ | `watcher_revert_vcs` | Undo changes by restoring files to a specific past version |
1013
+ | `watcher_exclude_vcs_path` | Exclude a path from version history (it stays in search), or remove the exclusion; untracks matching files |
1014
+ | `watcher_check_vcs_exclusion` | Check whether git ignores a path and why (rule, file, line) |
984
1015
 
985
1016
  ### Usage Patterns
986
1017
 
987
1018
  **View recent changes to a file:**
988
1019
 
989
1020
  ```
990
- watcher_vcs_history: glob="/data/tickets/PROJ-123.json", limit=10
1021
+ watcher_list_vcs_history: glob="/data/tickets/PROJ-123.json", limit=10
991
1022
  ```
992
1023
 
993
1024
  **See what changed between two versions:**
994
1025
 
995
1026
  ```
996
- watcher_vcs_diff: glob="/data/tickets/PROJ-123.json", commit="abc1234"
1027
+ watcher_diff_vcs: glob="/data/tickets/PROJ-123.json", commit="abc1234"
997
1028
  ```
998
1029
 
999
1030
  **Restore a file to a previous version:**
1000
1031
 
1001
1032
  ```
1002
- watcher_vcs_revert: glob="/data/tickets/PROJ-123.json", commit="abc1234"
1033
+ watcher_revert_vcs: glob="/data/tickets/PROJ-123.json", commit="abc1234"
1003
1034
  ```
1004
1035
 
1005
1036
  **View old content without restoring:**
1006
1037
 
1007
1038
  ```
1008
- watcher_vcs_show: path="/data/tickets/PROJ-123.json", commit="abc1234"
1039
+ watcher_show_vcs_file: path="/data/tickets/PROJ-123.json", commit="abc1234"
1009
1040
  ```
1010
1041
 
1011
1042
  **Exclude files that don't need versioning** (e.g., Jira issues that are read-only syncs):
1012
1043
 
1013
1044
  ```
1014
- watcher_vcs_exclude: glob="/data/tickets/**/*.json"
1045
+ watcher_exclude_vcs_path: glob="/data/tickets/**/*.json"
1015
1046
  ```
1016
1047
 
1017
1048
  **Check if a file is excluded and why:**
1018
1049
 
1019
1050
  ```
1020
- watcher_vcs_check: path="/data/tickets/PROJ-123.json"
1051
+ watcher_check_vcs_exclusion: path="/data/tickets/PROJ-123.json"
1021
1052
  ```
1022
1053
 
1023
1054
  ### Enabling Version Tracking
@@ -1028,9 +1059,17 @@ Squash retention activates by default when VCS is enabled, even without explicit
1028
1059
 
1029
1060
  ### Excluding Paths
1030
1061
 
1031
- Not all watched files need version history. For example, if Jira issues are synced read-only, there's nothing to "undo." Use `watcher_vcs_exclude` to stop tracking specific paths.
1062
+ Not all watched files need version history. For example, if Jira issues are synced read-only, there's nothing to "undo." Use `watcher_exclude_vcs_path` to stop tracking specific paths.
1063
+
1064
+ The exclusion is VCS-only: the pattern goes to the root's `.git/info/exclude`, so the files stop being versioned but stay indexed and searchable.
1032
1065
 
1033
- Exclusions are placed as close to the target as possible: if you exclude `/data/tickets/**/*.json`, the exclusion rule is written inside the `/data/tickets/` directory, not at the root. This keeps exclusions local and easy to reason about. Re-include a path by calling `watcher_vcs_exclude` with `remove: true`.
1066
+ - **Destructive.** Adding a pattern untracks the tracked files it matches, in one commit: their history ends at their last committed version (the files stay on disk). `remove: true` deletes the pattern but does not restore history; the files are versioned again from their next change.
1067
+ - **One gitignore pattern, root-anchored.** The pattern is written as `/` plus the glob relative to the root, so `/data/tickets/*.json` (root `/data`) matches only directly under `tickets/`; use `**/` for any depth. Pass `root` to give a root-relative glob (`tickets/**/*.json`, without a leading `/`: with `root`, a glob starting with `/` is a filesystem path and must lie under the root).
1068
+ - **Brackets are a character class.** `Report [draft].docx` matches `Report d.docx`; escape them to name the file: `Report \[draft\].docx`.
1069
+ - **Refused (`400`):** a newline, a leading `!` or `#`, braces, a pattern matching the whole root (`*`, `**`, `**/*`, ...), or a glob outside the given `root`.
1070
+ - **More than 100 files** to untrack need `confirm: true`; without it the tool reports `409 confirm_required` with the count and changes nothing. Check the count with the user before confirming.
1071
+ - **Busy root.** If version control is busy on the root for 30 s the tool reports `503 not_available`; retry after 30 s.
1072
+ - `watcher_check_vcs_exclusion` reports `excluded: false` for a tracked file even when a human `.gitignore` matches it (git never reports tracked paths as ignored); exclude it to untrack it.
1034
1073
 
1035
1074
  ### Score Interpretation
1036
1075
 
@@ -1046,6 +1085,19 @@ If the watcher is unreachable:
1046
1085
  - Fall back to direct `read` for known file paths
1047
1086
  - Do not retry silently in a loop
1048
1087
 
1088
+ If a tool fails with `HTTP 401` (`unauthorized`):
1089
+
1090
+ - The watcher sets `api.apiKey` and the plugin sent no key, or a different one.
1091
+ - Ask the user to set `plugins.entries.jeeves-watcher-openclaw.config.apiKey` in `openclaw.json` (or the `JEEVES_WATCHER_API_KEY` environment variable of the gateway) to the service's `api.apiKey`. The tools read it on every call, so no restart is needed.
1092
+ - Never ask for, print or guess the key. `GET /status` and `GET /openapi.json` never need it.
1093
+
1094
+ If a tool or call fails with `HTTP 503` (`not_available`) and a `Retry-After` header:
1095
+
1096
+ - `POST /metadata` (`watcher_set_metadata`), `POST /rules/reapply` and `POST /documents/delete` wait at most 10 s for a document that is being indexed, then answer `503` with `Retry-After: 10`. A single-document request changed nothing; a multi-document request kept the documents it already changed. All three are idempotent: retry the same request after 10 s.
1097
+ - `watcher_exclude_vcs_path` waits 30 s for a busy version-control root.
1098
+
1099
+ If a tool fails with `HTTP 500: {"error":"Response validation failed","code":"internal_error"}`, the watcher produced a response that doesn't match its own schema: a server bug. Report it with the request you made; retrying won't help.
1100
+
1049
1101
  If tools are unavailable (plugin not loaded in this session):
1050
1102
 
1051
1103
  - The watcher API is still accessible via direct HTTP calls
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * @module plugin/auth.test
3
- * Plugin-level bearer-key tests through the real entry point: every HTTP tool sends the key when configured and none when unset; the key never reaches logs or tool results.
3
+ * Plugin-level bearer-key tests through the real entry point: every HTTP tool (all catalog-built) sends the key when configured and none when unset; the key never reaches logs or tool results.
4
4
  */
5
5
  export {};
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @module plugin/catalogTools
3
+ * Every watcher tool, built from `WATCHER_CATALOG` by core's toolset (D38): the four standard tools (`watcher_status`, `watcher_service`, `watcher_config`, `watcher_config_apply`) and the 14 domain tools, all sending the bearer key where their entry requires it; `registerCatalogTool` registers one of them as an optional tool.
4
+ *
5
+ * @remarks
6
+ * Each tool is named `watcher_` plus its entry's verb-first name in
7
+ * snake_case (D77, `toToolName`). Parameters come from the entry's request
8
+ * schemas; descriptions keep the 0.17.0 wording where it is still true
9
+ * (`WATCHER_TOOL_SELECTION`). `watcher_search` and `watcher_scan` are
10
+ * `catalogMode: 'direct-only'`, so OpenClaw Tool Search keeps them
11
+ * model-visible: semantic search must be a direct tool or the model reaches
12
+ * for grep first. Every call runs on core's 120 s default timeout
13
+ * (`DEFAULT_FETCH_TIMEOUT_MS`, D137), the status probe on 5 s. Errors are
14
+ * reported as core's `HTTP <status>: <body>`; API-key guidance lives in the
15
+ * skill's troubleshooting section (spec #281 D31).
16
+ */
17
+ import { type JeevesComponentDescriptor, type PluginApi, type ToolDescriptor } from '@karmaniverous/jeeves';
18
+ /** `watcher_exclude_vcs_path` description (D103, D137). */
19
+ export declare const VCS_EXCLUDE_DESCRIPTION: string;
20
+ /** `watcher_check_vcs_exclusion` description (0.21.0, spec B4). */
21
+ export declare const VCS_CHECK_DESCRIPTION = "Check whether git ignores a path and why: the rule, the file holding it and its line";
22
+ /** The scope `watcher_reindex` sends when the caller omits one: 0.17.0's default (owner ruling, 2026-11-04). */
23
+ export declare const REINDEX_DEFAULT_SCOPE = "rules";
24
+ /** `watcher_reindex` description. */
25
+ export declare const REINDEX_DESCRIPTION = "Trigger a reindex of the watched files. Without scope, re-applies inference rules (scope \"rules\"); the HTTP API's own default is \"issues\".";
26
+ /**
27
+ * The 14 domain tools, in their 0.17.0 registration order, by catalog entry.
28
+ * Descriptions keep the 0.17.0 text unless it changed with the tool.
29
+ */
30
+ export declare const WATCHER_TOOL_SELECTION: {
31
+ readonly search: {
32
+ readonly description: "Semantic search over indexed documents. Supports Qdrant filters. This is a direct tool: call it directly, never through tool_call.";
33
+ readonly catalogMode: "direct-only";
34
+ };
35
+ readonly setMetadata: {
36
+ readonly description: "Set or update metadata on a document by file path or source reference.";
37
+ };
38
+ readonly validateConfig: {
39
+ readonly description: "Validate a candidate config (or current config if omitted). Optionally test file paths against the config to preview rule matching and metadata output.";
40
+ };
41
+ readonly reindex: {
42
+ readonly description: "Trigger a reindex of the watched files. Without scope, re-applies inference rules (scope \"rules\"); the HTTP API's own default is \"issues\".";
43
+ };
44
+ readonly scan: {
45
+ readonly description: "Filter-only point query without vector search. Returns metadata for points matching a Qdrant filter. Use for structural queries: file enumeration, staleness checks, delta computation. Use watcher_search for semantic/similarity queries. This is a direct tool: call it directly, never through tool_call.";
46
+ readonly catalogMode: "direct-only";
47
+ };
48
+ readonly listIssues: {
49
+ readonly description: "Get runtime processing failures. Shows the documents (file path or source reference) that failed processing and why.";
50
+ };
51
+ readonly walk: {
52
+ readonly description: "Walk watched filesystem paths with glob intersection. Returns matching file paths from all configured watch roots, applying watch.ignored and gitignore filtering.";
53
+ };
54
+ readonly getVcsStatus: {
55
+ readonly description: "Get version tracking health: enabled state, tracked roots, remote status, last activity";
56
+ };
57
+ readonly listVcsHistory: {
58
+ readonly description: "Query change history by path or glob with optional date range";
59
+ };
60
+ readonly showVcsFile: {
61
+ readonly description: "Retrieve file content at a specific version";
62
+ };
63
+ readonly diffVcs: {
64
+ readonly description: "Show what changed between two versions, or between a version and current";
65
+ };
66
+ readonly revertVcs: {
67
+ readonly description: "Undo changes by restoring files to a specific version";
68
+ };
69
+ readonly excludeVcsPath: {
70
+ readonly description: string;
71
+ };
72
+ readonly checkVcsExclusion: {
73
+ readonly description: "Check whether git ignores a path and why: the rule, the file holding it and its line";
74
+ };
75
+ };
76
+ /** Every tool the plugin registers, in registration order (0.17.0 order). */
77
+ export declare const WATCHER_TOOL_NAMES: readonly string[];
78
+ /**
79
+ * Core's toolset for the watcher: the four standard tools and the 14 domain
80
+ * tools. The base URL and the bearer key are resolved from `api` on every
81
+ * call.
82
+ *
83
+ * @param api - Plugin API (config source).
84
+ * @param component - Plugin-side component descriptor.
85
+ * @returns The tools, keyed by name.
86
+ */
87
+ export declare function createCatalogTools(api: PluginApi, component: JeevesComponentDescriptor): Map<string, ToolDescriptor>;
88
+ /**
89
+ * Register one catalog-built tool as an optional tool.
90
+ *
91
+ * @param api - Plugin API (tool registration target).
92
+ * @param catalogTools - Catalog-built tools, by name ({@link createCatalogTools}).
93
+ * @param name - Tool name.
94
+ * @throws When no tool of that name was built.
95
+ */
96
+ export declare function registerCatalogTool(api: Pick<PluginApi, 'registerTool'>, catalogTools: ReadonlyMap<string, ToolDescriptor>, name: string): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @module plugin/catalogTools.test
3
+ * Tests for the catalog-built toolset: the tool-name rule (D77), registration order, each rebuilt domain tool's request (method, path, query, body, bearer key), the destructive annotation, the 120 s timeout (D137), the error format (D31), and that every `watcher_*` name in the prompt rules and the skill is a built tool (Design 14).
4
+ */
5
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @module plugin/distSmoke.test
3
+ * Smoke test of the built plugin (`dist/index.js`, as OpenClaw loads it): it registers the 18 tools in their 0.17.0 order (under their D77 names) from its bundled watcher-core and Zod, keeps `watcher_search` and `watcher_scan` `direct-only`, and a catalog-built tool sends the bearer key. Runs after `npm run build`; in CI (which builds before testing) a missing `dist` fails. A `dist` older than its inputs (the plugin's and watcher-core's source, and watcher-core's build), or bundling a watcher-core build older than watcher-core's source, is skipped with a rebuild message instead of tested (spec D201); in CI it fails.
4
+ */
5
+ export {};
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * @remarks
5
5
  * Only watcher-specific resolution logic (API URL, API key, config root)
6
- * lives here. Core SDK types and utilities (`PluginApi`, `ToolResult`, `ok`,
7
- * `connectionFail`) should be imported directly from `@karmaniverous/jeeves`;
8
- * watcher API requests go through `requestJson` (`./http.js`).
6
+ * lives here. Core SDK types and utilities should be imported directly from
7
+ * `@karmaniverous/jeeves`; every watcher API request goes through core's
8
+ * catalog toolset (`catalogTools.ts`).
9
9
  *
10
10
  * @module plugin/helpers
11
11
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * @module plugin/manifest.test
3
- * Static checks on the published plugin manifest, package.json, and skill.
3
+ * Static checks on the published plugin manifest and package.json.
4
4
  */
5
5
  export {};
@@ -1,19 +1,19 @@
1
1
  /**
2
2
  * @module plugin/promptContext
3
- * Always-in-context watcher rules, injected via `before_prompt_build`.
3
+ * Injects the descriptor's always-in-context rules (`promptRules`, from watcher-core) via `before_prompt_build`.
4
4
  *
5
5
  * @remarks
6
6
  * Replaces the v0.x TOOLS.md `## Watcher` section that core's
7
- * `ComponentWriter` wrote on a timer. The rules are static; live numbers
8
- * (point counts, configured score thresholds, VCS roots) are served by
9
- * `watcher_status` and `watcher_config`, and detail lives in the skill.
7
+ * `ComponentWriter` wrote on a timer. Core registers nothing itself: the
8
+ * plugin passes the descriptor's `promptRules` to `registerPromptContext`.
10
9
  */
11
- import { type PluginApi } from '@karmaniverous/jeeves';
12
- /** Static watcher rules the agent must see on every turn. */
13
- export declare const WATCHER_PROMPT_CONTEXT: string;
10
+ import { type JeevesComponentDescriptor, type PluginApi } from '@karmaniverous/jeeves';
14
11
  /**
15
- * Register the watcher rules with OpenClaw's `before_prompt_build` hook.
12
+ * Register the component's prompt rules with OpenClaw's `before_prompt_build` hook.
16
13
  *
17
- * @returns `true` when the hook was registered.
14
+ * @param api - Plugin API.
15
+ * @param component - Descriptor carrying `promptRules`.
16
+ * @returns `true` when the hook was registered; `false` when the host has no
17
+ * `api.on` or the descriptor has no rules.
18
18
  */
19
- export declare function registerWatcherPromptContext(api: PluginApi): boolean;
19
+ export declare function registerWatcherPromptContext(api: PluginApi, component: Pick<JeevesComponentDescriptor, 'promptRules'>): boolean;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @module plugin/skillFiles
3
+ * Writes the watcher skill (descriptor `skill`, from watcher-core) as `SKILL.md` into the plugin's dist, the folder `openclaw.plugin.json` lists under `skills`. Used by the build (`scripts/build-skills.ts`); writes files.
4
+ */
5
+ /** Skill folder relative to the dist directory (manifest `skills[0]` minus `dist/`). */
6
+ export declare const SKILL_DIR = "skills/jeeves-watcher";
7
+ /**
8
+ * Write `skill` to `<distDir>/skills/jeeves-watcher/SKILL.md`.
9
+ *
10
+ * @param distDir - The plugin's dist directory.
11
+ * @param skill - Skill markdown (the descriptor's `skill`).
12
+ * @returns The path written.
13
+ */
14
+ export declare function writeSkill(distDir: string, skill: string): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @module test/distFreshness
3
+ * Whether a build output is older than its inputs (spec D201): the newest modification time of the files under the input directories, test files and `test` folders excluded, against the output's. Reads the file system; test-only.
4
+ */
5
+ /**
6
+ * Newest modification time of the build inputs under a directory: every
7
+ * file at any depth except test files and anything under a `test` folder.
8
+ *
9
+ * @param dir - The directory (missing: `0`).
10
+ * @returns The newest `mtimeMs`, or `0` when there is no input.
11
+ */
12
+ export declare function newestInputMtimeMs(dir: string): number;
13
+ /**
14
+ * Whether a built file is older than any input under the given directories.
15
+ *
16
+ * @param builtFile - The build output (must exist).
17
+ * @param inputDirs - Directories holding its inputs.
18
+ * @returns `true` when some input changed after the build.
19
+ */
20
+ export declare function isOlderThanInputs(builtFile: string, inputDirs: readonly string[]): boolean;