@karmaniverous/jeeves-watcher-openclaw 0.16.0-3 → 0.16.0-5

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.
package/README.md CHANGED
@@ -76,6 +76,14 @@ The plugin builds on [`@karmaniverous/jeeves`](https://www.npmjs.com/package/@ka
76
76
  | `watcher_vcs_exclude` | Exclude or re-include paths from version tracking |
77
77
  | `watcher_vcs_check` | Check whether a path is excluded from version tracking and why |
78
78
 
79
+ ### Direct tools under OpenClaw Tool Search
80
+
81
+ OpenClaw 2026.9+ **Tool Search** moves optional plugin tools out of the model-visible tool list into a hidden catalog reachable only through `tool_search`. `watcher_search` and `watcher_scan` are registered with `catalogMode: 'direct-only'`, so they stay directly visible to the model.
82
+
83
+ **Why:** semantic search only pays off if the model reaches for it _before_ falling back to grep or filesystem walks. Hidden behind a catalog lookup, it is rarely discovered and archive recall silently degrades.
84
+
85
+ **Trade-off:** these two tools always occupy space in the model's tool list, and they do **not** appear in `tool_search` results. All other `watcher_*` tools remain catalog-eligible. On OpenClaw versions without Tool Search the property is ignored.
86
+
79
87
  ## Documentation
80
88
 
81
89
  Full docs for the jeeves-watcher service and this plugin:
package/dist/index.js CHANGED
@@ -29356,6 +29356,10 @@ function warnIfConfigRootUnset(api) {
29356
29356
  /**
29357
29357
  * Wrap a tool so invocations that read `configRoot` (per `readsConfigRoot`)
29358
29358
  * resolve core before executing; all other invocations run unguarded.
29359
+ *
29360
+ * @remarks
29361
+ * Every other descriptor property (e.g. OpenClaw `catalogMode`) is carried
29362
+ * through unchanged; only `execute` is replaced.
29359
29363
  */
29360
29364
  function guardTool(tool, ensureCore, readsConfigRoot = () => true) {
29361
29365
  return {
@@ -29461,29 +29465,29 @@ function createWatcherComponent(pluginVersion) {
29461
29465
  }
29462
29466
 
29463
29467
  /**
29464
- * @module plugin/watcherTools
29465
- * Domain-specific watcher tool registrations (14 tools) for the OpenClaw plugin.
29468
+ * @module plugin/apiTool
29469
+ * Generic HTTP-backed watcher tool registration: config shape, request helpers, and ok/connectionFail wrapping.
29466
29470
  */
29467
29471
  /** Register a single API tool with standard try/catch + ok/connectionFail. */
29468
29472
  function registerApiTool(api, baseUrl, config) {
29469
- api.registerTool({
29473
+ const tool = {
29470
29474
  name: config.name,
29471
29475
  description: config.description,
29472
29476
  parameters: config.parameters,
29477
+ ...(config.catalogMode ? { catalogMode: config.catalogMode } : {}),
29473
29478
  execute: async (_id, params) => {
29474
29479
  try {
29475
29480
  const [endpoint, body] = config.buildRequest(params);
29476
29481
  const url = `${baseUrl}${endpoint}`;
29477
- const data = body !== undefined
29478
- ? await postJson(url, body)
29479
- : await fetchJson(url);
29482
+ const data = body !== undefined ? await postJson(url, body) : await fetchJson(url);
29480
29483
  return ok(data);
29481
29484
  }
29482
29485
  catch (error) {
29483
29486
  return connectionFail(error, baseUrl, PLUGIN_ID);
29484
29487
  }
29485
29488
  },
29486
- }, { optional: true });
29489
+ };
29490
+ api.registerTool(tool, { optional: true });
29487
29491
  }
29488
29492
  /** Build a query string from defined params. */
29489
29493
  function buildQuery(params, keys) {
@@ -29506,12 +29510,184 @@ function pickDefined(params, keys) {
29506
29510
  }
29507
29511
  return body;
29508
29512
  }
29509
- /** Register the 14 domain-specific watcher_* tools with the OpenClaw plugin API. */
29513
+
29514
+ /**
29515
+ * @module plugin/vcsTools
29516
+ * Version-tracking (watcher_vcs_*) tool configs for the OpenClaw plugin; each maps tool params to a watcher VCS endpoint.
29517
+ */
29518
+ /** The 7 watcher_vcs_* tool configs. */
29519
+ const vcsToolConfigs = [
29520
+ {
29521
+ name: 'watcher_vcs_status',
29522
+ description: 'Get version tracking health: enabled state, tracked roots, remote status, last activity',
29523
+ parameters: { type: 'object', properties: {} },
29524
+ buildRequest: () => [getEndpoint('vcsStatus').path],
29525
+ },
29526
+ {
29527
+ name: 'watcher_vcs_history',
29528
+ description: 'Query change history by path or glob with optional date range',
29529
+ parameters: {
29530
+ type: 'object',
29531
+ required: ['glob'],
29532
+ properties: {
29533
+ glob: {
29534
+ type: 'string',
29535
+ description: 'Path or glob pattern to query history for.',
29536
+ },
29537
+ since: {
29538
+ type: 'string',
29539
+ description: 'Start date (ISO 8601 or git date string).',
29540
+ },
29541
+ until: {
29542
+ type: 'string',
29543
+ description: 'End date (ISO 8601 or git date string).',
29544
+ },
29545
+ limit: {
29546
+ type: 'number',
29547
+ description: 'Maximum number of history entries to return.',
29548
+ },
29549
+ },
29550
+ },
29551
+ buildRequest: (params) => [
29552
+ `${getEndpoint('vcsHistory').path}${buildQuery(params, ['glob', 'since', 'until', 'limit'])}`,
29553
+ ],
29554
+ },
29555
+ {
29556
+ name: 'watcher_vcs_show',
29557
+ description: 'Retrieve file content at a specific version',
29558
+ parameters: {
29559
+ type: 'object',
29560
+ required: ['path', 'commit'],
29561
+ properties: {
29562
+ path: {
29563
+ type: 'string',
29564
+ description: 'File path to retrieve.',
29565
+ },
29566
+ commit: {
29567
+ type: 'string',
29568
+ description: 'Version identifier.',
29569
+ },
29570
+ },
29571
+ },
29572
+ buildRequest: (params) => [
29573
+ `${getEndpoint('vcsShow').path}${buildQuery(params, ['path', 'commit'])}`,
29574
+ ],
29575
+ },
29576
+ {
29577
+ name: 'watcher_vcs_diff',
29578
+ description: 'Show what changed between two versions, or between a version and current',
29579
+ parameters: {
29580
+ type: 'object',
29581
+ required: ['glob', 'commit'],
29582
+ properties: {
29583
+ glob: {
29584
+ type: 'string',
29585
+ description: 'Path or glob pattern to diff.',
29586
+ },
29587
+ commit: {
29588
+ type: 'string',
29589
+ description: 'Start version identifier.',
29590
+ },
29591
+ commitEnd: {
29592
+ type: 'string',
29593
+ description: 'End version identifier (defaults to current if omitted).',
29594
+ },
29595
+ },
29596
+ },
29597
+ buildRequest: (params) => [
29598
+ `${getEndpoint('vcsDiff').path}${buildQuery(params, ['glob', 'commit', 'commitEnd'])}`,
29599
+ ],
29600
+ },
29601
+ {
29602
+ name: 'watcher_vcs_revert',
29603
+ description: 'Undo changes by restoring files to a specific version',
29604
+ parameters: {
29605
+ type: 'object',
29606
+ required: ['glob', 'commit'],
29607
+ properties: {
29608
+ glob: {
29609
+ type: 'string',
29610
+ description: 'Path or glob pattern to revert.',
29611
+ },
29612
+ commit: {
29613
+ type: 'string',
29614
+ description: 'Version to restore files to.',
29615
+ },
29616
+ existingOnly: {
29617
+ type: 'boolean',
29618
+ description: 'When true, only revert files that currently exist (skip deleted files).',
29619
+ },
29620
+ },
29621
+ },
29622
+ buildRequest: (params) => {
29623
+ const body = pickDefined(params, ['glob', 'commit', 'existingOnly']);
29624
+ return [getEndpoint('vcsRevert').path, body];
29625
+ },
29626
+ },
29627
+ {
29628
+ name: 'watcher_vcs_exclude',
29629
+ description: 'Exclude or re-include paths from version tracking',
29630
+ parameters: {
29631
+ type: 'object',
29632
+ required: ['glob'],
29633
+ properties: {
29634
+ glob: {
29635
+ type: 'string',
29636
+ description: 'Glob pattern to exclude or re-include.',
29637
+ },
29638
+ root: {
29639
+ type: 'string',
29640
+ description: 'Tracked root to target (defaults to auto-detect).',
29641
+ },
29642
+ remove: {
29643
+ type: 'boolean',
29644
+ description: 'When true, remove the exclusion rule (re-include the path).',
29645
+ },
29646
+ },
29647
+ },
29648
+ buildRequest: (params) => {
29649
+ const body = pickDefined(params, ['glob', 'root', 'remove']);
29650
+ return [getEndpoint('vcsExclude').path, body];
29651
+ },
29652
+ },
29653
+ {
29654
+ name: 'watcher_vcs_check',
29655
+ description: 'Check whether a path is excluded from version tracking and why',
29656
+ parameters: {
29657
+ type: 'object',
29658
+ required: ['path'],
29659
+ properties: {
29660
+ path: {
29661
+ type: 'string',
29662
+ description: 'File path to check exclusion status for.',
29663
+ },
29664
+ },
29665
+ },
29666
+ buildRequest: (params) => [
29667
+ `${getEndpoint('vcsCheckExclusion').path}${buildQuery(params, ['path'])}`,
29668
+ ],
29669
+ },
29670
+ ];
29671
+
29672
+ /**
29673
+ * @module plugin/watcherTools
29674
+ * Domain-specific watcher tool registrations (14 tools) for the OpenClaw plugin.
29675
+ */
29676
+ /**
29677
+ * Register the 14 domain-specific watcher_* tools with the OpenClaw plugin API.
29678
+ *
29679
+ * @remarks
29680
+ * `watcher_search` and `watcher_scan` are `catalogMode: 'direct-only'` so
29681
+ * OpenClaw Tool Search keeps them model-visible: semantic search must be a
29682
+ * direct tool or the model reaches for grep first. All other tools remain
29683
+ * catalog-eligible.
29684
+ */
29510
29685
  function registerWatcherTools(api, baseUrl) {
29511
29686
  const tools = [
29512
29687
  {
29513
29688
  name: 'watcher_search',
29514
29689
  description: 'Semantic search over indexed documents. Supports Qdrant filters.',
29690
+ catalogMode: 'direct-only',
29515
29691
  parameters: {
29516
29692
  type: 'object',
29517
29693
  required: ['query'],
@@ -29615,6 +29791,7 @@ function registerWatcherTools(api, baseUrl) {
29615
29791
  {
29616
29792
  name: 'watcher_scan',
29617
29793
  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.',
29794
+ catalogMode: 'direct-only',
29618
29795
  parameters: {
29619
29796
  type: 'object',
29620
29797
  required: ['filter'],
@@ -29678,157 +29855,7 @@ function registerWatcherTools(api, baseUrl) {
29678
29855
  { globs: params.globs },
29679
29856
  ],
29680
29857
  },
29681
- // ── VCS tools ──────────────────────────────────────────────────────
29682
- {
29683
- name: 'watcher_vcs_status',
29684
- description: 'Get version tracking health: enabled state, tracked roots, remote status, last activity',
29685
- parameters: { type: 'object', properties: {} },
29686
- buildRequest: () => [getEndpoint('vcsStatus').path],
29687
- },
29688
- {
29689
- name: 'watcher_vcs_history',
29690
- description: 'Query change history by path or glob with optional date range',
29691
- parameters: {
29692
- type: 'object',
29693
- required: ['glob'],
29694
- properties: {
29695
- glob: {
29696
- type: 'string',
29697
- description: 'Path or glob pattern to query history for.',
29698
- },
29699
- since: {
29700
- type: 'string',
29701
- description: 'Start date (ISO 8601 or git date string).',
29702
- },
29703
- until: {
29704
- type: 'string',
29705
- description: 'End date (ISO 8601 or git date string).',
29706
- },
29707
- limit: {
29708
- type: 'number',
29709
- description: 'Maximum number of history entries to return.',
29710
- },
29711
- },
29712
- },
29713
- buildRequest: (params) => [
29714
- `${getEndpoint('vcsHistory').path}${buildQuery(params, ['glob', 'since', 'until', 'limit'])}`,
29715
- ],
29716
- },
29717
- {
29718
- name: 'watcher_vcs_show',
29719
- description: 'Retrieve file content at a specific version',
29720
- parameters: {
29721
- type: 'object',
29722
- required: ['path', 'commit'],
29723
- properties: {
29724
- path: {
29725
- type: 'string',
29726
- description: 'File path to retrieve.',
29727
- },
29728
- commit: {
29729
- type: 'string',
29730
- description: 'Version identifier.',
29731
- },
29732
- },
29733
- },
29734
- buildRequest: (params) => [
29735
- `${getEndpoint('vcsShow').path}${buildQuery(params, ['path', 'commit'])}`,
29736
- ],
29737
- },
29738
- {
29739
- name: 'watcher_vcs_diff',
29740
- description: 'Show what changed between two versions, or between a version and current',
29741
- parameters: {
29742
- type: 'object',
29743
- required: ['glob', 'commit'],
29744
- properties: {
29745
- glob: {
29746
- type: 'string',
29747
- description: 'Path or glob pattern to diff.',
29748
- },
29749
- commit: {
29750
- type: 'string',
29751
- description: 'Start version identifier.',
29752
- },
29753
- commitEnd: {
29754
- type: 'string',
29755
- description: 'End version identifier (defaults to current if omitted).',
29756
- },
29757
- },
29758
- },
29759
- buildRequest: (params) => [
29760
- `${getEndpoint('vcsDiff').path}${buildQuery(params, ['glob', 'commit', 'commitEnd'])}`,
29761
- ],
29762
- },
29763
- {
29764
- name: 'watcher_vcs_revert',
29765
- description: 'Undo changes by restoring files to a specific version',
29766
- parameters: {
29767
- type: 'object',
29768
- required: ['glob', 'commit'],
29769
- properties: {
29770
- glob: {
29771
- type: 'string',
29772
- description: 'Path or glob pattern to revert.',
29773
- },
29774
- commit: {
29775
- type: 'string',
29776
- description: 'Version to restore files to.',
29777
- },
29778
- existingOnly: {
29779
- type: 'boolean',
29780
- description: 'When true, only revert files that currently exist (skip deleted files).',
29781
- },
29782
- },
29783
- },
29784
- buildRequest: (params) => {
29785
- const body = pickDefined(params, ['glob', 'commit', 'existingOnly']);
29786
- return [getEndpoint('vcsRevert').path, body];
29787
- },
29788
- },
29789
- {
29790
- name: 'watcher_vcs_exclude',
29791
- description: 'Exclude or re-include paths from version tracking',
29792
- parameters: {
29793
- type: 'object',
29794
- required: ['glob'],
29795
- properties: {
29796
- glob: {
29797
- type: 'string',
29798
- description: 'Glob pattern to exclude or re-include.',
29799
- },
29800
- root: {
29801
- type: 'string',
29802
- description: 'Tracked root to target (defaults to auto-detect).',
29803
- },
29804
- remove: {
29805
- type: 'boolean',
29806
- description: 'When true, remove the exclusion rule (re-include the path).',
29807
- },
29808
- },
29809
- },
29810
- buildRequest: (params) => {
29811
- const body = pickDefined(params, ['glob', 'root', 'remove']);
29812
- return [getEndpoint('vcsExclude').path, body];
29813
- },
29814
- },
29815
- {
29816
- name: 'watcher_vcs_check',
29817
- description: 'Check whether a path is excluded from version tracking and why',
29818
- parameters: {
29819
- type: 'object',
29820
- required: ['path'],
29821
- properties: {
29822
- path: {
29823
- type: 'string',
29824
- description: 'File path to check exclusion status for.',
29825
- },
29826
- },
29827
- },
29828
- buildRequest: (params) => [
29829
- `${getEndpoint('vcsCheckExclusion').path}${buildQuery(params, ['path'])}`,
29830
- ],
29831
- },
29858
+ ...vcsToolConfigs,
29832
29859
  ];
29833
29860
  for (const tool of tools) {
29834
29861
  registerApiTool(api, baseUrl, tool);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @module plugin/apiTool
3
+ * Generic HTTP-backed watcher tool registration: config shape, request helpers, and ok/connectionFail wrapping.
4
+ */
5
+ import { type PluginApi, type ToolDescriptor } from '@karmaniverous/jeeves';
6
+ /**
7
+ * OpenClaw Tool Search catalog mode.
8
+ *
9
+ * @remarks
10
+ * Mirrors `AnyAgentTool['catalogMode']` in the OpenClaw plugin SDK
11
+ * (OpenClaw 2026.9+). `'direct-only'` keeps the tool model-visible instead of
12
+ * moving it into the hidden `tool_search` catalog. Declared locally because
13
+ * `ToolDescriptor` in `@karmaniverous/jeeves` does not carry it yet and
14
+ * `openclaw` is not a dependency of this package.
15
+ */
16
+ export type ToolCatalogMode = 'direct-only';
17
+ /** A tool descriptor carrying the optional OpenClaw catalog mode. */
18
+ export type CatalogToolDescriptor = ToolDescriptor & {
19
+ /** OpenClaw Tool Search catalog mode (omitted = catalog-eligible). */
20
+ catalogMode?: ToolCatalogMode;
21
+ };
22
+ /** Config for a watcher API tool. */
23
+ export interface ApiToolConfig {
24
+ name: string;
25
+ description: string;
26
+ parameters: Record<string, unknown>;
27
+ /** OpenClaw Tool Search catalog mode; omit to leave the tool catalog-eligible. */
28
+ catalogMode?: ToolCatalogMode;
29
+ /** Build the request: return [endpoint, body?]. No body = GET. */
30
+ buildRequest: (params: Record<string, unknown>) => [string, unknown?];
31
+ }
32
+ /** Register a single API tool with standard try/catch + ok/connectionFail. */
33
+ export declare function registerApiTool(api: PluginApi, baseUrl: string, config: ApiToolConfig): void;
34
+ /** Build a query string from defined params. */
35
+ export declare function buildQuery(params: Record<string, unknown>, keys: string[]): string;
36
+ /** Pick defined keys from params into a body object. */
37
+ export declare function pickDefined(params: Record<string, unknown>, keys: string[]): Record<string, unknown>;
@@ -0,0 +1 @@
1
+ export {};
@@ -29,8 +29,12 @@ export declare function warnIfConfigRootUnset(api: PluginApi): boolean;
29
29
  /**
30
30
  * Wrap a tool so invocations that read `configRoot` (per `readsConfigRoot`)
31
31
  * resolve core before executing; all other invocations run unguarded.
32
+ *
33
+ * @remarks
34
+ * Every other descriptor property (e.g. OpenClaw `catalogMode`) is carried
35
+ * through unchanged; only `execute` is replaced.
32
36
  */
33
- export declare function guardTool(tool: ToolDescriptor, ensureCore: EnsureCore, readsConfigRoot?: ConfigRootPredicate): ToolDescriptor;
37
+ export declare function guardTool<T extends ToolDescriptor>(tool: T, ensureCore: EnsureCore, readsConfigRoot?: ConfigRootPredicate): T;
34
38
  /**
35
39
  * Derive a plugin API whose `registerTool` guards the tools listed in
36
40
  * `readers` with {@link guardTool} (per call, using each tool's predicate);
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @module plugin/toolTestHarness
3
+ * Test-only helpers: a fetch stub and a plugin API that captures registered watcher tools by name. No side effects.
4
+ */
5
+ import type { ToolRegistrationOptions } from '@karmaniverous/jeeves';
6
+ import { type Mock } from 'vitest';
7
+ import type { CatalogToolDescriptor } from './apiTool.js';
8
+ /** Base URL used by tool tests. */
9
+ export declare const BASE = "http://localhost:1936";
10
+ /** Signature of the `fetch` stub. */
11
+ type FetchStub = (...args: unknown[]) => Promise<unknown>;
12
+ /** A `fetch` stub resolving to an OK JSON response carrying `data`. */
13
+ export declare function mockFetch(data?: unknown): Mock<FetchStub>;
14
+ /** A registered tool plus the options it was registered with. */
15
+ export interface CapturedTool {
16
+ tool: CatalogToolDescriptor;
17
+ options?: ToolRegistrationOptions;
18
+ }
19
+ /** Register the watcher tools against a capturing API; returns them by name, in order. */
20
+ export declare function captureTools(baseUrl?: string): Map<string, CapturedTool>;
21
+ /** Invoke a captured tool by name. */
22
+ export declare function run(tools: Map<string, CapturedTool>, name: string, params: Record<string, unknown>): Promise<import("@karmaniverous/jeeves").ToolResult>;
23
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @module plugin/vcsTools
3
+ * Version-tracking (watcher_vcs_*) tool configs for the OpenClaw plugin; each maps tool params to a watcher VCS endpoint.
4
+ */
5
+ import { type ApiToolConfig } from './apiTool.js';
6
+ /** The 7 watcher_vcs_* tool configs. */
7
+ export declare const vcsToolConfigs: ApiToolConfig[];
@@ -0,0 +1 @@
1
+ export {};
@@ -3,5 +3,13 @@
3
3
  * Domain-specific watcher tool registrations (14 tools) for the OpenClaw plugin.
4
4
  */
5
5
  import { type PluginApi } from '@karmaniverous/jeeves';
6
- /** Register the 14 domain-specific watcher_* tools with the OpenClaw plugin API. */
6
+ /**
7
+ * Register the 14 domain-specific watcher_* tools with the OpenClaw plugin API.
8
+ *
9
+ * @remarks
10
+ * `watcher_search` and `watcher_scan` are `catalogMode: 'direct-only'` so
11
+ * OpenClaw Tool Search keeps them model-visible: semantic search must be a
12
+ * direct tool or the model reaches for grep first. All other tools remain
13
+ * catalog-eligible.
14
+ */
7
15
  export declare function registerWatcherTools(api: PluginApi, baseUrl: string): void;
@@ -2,7 +2,7 @@
2
2
  "id": "jeeves-watcher-openclaw",
3
3
  "name": "Jeeves Watcher",
4
4
  "description": "Semantic search, metadata enrichment, and instance administration for a jeeves-watcher deployment.",
5
- "version": "0.16.0-3",
5
+ "version": "0.16.0-5",
6
6
  "skills": ["dist/skills/jeeves-watcher"],
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -4,8 +4,8 @@
4
4
  "url": "https://github.com/karmaniverous/jeeves-watcher/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@karmaniverous/jeeves": "0.6.0-8",
8
- "@karmaniverous/jeeves-watcher-core": "^0.2.6-1"
7
+ "@karmaniverous/jeeves": "0.6.0-9",
8
+ "@karmaniverous/jeeves-watcher-core": "^0.2.6-2"
9
9
  },
10
10
  "description": "OpenClaw plugin for jeeves-watcher — semantic search and metadata enrichment tools",
11
11
  "devDependencies": {
@@ -115,5 +115,5 @@
115
115
  },
116
116
  "type": "module",
117
117
  "types": "dist/index.d.ts",
118
- "version": "0.16.0-3"
118
+ "version": "0.16.0-5"
119
119
  }