@karmaniverous/jeeves-meta-openclaw 0.1.2 → 0.1.3

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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { HttpWatcherClient, paginatedScan, buildMetaFilter, isLocked, normalizePath, discoverMetas, buildOwnershipTree, findNode, actualStaleness, computeEffectiveStaleness, selectCandidate, filterInScope, computeStructureHash, readLatestArchive, hasSteerChanged, isArchitectTriggered, loadSynthConfig } from '@karmaniverous/jeeves-meta';
1
+ import { HttpWatcherClient, listMetas, normalizePath, findNode, computeEffectiveStaleness, selectCandidate, paginatedScan, filterInScope, computeStructureHash, readLatestArchive, hasSteerChanged, isArchitectTriggered, actualStaleness, loadSynthConfig } from '@karmaniverous/jeeves-meta';
2
2
  import { readFile, writeFile } from 'node:fs/promises';
3
3
  import { resolve } from 'node:path';
4
4
 
@@ -255,6 +255,8 @@ function registerSynthTools(api) {
255
255
  };
256
256
  /** Derive watcherUrl from loaded config. */
257
257
  const getWatcherUrl = () => getConfig().watcherUrl;
258
+ /** Create a watcher client. */
259
+ const getWatcher = () => new HttpWatcherClient({ baseUrl: getWatcherUrl() });
258
260
  // ─── synth_list ──────────────────────────────────────────────
259
261
  api.registerTool({
260
262
  name: 'synth_list',
@@ -286,34 +288,31 @@ function registerSynthTools(api) {
286
288
  execute: async (_id, params) => {
287
289
  try {
288
290
  const pathPrefix = params.pathPrefix;
289
- const watcher = new HttpWatcherClient({ baseUrl: getWatcherUrl() });
290
291
  const config = getConfig();
291
- // Query watcher for synth entity points using configured filter
292
- const scanFiles = await paginatedScan(watcher, {
293
- ...(pathPrefix ? { pathPrefix } : {}),
294
- filter: buildMetaFilter(config),
295
- fields: [
296
- 'file_path',
297
- 'synth_depth',
298
- 'synth_emphasis',
299
- 'synth_architect_tokens',
300
- 'synth_builder_tokens',
301
- 'synth_critic_tokens',
302
- 'has_error',
303
- 'generated_at_unix',
304
- 'synth_error_step',
305
- ],
306
- });
307
- // Deduplicate by file_path (watcher chunks files into multiple points)
308
- const seenPaths = new Set();
309
- const dedupedFiles = scanFiles.filter((sf) => {
310
- const fp = sf.file_path;
311
- if (seenPaths.has(fp))
312
- return false;
313
- seenPaths.add(fp);
314
- return true;
315
- });
316
- const entities = [];
292
+ const result = await listMetas(config, getWatcher());
293
+ // Apply path prefix filter
294
+ let entries = result.entries;
295
+ if (pathPrefix) {
296
+ entries = entries.filter((e) => e.path.includes(pathPrefix));
297
+ }
298
+ // Apply structured filter
299
+ const filter = params.filter;
300
+ if (filter) {
301
+ entries = entries.filter((e) => {
302
+ if (filter.hasError !== undefined && e.hasError !== filter.hasError)
303
+ return false;
304
+ if (filter.neverSynthesized !== undefined &&
305
+ (e.stalenessSeconds === Infinity) !== filter.neverSynthesized)
306
+ return false;
307
+ if (filter.locked !== undefined && e.locked !== filter.locked)
308
+ return false;
309
+ if (typeof filter.staleHours === 'number' &&
310
+ e.stalenessSeconds < filter.staleHours * 3600)
311
+ return false;
312
+ return true;
313
+ });
314
+ }
315
+ // Recompute summary for filtered entries
317
316
  let staleCount = 0;
318
317
  let errorCount = 0;
319
318
  let lockedCount = 0;
@@ -325,112 +324,69 @@ function registerSynthTools(api) {
325
324
  let lastSynthAt = null;
326
325
  let stalestPath = null;
327
326
  let stalestEffective = -1;
328
- for (const sf of dedupedFiles) {
329
- const filePath = sf.file_path;
330
- const depth = typeof sf['synth_depth'] === 'number' ? sf['synth_depth'] : 0;
331
- const emphasis = typeof sf['synth_emphasis'] === 'number' ? sf['synth_emphasis'] : 1;
332
- const hasError = sf['has_error'] === true || sf['has_error'] === 'true';
333
- const archTokens = typeof sf['synth_architect_tokens'] === 'number'
334
- ? sf['synth_architect_tokens']
335
- : 0;
336
- const buildTokens = typeof sf['synth_builder_tokens'] === 'number'
337
- ? sf['synth_builder_tokens']
338
- : 0;
339
- const critTokens = typeof sf['synth_critic_tokens'] === 'number'
340
- ? sf['synth_critic_tokens']
341
- : 0;
342
- const genAtUnix = typeof sf['generated_at_unix'] === 'number'
343
- ? sf['generated_at_unix']
344
- : 0;
345
- const locked = isLocked(normalizePath(filePath));
346
- const neverSynth = genAtUnix === 0;
347
- // Compute staleness from generated_at_unix
348
- let stalenessSeconds;
349
- if (neverSynth) {
350
- stalenessSeconds = Infinity;
351
- }
352
- else {
353
- stalenessSeconds = Math.floor(Date.now() / 1000) - genAtUnix;
354
- if (stalenessSeconds < 0)
355
- stalenessSeconds = 0;
356
- }
357
- // Apply structured filter
358
- const filter = params.filter;
359
- if (filter) {
360
- if (filter.hasError !== undefined && hasError !== filter.hasError)
361
- continue;
362
- if (filter.neverSynthesized !== undefined &&
363
- neverSynth !== filter.neverSynthesized)
364
- continue;
365
- if (filter.locked !== undefined && locked !== filter.locked)
366
- continue;
367
- if (typeof filter.staleHours === 'number' &&
368
- stalenessSeconds < filter.staleHours * 3600)
369
- continue;
370
- }
371
- if (stalenessSeconds > 0)
327
+ for (const e of entries) {
328
+ if (e.stalenessSeconds > 0)
372
329
  staleCount++;
373
- if (hasError)
330
+ if (e.hasError)
374
331
  errorCount++;
375
- if (locked)
332
+ if (e.locked)
376
333
  lockedCount++;
377
- if (neverSynth)
334
+ if (e.stalenessSeconds === Infinity)
378
335
  neverSynthesizedCount++;
379
- if (archTokens > 0)
380
- totalArchTokens += archTokens;
381
- if (buildTokens > 0)
382
- totalBuilderTokens += buildTokens;
383
- if (critTokens > 0)
384
- totalCriticTokens += critTokens;
385
- const genAtIso = genAtUnix > 0 ? new Date(genAtUnix * 1000).toISOString() : null;
386
- if (genAtIso && (!lastSynthAt || genAtIso > lastSynthAt)) {
387
- lastSynthAt = genAtIso;
388
- lastSynthPath = filePath;
336
+ if (e.architectTokens)
337
+ totalArchTokens += e.architectTokens;
338
+ if (e.builderTokens)
339
+ totalBuilderTokens += e.builderTokens;
340
+ if (e.criticTokens)
341
+ totalCriticTokens += e.criticTokens;
342
+ if (e.lastSynthesized &&
343
+ (!lastSynthAt || e.lastSynthesized > lastSynthAt)) {
344
+ lastSynthAt = e.lastSynthesized;
345
+ lastSynthPath = e.path;
389
346
  }
390
- // Effective staleness for stalest computation
391
- const depthFactor = Math.pow(1 + config.depthWeight, depth);
392
- const effectiveStaleness = (stalenessSeconds === Infinity
347
+ const depthFactor = Math.pow(1 + config.depthWeight, e.depth);
348
+ const effectiveStaleness = (e.stalenessSeconds === Infinity
393
349
  ? Number.MAX_SAFE_INTEGER
394
- : stalenessSeconds) *
350
+ : e.stalenessSeconds) *
395
351
  depthFactor *
396
- emphasis;
352
+ e.emphasis;
397
353
  if (effectiveStaleness > stalestEffective) {
398
354
  stalestEffective = effectiveStaleness;
399
- stalestPath = filePath;
355
+ stalestPath = e.path;
400
356
  }
401
- // Derive meta path from file_path (strip /meta.json)
402
- const metaPath = filePath.replace(/\/meta\.json$/, '');
403
- const fields = params.fields;
404
- const raw = {
405
- path: metaPath,
406
- depth,
407
- emphasis,
408
- stalenessSeconds: stalenessSeconds === Infinity
409
- ? 'never-synthesized'
410
- : Math.round(stalenessSeconds),
411
- lastSynthesized: genAtIso,
412
- hasError,
413
- locked,
414
- architectTokens: archTokens > 0 ? archTokens : null,
415
- builderTokens: buildTokens > 0 ? buildTokens : null,
416
- criticTokens: critTokens > 0 ? critTokens : null,
417
- children: 0,
357
+ }
358
+ // Project fields
359
+ const fields = params.fields;
360
+ const items = entries.map((e) => {
361
+ const stalenessDisplay = e.stalenessSeconds === Infinity
362
+ ? 'never-synthesized'
363
+ : Math.round(e.stalenessSeconds);
364
+ const display = {
365
+ path: e.path,
366
+ depth: e.depth,
367
+ emphasis: e.emphasis,
368
+ stalenessSeconds: stalenessDisplay,
369
+ lastSynthesized: e.lastSynthesized,
370
+ hasError: e.hasError,
371
+ locked: e.locked,
372
+ architectTokens: e.architectTokens,
373
+ builderTokens: e.builderTokens,
374
+ criticTokens: e.criticTokens,
375
+ children: e.children,
418
376
  };
419
377
  if (fields) {
420
378
  const projected = {};
421
379
  for (const f of fields) {
422
- if (f in raw)
423
- projected[f] = raw[f];
380
+ if (f in display)
381
+ projected[f] = display[f];
424
382
  }
425
- entities.push(projected);
426
- }
427
- else {
428
- entities.push(raw);
383
+ return projected;
429
384
  }
430
- }
385
+ return display;
386
+ });
431
387
  return ok({
432
388
  summary: {
433
- total: entities.length,
389
+ total: entries.length,
434
390
  stale: staleCount,
435
391
  errors: errorCount,
436
392
  locked: lockedCount,
@@ -444,7 +400,11 @@ function registerSynthTools(api) {
444
400
  lastSynthesizedPath: lastSynthPath,
445
401
  lastSynthesizedAt: lastSynthAt,
446
402
  },
447
- items: entities.sort((a, b) => String(a.path ?? '').localeCompare(String(b.path ?? ''))),
403
+ items: items.sort((a, b) => {
404
+ const ap = typeof a.path === 'string' ? a.path : '';
405
+ const bp = typeof b.path === 'string' ? b.path : '';
406
+ return ap.localeCompare(bp);
407
+ }),
448
408
  });
449
409
  }
450
410
  catch (error) {
@@ -487,10 +447,8 @@ function registerSynthTools(api) {
487
447
  '_feedback',
488
448
  ]);
489
449
  const fields = params.fields;
490
- const watcher = new HttpWatcherClient({ baseUrl: getWatcherUrl() });
491
- const metaPaths = await discoverMetas(getConfig(), watcher);
492
- const tree = buildOwnershipTree(metaPaths);
493
- const targetNode = findNode(tree, targetPath);
450
+ const result = await listMetas(getConfig(), getWatcher());
451
+ const targetNode = findNode(result.tree, targetPath);
494
452
  if (!targetNode) {
495
453
  return fail('Meta path not found: ' + targetPath);
496
454
  }
@@ -505,7 +463,6 @@ function registerSynthTools(api) {
505
463
  result[f] = m[f];
506
464
  return result;
507
465
  }
508
- // Default: exclude big text blobs
509
466
  const result = {};
510
467
  for (const [k, v] of Object.entries(m)) {
511
468
  if (!defaultExclude.has(k))
@@ -525,7 +482,6 @@ function registerSynthTools(api) {
525
482
  const limit = typeof includeArchive === 'number'
526
483
  ? includeArchive
527
484
  : archiveFiles.length;
528
- // Most recent first (files are sorted by timestamp)
529
485
  const selected = archiveFiles.slice(-limit).reverse();
530
486
  const archives = selected.map((af) => {
531
487
  const raw = readFileSync(join(targetNode.metaPath, 'archive', af), 'utf8');
@@ -557,35 +513,27 @@ function registerSynthTools(api) {
557
513
  execute: async (_id, params) => {
558
514
  try {
559
515
  const targetPath = params.path;
560
- const watcher = new HttpWatcherClient({ baseUrl: getWatcherUrl() });
561
- const metaPaths = await discoverMetas(getConfig(), watcher);
562
- const tree = buildOwnershipTree(metaPaths);
516
+ const config = getConfig();
517
+ const watcher = getWatcher();
518
+ const result = await listMetas(config, watcher);
563
519
  let targetNode;
564
520
  if (targetPath) {
565
521
  const normalized = normalizePath(targetPath);
566
- targetNode = findNode(tree, normalized);
522
+ targetNode = findNode(result.tree, normalized);
567
523
  if (!targetNode) {
568
524
  return fail('Meta path not found: ' + targetPath);
569
525
  }
570
526
  }
571
527
  else {
572
- // Select stalest
573
- const { readFileSync: readFs } = await import('node:fs');
574
- const { join: joinPath } = await import('node:path');
575
- const candidates = [];
576
- for (const node of tree.nodes.values()) {
577
- try {
578
- const meta = JSON.parse(readFs(joinPath(node.metaPath, 'meta.json'), 'utf8'));
579
- const staleness = actualStaleness(meta);
580
- if (staleness > 0) {
581
- candidates.push({ node, meta, actualStaleness: staleness });
582
- }
583
- }
584
- catch {
585
- // skip unreadable
586
- }
587
- }
588
- const weighted = computeEffectiveStaleness(candidates, getConfig().depthWeight);
528
+ // Select stalest candidate
529
+ const candidates = result.entries
530
+ .filter((e) => e.stalenessSeconds > 0)
531
+ .map((e) => ({
532
+ node: e.node,
533
+ meta: e.meta,
534
+ actualStaleness: e.stalenessSeconds,
535
+ }));
536
+ const weighted = computeEffectiveStaleness(candidates, config.depthWeight);
589
537
  const winner = selectCandidate(weighted);
590
538
  if (!winner) {
591
539
  return ok({
@@ -597,21 +545,17 @@ function registerSynthTools(api) {
597
545
  const { readFileSync: readMeta } = await import('node:fs');
598
546
  const { join: joinMeta } = await import('node:path');
599
547
  const meta = JSON.parse(readMeta(joinMeta(targetNode.metaPath, 'meta.json'), 'utf8'));
600
- // Scope files (paginated for completeness)
548
+ // Scope files
601
549
  const allScanFiles = await paginatedScan(watcher, {
602
550
  pathPrefix: targetNode.ownerPath,
603
551
  });
604
552
  const allFiles = allScanFiles.map((f) => f.file_path);
605
553
  const scopeFiles = filterInScope(targetNode, allFiles);
606
- // Structure hash on scope-filtered files (matches orchestrator)
607
554
  const structureHash = computeStructureHash(scopeFiles);
608
555
  const structureChanged = structureHash !== meta._structureHash;
609
- // Steer change
610
556
  const latestArchive = readLatestArchive(targetNode.metaPath);
611
557
  const steerChanged = hasSteerChanged(meta._steer, latestArchive?._steer, Boolean(latestArchive));
612
- // Architect trigger check
613
- const architectTriggered = isArchitectTriggered(meta, structureChanged, steerChanged, getConfig().architectEvery);
614
- // Delta files
558
+ const architectTriggered = isArchitectTriggered(meta, structureChanged, steerChanged, config.architectEvery);
615
559
  let deltaFiles = [];
616
560
  if (meta._generatedAt) {
617
561
  const modifiedAfter = Math.floor(new Date(meta._generatedAt).getTime() / 1000);
@@ -646,7 +590,7 @@ function registerSynthTools(api) {
646
590
  ...(!meta._builder ? ['no cached builder (first run)'] : []),
647
591
  ...(structureChanged ? ['structure changed'] : []),
648
592
  ...(steerChanged ? ['steer changed'] : []),
649
- ...((meta._synthesisCount ?? 0) >= getConfig().architectEvery
593
+ ...((meta._synthesisCount ?? 0) >= config.architectEvery
650
594
  ? ['periodic refresh (architectEvery)']
651
595
  : []),
652
596
  ],
@@ -678,13 +622,12 @@ function registerSynthTools(api) {
678
622
  try {
679
623
  const { orchestrate } = await import('@karmaniverous/jeeves-meta');
680
624
  const { GatewayExecutor } = await import('@karmaniverous/jeeves-meta');
681
- // Load config from canonical config file
682
625
  const config = getConfig();
683
626
  const executor = new GatewayExecutor({
684
627
  gatewayUrl: config.gatewayUrl,
685
628
  apiKey: config.gatewayApiKey,
686
629
  });
687
- const watcher = new HttpWatcherClient({ baseUrl: getWatcherUrl() });
630
+ const watcher = getWatcher();
688
631
  const targetPath = params.path;
689
632
  const results = await orchestrate(config, executor, watcher, targetPath);
690
633
  const synthesized = results.filter((r) => r.synthesized);
@@ -729,48 +672,28 @@ function registerSynthTools(api) {
729
672
  * 2. No entities found - ACTION REQUIRED with setup guidance
730
673
  * 3. Healthy - entity stats + tool listing + skill reference
731
674
  *
732
- * @param watcherUrl - Watcher API base URL.
675
+ * @param config - Full synth config (for listMetas and watcherUrl).
733
676
  * @returns Markdown string for the Meta section.
734
677
  */
735
- async function generateMetaMenu(watcherUrl, metaFilter) {
736
- let entities = [];
678
+ async function generateMetaMenu(config) {
679
+ let result;
737
680
  try {
738
- const watcher = new HttpWatcherClient({ baseUrl: watcherUrl });
739
- entities = await paginatedScan(watcher, {
740
- filter: metaFilter,
741
- fields: [
742
- 'synth_depth',
743
- 'synth_emphasis',
744
- 'synth_synthesis_count',
745
- 'synth_architect_tokens',
746
- 'synth_builder_tokens',
747
- 'synth_critic_tokens',
748
- 'synth_error_step',
749
- 'generated_at_unix',
750
- 'has_error',
751
- ],
752
- });
681
+ const watcher = new HttpWatcherClient({ baseUrl: config.watcherUrl });
682
+ result = await listMetas(config, watcher);
753
683
  }
754
684
  catch {
755
685
  return [
756
686
  '> **ACTION REQUIRED: jeeves-watcher is unreachable.**',
757
687
  '> The jeeves-meta synthesis engine requires a running jeeves-watcher service.',
758
- `> The watcher API at ${watcherUrl} is down or not configured.`,
688
+ '> The watcher API at ' +
689
+ config.watcherUrl +
690
+ ' is down or not configured.',
759
691
  '>',
760
692
  "> **Read the `jeeves-meta` skill's Bootstrap section immediately**",
761
693
  '> for setup instructions. Do not attempt synthesis until watcher is available.',
762
694
  ].join('\n');
763
695
  }
764
- // Deduplicate by file_path (watcher chunks files into multiple points)
765
- const seenPaths = new Set();
766
- entities = entities.filter((e) => {
767
- const fp = e.file_path;
768
- if (seenPaths.has(fp))
769
- return false;
770
- seenPaths.add(fp);
771
- return true;
772
- });
773
- if (entities.length === 0) {
696
+ if (result.entries.length === 0) {
774
697
  return [
775
698
  '> **ACTION REQUIRED: No synthesis entities found.**',
776
699
  '> The watcher is running but no `.meta/` directories were discovered',
@@ -780,55 +703,7 @@ async function generateMetaMenu(watcherUrl, metaFilter) {
780
703
  '> on creating `.meta/` directories and configuring watch paths.',
781
704
  ].join('\n');
782
705
  }
783
- // Compute stats
784
- const now = Math.floor(Date.now() / 1000);
785
- let staleCount = 0;
786
- let errorCount = 0;
787
- let neverSynthesized = 0;
788
- let totalArchTokens = 0;
789
- let totalBuilderTokens = 0;
790
- let totalCriticTokens = 0;
791
- let stalestPath = '';
792
- let stalestAge = 0;
793
- let lastSynthPath = '';
794
- let lastSynthUnix = 0;
795
- for (const e of entities) {
796
- const generatedAt = e['generated_at_unix'];
797
- const hasError = e['has_error'];
798
- const archTokens = e['synth_architect_tokens'];
799
- const builderTokens = e['synth_builder_tokens'];
800
- const criticTokens = e['synth_critic_tokens'];
801
- if (!generatedAt) {
802
- neverSynthesized++;
803
- staleCount++;
804
- if (!isFinite(stalestAge)) ;
805
- else {
806
- stalestAge = Infinity;
807
- stalestPath = e.file_path;
808
- }
809
- }
810
- else {
811
- const age = now - generatedAt;
812
- if (age > 0)
813
- staleCount++;
814
- if (age > stalestAge && isFinite(age)) {
815
- stalestAge = age;
816
- stalestPath = e.file_path;
817
- }
818
- if (generatedAt > lastSynthUnix) {
819
- lastSynthUnix = generatedAt;
820
- lastSynthPath = e.file_path;
821
- }
822
- }
823
- if (hasError)
824
- errorCount++;
825
- if (archTokens)
826
- totalArchTokens += archTokens;
827
- if (builderTokens)
828
- totalBuilderTokens += builderTokens;
829
- if (criticTokens)
830
- totalCriticTokens += criticTokens;
831
- }
706
+ const { summary, entries } = result;
832
707
  const formatAge = (seconds) => {
833
708
  if (!isFinite(seconds))
834
709
  return 'never synthesized';
@@ -838,25 +713,42 @@ async function generateMetaMenu(watcherUrl, metaFilter) {
838
713
  return Math.round(seconds / 3600).toString() + 'h';
839
714
  return Math.round(seconds / 86400).toString() + 'd';
840
715
  };
716
+ // Find stalest age for display
717
+ let stalestAge = 0;
718
+ for (const e of entries) {
719
+ if (e.stalenessSeconds > stalestAge)
720
+ stalestAge = e.stalenessSeconds;
721
+ }
722
+ const stalestDisplay = summary.stalestPath
723
+ ? summary.stalestPath + ' (' + formatAge(stalestAge) + ')'
724
+ : 'n/a';
725
+ const lastSynthDisplay = summary.lastSynthesizedAt
726
+ ? (summary.lastSynthesizedPath ?? '') +
727
+ ' (' +
728
+ summary.lastSynthesizedAt +
729
+ ')'
730
+ : 'n/a';
841
731
  const lines = [
842
- `The jeeves-meta synthesis engine manages ${entities.length.toString()} meta entities.`,
732
+ 'The jeeves-meta synthesis engine manages ' +
733
+ entries.length.toString() +
734
+ ' meta entities.',
843
735
  '',
844
736
  '### Entity Summary',
845
737
  '| Metric | Value |',
846
738
  '|--------|-------|',
847
- `| Total | ${entities.length.toString()} |`,
848
- `| Stale | ${staleCount.toString()} |`,
849
- `| Errors | ${errorCount.toString()} |`,
850
- `| Never synthesized | ${neverSynthesized.toString()} |`,
851
- `| Stalest | ${stalestPath ? stalestPath + ' (' + formatAge(stalestAge) + ')' : 'n/a'} |`,
852
- `| Last synthesized | ${lastSynthPath ? lastSynthPath + ' (' + new Date(lastSynthUnix * 1000).toISOString() + ')' : 'n/a'} |`,
739
+ '| Total | ' + summary.total.toString() + ' |',
740
+ '| Stale | ' + summary.stale.toString() + ' |',
741
+ '| Errors | ' + summary.errors.toString() + ' |',
742
+ '| Never synthesized | ' + summary.neverSynthesized.toString() + ' |',
743
+ '| Stalest | ' + stalestDisplay + ' |',
744
+ '| Last synthesized | ' + lastSynthDisplay + ' |',
853
745
  '',
854
746
  '### Token Usage (cumulative)',
855
747
  '| Step | Tokens |',
856
748
  '|------|--------|',
857
- `| Architect | ${totalArchTokens.toLocaleString()} |`,
858
- `| Builder | ${totalBuilderTokens.toLocaleString()} |`,
859
- `| Critic | ${totalCriticTokens.toLocaleString()} |`,
749
+ '| Architect | ' + summary.tokens.architect.toLocaleString() + ' |',
750
+ '| Builder | ' + summary.tokens.builder.toLocaleString() + ' |',
751
+ '| Critic | ' + summary.tokens.critic.toLocaleString() + ' |',
860
752
  '',
861
753
  '### Tools',
862
754
  '| Tool | Description |',
@@ -946,7 +838,7 @@ function upsertMetaSection(existing, metaMenu) {
946
838
  * @returns True if the file was updated.
947
839
  */
948
840
  async function refreshToolsMd(api, config) {
949
- const menu = await generateMetaMenu(config.watcherUrl, buildMetaFilter(config));
841
+ const menu = await generateMetaMenu(config);
950
842
  if (menu === lastWrittenMenu) {
951
843
  return false;
952
844
  }
@@ -6,6 +6,7 @@
6
6
  *
7
7
  * @module promptInjection
8
8
  */
9
+ import { type SynthConfig } from '@karmaniverous/jeeves-meta';
9
10
  /**
10
11
  * Generate the Meta menu Markdown for TOOLS.md.
11
12
  *
@@ -14,7 +15,7 @@
14
15
  * 2. No entities found - ACTION REQUIRED with setup guidance
15
16
  * 3. Healthy - entity stats + tool listing + skill reference
16
17
  *
17
- * @param watcherUrl - Watcher API base URL.
18
+ * @param config - Full synth config (for listMetas and watcherUrl).
18
19
  * @returns Markdown string for the Meta section.
19
20
  */
20
- export declare function generateMetaMenu(watcherUrl: string, metaFilter: Record<string, unknown>): Promise<string>;
21
+ export declare function generateMetaMenu(config: SynthConfig): Promise<string>;
@@ -2,7 +2,7 @@
2
2
  "id": "jeeves-meta-openclaw",
3
3
  "name": "Jeeves Meta",
4
4
  "description": "Knowledge synthesis tools — trigger synthesis, view status, manage entities.",
5
- "version": "0.1.2",
5
+ "version": "0.1.3",
6
6
  "skills": [
7
7
  "dist/skills/jeeves-meta"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-meta-openclaw",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "OpenClaw plugin for jeeves-meta — synthesis tools and virtual rule registration",
6
6
  "license": "BSD-3-Clause",