@karmaniverous/jeeves-watcher-openclaw 0.15.5 → 0.16.0-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,10 +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
5
- recall prior context, find documents, answer questions that require searching
6
- across indexed domains, enrich document metadata, manage watcher config, or
7
- diagnose indexing issues.
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.
8
5
  ---
9
6
 
10
7
  # jeeves-watcher — Search, Discovery & Administration
@@ -13,8 +10,7 @@ description: >
13
10
 
14
11
  The watcher is an HTTP API running as a background service (typically NSSM on Windows, systemd on Linux).
15
12
 
16
- **Default port:** 1936 (configurable via `api.port` in watcher config)
17
- **Non-default port:** If the watcher runs on a different port, the user must set `plugins.entries.jeeves-watcher.config.apiUrl` in `openclaw.json`. The plugin cannot auto-discover a non-default port.
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.
18
14
 
19
15
  **Health check:** `GET /status` returns `name`, `version`, `uptime`, `status` (`healthy`/`degraded`/`unhealthy`), and a `health` object containing `collection` (point count, dimensions), `reindex` status, and `initialScan` progress.
20
16
 
@@ -34,8 +30,9 @@ curl http://127.0.0.1:<PORT>/config
34
30
  ```
35
31
 
36
32
  **Key endpoints:**
33
+
37
34
  | Endpoint | Method | Purpose |
38
- |----------|--------|---------|
35
+ | --- | --- | --- |
39
36
  | `/status` | GET | Health check, uptime, collection stats |
40
37
  | `/search` | POST | Semantic search (main query interface) |
41
38
  | `/config` | GET | Full resolved config; optional `?path=<jsonpath>` filter |
@@ -96,20 +93,9 @@ You have access to a **semantic archive** of your human's working world. Documen
96
93
 
97
94
  ## Plugin Installation
98
95
 
99
- ```
100
- npx @karmaniverous/jeeves-watcher-openclaw install
101
- ```
102
-
103
- This copies the plugin to OpenClaw's extensions directory and patches `openclaw.json` to register it.
96
+ 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.
104
97
 
105
- **Important:** Add `"jeeves-watcher-openclaw"` to the `tools.allow` array in `openclaw.json` so the agent can use the plugin's tools.
106
-
107
- Restart the gateway to load the plugin.
108
-
109
- To remove:
110
- ```
111
- npx @karmaniverous/jeeves-watcher-openclaw uninstall
112
- ```
98
+ `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.
113
99
 
114
100
  ## Quick Start (Existing Deployment)
115
101
 
@@ -127,6 +113,7 @@ When the plugin loads and the watcher service is NOT yet set up, drive the entir
127
113
  ### Step 1: Check Node.js
128
114
 
129
115
  Verify Node.js is installed and version ≥ 22:
116
+
130
117
  ```bash
131
118
  node --version
132
119
  ```
@@ -136,6 +123,7 @@ If missing or too old, guide the user to install Node.js 22+ from https://nodejs
136
123
  ### Step 2: Install Qdrant
137
124
 
138
125
  Check if Qdrant is already running:
126
+
139
127
  ```bash
140
128
  curl -s http://localhost:6333/healthz
141
129
  ```
@@ -143,6 +131,7 @@ curl -s http://localhost:6333/healthz
143
131
  If not running, install it. **Prefer native installation** (especially on cloud instances where Docker may not be available):
144
132
 
145
133
  **Linux (recommended for servers):**
134
+
146
135
  ```bash
147
136
  # Download and install binary
148
137
  curl -L https://github.com/qdrant/qdrant/releases/latest/download/qdrant-x86_64-unknown-linux-musl.tar.gz -o /tmp/qdrant.tar.gz
@@ -185,6 +174,7 @@ sudo systemctl enable --now qdrant
185
174
  ```
186
175
 
187
176
  **Windows:**
177
+
188
178
  ```powershell
189
179
  # Download from GitHub releases page
190
180
  # https://github.com/qdrant/qdrant/releases
@@ -194,11 +184,13 @@ nssm start Qdrant
194
184
  ```
195
185
 
196
186
  **Docker (fallback, if available):**
187
+
197
188
  ```bash
198
189
  docker run -d -p 6333:6333 -v qdrant_data:/qdrant/storage qdrant/qdrant
199
190
  ```
200
191
 
201
192
  After installation, verify:
193
+
202
194
  ```bash
203
195
  curl -s http://localhost:6333/healthz
204
196
  ```
@@ -206,11 +198,13 @@ curl -s http://localhost:6333/healthz
206
198
  ### Step 3: Install Watcher Service
207
199
 
208
200
  Install the watcher CLI globally:
201
+
209
202
  ```bash
210
203
  npm install -g @karmaniverous/jeeves-watcher
211
204
  ```
212
205
 
213
206
  Verify:
207
+
214
208
  ```bash
215
209
  jeeves-watcher --version
216
210
  ```
@@ -220,6 +214,7 @@ jeeves-watcher --version
220
214
  The watcher uses Google Gemini for embeddings by default (`gemini-embedding-001`, 3072 dimensions).
221
215
 
222
216
  Check for an existing API key:
217
+
223
218
  ```bash
224
219
  echo $GOOGLE_API_KEY # Linux/Mac
225
220
  echo %GOOGLE_API_KEY% # Windows cmd
@@ -227,6 +222,7 @@ $env:GOOGLE_API_KEY # PowerShell
227
222
  ```
228
223
 
229
224
  If not set, guide the user:
225
+
230
226
  1. Go to https://aistudio.google.com/apikey
231
227
  2. Create an API key (free tier supports 1,000 embedding requests/minute)
232
228
  3. Set it as a persistent environment variable:
@@ -235,6 +231,7 @@ If not set, guide the user:
235
231
  - **macOS:** Add to `~/.zshrc` or use `launchctl setenv`
236
232
 
237
233
  Verify the key works by testing a Gemini API call:
234
+
238
235
  ```bash
239
236
  curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:embedContent?key=$GOOGLE_API_KEY" \
240
237
  -H "Content-Type: application/json" \
@@ -246,6 +243,7 @@ A successful response contains an `embedding.values` array.
246
243
  ### Step 5: Author Initial Config
247
244
 
248
245
  Ask the user these questions:
246
+
249
247
  - **What directories should the watcher index?** (e.g., `~/documents`, `~/projects`, a workspace path)
250
248
  - **What types of files matter?** (helps determine file extensions for watch globs)
251
249
  - **Are there directories to exclude?** (node_modules, .git, build outputs, etc.)
@@ -257,9 +255,7 @@ Then generate a starter config file. Example minimal config:
257
255
  "description": "Personal knowledge base indexing",
258
256
  "api": { "port": 1936 },
259
257
  "watch": {
260
- "paths": [
261
- "/home/user/documents/**/*.{md,txt,json,pdf,html,docx}"
262
- ],
258
+ "paths": ["/home/user/documents/**/*.{md,txt,json,pdf,html,docx}"],
263
259
  "ignored": ["**/node_modules/**", "**/.git/**", "**/dist/**"]
264
260
  },
265
261
  "embedding": {
@@ -286,6 +282,7 @@ Then generate a starter config file. Example minimal config:
286
282
  ```
287
283
 
288
284
  Write the config to a sensible location (e.g., `~/.config/jeeves-watcher.config.json` on Linux, or alongside the user's workspace). Validate with:
285
+
289
286
  ```bash
290
287
  jeeves-watcher validate -c <config-path>
291
288
  ```
@@ -295,6 +292,7 @@ jeeves-watcher validate -c <config-path>
295
292
  **The watcher should run as a persistent service, not a foreground process.**
296
293
 
297
294
  **Linux (systemd):**
295
+
298
296
  ```bash
299
297
  sudo tee /etc/systemd/system/jeeves-watcher.service > /dev/null <<EOF
300
298
  [Unit]
@@ -316,6 +314,7 @@ sudo systemctl enable --now jeeves-watcher
316
314
  ```
317
315
 
318
316
  **Windows (NSSM):**
317
+
319
318
  ```powershell
320
319
  jeeves-watcher service install
321
320
  # Or manually:
@@ -325,6 +324,7 @@ nssm start jeeves-watcher
325
324
  ```
326
325
 
327
326
  Verify the service started:
327
+
328
328
  ```bash
329
329
  curl -s http://127.0.0.1:1936/status
330
330
  ```
@@ -332,6 +332,7 @@ curl -s http://127.0.0.1:1936/status
332
332
  ### Step 7: Verify Health
333
333
 
334
334
  Call `watcher_status` (or `curl http://127.0.0.1:1936/status`). Confirm:
335
+
335
336
  - Service is running
336
337
  - Qdrant collection exists with expected dimensions (3072)
337
338
  - Point count is increasing (initial indexing in progress)
@@ -359,47 +360,61 @@ On sessions after bootstrap is complete:
359
360
  ## Tools
360
361
 
361
362
  ### `watcher_search`
363
+
362
364
  Semantic search over indexed documents.
365
+
363
366
  - `query` (string, required) — natural language search query
364
367
  - `limit` (number, optional) — max results, default 10
365
368
  - `offset` (number, optional) — skip N results for pagination
366
369
  - `filter` (object, optional) — Qdrant filter for metadata filtering
367
370
 
368
371
  ### `watcher_enrich`
372
+
369
373
  Set or update metadata on a document.
374
+
370
375
  - `path` (string, required) — file path of the document
371
376
  - `metadata` (object, required) — key-value metadata to merge
372
377
 
373
378
  ### `watcher_status`
379
+
374
380
  Service health check. Returns `name`, `version`, `uptime`, `status`, and `health` object with collection stats, reindex status, and initial scan progress.
375
381
 
376
382
  After a service restart, the `health.initialScan` field shows scan progress:
383
+
377
384
  - `active: true` — filesystem walk in progress; `filesMatched` and `filesEnqueued` grow until chokidar completes
378
385
  - `active: false` with `completedAt`/`durationMs` — scan finished
379
386
 
380
387
  Use this to determine if the service is still initializing after a restart.
381
388
 
382
389
  ### `watcher_config`
390
+
383
391
  Query the effective runtime config via JSONPath. Returns the full resolved merged document when no path is provided.
392
+
384
393
  - `path` (string, optional) — JSONPath expression
385
394
 
386
395
  ### `watcher_validate`
396
+
387
397
  Validate config and optionally test file paths.
398
+
388
399
  - `config` (object, optional) — candidate config (partial or full). Omit to validate current config.
389
400
  - `testPaths` (string[], optional) — file paths to test against the config
390
401
 
391
402
  Partial configs merge with current config by rule name. If `config` is omitted, tests against the running config.
392
403
 
393
404
  ### `watcher_config_apply`
405
+
394
406
  Apply config changes atomically.
407
+
395
408
  - `config` (object, required) — full or partial config to apply
396
409
 
397
410
  Validates, writes to disk, and triggers configured reindex behavior. Returns validation errors if invalid. Config changes take full effect without service restart — including new/removed watch paths (filesystem watcher is rebuilt), inference rule changes, move detection settings, and gitignore filter updates.
398
411
 
399
412
  ### `watcher_reindex`
413
+
400
414
  Trigger a reindex operation. Non-prune scopes return a `plan` object showing blast area before execution begins. Live prune (non-dry-run) returns immediately without a plan.
401
415
 
402
416
  **Parameters:**
417
+
403
418
  - `scope` (string, optional) — Reindex scope. Default: `"rules"`. One of:
404
419
  - `"rules"` — Re-apply inference rules to all watched files. No re-embedding. Lightweight.
405
420
  - `"full"` — Re-extract text, re-embed, and re-apply rules for all watched files. Expensive.
@@ -410,29 +425,52 @@ Trigger a reindex operation. Non-prune scopes return a `plan` object showing bla
410
425
  - `dryRun` (boolean, optional) — When `true`, compute and return the blast area plan without executing. Returns synchronously.
411
426
 
412
427
  **Response (normal):**
428
+
413
429
  ```json
414
- { "status": "started", "scope": "rules", "plan": { "total": 148000, "toProcess": 148000, "toDelete": 0, "byRoot": { "j:/domains": 95000, "j:/config": 3000 } } }
430
+ {
431
+ "status": "started",
432
+ "scope": "rules",
433
+ "plan": {
434
+ "total": 148000,
435
+ "toProcess": 148000,
436
+ "toDelete": 0,
437
+ "byRoot": { "j:/domains": 95000, "j:/config": 3000 }
438
+ }
439
+ }
415
440
  ```
416
441
 
417
442
  **Response (dryRun):**
443
+
418
444
  ```json
419
- { "status": "dry_run", "scope": "prune", "plan": { "total": 562000, "toProcess": 0, "toDelete": 2300, "byRoot": { "j:/jeeves/node_modules": 1800, "j:/jeeves/.bridge": 500 } } }
445
+ {
446
+ "status": "dry_run",
447
+ "scope": "prune",
448
+ "plan": {
449
+ "total": 562000,
450
+ "toProcess": 0,
451
+ "toDelete": 2300,
452
+ "byRoot": { "j:/jeeves/node_modules": 1800, "j:/jeeves/.bridge": 500 }
453
+ }
454
+ }
420
455
  ```
421
456
 
422
457
  **Plan fields:**
458
+
423
459
  - `total` — Total points (prune) or files (other scopes) examined.
424
460
  - `toProcess` — Items to embed/re-apply rules (0 for prune).
425
461
  - `toDelete` — Points to delete (prune only, 0 for others).
426
462
  - `byRoot` — Counts grouped by watch root prefix. Shows where the impact concentrates.
427
463
 
428
464
  **Guidance:**
465
+
429
466
  - Use `dryRun: true` before any large-blast operation to preview impact.
430
467
  - `prune` is safe — it only deletes orphaned points, never re-embeds. Use after changing watch paths, fixing gitignore, or cleaning up stale data.
431
468
  - `prune` is NOT triggered by config-watch auto-reindex (too dangerous for auto-trigger).
432
469
 
433
-
434
470
  ### `watcher_scan`
471
+
435
472
  Filter-only point query without vector search. Use for structural queries where the question has no semantic dimension.
473
+
436
474
  - `filter` (object, required) — Qdrant filter object. Required to prevent accidental full-collection scans.
437
475
  - `limit` (number, optional) — page size, default 100, max 1000
438
476
  - `cursor` (string, optional) — opaque cursor from previous response for pagination
@@ -440,6 +478,7 @@ Filter-only point query without vector search. Use for structural queries where
440
478
  - `countOnly` (boolean, optional) — if true, return `{ count }` instead of points
441
479
 
442
480
  **Response (normal):**
481
+
443
482
  ```json
444
483
  {
445
484
  "points": [{ "id": "uuid", "payload": { ... } }],
@@ -448,17 +487,20 @@ Filter-only point query without vector search. Use for structural queries where
448
487
  ```
449
488
 
450
489
  **Response (countOnly):**
490
+
451
491
  ```json
452
492
  { "count": 1234 }
453
493
  ```
454
494
 
455
495
  **Key differences from `watcher_search`:**
496
+
456
497
  - No `query` parameter — does NOT use embeddings
457
498
  - No `score` field — results are unranked filter matches
458
499
  - Cursor-based pagination (not offset-based)
459
500
  - Zero cost per call beyond Qdrant's filtered scroll
460
501
 
461
502
  **Pagination pattern:**
503
+
462
504
  ```
463
505
  let cursor = undefined;
464
506
  do {
@@ -469,19 +511,25 @@ do {
469
511
  ```
470
512
 
471
513
  ### `watcher_service`
514
+
472
515
  Manage the watcher background service (install, uninstall, start, stop, restart, check status).
516
+
473
517
  - `action` (string, required) — one of: `install`, `uninstall`, `start`, `stop`, `restart`, `status`
474
518
 
475
519
  Returns the service manager's response. On Windows uses NSSM, on Linux uses systemd.
476
520
 
477
521
  ### `watcher_issues`
522
+
478
523
  Get runtime embedding failures. Returns `{ filePath: IssueRecord }` showing files that failed and why.
479
524
 
480
525
  ### `watcher_walk`
526
+
481
527
  Walk watched filesystem paths with glob intersection. Returns matching file paths from all configured watch roots.
528
+
482
529
  - `globs` (string[], required) — glob patterns to intersect with watch paths
483
530
 
484
531
  **Response:**
532
+
485
533
  ```json
486
534
  {
487
535
  "paths": ["j:/domains/foo/.meta/meta.json", "j:/domains/bar/.meta/meta.json"],
@@ -491,11 +539,13 @@ Walk watched filesystem paths with glob intersection. Returns matching file path
491
539
  ```
492
540
 
493
541
  **Use cases:**
542
+
494
543
  - Discover files matching a pattern across all watched directories (e.g., `["**/.meta/meta.json"]`)
495
544
  - Enumerate files before rule registration to understand scope
496
545
  - Find files that aren't yet indexed (no Qdrant dependency — works even before first embedding)
497
546
 
498
547
  **Key differences from `watcher_scan`:**
548
+
499
549
  - Walks the actual filesystem, not the Qdrant index
500
550
  - No embedding or indexing required — works immediately after service start
501
551
  - Returns file paths only (no metadata, no vectors)
@@ -506,7 +556,7 @@ Walk watched filesystem paths with glob intersection. Returns matching file path
506
556
  **Decision rule:** If the query has no semantic/natural-language dimension, use `watcher_scan`. If you need meaning-based similarity, use `watcher_search`.
507
557
 
508
558
  | Use `watcher_scan` | Use `watcher_search` |
509
- |---------------------|----------------------|
559
+ | --- | --- |
510
560
  | "List all files in domain X" | "Find documents about authentication" |
511
561
  | "Files modified after timestamp T" | "What discusses rate limiting?" |
512
562
  | "Enumerate paths under prefix P" | "Prior conversations about deployment" |
@@ -516,21 +566,25 @@ Walk watched filesystem paths with glob intersection. Returns matching file path
516
566
  **Scan-specific filter examples:**
517
567
 
518
568
  **Domain enumeration:**
569
+
519
570
  ```json
520
571
  { "must": [{ "key": "domains", "match": { "value": "email" } }] }
521
572
  ```
522
573
 
523
574
  **Modified after timestamp:**
575
+
524
576
  ```json
525
577
  { "must": [{ "key": "modified_at", "range": { "gte": 1772800000 } }] }
526
578
  ```
527
579
 
528
580
  **Path prefix matching:**
581
+
529
582
  ```json
530
583
  { "must": [{ "key": "file_path", "match": { "text": "j:/domains/jira" } }] }
531
584
  ```
532
585
 
533
586
  **Count files in a domain (no point data transferred):**
587
+
534
588
  ```
535
589
  watcher_scan: filter={"must":[{"key":"domains","match":{"value":"github"}}]}, countOnly=true
536
590
  ```
@@ -544,16 +598,19 @@ Filters use Qdrant's native JSON filter format, passed as the `filter` parameter
544
598
  ### Basic Patterns
545
599
 
546
600
  **Match exact value:**
601
+
547
602
  ```json
548
603
  { "must": [{ "key": "domain", "match": { "value": "email" } }] }
549
604
  ```
550
605
 
551
606
  **Match text (full-text search within field):**
607
+
552
608
  ```json
553
609
  { "must": [{ "key": "chunk_text", "match": { "text": "authentication" } }] }
554
610
  ```
555
611
 
556
612
  **Combine conditions (AND):**
613
+
557
614
  ```json
558
615
  {
559
616
  "must": [
@@ -564,6 +621,7 @@ Filters use Qdrant's native JSON filter format, passed as the `filter` parameter
564
621
  ```
565
622
 
566
623
  **Exclude (NOT):**
624
+
567
625
  ```json
568
626
  {
569
627
  "must_not": [{ "key": "domain", "match": { "value": "repos" } }]
@@ -571,6 +629,7 @@ Filters use Qdrant's native JSON filter format, passed as the `filter` parameter
571
629
  ```
572
630
 
573
631
  **Any of (OR):**
632
+
574
633
  ```json
575
634
  {
576
635
  "should": [
@@ -581,6 +640,7 @@ Filters use Qdrant's native JSON filter format, passed as the `filter` parameter
581
640
  ```
582
641
 
583
642
  **Nested (combine AND + NOT):**
643
+
584
644
  ```json
585
645
  {
586
646
  "must": [{ "key": "domain", "match": { "value": "jira" } }],
@@ -589,6 +649,7 @@ Filters use Qdrant's native JSON filter format, passed as the `filter` parameter
589
649
  ```
590
650
 
591
651
  ### Key Differences
652
+
592
653
  - `match.value` — exact match (case-sensitive, for keyword fields like `domain`, `status`)
593
654
  - `match.text` — full-text match (for text fields like `chunk_text`)
594
655
 
@@ -597,7 +658,7 @@ Filters use Qdrant's native JSON filter format, passed as the `filter` parameter
597
658
  Each result from `watcher_search` contains:
598
659
 
599
660
  | Field | Type | Description |
600
- |-------|------|-------------|
661
+ | --- | --- | --- |
601
662
  | `id` | string | Qdrant point ID |
602
663
  | `score` | number | Similarity score (0-1, higher = more relevant) |
603
664
  | `payload.file_path` | string | Source file path |
@@ -614,6 +675,7 @@ Additional metadata fields depend on the deployment's inference rules (e.g., `do
614
675
  Identify relevant rule(s) from the orientation model, then retrieve their schemas:
615
676
 
616
677
  **Retrieve complete schema for a rule:**
678
+
617
679
  ```
618
680
  watcher_config: path="$.inferenceRules[?(@.name=='jira-issue')].schema"
619
681
  resolve=["files","globals"]
@@ -622,6 +684,7 @@ watcher_config: path="$.inferenceRules[?(@.name=='jira-issue')].schema"
622
684
  Returns the fully merged schema with properties, types, `set` provenance, `uiHint`, `enum`, etc.
623
685
 
624
686
  **For select/multiselect fields without `enum` in schema:**
687
+
625
688
  ```
626
689
  watcher_config: path="$.inferenceRules[?(@.name=='jira-issue')].values.status"
627
690
  ```
@@ -637,14 +700,14 @@ Retrieves valid filter values from the runtime values index (distinct values acc
637
700
  Use `uiHint` to determine filter construction strategy. **This table is explicit, not intuited:**
638
701
 
639
702
  | `uiHint` | Qdrant filter | Notes |
640
- |----------|--------------|-------|
703
+ | --- | --- | --- |
641
704
  | `text` | `{ "key": "<field>", "match": { "text": "<value>" } }` | Substring/keyword match |
642
705
  | `select` | `{ "key": "<field>", "match": { "value": "<enum_value>" } }` | Exact match; use `enum` values from schema or runtime values index |
643
706
  | `multiselect` | `{ "key": "<field>", "match": { "value": "<enum_value>" } }` | Any-element match on array field; use `enum` or runtime values index |
644
707
  | `date` | `{ "key": "<field>", "range": { "gte": <unix_ts>, "lt": <unix_ts> } }` | Range filter against integer fields holding Unix timestamps (seconds). Source dates should be normalized in config via `{{toUnix ...}}` in `set` expressions. | for open-ended ranges (e.g., "after January" → `gte` only) |
645
708
  | `number` | `{ "key": "<field>", "range": { "gte": <n>, "lte": <n> } }` | Either bound optional for open-ended ranges |
646
709
  | `check` | `{ "key": "<field>", "match": { "value": true } }` | Boolean match |
647
- | *(absent)* | Do not use in filters | Internal bookkeeping field, not intended for search |
710
+ | _(absent)_ | Do not use in filters | Internal bookkeeping field, not intended for search |
648
711
 
649
712
  **Fallback:** If a `select`/`multiselect` field has neither `enum` in schema nor values in the index, treat it as `text` (substring match instead of exact match).
650
713
 
@@ -655,12 +718,13 @@ Use `uiHint` to determine filter construction strategy. **This table is explicit
655
718
  Compose individual field conditions into complex queries using three combinators:
656
719
 
657
720
  | Combinator | Semantics | Use case |
658
- |-----------|-----------|----------|
721
+ | --- | --- | --- |
659
722
  | `must` | AND — all conditions required | Intersecting constraints (domain + date range + assignee) |
660
723
  | `should` | OR — at least one must match | Alternative values, fuzzy criteria ("assigned to X or Y") |
661
724
  | `must_not` | Exclusion — any match triggers exclude | Filtering out noise (exclude Done, exclude codebase domain) |
662
725
 
663
726
  **Combinators nest arbitrarily for complex boolean logic:**
727
+
664
728
  ```json
665
729
  {
666
730
  "must": [
@@ -671,9 +735,7 @@ Compose individual field conditions into complex queries using three combinators
671
735
  { "key": "assignee", "match": { "value": "Jane Doe" } },
672
736
  { "key": "assignee", "match": { "value": null } }
673
737
  ],
674
- "must_not": [
675
- { "key": "status", "match": { "value": "Done" } }
676
- ]
738
+ "must_not": [{ "key": "status", "match": { "value": "Done" } }]
677
739
  }
678
740
  ```
679
741
 
@@ -686,6 +748,7 @@ A consuming UI will necessarily compose simple single-field filters. The assista
686
748
  **Plain semantic search is valid and often sufficient.** Not every query needs metadata filters. When the user's question is broad or exploratory, a natural language query with no filter object is the right starting point. Add filters to narrow, not as a default.
687
749
 
688
750
  **Result limit guidance:**
751
+
689
752
  - Default: 10 results
690
753
  - Broad discovery / exploratory: 20–30, apply score threshold cutoff from config
691
754
  - Targeted retrieval with tight filters: 5
@@ -696,22 +759,28 @@ A consuming UI will necessarily compose simple single-field filters. The assista
696
759
  ## Post-Processing Guidance
697
760
 
698
761
  ### Score Interpretation
762
+
699
763
  Use `scoreThresholds` from config (queried during orientation). Values are deployment-specific, constrained to [-1, 1]:
764
+
700
765
  - `strong` — minimum score for a strong match. **Action:** High confidence. Use these results directly.
701
766
  - `relevant` — minimum score for relevance. **Action:** Likely useful but verify context before relying on them.
702
767
  - `noise` — maximum score below which results are noise. **Action:** Discard. If all results fall below this threshold, broaden your query or try different terms.
703
768
 
704
769
  ### Chunk Grouping
770
+
705
771
  Multiple results with the same `file_path` are chunks of one document. Read the full file for complete context.
706
772
 
707
773
  ### Schema Lookup
774
+
708
775
  Use `matched_rules` on results to look up applicable schemas for metadata interpretation:
776
+
709
777
  ```
710
778
  watcher_config: path="$.inferenceRules[?(@.name=='jira-issue')].schema"
711
779
  resolve=["files","globals"]
712
780
  ```
713
781
 
714
782
  ### Full Context
783
+
715
784
  Search gives you chunks; use `read` with `file_path` for the complete document.
716
785
 
717
786
  ---
@@ -719,11 +788,13 @@ Search gives you chunks; use `read` with `file_path` for the complete document.
719
788
  ## Path Testing
720
789
 
721
790
  When uncertain whether a file is indexed, use the path test endpoint:
791
+
722
792
  ```
723
793
  watcher_config: path="$.inferenceRules[?(@.name=='<rule>')].match"
724
794
  ```
725
795
 
726
796
  Or check if a specific path would match:
797
+
727
798
  - Returns matching rule names and watch scope status
728
799
  - Empty `rules` array means no inference rules match
729
800
  - `watched: false` means the path falls outside watch paths or is excluded by ignore patterns
@@ -733,7 +804,9 @@ Or check if a specific path would match:
733
804
  ## Config Authoring
734
805
 
735
806
  ### Rule Structure
807
+
736
808
  Each inference rule has:
809
+
737
810
  - `name` (required) — unique identifier
738
811
  - `description` (required) — human-readable purpose
739
812
  - `match` — JSON Schema with picomatch glob for path matching
@@ -742,6 +815,7 @@ Each inference rule has:
742
815
  - `template` (optional) — named Handlebars template
743
816
 
744
817
  ### Config Workflow
818
+
745
819
  1. Edit config (or build partial config object)
746
820
  2. Validate: `watcher_validate` with optional `testPaths` for dry-run preview
747
821
  3. Apply: `watcher_config_apply` — validates, writes, triggers reindex
@@ -754,6 +828,7 @@ Reindex operations process files concurrently using `reindex.concurrency` (defau
754
828
  Progress is reported via `watcher_status` (`reindex.filesProcessed` / `reindex.totalFiles`).
755
829
 
756
830
  ### When to Reindex
831
+
757
832
  - **Rules scope** (`"rules"`): Changed rule matching patterns, set expressions, schema mappings. No re-embedding needed.
758
833
  - **Full scope** (`"full"`): Changed embedding config, added watch paths, broad schema restructuring. Re-embeds everything.
759
834
  - **Issues scope** (`"issues"`): After fixing the root cause of embedding failures (permissions, encoding, file format). Re-processes only failed files.
@@ -765,12 +840,14 @@ Progress is reported via `watcher_status` (`reindex.filesProcessed` / `reindex.t
765
840
  ## Diagnostics
766
841
 
767
842
  ### Escalation Path
843
+
768
844
  1. `watcher_status` — is the service healthy? Is a reindex running? Is the initial scan still active?
769
845
  2. `watcher_issues` — what files are failing and why?
770
846
  3. `watcher_config` with `$.issues` — same data via JSONPath
771
847
  4. Check logs at the configured log path
772
848
 
773
849
  ### Error Categories
850
+
774
851
  - `type_collision` — metadata field type mismatch during extraction (includes `property`, `rules[]`, `types[]`)
775
852
  - `interpolation` / `interpolation_error` — template/set expression failed to resolve (includes `property`, `rule`)
776
853
  - `read_failure` — file couldn't be read (permissions, encoding)
@@ -785,6 +862,7 @@ Progress is reported via `watcher_status` (`reindex.filesProcessed` / `reindex.t
785
862
  Helpers use namespace prefixing: config key becomes prefix. A helper named `slack` exports `slack_extractParticipants`.
786
863
 
787
864
  Enumerate loaded helpers:
865
+
788
866
  ```
789
867
  $.mapHelpers — JsonMap helper namespaces with exports
790
868
  $.templateHelpers — Handlebars helper namespaces with exports
@@ -795,9 +873,11 @@ $.templateHelpers — Handlebars helper namespaces with exports
795
873
  The following helpers are available in every JsonMap `lib` context without any helper config:
796
874
 
797
875
  #### `fetchSiblings(filePath, options?)`
876
+
798
877
  Retrieve extracted text from neighboring files in the same directory. Useful for contextual embedding — e.g., injecting surrounding email messages into a thread member's embedding for better semantic search.
799
878
 
800
879
  **Parameters:**
880
+
801
881
  - `filePath` (string) — the current file path (typically `$file_path`)
802
882
  - `options` (object, optional):
803
883
  - `before` (number, default 3) — number of preceding siblings to include
@@ -807,8 +887,14 @@ Retrieve extracted text from neighboring files in the same directory. Useful for
807
887
  **Returns:** `string[]` — extracted text from sibling files, in sort order. Files that fail extraction are silently skipped.
808
888
 
809
889
  **Example** (in a JsonMap `$set` expression):
890
+
810
891
  ```json
811
- { "context": { "$fn": "fetchSiblings", "$args": ["$file_path", { "before": 2, "after": 1 }] } }
892
+ {
893
+ "context": {
894
+ "$fn": "fetchSiblings",
895
+ "$args": ["$file_path", { "before": 2, "after": 1 }]
896
+ }
897
+ }
812
898
  ```
813
899
 
814
900
  ---
@@ -820,6 +906,7 @@ Use `watcher_enrich` to tag documents after analysis (e.g., `reviewed: true`, pr
820
906
  **Enrichments are durable.** Stored in a SQLite database (`<stateDir>/enrichments.sqlite`), enrichments survive full reindexes. When the watcher re-processes a file, enrichments are merged with inference rule output using composable semantics: scalar fields overwrite, array fields union+deduplicate.
821
907
 
822
908
  **Metadata is validated against the file's matched rule schemas.** Validation errors return structured messages:
909
+
823
910
  ```json
824
911
  {
825
912
  "error": "Validation failed",
@@ -844,7 +931,7 @@ The watcher includes built-in history and undo for your watched files. When enab
844
931
  ### Tools
845
932
 
846
933
  | Tool | Purpose |
847
- |------|---------|
934
+ | --- | --- |
848
935
  | `watcher_vcs_status` | Check version tracking health: enabled state, tracked roots, remote sync status |
849
936
  | `watcher_vcs_history` | View change history for a file or folder, with optional date range and limit |
850
937
  | `watcher_vcs_show` | Retrieve the content of a file at a specific past version |
@@ -856,31 +943,37 @@ The watcher includes built-in history and undo for your watched files. When enab
856
943
  ### Usage Patterns
857
944
 
858
945
  **View recent changes to a file:**
946
+
859
947
  ```
860
948
  watcher_vcs_history: glob="J:/domains/jira/PROJ-123.json", limit=10
861
949
  ```
862
950
 
863
951
  **See what changed between two versions:**
952
+
864
953
  ```
865
954
  watcher_vcs_diff: glob="J:/domains/jira/PROJ-123.json", commit="abc1234"
866
955
  ```
867
956
 
868
957
  **Restore a file to a previous version:**
958
+
869
959
  ```
870
960
  watcher_vcs_revert: glob="J:/domains/jira/PROJ-123.json", commit="abc1234"
871
961
  ```
872
962
 
873
963
  **View old content without restoring:**
964
+
874
965
  ```
875
966
  watcher_vcs_show: path="J:/domains/jira/PROJ-123.json", commit="abc1234"
876
967
  ```
877
968
 
878
969
  **Exclude files that don't need versioning** (e.g., Jira issues that are read-only syncs):
970
+
879
971
  ```
880
972
  watcher_vcs_exclude: glob="J:/domains/jira/**/*.json"
881
973
  ```
882
974
 
883
975
  **Check if a file is excluded and why:**
976
+
884
977
  ```
885
978
  watcher_vcs_check: path="J:/domains/jira/PROJ-123.json"
886
979
  ```
@@ -906,16 +999,19 @@ VCS tools do not use vector search. There are no relevance scores to interpret
906
999
  ## Error Handling
907
1000
 
908
1001
  If the watcher is unreachable:
1002
+
909
1003
  - Inform the user that semantic search is temporarily unavailable
910
1004
  - Fall back to direct `read` for known file paths
911
1005
  - Do not retry silently in a loop
912
1006
 
913
1007
  If tools are unavailable (plugin not loaded in this session):
1008
+
914
1009
  - The watcher API is still accessible via direct HTTP calls
915
1010
  - Use `exec` to call the endpoints listed in Service Architecture
916
1011
  - Default: `http://127.0.0.1:1936`
917
1012
 
918
1013
  **CLI Fallbacks:**
1014
+
919
1015
  - `jeeves-watcher status` — check if the service is running
920
1016
  - `jeeves-watcher validate` — validate config from CLI
921
1017
  - Restart via NSSM (Windows) or systemctl (Linux)
@@ -926,7 +1022,3 @@ If tools are unavailable (plugin not loaded in this session):
926
1022
 
927
1023
  - [JSONPath Plus documentation](https://www.npmjs.com/package/jsonpath-plus) for JSONPath syntax
928
1024
  - [Qdrant filtering documentation](https://qdrant.tech/documentation/concepts/filtering/) for advanced query patterns and search response format
929
-
930
-
931
-
932
-