claude-mem-lite 4.0.3 → 5.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.
Files changed (49) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +0 -49
  4. package/README.zh-CN.md +0 -48
  5. package/adopt-content.mjs +0 -1
  6. package/cli/common.mjs +0 -17
  7. package/cli.mjs +19 -3
  8. package/commands/update.md +4 -11
  9. package/format-utils.mjs +0 -21
  10. package/hook-context.mjs +6 -1
  11. package/hook-handoff.mjs +9 -0
  12. package/hook-update.mjs +37 -12
  13. package/hook.mjs +5 -29
  14. package/hooks/hooks.json +0 -10
  15. package/install.mjs +24 -666
  16. package/lib/doctor-drift.mjs +0 -1
  17. package/lib/fast-summary.mjs +11 -0
  18. package/lib/frontmatter.mjs +1 -2
  19. package/lib/hook-prune.mjs +133 -0
  20. package/lib/hook-stdin.mjs +1 -1
  21. package/lib/hook-telemetry.mjs +1 -1
  22. package/lib/metrics.mjs +2 -2
  23. package/lib/shard-gc.mjs +11 -7
  24. package/mem-cli.mjs +2 -423
  25. package/nlp.mjs +1 -1
  26. package/npm-shrinkwrap.json +2 -2
  27. package/package.json +3 -15
  28. package/schema.mjs +0 -1
  29. package/scripts/hook-launcher.mjs +2 -2
  30. package/scripts/prompt-search-utils.mjs +0 -30
  31. package/scripts/user-prompt-search.js +3 -125
  32. package/server.mjs +11 -449
  33. package/source-files.mjs +11 -28
  34. package/synonyms.mjs +2 -1
  35. package/tool-schemas.mjs +0 -80
  36. package/utils.mjs +5 -13
  37. package/commands/tools.md +0 -67
  38. package/install-metadata.mjs +0 -2193
  39. package/lib/registry-core.mjs +0 -264
  40. package/registry/preinstalled.json +0 -2419
  41. package/registry-enricher.mjs +0 -124
  42. package/registry-github.mjs +0 -86
  43. package/registry-importer.mjs +0 -569
  44. package/registry-recommend.mjs +0 -503
  45. package/registry-retriever.mjs +0 -611
  46. package/registry-scanner.mjs +0 -261
  47. package/registry.mjs +0 -665
  48. package/resource-discovery.mjs +0 -199
  49. package/scripts/pre-skill-bridge.js +0 -146
package/mem-cli.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // No MCP SDK or heavy deps — only imports schema.mjs and utils.mjs
4
4
 
5
5
  import { homedir } from 'os';
6
- import { ensureDbWithWalRecovery, DB_PATH, DB_DIR, REGISTRY_DB_PATH } from './schema.mjs';
6
+ import { ensureDbWithWalRecovery, DB_PATH, DB_DIR } from './schema.mjs';
7
7
  import { resolveRuntimeDir } from './lib/resolve-data-dir.mjs';
8
8
  import { truncate, typeIcon, inferProject, scrubSecrets, COMPRESSED_PENDING_PURGE } from './utils.mjs';
9
9
  import { resolveProject } from './project-utils.mjs';
@@ -36,32 +36,6 @@ import {
36
36
  BROWSE_TIER_LABELS,
37
37
  } from './lib/browse-core.mjs';
38
38
  import { deepSearch, resolveDeepMode, shouldEscalateToDeep, autoDeepLlmReady } from './deep-search.mjs';
39
- import {
40
- ensureRegistryDb,
41
- collectRegistryStats,
42
- listResourcesRanked,
43
- formatRegistryListLine,
44
- } from './registry.mjs';
45
- import {
46
- IMPORT_STRING_FIELDS,
47
- importResource,
48
- removeResource,
49
- reindexResources,
50
- enrichResourceRow,
51
- enrichImportedResources,
52
- enrichNamedResource,
53
- REGISTRY_CONFINE_ENV,
54
- resourceUseHint,
55
- } from './lib/registry-core.mjs';
56
- import { searchResources } from './registry-retriever.mjs';
57
- import {
58
- computeFunnel,
59
- formatFunnel,
60
- computeSweep,
61
- formatSweep,
62
- DEFAULT_SWEEP_FLOORS,
63
- DEFAULT_SWEEP_MARGINS,
64
- } from './registry-recommend.mjs';
65
39
  import { selectCompressionCandidates, groupByProjectWeek, compressGroup } from './lib/compress-core.mjs';
66
40
  import {
67
41
  runMaintainOps,
@@ -91,7 +65,7 @@ import {
91
65
  bucketIdTokens,
92
66
  splitDeferredTokens,
93
67
  } from './lib/id-routing.mjs';
94
- import { join, sep, dirname } from 'path';
68
+ import { join, dirname } from 'path';
95
69
  import { spawnSync } from 'child_process';
96
70
  import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
97
71
 
@@ -2685,233 +2659,6 @@ import { cmdFtsCheck } from './cli/fts-check.mjs';
2685
2659
 
2686
2660
  // ─── Registry ─────────────────────────────────────────────────────────────────
2687
2661
 
2688
- function cmdRegistry(_memDb, args) {
2689
- const { positional, flags } = parseArgs(args);
2690
- const action = positional[0];
2691
- if (
2692
- !action ||
2693
- !['list', 'stats', 'search', 'import', 'remove', 'reindex', 'recommend-stats'].includes(action)
2694
- ) {
2695
- fail(
2696
- '[mem] Usage: claude-mem-lite registry <list|stats|search|import|remove|reindex|recommend-stats> [--type skill|agent] [--query Q] [--name N] [--resource-type T]',
2697
- );
2698
- return;
2699
- }
2700
-
2701
- // recommend-stats reads the shadow-recommendation JSONL (filesystem), not the
2702
- // registry DB — handle it before opening rdb so it works without a registry DB.
2703
- if (action === 'recommend-stats') {
2704
- const days =
2705
- (typeof flags.days === 'string' || typeof flags.days === 'number') && +flags.days > 0
2706
- ? Math.floor(+flags.days)
2707
- : 7;
2708
- out(formatFunnel(computeFunnel(days)));
2709
- if (flags.sweep) {
2710
- // Offline ROC over the shipped gate thresholds (B3). Parse `--floors`/`--margins` as
2711
- // comma lists of finite numbers; fall back to the spanning defaults on bad/empty input.
2712
- const parseNums = (v, fallback) => {
2713
- if (typeof v !== 'string') return fallback;
2714
- const nums = v
2715
- .split(',')
2716
- .map((x) => Number(x.trim()))
2717
- .filter(Number.isFinite);
2718
- return nums.length ? nums : fallback;
2719
- };
2720
- const floors = parseNums(flags.floors, DEFAULT_SWEEP_FLOORS);
2721
- const margins = parseNums(flags.margins, DEFAULT_SWEEP_MARGINS);
2722
- out('');
2723
- out(formatSweep(computeSweep(days, floors, margins)));
2724
- }
2725
- return;
2726
- }
2727
-
2728
- let rdb;
2729
- try {
2730
- rdb = ensureRegistryDb(REGISTRY_DB_PATH);
2731
- rdb.pragma('busy_timeout = 3000');
2732
- } catch (e) {
2733
- out(`[mem] Registry DB not available: ${e.message}`);
2734
- return;
2735
- }
2736
-
2737
- // `--type` and `--resource-type` are both constrained to skill|agent across
2738
- // registry sub-actions. Validating once here means search/list/import/remove
2739
- // all reject typos like `--type sklil` instead of silently returning
2740
- // "No resources found." (which looked like the registry was empty for that
2741
- // type, not like a typo).
2742
- if (flags.type !== undefined && flags.type !== 'skill' && flags.type !== 'agent') {
2743
- fail(`[mem] Invalid --type "${flags.type}". Use: skill, agent`);
2744
- return;
2745
- }
2746
- if (
2747
- flags['resource-type'] !== undefined &&
2748
- flags['resource-type'] !== 'skill' &&
2749
- flags['resource-type'] !== 'agent'
2750
- ) {
2751
- fail(`[mem] Invalid --resource-type "${flags['resource-type']}". Use: skill, agent`);
2752
- return;
2753
- }
2754
-
2755
- try {
2756
- if (action === 'search') {
2757
- // Bare `--query` parses to boolean true; `true || ...` would search for the literal
2758
- // string "true". Reject it cleanly (#8470) before it becomes a confusing no-match.
2759
- if (rejectBareStringFlags(flags, ['query', 'category', 'quality'])) return;
2760
- const query = flags.query || positional.slice(1).join(' ');
2761
- if (!query) {
2762
- fail(
2763
- '[mem] Usage: claude-mem-lite registry search <query> [--type skill|agent] [--category C] [--quality Q]',
2764
- );
2765
- return;
2766
- }
2767
- let results = searchResources(rdb, query, {
2768
- type: flags.type || undefined,
2769
- limit: flags.category || flags.quality ? 20 : 10,
2770
- });
2771
- // Apply category/quality post-filters (aligned with MCP mem_registry)
2772
- if (flags.category) results = results.filter((r) => r.category === flags.category);
2773
- if (flags.quality) results = results.filter((r) => r.quality_tier === flags.quality);
2774
- // Prioritize directly invocable resources (aligned with MCP mem_registry)
2775
- results.sort((a, b) => {
2776
- const aInvocable = a.invocation_name ? 1 : 0;
2777
- const bInvocable = b.invocation_name ? 1 : 0;
2778
- if (aInvocable !== bInvocable) return bInvocable - aInvocable;
2779
- return 0;
2780
- });
2781
- results = results.slice(0, 5);
2782
- if (results.length === 0) {
2783
- out(`[mem] No matching resources for: "${query}"`);
2784
- return;
2785
- }
2786
- out(`[mem] ${results.length} resource(s) for "${query}":`);
2787
- const home = homedir();
2788
- for (const r of results) {
2789
- const badge = r.quality_tier === 'installed' ? '[✓]' : r.quality_tier === 'verified' ? '[★]' : '[○]';
2790
- const categoryLabel = r.category ? ` [${r.category}]` : '';
2791
- // P2-6: the invocation rule is shared; only the four-space indent is this face's.
2792
- const { portablePath, howToUse } = resourceUseHint(r, {
2793
- home,
2794
- managedPrefix: join(DB_DIR, 'managed') + sep,
2795
- });
2796
- const pathLine = portablePath ? `\n Path: ${portablePath}` : '';
2797
- out(
2798
- ` ${badge} ${r.type === 'skill' ? 'S' : 'A'} ${r.name}${categoryLabel} — ${truncate(r.capability_summary || '', 80)}${pathLine}\n Use: ${howToUse}`,
2799
- );
2800
- }
2801
- return;
2802
- }
2803
-
2804
- if (action === 'list') {
2805
- const typeFilter = flags.type;
2806
- const listLimit = parseIntFlag(flags.limit, { name: '--limit', defaultValue: 20, max: 1000 });
2807
- // Shared ranked query + row line (registry.mjs, P2-12): COALESCE ordering and
2808
- // the 80-char/adopt:0 row shape had drifted between the faces.
2809
- const allResources = listResourcesRanked(rdb, { type: typeFilter });
2810
- if (allResources.length === 0) {
2811
- out('[mem] No resources found.');
2812
- return;
2813
- }
2814
- const resources = allResources.slice(0, listLimit);
2815
- out(`[mem] Resources (showing ${resources.length} of ${allResources.length}):`);
2816
- for (const r of resources) out(` ${formatRegistryListLine(r)}`);
2817
- if (allResources.length > listLimit) {
2818
- out(`[mem] Use --limit N to see more, or "registry search <query>" to find specific resources.`);
2819
- }
2820
- return;
2821
- }
2822
-
2823
- if (action === 'stats') {
2824
- // Shared collection (registry.mjs collectRegistryStats, P2-12) — single
2825
- // source with the MCP mem_registry stats action.
2826
- const s = collectRegistryStats(rdb);
2827
- out(`[mem] Registry Stats:`);
2828
- out(` Total active: ${s.total}`);
2829
- for (const t of s.byType) out(` ${t.type}: ${t.c}`);
2830
- out(` User-added: ${s.userAdded}`);
2831
- out(` Zero adoption (recommended but never adopted): ${s.zeroAdopt}`);
2832
- if (s.topAdopted.length > 0) {
2833
- out(' Top adopted:');
2834
- for (const r of s.topAdopted) out(` ${r.name} (${r.type}): ${r.adopt_count}/${r.recommend_count}`);
2835
- }
2836
- return;
2837
- }
2838
-
2839
- if (action === 'import') {
2840
- // Bare value-less flags → boolean true → SQLite-bind crash in upsertResource (#8470).
2841
- // Shared guard — single source with update/remove/the other commands.
2842
- if (
2843
- rejectBareStringFlags(flags, [
2844
- 'name',
2845
- 'resource-type',
2846
- 'invocation-name',
2847
- 'source',
2848
- 'repo-url',
2849
- 'local-path',
2850
- 'intent-tags',
2851
- 'domain-tags',
2852
- 'trigger-patterns',
2853
- 'capability-summary',
2854
- 'keywords',
2855
- 'tech-stack',
2856
- 'use-cases',
2857
- ])
2858
- )
2859
- return;
2860
- const name = flags.name;
2861
- const resourceType = flags['resource-type'];
2862
- if (!name || !resourceType) {
2863
- fail(
2864
- '[mem] Usage: claude-mem-lite registry import --name N --resource-type skill|agent [--invocation-name I] [--capability-summary S]',
2865
- );
2866
- return;
2867
- }
2868
- // Validate --source against its CHECK enum (parity with memRegistrySchema.source on MCP):
2869
- // an invalid value otherwise reaches the INSERT and throws a raw SqliteError stacktrace
2870
- // (the dispatcher's catch only special-cases SQLITE_BUSY/LOCKED).
2871
- if (flags.source && !new Set(['preinstalled', 'user', 'github']).has(flags.source)) {
2872
- fail(`[mem] Invalid --source "${flags.source}". Valid: preinstalled, user, github`);
2873
- return;
2874
- }
2875
- // Provenance preservation + the 'installed' tier grant live in lib/registry-core.mjs,
2876
- // shared with the mem_registry MCP twin (audit 2026-08-22 P1-3). CLI flags are
2877
- // kebab-case; the core's field list is the canonical snake_case source.
2878
- const fields = {};
2879
- for (const f of IMPORT_STRING_FIELDS) fields[f] = flags[f.replace(/_/g, '-')] || '';
2880
- const { id } = importResource(rdb, { name, type: resourceType, source: flags.source, fields });
2881
- out(`[mem] Imported: ${resourceType}:${name} (id=${id})`);
2882
- if (!flags['capability-summary'] && !flags['use-cases']) {
2883
- out('[mem] Tip: Add --capability-summary or --use-cases so the resource appears in searches.');
2884
- }
2885
- return;
2886
- }
2887
-
2888
- if (action === 'remove') {
2889
- // Bare value-less --name / --resource-type → boolean true → SQLite-bind crash on
2890
- // the DELETE below; shared guard, single source with import/update.
2891
- if (rejectBareStringFlags(flags, ['name', 'resource-type'])) return;
2892
- const name = flags.name;
2893
- const resourceType = flags['resource-type'];
2894
- if (!name || !resourceType) {
2895
- fail('[mem] Usage: claude-mem-lite registry remove --name N --resource-type skill|agent');
2896
- return;
2897
- }
2898
- const { removed } = removeResource(rdb, { name, type: resourceType });
2899
- out(removed ? `[mem] Removed: ${resourceType}:${name}` : `[mem] Not found: ${resourceType}:${name}`);
2900
- return;
2901
- }
2902
-
2903
- if (action === 'reindex') {
2904
- const { activeCount } = reindexResources(rdb);
2905
- out(`[mem] FTS5 reindexed. ${activeCount} active resources.`);
2906
- return;
2907
- }
2908
- } finally {
2909
- try {
2910
- rdb.close();
2911
- } catch {}
2912
- }
2913
- }
2914
-
2915
2662
  // ─── memdir-audit ────────────────────────────────────────────────────────────
2916
2663
  // Body-structure audit for ~/.claude/projects/<encoded>/memory/feedback_*.md
2917
2664
  // and project_*.md. CLI-only by design — running this every session would be
@@ -3453,25 +3200,9 @@ Commands:
3453
3200
  --days N Cite-rate window in days (default 7)
3454
3201
  --json Output as JSON: {window_days,per_project:[],decay_queue:[],promoted:[]}
3455
3202
 
3456
- registry <action> Manage tool resource registry
3457
- list List resources [--type skill|agent] [--limit N] (default 20)
3458
- stats Registry statistics
3459
- search <query> Search resources [--type skill|agent] [--category C] [--quality Q]
3460
- import Import resource --name N --resource-type T [--repo-url U] [--local-path P] [--use-cases U]
3461
- remove Remove resource --name N --resource-type T
3462
- reindex Rebuild FTS5 index
3463
- recommend-stats Shadow recommendation funnel [--days N] [--sweep] [--json]
3464
-
3465
3203
  import-jsonl <file-or-dir> Import Claude Code JSONL transcripts (cold-start backfill)
3466
3204
  --project P Project name (default: inferred from cwd)
3467
3205
 
3468
- import <github-url> Import skills/agents into the resource registry from a GitHub repo
3469
- --enrich Auto-enrich each imported resource with a Haiku capability summary
3470
-
3471
- enrich <name> Re-enrich a single registry resource (Haiku capability summary)
3472
- --all Enrich every active resource missing or failed enrichment
3473
- --batch Skip the inter-call delay (use only with low rate-limit risk)
3474
-
3475
3206
  activity <action> Non-memdir event log (v2.31) — bugfix/lesson/bug/discovery/etc.
3476
3207
  save --type T "<title>" [--body "<text>"] [--files f1,f2] [--file path] [--importance 1-3] [--project P]
3477
3208
  search "<query>" Search events [--type T] [--limit N] [--project P]
@@ -3512,69 +3243,6 @@ DB: ${DB_PATH}`);
3512
3243
 
3513
3244
  // ─── Import (GitHub) ────────────────────────────────────────────────────────
3514
3245
 
3515
- async function cmdImport(argv) {
3516
- const { positional, flags } = parseArgs(argv);
3517
- const url = positional[0];
3518
-
3519
- if (!url) {
3520
- fail('[mem] Usage: claude-mem-lite import <github-url> [--enrich]');
3521
- return;
3522
- }
3523
-
3524
- let rdb;
3525
- try {
3526
- rdb = ensureRegistryDb(REGISTRY_DB_PATH);
3527
- rdb.pragma('busy_timeout = 3000');
3528
- } catch (e) {
3529
- fail(`[mem] Registry DB error: ${e.message}`);
3530
- return;
3531
- }
3532
-
3533
- try {
3534
- const { importFromGitHub, formatImportSkips } = await import('./registry-importer.mjs');
3535
- out(`[mem] Importing from ${url}...`);
3536
- // `skipped` sink + shared summary (R6 Q1) — same helper the MCP twin renders, so the
3537
- // bounds cannot end up enforced-but-silent on one of the two faces.
3538
- const skipped = [];
3539
- const results = await importFromGitHub(rdb, url, { skipped });
3540
- const refusal = formatImportSkips(skipped);
3541
-
3542
- if (results.length === 0) {
3543
- out('[mem] No skills/agents found in this repository.');
3544
- if (refusal) out(`[mem] ${refusal}`);
3545
- return;
3546
- }
3547
-
3548
- out(`[mem] Imported ${results.length} resource(s):`);
3549
- for (const r of results) {
3550
- out(` ${r.type === 'skill' ? 'S' : 'A'} ${r.name} (id=${r.id})`);
3551
- }
3552
- if (refusal) out(`[mem] ${refusal}`);
3553
-
3554
- if (flags.enrich) {
3555
- out('[mem] Running LLM enrichment...');
3556
- const { enrichResource } = await import('./registry-enricher.mjs');
3557
- const { ok: enriched, denied } = await enrichImportedResources(rdb, results, {
3558
- confineTo: DB_DIR,
3559
- enrichResource,
3560
- });
3561
- out(`[mem] Enriched ${enriched}/${results.length} resources.`);
3562
- if (denied)
3563
- out(
3564
- `[mem] Refused ${denied}: local_path outside the managed directory (${REGISTRY_CONFINE_ENV}=off to override).`,
3565
- );
3566
- }
3567
- } catch (e) {
3568
- fail(`[mem] Import failed: ${e.message}`);
3569
- } finally {
3570
- try {
3571
- rdb.close();
3572
- } catch {}
3573
- }
3574
- }
3575
-
3576
- // ─── Import (Claude Code JSONL transcript — cold-start backfill) ─────────────
3577
-
3578
3246
  async function cmdImportJsonl(db, argv) {
3579
3247
  const { positional, flags } = parseArgs(argv);
3580
3248
  const target = positional[0];
@@ -3682,86 +3350,6 @@ async function cmdImportJsonl(db, argv) {
3682
3350
 
3683
3351
  // ─── Enrich ─────────────────────────────────────────────────────────────────
3684
3352
 
3685
- async function cmdEnrich(argv) {
3686
- const { positional, flags } = parseArgs(argv);
3687
- const name = positional[0];
3688
-
3689
- let rdb;
3690
- try {
3691
- rdb = ensureRegistryDb(REGISTRY_DB_PATH);
3692
- rdb.pragma('busy_timeout = 3000');
3693
- } catch (e) {
3694
- fail(`[mem] Registry DB error: ${e.message}`);
3695
- return;
3696
- }
3697
-
3698
- try {
3699
- const { enrichResource } = await import('./registry-enricher.mjs');
3700
-
3701
- if (flags.all) {
3702
- const rows = rdb
3703
- .prepare(
3704
- "SELECT name, type, local_path FROM resources WHERE status = 'active' AND (enrichment_status IS NULL OR enrichment_status = 'failed')",
3705
- )
3706
- .all();
3707
- if (rows.length === 0) {
3708
- out('[mem] All resources already enriched.');
3709
- return;
3710
- }
3711
- out(`[mem] Enriching ${rows.length} resources...`);
3712
- let ok = 0,
3713
- failCount = 0,
3714
- denied = 0;
3715
- for (const r of rows) {
3716
- const { status, error } = await enrichResourceRow(rdb, r, { confineTo: DB_DIR, enrichResource });
3717
- if (status === 'enriched') ok++;
3718
- else if (status === 'denied') denied++;
3719
- else failCount++;
3720
- // Pace exactly where the pre-refactor loop did: after an enrichResource call that
3721
- // RETURNED. A missing path, an unreadable file, a gate refusal and a throw out of
3722
- // enrichResource all skipped the sleep before, and still do — `error` present on a
3723
- // 'failed' row is what distinguishes "returned false" from "threw".
3724
- const apiReturned = status === 'enriched' || (status === 'failed' && !error);
3725
- if (apiReturned && !flags.batch) await new Promise((resolve) => setTimeout(resolve, 500));
3726
- }
3727
- out(`[mem] Done: ${ok} enriched, ${failCount} failed.`);
3728
- if (denied)
3729
- out(
3730
- `[mem] Refused ${denied}: local_path outside the managed directory (${REGISTRY_CONFINE_ENV}=off to override).`,
3731
- );
3732
- } else if (name) {
3733
- const { status, error } = await enrichNamedResource(rdb, name, { confineTo: DB_DIR, enrichResource });
3734
- if (status === 'not-found') {
3735
- fail(`[mem] Resource not found: ${name}`);
3736
- return;
3737
- }
3738
- if (status === 'no-path') {
3739
- fail(`[mem] No local_path for ${name}`);
3740
- return;
3741
- }
3742
- if (status === 'denied') {
3743
- fail(
3744
- `[mem] Access denied: local_path outside the managed directory (${REGISTRY_CONFINE_ENV}=off to override).`,
3745
- );
3746
- return;
3747
- }
3748
- if (status === 'unreadable') {
3749
- fail(`[mem] Enrich error: ${error?.message || 'cannot read local_path'}`);
3750
- return;
3751
- }
3752
- out(status === 'enriched' ? `[mem] Enriched: ${name}` : `[mem] Enrichment failed for ${name}`);
3753
- } else {
3754
- fail('[mem] Usage: claude-mem-lite enrich <name> OR claude-mem-lite enrich --all [--batch]');
3755
- }
3756
- } catch (e) {
3757
- fail(`[mem] Enrich error: ${e.message}`);
3758
- } finally {
3759
- try {
3760
- rdb.close();
3761
- } catch {}
3762
- }
3763
- }
3764
-
3765
3353
  async function cmdOptimize(db, args) {
3766
3354
  // cmdOptimize parses flags positionally (args.indexOf('--task') + args[idx+1])
3767
3355
  // instead of the shared parseArgs, so the GNU `--flag=value` form silently
@@ -4112,18 +3700,9 @@ export async function run(argv) {
4112
3700
  case 'citation-stats':
4113
3701
  cmdCitationStats(db, cmdArgs);
4114
3702
  break;
4115
- case 'registry':
4116
- cmdRegistry(db, cmdArgs);
4117
- break;
4118
- case 'import':
4119
- await cmdImport(cmdArgs);
4120
- break;
4121
3703
  case 'import-jsonl':
4122
3704
  await cmdImportJsonl(db, cmdArgs);
4123
3705
  break;
4124
- case 'enrich':
4125
- await cmdEnrich(cmdArgs);
4126
- break;
4127
3706
  case 'doctor':
4128
3707
  await cmdDoctor(db, cmdArgs);
4129
3708
  break;
package/nlp.mjs CHANGED
@@ -361,7 +361,7 @@ export function sanitizeFtsQuery(query, opts = {}) {
361
361
  // "redis" in "redis缓存问题". extractCjkKeywords + cjkBigrams only see CJK runs, so
362
362
  // without this the Latin anchor (often the single most precise term — a proper noun
363
363
  // like redis/grafana/oauth with no CJK synonym) was dropped, zeroing recall on
364
- // whitespace-free mixed-script prompts. Mirrors registry-retriever.mjs's embedded-
364
+ // whitespace-free mixed-script prompts. Mirrors the embedded-
365
365
  // English extraction. Lowercased for parity with the write-path unicode61 folding.
366
366
  for (const en of remainder.match(/[a-zA-Z]{2,}/g) || []) {
367
367
  const low = en.toLowerCase();
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "4.0.3",
3
+ "version": "5.0.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "claude-mem-lite",
9
- "version": "4.0.3",
9
+ "version": "5.0.0",
10
10
  "dependencies": {
11
11
  "@modelcontextprotocol/sdk": "^1.30.0",
12
12
  "better-sqlite3": "^13.0.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "4.0.3",
3
+ "version": "5.0.0",
4
4
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
@@ -99,6 +99,7 @@
99
99
  "lib/hook-stdout.mjs",
100
100
  "lib/proc-lock.mjs",
101
101
  "lib/atomic-write.mjs",
102
+ "lib/hook-prune.mjs",
102
103
  "lib/proxy-fetch.mjs",
103
104
  "lib/llm-provider-probe.mjs",
104
105
  "lib/release-digest.mjs",
@@ -116,7 +117,6 @@
116
117
  "lib/save-observation.mjs",
117
118
  "lib/observation-write.mjs",
118
119
  "lib/recall-core.mjs",
119
- "lib/registry-core.mjs",
120
120
  "lib/timeline-core.mjs",
121
121
  "lib/search-core.mjs",
122
122
  "lib/recent-core.mjs",
@@ -146,18 +146,9 @@
146
146
  "cli/doctor.mjs",
147
147
  "cli/activity.mjs",
148
148
  "server/fts-check.mjs",
149
- "registry.mjs",
150
- "registry-retriever.mjs",
151
- "registry-recommend.mjs",
152
- "registry-scanner.mjs",
153
- "registry-github.mjs",
154
- "registry-importer.mjs",
155
- "registry-enricher.mjs",
156
- "resource-discovery.mjs",
157
149
  "source-files.mjs",
158
150
  "npm-shrinkwrap.json",
159
151
  "install.mjs",
160
- "install-metadata.mjs",
161
152
  "skip-tools.mjs",
162
153
  "tool-schemas.mjs",
163
154
  "utils.mjs",
@@ -177,7 +168,6 @@
177
168
  "commands/mem.md",
178
169
  "commands/memory.md",
179
170
  "commands/update.md",
180
- "commands/tools.md",
181
171
  "commands/adopt.md",
182
172
  "commands/unadopt.md",
183
173
  "commands/lesson.md",
@@ -190,7 +180,6 @@
190
180
  "scripts/user-prompt-search.js",
191
181
  "scripts/pre-tool-recall.js",
192
182
  "scripts/post-tool-recall.js",
193
- "scripts/pre-skill-bridge.js",
194
183
  "scripts/pre-agent-inject.sh",
195
184
  "scripts/pre-agent-inject.js",
196
185
  "scripts/prompt-search-utils.mjs",
@@ -199,8 +188,7 @@
199
188
  ".mcp.json",
200
189
  ".claude-plugin/plugin.json",
201
190
  ".claude-plugin/marketplace.json",
202
- "README.md",
203
- "registry/preinstalled.json"
191
+ "README.md"
204
192
  ],
205
193
  "dependencies": {
206
194
  "@modelcontextprotocol/sdk": "^1.30.0",
package/schema.mjs CHANGED
@@ -13,7 +13,6 @@ import { resolveDataDir } from './lib/resolve-data-dir.mjs';
13
13
  // CLAUDE_MEM_DIR so users can relocate state to a larger/faster volume.
14
14
  export const DB_DIR = resolveDataDir(process.env.CLAUDE_MEM_DIR);
15
15
  export const DB_PATH = join(DB_DIR, 'claude-mem-lite.db');
16
- export const REGISTRY_DB_PATH = join(DB_DIR, 'resource-registry.db');
17
16
  // CODE / install location — server.mjs, hook.mjs, cli.mjs, package.json live
18
17
  // here. ALWAYS homedir-rooted: Claude Code's settings.json + MCP registration
19
18
  // bake ABSOLUTE paths to server.mjs/hooks, so the code must NOT follow the
@@ -54,7 +54,7 @@ const DATA_DIR = MEM_DIR && isAbsolute(MEM_DIR) ? MEM_DIR : join(homedir(), '.cl
54
54
  // declared exception in the tree: the launcher runs BEFORE the native binding is known to
55
55
  // work, so it imports only `node:` builtins on purpose — even a leaf lib module is a
56
56
  // resolution it declines to make on the path whose job is surviving a broken install. The
57
- // standalone hook scripts it mirrors (pre-tool-recall / pre-skill-bridge) honour the same
57
+ // standalone hook scripts it mirrors (pre-tool-recall / post-tool-recall) honour the same
58
58
  // variable and write 78 of every 79 of these markers, so reading a different dir would mean
59
59
  // never healing. `resolveRuntimeDir` is the canonical rule (audit 2026-09-02 P1-14) and this
60
60
  // is NOT identical to it: the resolver additionally makes a RELATIVE override absolute
@@ -342,7 +342,7 @@ async function attemptHeal(reason) {
342
342
  //
343
343
  // Since the heal moved off the hot path, a hot-path fire that hits a missing module records
344
344
  // the breakage and defers. But this launcher fronts several entries — hook.mjs plus
345
- // pre-tool-recall.js, pre-skill-bridge.js, post-tool-recall.js, user-prompt-search.js — and
345
+ // pre-tool-recall.js, post-tool-recall.js, user-prompt-search.js — and
346
346
  // the missing module may sit on one of THEIR import chains and not on hook.mjs's. Then
347
347
  // session-start's own entry imports cleanly, the catch below never fires, and before this
348
348
  // function existed the clean fire simply cleared the marker: nothing ever repaired it. (That
@@ -244,36 +244,6 @@ export function shouldSkipByDedup(newIds, injectedFile, sessionId) {
244
244
  }
245
245
  }
246
246
 
247
- // ─── Registry Skill Name Matching ───────────────────────────────────────────
248
-
249
- /**
250
- * Check if prompt text contains a known managed skill name.
251
- * Returns the matched name or null.
252
- * @param {string} text - user prompt
253
- * @param {Set<string>} skillNames - set of known managed skill names (lowercase)
254
- * @returns {string|null}
255
- */
256
- export function matchRegistrySkillName(text, skillNames) {
257
- if (!text || skillNames.size === 0) return null;
258
- const lower = text.toLowerCase();
259
-
260
- // Sort names longest-first to match "code-review-expert" before "code-review"
261
- const sorted = [...skillNames].sort((a, b) => b.length - a.length);
262
-
263
- for (const name of sorted) {
264
- const idx = lower.indexOf(name);
265
- if (idx === -1) continue;
266
-
267
- // Check word boundaries: char before and after must be non-alphanumeric (or start/end)
268
- const before = idx === 0 ? ' ' : lower[idx - 1];
269
- const after = idx + name.length >= lower.length ? ' ' : lower[idx + name.length];
270
- if (/[a-z0-9]/.test(before) || /[a-z0-9]/.test(after)) continue;
271
-
272
- return name;
273
- }
274
- return null;
275
- }
276
-
277
247
  // ─── File Path Detection ─────────────────────────────────────────────────────
278
248
 
279
249
  /** Detect file paths in text — excludes URLs and pure version numbers */