claude-mem-lite 2.5.4 → 2.9.2
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +0 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/README.zh-CN.md +0 -0
- package/commands/mem.md +0 -0
- package/commands/memory.md +0 -0
- package/commands/tools.md +0 -0
- package/commands/update.md +0 -0
- package/dispatch-feedback.mjs +129 -24
- package/dispatch-inject.mjs +73 -34
- package/dispatch-patterns.mjs +173 -0
- package/dispatch-workflow.mjs +0 -0
- package/dispatch.mjs +359 -271
- package/haiku-client.mjs +0 -0
- package/hook-context.mjs +24 -6
- package/hook-episode.mjs +2 -2
- package/hook-handoff.mjs +38 -18
- package/hook-llm.mjs +98 -21
- package/hook-memory.mjs +47 -15
- package/hook-semaphore.mjs +0 -0
- package/hook-shared.mjs +21 -0
- package/hook-update.mjs +262 -0
- package/hook.mjs +165 -28
- package/hooks/hooks.json +0 -0
- package/install.mjs +149 -4
- package/package.json +3 -1
- package/registry/preinstalled.json +13 -0
- package/registry-indexer.mjs +0 -0
- package/registry-retriever.mjs +13 -8
- package/registry-scanner.mjs +0 -0
- package/registry.mjs +15 -7
- package/resource-discovery.mjs +0 -0
- package/schema.mjs +0 -0
- package/scripts/launch.mjs +0 -0
- package/server-internals.mjs +0 -0
- package/server.mjs +58 -13
- package/skill.md +0 -0
- package/tool-schemas.mjs +41 -16
- package/utils.mjs +87 -30
package/install.mjs
CHANGED
|
@@ -1854,6 +1854,75 @@ const RESOURCE_METADATA = {
|
|
|
1854
1854
|
|
|
1855
1855
|
};
|
|
1856
1856
|
|
|
1857
|
+
// ─── Marketing / SEO resources → on_request mode ────────────────────────────
|
|
1858
|
+
// These resources are rarely useful during programming sessions.
|
|
1859
|
+
// They only surface when the user explicitly asks for marketing/SEO help.
|
|
1860
|
+
const MARKETING_ON_REQUEST = new Set([
|
|
1861
|
+
// Skills — marketing domain
|
|
1862
|
+
'skill:ab-test-setup',
|
|
1863
|
+
'skill:ad-creative',
|
|
1864
|
+
'skill:ai-seo',
|
|
1865
|
+
'skill:analytics-tracking',
|
|
1866
|
+
'skill:churn-prevention',
|
|
1867
|
+
'skill:cold-email',
|
|
1868
|
+
'skill:competitor-alternatives',
|
|
1869
|
+
'skill:content-strategy',
|
|
1870
|
+
'skill:copy-editing',
|
|
1871
|
+
'skill:copywriting',
|
|
1872
|
+
'skill:email-sequence',
|
|
1873
|
+
'skill:form-cro',
|
|
1874
|
+
'skill:free-tool-strategy',
|
|
1875
|
+
'skill:launch-strategy',
|
|
1876
|
+
'skill:marketing-ideas',
|
|
1877
|
+
'skill:marketing-psychology',
|
|
1878
|
+
'skill:onboarding-cro',
|
|
1879
|
+
'skill:page-cro',
|
|
1880
|
+
'skill:paid-ads',
|
|
1881
|
+
'skill:paywall-upgrade-cro',
|
|
1882
|
+
'skill:popup-cro',
|
|
1883
|
+
'skill:pricing-strategy',
|
|
1884
|
+
'skill:product-marketing-context',
|
|
1885
|
+
'skill:programmatic-seo',
|
|
1886
|
+
'skill:referral-program',
|
|
1887
|
+
'skill:schema-markup',
|
|
1888
|
+
'skill:mktg-seo-audit',
|
|
1889
|
+
'skill:signup-flow-cro',
|
|
1890
|
+
'skill:social-content',
|
|
1891
|
+
// Skills — SEO domain
|
|
1892
|
+
'skill:seo-audit',
|
|
1893
|
+
'skill:seo-competitor-pages',
|
|
1894
|
+
'skill:seo-content',
|
|
1895
|
+
'skill:seo-geo',
|
|
1896
|
+
'skill:seo-hreflang',
|
|
1897
|
+
'skill:seo-images',
|
|
1898
|
+
'skill:seo-page',
|
|
1899
|
+
'skill:seo-plan',
|
|
1900
|
+
'skill:seo-programmatic',
|
|
1901
|
+
'skill:seo-schema',
|
|
1902
|
+
'skill:seo-sitemap',
|
|
1903
|
+
'skill:seo-technical',
|
|
1904
|
+
// Agents — SEO
|
|
1905
|
+
'agent:seo-content-agent',
|
|
1906
|
+
'agent:seo-performance-agent',
|
|
1907
|
+
'agent:seo-schema-agent',
|
|
1908
|
+
'agent:seo-sitemap-agent',
|
|
1909
|
+
'agent:seo-technical-agent',
|
|
1910
|
+
'agent:seo-visual-agent',
|
|
1911
|
+
'agent:seo-analysis-monitoring',
|
|
1912
|
+
'agent:seo-content-creation',
|
|
1913
|
+
'agent:seo-technical-optimization',
|
|
1914
|
+
// Agents — marketing / sales
|
|
1915
|
+
'agent:content-marketing',
|
|
1916
|
+
'agent:customer-sales-automation',
|
|
1917
|
+
]);
|
|
1918
|
+
|
|
1919
|
+
// Stamp recommendation_mode into metadata so both insert and update paths read it
|
|
1920
|
+
for (const key of MARKETING_ON_REQUEST) {
|
|
1921
|
+
if (RESOURCE_METADATA[key]) {
|
|
1922
|
+
RESOURCE_METADATA[key].recommendation_mode = 'on_request';
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1857
1926
|
/**
|
|
1858
1927
|
* Apply curated metadata to existing resource DB entries.
|
|
1859
1928
|
* Fixes existing installs that have generic name-echo metadata.
|
|
@@ -1865,6 +1934,7 @@ function reindexKnownResources(rdb) {
|
|
|
1865
1934
|
intent_tags = ?, domain_tags = ?,
|
|
1866
1935
|
capability_summary = ?, trigger_patterns = ?,
|
|
1867
1936
|
invocation_name = CASE WHEN ? != '' THEN ? ELSE invocation_name END,
|
|
1937
|
+
recommendation_mode = CASE WHEN ? != '' THEN ? ELSE recommendation_mode END,
|
|
1868
1938
|
updated_at = datetime('now')
|
|
1869
1939
|
WHERE type = ? AND name = ?
|
|
1870
1940
|
`);
|
|
@@ -1876,10 +1946,12 @@ function reindexKnownResources(rdb) {
|
|
|
1876
1946
|
const type = key.slice(0, sep);
|
|
1877
1947
|
const name = key.slice(sep + 1);
|
|
1878
1948
|
const invName = meta.invocation_name || '';
|
|
1949
|
+
const recMode = meta.recommendation_mode || '';
|
|
1879
1950
|
update.run(
|
|
1880
1951
|
meta.intent_tags, meta.domain_tags,
|
|
1881
1952
|
meta.capability_summary, meta.trigger_patterns,
|
|
1882
1953
|
invName, invName,
|
|
1954
|
+
recMode, recMode,
|
|
1883
1955
|
type, name
|
|
1884
1956
|
);
|
|
1885
1957
|
}
|
|
@@ -1897,9 +1969,9 @@ function registerVirtualResources(rdb) {
|
|
|
1897
1969
|
const insert = rdb.prepare(`
|
|
1898
1970
|
INSERT OR IGNORE INTO resources (name, type, status, source, local_path, invocation_name,
|
|
1899
1971
|
intent_tags, domain_tags, capability_summary, trigger_patterns,
|
|
1900
|
-
keywords, tech_stack, use_cases,
|
|
1972
|
+
keywords, tech_stack, use_cases, recommendation_mode,
|
|
1901
1973
|
created_at, updated_at)
|
|
1902
|
-
VALUES (?, ?, 'active', 'preinstalled', '', ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
|
1974
|
+
VALUES (?, ?, 'active', 'preinstalled', '', ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
|
1903
1975
|
`);
|
|
1904
1976
|
|
|
1905
1977
|
// Backfill FTS5 fields for existing resources that have empty keywords/tech_stack/use_cases
|
|
@@ -1930,6 +2002,7 @@ function registerVirtualResources(rdb) {
|
|
|
1930
2002
|
meta.keywords || '',
|
|
1931
2003
|
meta.tech_stack || '',
|
|
1932
2004
|
meta.use_cases || '',
|
|
2005
|
+
meta.recommendation_mode || 'proactive',
|
|
1933
2006
|
);
|
|
1934
2007
|
count += changes;
|
|
1935
2008
|
|
|
@@ -1989,11 +2062,11 @@ async function install() {
|
|
|
1989
2062
|
const SOURCE_FILES = [
|
|
1990
2063
|
'server.mjs', 'server-internals.mjs', 'tool-schemas.mjs',
|
|
1991
2064
|
'hook.mjs', 'hook-shared.mjs', 'hook-llm.mjs', 'hook-memory.mjs',
|
|
1992
|
-
'hook-semaphore.mjs', 'hook-episode.mjs', 'hook-context.mjs', 'hook-handoff.mjs',
|
|
2065
|
+
'hook-semaphore.mjs', 'hook-episode.mjs', 'hook-context.mjs', 'hook-handoff.mjs', 'hook-update.mjs',
|
|
1993
2066
|
'haiku-client.mjs', 'utils.mjs', 'schema.mjs', 'package.json', 'skill.md',
|
|
1994
2067
|
'registry.mjs', 'registry-scanner.mjs', 'registry-indexer.mjs',
|
|
1995
2068
|
'registry-retriever.mjs', 'resource-discovery.mjs',
|
|
1996
|
-
'dispatch.mjs', 'dispatch-inject.mjs', 'dispatch-feedback.mjs', 'dispatch-workflow.mjs',
|
|
2069
|
+
'dispatch.mjs', 'dispatch-inject.mjs', 'dispatch-feedback.mjs', 'dispatch-patterns.mjs', 'dispatch-workflow.mjs',
|
|
1997
2070
|
];
|
|
1998
2071
|
|
|
1999
2072
|
if (IS_DEV) {
|
|
@@ -2742,6 +2815,70 @@ function writeSettings(settings) {
|
|
|
2742
2815
|
renameSync(tmp, SETTINGS_PATH);
|
|
2743
2816
|
}
|
|
2744
2817
|
|
|
2818
|
+
// ─── Manual Update ───────────────────────────────────────────────────────────
|
|
2819
|
+
|
|
2820
|
+
async function manualUpdate() {
|
|
2821
|
+
console.log('\nclaude-mem-lite update\n');
|
|
2822
|
+
|
|
2823
|
+
// Force check by importing hook-update (bypasses throttle for manual use)
|
|
2824
|
+
const { checkForUpdate, getCurrentVersion } = await import('./hook-update.mjs');
|
|
2825
|
+
log('Checking for updates...');
|
|
2826
|
+
const result = await checkForUpdate();
|
|
2827
|
+
|
|
2828
|
+
if (result?.updated) {
|
|
2829
|
+
ok(`Updated: v${result.from} → v${result.to}`);
|
|
2830
|
+
} else if (result?.updateAvailable) {
|
|
2831
|
+
warn(`v${result.to} available but install failed — try: node install.mjs install`);
|
|
2832
|
+
} else {
|
|
2833
|
+
const ver = getCurrentVersion();
|
|
2834
|
+
ok(`Already up to date (v${ver})`);
|
|
2835
|
+
}
|
|
2836
|
+
console.log('');
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
// ─── Release: Sync Versions ─────────────────────────────────────────────────
|
|
2840
|
+
|
|
2841
|
+
function syncVersions() {
|
|
2842
|
+
console.log('\nclaude-mem-lite release — sync versions\n');
|
|
2843
|
+
|
|
2844
|
+
const pkg = JSON.parse(readFileSync(join(PROJECT_DIR, 'package.json'), 'utf8'));
|
|
2845
|
+
const version = pkg.version;
|
|
2846
|
+
log(`package.json version: ${version}`);
|
|
2847
|
+
|
|
2848
|
+
// Sync plugin.json
|
|
2849
|
+
const pluginJsonPath = join(PROJECT_DIR, '.claude-plugin', 'plugin.json');
|
|
2850
|
+
if (existsSync(pluginJsonPath)) {
|
|
2851
|
+
const pluginJson = JSON.parse(readFileSync(pluginJsonPath, 'utf8'));
|
|
2852
|
+
if (pluginJson.version !== version) {
|
|
2853
|
+
pluginJson.version = version;
|
|
2854
|
+
writeFileSync(pluginJsonPath, JSON.stringify(pluginJson, null, 2) + '\n');
|
|
2855
|
+
ok(`plugin.json: ${pluginJson.version} → ${version}`);
|
|
2856
|
+
} else {
|
|
2857
|
+
ok(`plugin.json: already ${version}`);
|
|
2858
|
+
}
|
|
2859
|
+
} else {
|
|
2860
|
+
warn('plugin.json not found');
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
// Sync marketplace.json
|
|
2864
|
+
const marketJsonPath = join(PROJECT_DIR, '.claude-plugin', 'marketplace.json');
|
|
2865
|
+
if (existsSync(marketJsonPath)) {
|
|
2866
|
+
const marketJson = JSON.parse(readFileSync(marketJsonPath, 'utf8'));
|
|
2867
|
+
const plugin = marketJson.plugins?.[0];
|
|
2868
|
+
if (plugin && plugin.version !== version) {
|
|
2869
|
+
plugin.version = version;
|
|
2870
|
+
writeFileSync(marketJsonPath, JSON.stringify(marketJson, null, 2) + '\n');
|
|
2871
|
+
ok(`marketplace.json: ${plugin.version} → ${version}`);
|
|
2872
|
+
} else if (plugin) {
|
|
2873
|
+
ok(`marketplace.json: already ${version}`);
|
|
2874
|
+
}
|
|
2875
|
+
} else {
|
|
2876
|
+
warn('marketplace.json not found');
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
console.log('');
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2745
2882
|
// ─── Main ───────────────────────────────────────────────────────────────────
|
|
2746
2883
|
|
|
2747
2884
|
switch (cmd) {
|
|
@@ -2757,6 +2894,12 @@ switch (cmd) {
|
|
|
2757
2894
|
case 'doctor':
|
|
2758
2895
|
await doctor();
|
|
2759
2896
|
break;
|
|
2897
|
+
case 'update':
|
|
2898
|
+
await manualUpdate();
|
|
2899
|
+
break;
|
|
2900
|
+
case 'release':
|
|
2901
|
+
syncVersions();
|
|
2902
|
+
break;
|
|
2760
2903
|
default:
|
|
2761
2904
|
if (IS_NPX) {
|
|
2762
2905
|
// npx claude-mem-lite (no args) → auto install
|
|
@@ -2772,6 +2915,8 @@ Usage:
|
|
|
2772
2915
|
node install.mjs uninstall --purge Remove and delete all data
|
|
2773
2916
|
node install.mjs status Show current status
|
|
2774
2917
|
node install.mjs doctor Diagnose issues
|
|
2918
|
+
node install.mjs update Check for and install updates
|
|
2919
|
+
node install.mjs release Sync version to plugin.json + marketplace.json
|
|
2775
2920
|
|
|
2776
2921
|
npx claude-mem-lite Install via npx (one-liner)
|
|
2777
2922
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-mem-lite",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.2",
|
|
4
4
|
"description": "Lightweight persistent memory system for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -33,10 +33,12 @@
|
|
|
33
33
|
"hook-episode.mjs",
|
|
34
34
|
"hook-context.mjs",
|
|
35
35
|
"hook-handoff.mjs",
|
|
36
|
+
"hook-update.mjs",
|
|
36
37
|
"haiku-client.mjs",
|
|
37
38
|
"dispatch.mjs",
|
|
38
39
|
"dispatch-inject.mjs",
|
|
39
40
|
"dispatch-feedback.mjs",
|
|
41
|
+
"dispatch-patterns.mjs",
|
|
40
42
|
"dispatch-workflow.mjs",
|
|
41
43
|
"registry.mjs",
|
|
42
44
|
"registry-retriever.mjs",
|
|
@@ -1127,6 +1127,19 @@
|
|
|
1127
1127
|
"natural"
|
|
1128
1128
|
]
|
|
1129
1129
|
},
|
|
1130
|
+
{
|
|
1131
|
+
"name": "claudeception",
|
|
1132
|
+
"type": "skill",
|
|
1133
|
+
"repo": "https://github.com/blader/Claudeception",
|
|
1134
|
+
"path": ".",
|
|
1135
|
+
"tags": [
|
|
1136
|
+
"meta",
|
|
1137
|
+
"skill-extraction",
|
|
1138
|
+
"learning",
|
|
1139
|
+
"continuous",
|
|
1140
|
+
"autonomous"
|
|
1141
|
+
]
|
|
1142
|
+
},
|
|
1130
1143
|
{
|
|
1131
1144
|
"name": "anthropic-architect",
|
|
1132
1145
|
"type": "skill",
|
package/registry-indexer.mjs
CHANGED
|
File without changes
|
package/registry-retriever.mjs
CHANGED
|
@@ -23,6 +23,7 @@ export const DISPATCH_SYNONYMS = {
|
|
|
23
23
|
'plan': ['planning', 'architecture', 'spec', 'blueprint', 'rfc', 'proposal', 'roadmap'],
|
|
24
24
|
'build': ['compile', 'bundle', 'webpack', 'vite', 'typescript', 'tsc', 'esbuild', 'rollup', 'parcel', 'babel', 'swc', 'transpile'],
|
|
25
25
|
'lint': ['eslint', 'prettier', 'biome', 'stylelint', 'format', 'style'],
|
|
26
|
+
'search': ['lookup', 'latest', 'best-practices', 'perplexity'],
|
|
26
27
|
// Chinese intent mappings
|
|
27
28
|
'清理': ['refactor', 'clean', 'lint', 'format', 'simplify'],
|
|
28
29
|
'测试': ['test', 'testing', 'tdd', 'qa', 'spec', 'jest', 'vitest', 'pytest'],
|
|
@@ -44,6 +45,7 @@ export const DISPATCH_SYNONYMS = {
|
|
|
44
45
|
'打包': ['bundle', 'build', 'webpack', 'vite'],
|
|
45
46
|
'容器': ['docker', 'container', 'kubernetes', 'infrastructure'],
|
|
46
47
|
'运维': ['devops', 'infrastructure', 'deploy', 'docker'],
|
|
48
|
+
'搜索': ['search', 'lookup', 'latest', 'perplexity'],
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
// ─── CJK Tokenization ───────────────────────────────────────────────────────
|
|
@@ -98,6 +100,9 @@ const CJK_INTENT_MAP = {
|
|
|
98
100
|
'接口': 'api', '路由': 'route',
|
|
99
101
|
// plan
|
|
100
102
|
'规划': 'planning', '架构': 'architecture', '方案': 'plan', '设计方案': 'architecture',
|
|
103
|
+
// search — only web/info search, NOT code search (grep/find)
|
|
104
|
+
'联网搜索': 'search', '网上搜索': 'search', '查资料': 'search', '找资料': 'search',
|
|
105
|
+
'搜索最新': 'search', '搜索资料': 'search', '搜索文档': 'search',
|
|
101
106
|
};
|
|
102
107
|
|
|
103
108
|
// Merge all CJK keys from both maps, longest-first to avoid partial matches
|
|
@@ -346,30 +351,30 @@ const COMPOSITE_EXPR = `(
|
|
|
346
351
|
bm25(resources_fts, 5.0, 3.0, 3.0, 2.0, 2.0, 1.0, 1.0, 1.0) * 0.4
|
|
347
352
|
- COALESCE(r.repo_stars * 1.0 / (r.repo_stars + 100.0), 0) * 0.15
|
|
348
353
|
- (
|
|
349
|
-
(r.success_count + 1.0) / (r.recommend_count + 2.0) * 0.5
|
|
354
|
+
(COALESCE(r.success_count, 0) + 1.0) / (COALESCE(r.recommend_count, 0) + 2.0) * 0.5
|
|
350
355
|
+ COALESCE(
|
|
351
356
|
(SELECT (SUM(CASE WHEN i.outcome='success' THEN 1 ELSE 0 END) + 1.0)
|
|
352
357
|
/ (COUNT(*) + 2.0)
|
|
353
358
|
FROM invocations i WHERE i.resource_id = r.id
|
|
354
359
|
AND i.created_at > datetime('now', '-30 days')),
|
|
355
|
-
(r.success_count + 1.0) / (r.recommend_count + 2.0)
|
|
360
|
+
(COALESCE(r.success_count, 0) + 1.0) / (COALESCE(r.recommend_count, 0) + 2.0)
|
|
356
361
|
) * 0.5
|
|
357
362
|
) * 0.15
|
|
358
363
|
- (
|
|
359
|
-
(r.adopt_count + 1.0) / (r.recommend_count + 2.0) * 0.5
|
|
364
|
+
(COALESCE(r.adopt_count, 0) + 1.0) / (COALESCE(r.recommend_count, 0) + 2.0) * 0.5
|
|
360
365
|
+ COALESCE(
|
|
361
366
|
(SELECT (SUM(CASE WHEN i.adopted=1 THEN 1 ELSE 0 END) + 1.0)
|
|
362
367
|
/ (COUNT(*) + 2.0)
|
|
363
368
|
FROM invocations i WHERE i.resource_id = r.id
|
|
364
369
|
AND i.created_at > datetime('now', '-30 days')),
|
|
365
|
-
(r.adopt_count + 1.0) / (r.recommend_count + 2.0)
|
|
370
|
+
(COALESCE(r.adopt_count, 0) + 1.0) / (COALESCE(r.recommend_count, 0) + 2.0)
|
|
366
371
|
) * 0.5
|
|
367
372
|
) * 0.10
|
|
368
|
-
- CASE WHEN r.recommend_count < 10
|
|
369
|
-
THEN 0.10 * (1.0 - r.recommend_count * 1.0 / 10.0)
|
|
373
|
+
- CASE WHEN COALESCE(r.recommend_count, 0) < 10
|
|
374
|
+
THEN 0.10 * (1.0 - COALESCE(r.recommend_count, 0) * 1.0 / 10.0)
|
|
370
375
|
ELSE 0 END
|
|
371
|
-
+ CASE WHEN r.recommend_count >
|
|
372
|
-
AND (r.adopt_count + 1.0) / (r.recommend_count + 2.0) < 0.1
|
|
376
|
+
+ CASE WHEN COALESCE(r.recommend_count, 0) > 8
|
|
377
|
+
AND (COALESCE(r.adopt_count, 0) + 1.0) / (COALESCE(r.recommend_count, 0) + 2.0) < 0.1
|
|
373
378
|
THEN 0.10
|
|
374
379
|
ELSE 0 END
|
|
375
380
|
)`;
|
package/registry-scanner.mjs
CHANGED
|
File without changes
|
package/registry.mjs
CHANGED
|
@@ -37,6 +37,9 @@ const RESOURCES_SCHEMA = `
|
|
|
37
37
|
recommend_count INTEGER DEFAULT 0,
|
|
38
38
|
adopt_count INTEGER DEFAULT 0,
|
|
39
39
|
success_count INTEGER DEFAULT 0,
|
|
40
|
+
silenced_until TEXT,
|
|
41
|
+
cooldown_hours INTEGER DEFAULT 0,
|
|
42
|
+
recommendation_mode TEXT DEFAULT 'proactive',
|
|
40
43
|
indexed_at TEXT,
|
|
41
44
|
created_at TEXT DEFAULT (datetime('now')),
|
|
42
45
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
@@ -106,7 +109,7 @@ const INVOCATIONS_SCHEMA = `
|
|
|
106
109
|
adopted INTEGER DEFAULT 0,
|
|
107
110
|
outcome TEXT CHECK(outcome IN ('success','partial','failure','skipped','ignored') OR outcome IS NULL),
|
|
108
111
|
score REAL,
|
|
109
|
-
rejection_reason TEXT CHECK(rejection_reason IN ('alternative','manual','context_switch','session_end','unknown') OR rejection_reason IS NULL),
|
|
112
|
+
rejection_reason TEXT CHECK(rejection_reason IN ('alternative','manual','context_switch','session_end','unknown','no_events','unclassified') OR rejection_reason IS NULL),
|
|
110
113
|
created_at TEXT DEFAULT (datetime('now'))
|
|
111
114
|
);
|
|
112
115
|
|
|
@@ -115,6 +118,9 @@ const INVOCATIONS_SCHEMA = `
|
|
|
115
118
|
|
|
116
119
|
CREATE INDEX IF NOT EXISTS idx_inv_session
|
|
117
120
|
ON invocations(session_id);
|
|
121
|
+
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_inv_created_at
|
|
123
|
+
ON invocations(created_at);
|
|
118
124
|
`;
|
|
119
125
|
|
|
120
126
|
const PREINSTALLED_SCHEMA = `
|
|
@@ -156,13 +162,15 @@ export function ensureRegistryDb(dbPath) {
|
|
|
156
162
|
|
|
157
163
|
db.exec(RESOURCES_SCHEMA);
|
|
158
164
|
|
|
159
|
-
// Migrate: add
|
|
165
|
+
// Migrate: add missing columns to resources (single PRAGMA call for all)
|
|
160
166
|
try {
|
|
161
|
-
const
|
|
162
|
-
if (!
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
const resCols = new Set(db.prepare("PRAGMA table_info(resources)").all().map(c => c.name));
|
|
168
|
+
if (!resCols.has('invocation_name')) db.exec("ALTER TABLE resources ADD COLUMN invocation_name TEXT DEFAULT ''");
|
|
169
|
+
if (!resCols.has('silenced_until')) db.exec("ALTER TABLE resources ADD COLUMN silenced_until TEXT");
|
|
170
|
+
if (!resCols.has('cooldown_hours')) db.exec("ALTER TABLE resources ADD COLUMN cooldown_hours INTEGER DEFAULT 0");
|
|
171
|
+
// recommendation_mode: 'proactive' (default, actively recommended), 'on_request' (only when explicitly asked)
|
|
172
|
+
if (!resCols.has('recommendation_mode')) db.exec("ALTER TABLE resources ADD COLUMN recommendation_mode TEXT DEFAULT 'proactive'");
|
|
173
|
+
} catch (e) { debugCatch(e, 'resources-column-migration'); }
|
|
166
174
|
|
|
167
175
|
// FTS5 + triggers: only create if not exists
|
|
168
176
|
const hasFts = db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='resources_fts'`).get();
|
package/resource-discovery.mjs
CHANGED
|
File without changes
|
package/schema.mjs
CHANGED
|
File without changes
|
package/scripts/launch.mjs
CHANGED
|
File without changes
|
package/server-internals.mjs
CHANGED
|
File without changes
|
package/server.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
|
-
import { jaccardSimilarity, truncate, typeIcon, sanitizeFtsQuery, relaxFtsQueryToOr, inferProject, computeMinHash, estimateJaccardFromMinHash, scrubSecrets, cjkBigrams, fmtDate, isoWeekKey, debugLog, debugCatch,
|
|
7
|
+
import { jaccardSimilarity, truncate, typeIcon, sanitizeFtsQuery, relaxFtsQueryToOr, inferProject, computeMinHash, estimateJaccardFromMinHash, scrubSecrets, cjkBigrams, fmtDate, isoWeekKey, debugLog, debugCatch, COMPRESSED_PENDING_PURGE } from './utils.mjs';
|
|
8
8
|
import { ensureDb, DB_PATH, REGISTRY_DB_PATH } from './schema.mjs';
|
|
9
9
|
import { reRankWithContext, markSuperseded, extractPRFTerms, expandQueryByConcepts, autoBoostIfNeeded, runIdleCleanup } from './server-internals.mjs';
|
|
10
10
|
import { memSearchSchema, memTimelineSchema, memGetSchema, memDeleteSchema, memSaveSchema, memStatsSchema, memCompressSchema, memMaintainSchema, memRegistrySchema } from './tool-schemas.mjs';
|
|
@@ -56,6 +56,27 @@ function getRegistryDb() {
|
|
|
56
56
|
|
|
57
57
|
// inferProject, jaccardSimilarity, sanitizeFtsQuery, typeIcon, truncate, fmtDate imported from utils.mjs
|
|
58
58
|
|
|
59
|
+
// ─── Project Name Resolution ────────────────────────────────────────────────
|
|
60
|
+
// Users naturally type short names like "mem" but inferProject() stores
|
|
61
|
+
// "projects--mem" (parent--base from CWD). resolveProject() bridges this gap.
|
|
62
|
+
|
|
63
|
+
const _projectCache = new Map();
|
|
64
|
+
|
|
65
|
+
function resolveProject(name) {
|
|
66
|
+
if (!name) return name;
|
|
67
|
+
if (_projectCache.has(name)) return _projectCache.get(name);
|
|
68
|
+
// Already a canonical name (contains "--")? Use as-is.
|
|
69
|
+
if (name.includes('--')) { _projectCache.set(name, name); return name; }
|
|
70
|
+
// Short name: prefer the canonical "parent--name" form (from inferProject())
|
|
71
|
+
// which typically has far more data than manually-saved short names.
|
|
72
|
+
const suffixed = db.prepare(
|
|
73
|
+
'SELECT project FROM observations WHERE project LIKE ? GROUP BY project ORDER BY COUNT(*) DESC LIMIT 1'
|
|
74
|
+
).get(`%--${name}`);
|
|
75
|
+
const resolved = suffixed ? suffixed.project : name;
|
|
76
|
+
_projectCache.set(name, resolved);
|
|
77
|
+
return resolved;
|
|
78
|
+
}
|
|
79
|
+
|
|
59
80
|
// ─── Scoring Model Constants ────────────────────────────────────────────────
|
|
60
81
|
//
|
|
61
82
|
// Composite scoring: BM25(weights) × recency_decay × [project_boost] × [importance] × [access_bonus]
|
|
@@ -97,8 +118,12 @@ const server = new McpServer(
|
|
|
97
118
|
'- Non-obvious debugging discovery → mem_save with type="bugfix"',
|
|
98
119
|
'- Key architecture decision → mem_save with type="decision"',
|
|
99
120
|
'- Important pattern or convention found → mem_save with type="discovery"',
|
|
121
|
+
'- Do NOT save: ephemeral task state, git history, obvious code patterns, or info derivable from code.',
|
|
100
122
|
'',
|
|
101
|
-
'WORKFLOW: mem_search → mem_timeline(anchor=ID) → mem_get(ids=[...]) for full
|
|
123
|
+
'SEARCH WORKFLOW: mem_search → mem_timeline(anchor=ID) for surrounding context → mem_get(ids=[...]) for full details.',
|
|
124
|
+
'Search tips: use short keywords (2-3 words), not full sentences. Filter with obs_type when relevant.',
|
|
125
|
+
'',
|
|
126
|
+
'MAINTENANCE: mem_compress consolidates old observations. mem_maintain runs dedup/cleanup/reindex.',
|
|
102
127
|
].join('\n'),
|
|
103
128
|
},
|
|
104
129
|
);
|
|
@@ -214,15 +239,15 @@ function searchObservations(ctx) {
|
|
|
214
239
|
const orQuery = relaxFtsQueryToOr(ftsQuery);
|
|
215
240
|
if (orQuery) {
|
|
216
241
|
try {
|
|
217
|
-
const orRows = db.prepare(buildObsFtsQuery('full', { multiplier: 0.
|
|
242
|
+
const orRows = db.prepare(buildObsFtsQuery('full', { multiplier: 0.5, withSnippet: true, withOffset: true }))
|
|
218
243
|
.all(...buildObsFtsParams({ now, projectBoost, ftsQuery: orQuery, args, epochFrom, epochTo, limit: perSourceLimit, offset: perSourceOffset }));
|
|
219
244
|
for (const r of orRows) results.push(ftsRowToResult(r, { snippet: true }));
|
|
220
245
|
} catch (e) { debugCatch(e, 'searchObservations-or-fallback'); }
|
|
221
246
|
}
|
|
222
247
|
}
|
|
223
248
|
|
|
224
|
-
// Two-phase query expansion for sparse results
|
|
225
|
-
if (rows.length > 0 && results.length < limit) {
|
|
249
|
+
// Two-phase query expansion for sparse results (only when well below limit)
|
|
250
|
+
if (rows.length > 0 && results.length < Math.ceil(limit / 2)) {
|
|
226
251
|
const existingIds = new Set(results.map(r => r.id));
|
|
227
252
|
expandObsByConceptCo(ctx, now, existingIds, results);
|
|
228
253
|
expandObsByPRF(ctx, now, rows.length, existingIds, results);
|
|
@@ -404,11 +429,17 @@ function searchPrompts(ctx) {
|
|
|
404
429
|
|
|
405
430
|
function formatSearchOutput(paginatedResults, args, ftsQuery, totalCount, isCrossSource) {
|
|
406
431
|
if (paginatedResults.length === 0) {
|
|
407
|
-
const hint = [
|
|
408
|
-
if (args.query) {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
432
|
+
const hint = [];
|
|
433
|
+
if (args.query && !ftsQuery) {
|
|
434
|
+
hint.push(`Query "${args.query}" was filtered (FTS5 keywords/special chars only).`);
|
|
435
|
+
hint.push('Tip: use content words instead of operators (AND, OR, NOT, NEAR).');
|
|
436
|
+
} else {
|
|
437
|
+
hint.push('No results found.');
|
|
438
|
+
if (args.query) {
|
|
439
|
+
const expanded = ftsQuery || args.query;
|
|
440
|
+
if (expanded !== args.query) hint.push(`Searched as: ${expanded}`);
|
|
441
|
+
hint.push('Tip: check spelling, try broader terms, or use mem_stats to see available data.');
|
|
442
|
+
}
|
|
412
443
|
}
|
|
413
444
|
return { content: [{ type: 'text', text: hint.join('\n') }] };
|
|
414
445
|
}
|
|
@@ -446,6 +477,7 @@ server.registerTool(
|
|
|
446
477
|
inputSchema: memSearchSchema,
|
|
447
478
|
},
|
|
448
479
|
safeHandler(async (args) => {
|
|
480
|
+
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
449
481
|
const limit = args.limit ?? 20;
|
|
450
482
|
const offset = args.offset ?? 0;
|
|
451
483
|
const ftsQuery = sanitizeFtsQuery(args.query);
|
|
@@ -463,8 +495,13 @@ server.registerTool(
|
|
|
463
495
|
if (epochTo !== null && args.date_to && /^\d{4}-\d{2}-\d{2}$/.test(args.date_to)) {
|
|
464
496
|
epochTo += 86400000 - 1; // extend to 23:59:59.999
|
|
465
497
|
}
|
|
466
|
-
if (epochFrom !== null && isNaN(epochFrom)) throw new Error(`Invalid date_from: ${args.date_from}`);
|
|
467
|
-
if (epochTo !== null && isNaN(epochTo)) throw new Error(`Invalid date_to: ${args.date_to}`);
|
|
498
|
+
if (epochFrom !== null && isNaN(epochFrom)) throw new Error(`Invalid date_from: "${args.date_from}" (use ISO 8601 or YYYY-MM-DD)`);
|
|
499
|
+
if (epochTo !== null && isNaN(epochTo)) throw new Error(`Invalid date_to: "${args.date_to}" (use ISO 8601 or YYYY-MM-DD)`);
|
|
500
|
+
|
|
501
|
+
// Early return when query was provided but sanitized to nothing (all FTS5 keywords/special chars)
|
|
502
|
+
if (args.query && !ftsQuery && !epochFrom && !epochTo && !args.obs_type && !args.importance) {
|
|
503
|
+
return formatSearchOutput([], args, ftsQuery, 0, false);
|
|
504
|
+
}
|
|
468
505
|
|
|
469
506
|
const ctx = { ftsQuery, searchType, args, epochFrom, epochTo, perSourceLimit, perSourceOffset, currentProject, limit };
|
|
470
507
|
const results = [];
|
|
@@ -506,6 +543,7 @@ server.registerTool(
|
|
|
506
543
|
inputSchema: memTimelineSchema,
|
|
507
544
|
},
|
|
508
545
|
safeHandler(async (args) => {
|
|
546
|
+
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
509
547
|
const before = args.before ?? 5;
|
|
510
548
|
const after = args.after ?? 5;
|
|
511
549
|
let anchorId = args.anchor;
|
|
@@ -705,7 +743,10 @@ server.registerTool(
|
|
|
705
743
|
});
|
|
706
744
|
const result = deleteTx();
|
|
707
745
|
|
|
708
|
-
|
|
746
|
+
const missing = args.ids.filter(id => !rows.some(r => r.id === id));
|
|
747
|
+
const msg = [`Deleted ${result.changes} observation(s).`];
|
|
748
|
+
if (missing.length > 0) msg.push(`Note: ID(s) ${missing.join(', ')} not found.`);
|
|
749
|
+
return { content: [{ type: 'text', text: msg.join(' ') }] };
|
|
709
750
|
})
|
|
710
751
|
);
|
|
711
752
|
|
|
@@ -718,6 +759,7 @@ server.registerTool(
|
|
|
718
759
|
inputSchema: memSaveSchema,
|
|
719
760
|
},
|
|
720
761
|
safeHandler(async (args) => {
|
|
762
|
+
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
721
763
|
const now = new Date();
|
|
722
764
|
const project = args.project || inferProject();
|
|
723
765
|
const type = args.type || 'discovery';
|
|
@@ -770,6 +812,7 @@ server.registerTool(
|
|
|
770
812
|
inputSchema: memStatsSchema,
|
|
771
813
|
},
|
|
772
814
|
safeHandler(async (args) => {
|
|
815
|
+
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
773
816
|
const days = args.days ?? 30;
|
|
774
817
|
const cutoff = Date.now() - days * 86400000;
|
|
775
818
|
const projectFilter = args.project ? 'AND project = ?' : '';
|
|
@@ -865,6 +908,7 @@ server.registerTool(
|
|
|
865
908
|
inputSchema: memCompressSchema,
|
|
866
909
|
},
|
|
867
910
|
safeHandler(async (args) => {
|
|
911
|
+
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
868
912
|
const preview = args.preview !== false;
|
|
869
913
|
const ageDays = args.age_days ?? 60;
|
|
870
914
|
const cutoff = Date.now() - ageDays * 86400000;
|
|
@@ -977,6 +1021,7 @@ server.registerTool(
|
|
|
977
1021
|
inputSchema: memMaintainSchema,
|
|
978
1022
|
},
|
|
979
1023
|
safeHandler(async (args) => {
|
|
1024
|
+
if (args.project) args = { ...args, project: resolveProject(args.project) };
|
|
980
1025
|
const STALE_AGE_MS = 30 * 86400000;
|
|
981
1026
|
const SIMILARITY_THRESHOLD = 0.7;
|
|
982
1027
|
const SCAN_LIMIT = 500;
|
package/skill.md
CHANGED
|
File without changes
|