claude-mem-lite 3.96.0 → 3.97.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/adopt-cli.mjs +125 -51
- package/bash-utils.mjs +105 -29
- package/claudemd.mjs +63 -19
- package/cli/activity.mjs +36 -18
- package/cli/common.mjs +101 -18
- package/cli/doctor.mjs +29 -9
- package/cli.mjs +69 -10
- package/deep-search.mjs +63 -20
- package/format-utils.mjs +15 -7
- package/haiku-client.mjs +189 -111
- package/hash-utils.mjs +15 -6
- package/hook-context.mjs +135 -46
- package/hook-episode.mjs +76 -23
- package/hook-handoff.mjs +216 -84
- package/hook-llm.mjs +476 -216
- package/hook-memory.mjs +106 -43
- package/hook-optimize.mjs +272 -109
- package/hook-precompact.mjs +1 -1
- package/hook-semaphore.mjs +46 -12
- package/hook-shared.mjs +101 -131
- package/hook-update.mjs +286 -88
- package/hook.mjs +1207 -617
- package/install-metadata.mjs +246 -125
- package/install.mjs +1327 -917
- package/lib/activity.mjs +52 -29
- package/lib/atomic-write.mjs +23 -4
- package/lib/binding-probe.mjs +40 -38
- package/lib/browse-core.mjs +22 -8
- package/lib/citation-tracker.mjs +189 -79
- package/lib/cite-back-hint.mjs +37 -19
- package/lib/cite-recall-path.mjs +3 -1
- package/lib/cli-flags.mjs +4 -2
- package/lib/cli-project.mjs +8 -5
- package/lib/compress-core.mjs +34 -9
- package/lib/cooldown-path.mjs +3 -1
- package/lib/db-backup.mjs +33 -11
- package/lib/deferred-work.mjs +73 -32
- package/lib/delete-core.mjs +25 -9
- package/lib/doctor-benchmark.mjs +5 -1
- package/lib/doctor-drift.mjs +25 -8
- package/lib/edge-attribution.mjs +23 -9
- package/lib/env-number.mjs +7 -8
- package/lib/err-sampler.mjs +22 -11
- package/lib/error-recall-core.mjs +15 -17
- package/lib/events-injection.mjs +25 -8
- package/lib/export-columns.mjs +49 -10
- package/lib/fast-summary.mjs +22 -9
- package/lib/file-edge-match.mjs +4 -2
- package/lib/file-intel.mjs +38 -13
- package/lib/frontmatter.mjs +22 -5
- package/lib/get-core.mjs +78 -11
- package/lib/handoff-constants.mjs +18 -0
- package/lib/hook-stdin.mjs +10 -3
- package/lib/hook-stdout.mjs +21 -7
- package/lib/hook-telemetry.mjs +44 -17
- package/lib/id-routing.mjs +11 -6
- package/lib/import-jsonl.mjs +92 -41
- package/lib/inject-search-core.mjs +5 -2
- package/lib/injected-ids.mjs +19 -12
- package/lib/install-shape.mjs +47 -23
- package/lib/keyctx-marker.mjs +3 -1
- package/lib/lesson-bridge.mjs +8 -5
- package/lib/llm-call.mjs +76 -0
- package/lib/llm-provider-probe.mjs +24 -12
- package/lib/low-signal-patterns.mjs +25 -22
- package/lib/maintain-core.mjs +221 -92
- package/lib/mem-override.mjs +4 -2
- package/lib/metrics.mjs +30 -25
- package/lib/native-binding-hint.mjs +29 -8
- package/lib/observation-write.mjs +74 -19
- package/lib/patha-exclude-meter.mjs +10 -6
- package/lib/persist-reminder.mjs +22 -10
- package/lib/plan-reader.mjs +2 -2
- package/lib/private-strip.mjs +3 -3
- package/lib/proc-lock.mjs +15 -3
- package/lib/proxy-fetch.mjs +49 -13
- package/lib/quiet-scope.mjs +42 -0
- package/lib/recall-core.mjs +19 -7
- package/lib/recent-core.mjs +18 -5
- package/lib/registry-core.mjs +12 -6
- package/lib/release-digest.mjs +4 -1
- package/lib/relevance-floor.mjs +5 -2
- package/lib/reread-guard.mjs +16 -8
- package/lib/resolve-data-dir.mjs +1 -1
- package/lib/rrf.mjs +4 -1
- package/lib/save-enrich.mjs +36 -18
- package/lib/save-observation.mjs +78 -35
- package/lib/scrub-record.mjs +20 -6
- package/lib/search-core.mjs +456 -123
- package/lib/shard-gc.mjs +50 -0
- package/lib/startup-dashboard.mjs +20 -9
- package/lib/stats-core.mjs +101 -35
- package/lib/stats-quality.mjs +56 -20
- package/lib/summary-extractor.mjs +2 -2
- package/lib/task-imperative.mjs +12 -4
- package/lib/timeline-core.mjs +88 -28
- package/lib/tmp-fixture-sweep.mjs +32 -9
- package/lib/transcript-scan.mjs +20 -4
- package/lib/upgrade-banner.mjs +11 -7
- package/mem-cli.mjs +1314 -506
- package/memdir.mjs +53 -21
- package/nlp.mjs +68 -38
- package/npm-shrinkwrap.json +2 -2
- package/package.json +9 -2
- package/plugin-cache-guard.mjs +31 -15
- package/project-utils.mjs +44 -20
- package/registry-enricher.mjs +26 -6
- package/registry-github.mjs +4 -2
- package/registry-importer.mjs +151 -40
- package/registry-recommend.mjs +189 -96
- package/registry-retriever.mjs +229 -70
- package/registry-scanner.mjs +11 -6
- package/registry.mjs +163 -66
- package/rerank.mjs +18 -4
- package/resource-discovery.mjs +14 -4
- package/schema.mjs +258 -99
- package/scoring-sql.mjs +21 -10
- package/scripts/binding-probe-cli.mjs +17 -12
- package/scripts/hook-launcher.mjs +148 -31
- package/scripts/launch-preflight.mjs +5 -7
- package/scripts/launch.mjs +21 -9
- package/scripts/post-tool-recall.js +21 -5
- package/scripts/pre-agent-inject.js +51 -13
- package/scripts/pre-skill-bridge.js +22 -7
- package/scripts/pre-tool-recall.js +157 -77
- package/scripts/prompt-search-utils.mjs +70 -25
- package/scripts/setup.sh +16 -1
- package/scripts/user-prompt-search.js +238 -97
- package/search-engine.mjs +329 -96
- package/search-scoring.mjs +75 -33
- package/secret-scrub.mjs +48 -12
- package/server/fts-check.mjs +12 -9
- package/server.mjs +674 -243
- package/skip-tools.mjs +19 -8
- package/source-files.mjs +54 -21
- package/stop-words.mjs +152 -19
- package/synonyms.mjs +579 -164
- package/tfidf.mjs +130 -40
- package/tier.mjs +1 -1
- package/tool-schemas.mjs +381 -131
- package/utils.mjs +184 -36
package/tool-schemas.mjs
CHANGED
|
@@ -9,48 +9,51 @@ export const OBS_TYPE_ENUM = z.enum([...OBS_TYPES]);
|
|
|
9
9
|
|
|
10
10
|
// LLM-friendly coercion: accept string numbers and normalize to proper types
|
|
11
11
|
const coerceInt = z.preprocess(
|
|
12
|
-
(v) => (typeof v === 'string' && /^-?\d+$/.test(v.trim())
|
|
13
|
-
z.number().int()
|
|
12
|
+
(v) => (typeof v === 'string' && /^-?\d+$/.test(v.trim()) ? parseInt(v.trim(), 10) : v),
|
|
13
|
+
z.number().int(),
|
|
14
14
|
);
|
|
15
15
|
|
|
16
16
|
// LLM-friendly coercion: accept "true"/"false"/"True"/"TRUE" strings as boolean
|
|
17
17
|
const coerceBool = z.preprocess(
|
|
18
|
-
(v) => typeof v === 'string' ? ({ true: true, false: false }
|
|
19
|
-
z.boolean()
|
|
18
|
+
(v) => (typeof v === 'string' ? ({ true: true, false: false }[v.toLowerCase()] ?? v) : v),
|
|
19
|
+
z.boolean(),
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
// Coerce ids: accept single number, string "123", comma-separated "1,2,3", or array
|
|
23
|
-
const coerceIntArray = z.preprocess(
|
|
24
|
-
(v) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
const coerceIntArray = z.preprocess((v) => {
|
|
24
|
+
if (Array.isArray(v)) return v.map((x) => (typeof x === 'string' ? parseInt(x, 10) : x));
|
|
25
|
+
if (typeof v === 'number') return [v];
|
|
26
|
+
if (typeof v === 'string')
|
|
27
|
+
return v
|
|
28
|
+
.split(',')
|
|
29
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
30
|
+
.filter((n) => !isNaN(n));
|
|
31
|
+
return v;
|
|
32
|
+
}, z.array(z.number().int()));
|
|
32
33
|
|
|
33
34
|
// Coerce string arrays: accept array, comma-separated string, JSON-array string, or bare string.
|
|
34
35
|
// MCP bridges sometimes JSON-stringify complex args — bare `z.array(z.string())` rejects those
|
|
35
36
|
// with "expected array, received string" and the caller loses the field silently. Parity with
|
|
36
37
|
// coerceIntArray: tolerate the same shapes so files/fields survive client serialization quirks.
|
|
37
|
-
const coerceStringArray = z.preprocess(
|
|
38
|
-
(v) =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
const coerceStringArray = z.preprocess((v) => {
|
|
39
|
+
if (Array.isArray(v)) return v.map((x) => (typeof x === 'string' ? x : String(x)));
|
|
40
|
+
if (typeof v === 'string') {
|
|
41
|
+
const s = v.trim();
|
|
42
|
+
if (s.startsWith('[') && s.endsWith(']')) {
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(s);
|
|
45
|
+
if (Array.isArray(parsed)) return parsed.map((x) => (typeof x === 'string' ? x : String(x)));
|
|
46
|
+
} catch {
|
|
47
|
+
/* fall through to comma-split */
|
|
47
48
|
}
|
|
48
|
-
return s.split(',').map(x => x.trim()).filter(x => x.length > 0);
|
|
49
49
|
}
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
);
|
|
50
|
+
return s
|
|
51
|
+
.split(',')
|
|
52
|
+
.map((x) => x.trim())
|
|
53
|
+
.filter((x) => x.length > 0);
|
|
54
|
+
}
|
|
55
|
+
return v;
|
|
56
|
+
}, z.array(z.string()));
|
|
54
57
|
|
|
55
58
|
// Coerce mixed ID tokens (#N / P#N / S#N / D#N / bare N) for mem_get. Accepts:
|
|
56
59
|
// - native arrays: [1, "P#2", "#3"]
|
|
@@ -61,45 +64,93 @@ const coerceStringArray = z.preprocess(
|
|
|
61
64
|
// at the handler. Closes the CLI↔MCP gap noted in #8127.
|
|
62
65
|
const coerceMixedIdTokens = z.preprocess(
|
|
63
66
|
(v) => {
|
|
64
|
-
const norm = (x) => typeof x === 'string' ? x.trim() : String(x);
|
|
65
|
-
if (Array.isArray(v)) return v.map(norm).filter(s => s.length > 0);
|
|
67
|
+
const norm = (x) => (typeof x === 'string' ? x.trim() : String(x));
|
|
68
|
+
if (Array.isArray(v)) return v.map(norm).filter((s) => s.length > 0);
|
|
66
69
|
if (typeof v === 'number') return [String(v)];
|
|
67
70
|
if (typeof v === 'string') {
|
|
68
71
|
const s = v.trim();
|
|
69
72
|
if (s.startsWith('[') && s.endsWith(']')) {
|
|
70
73
|
try {
|
|
71
74
|
const parsed = JSON.parse(s);
|
|
72
|
-
if (Array.isArray(parsed)) return parsed.map(norm).filter(x => x.length > 0);
|
|
73
|
-
} catch {
|
|
75
|
+
if (Array.isArray(parsed)) return parsed.map(norm).filter((x) => x.length > 0);
|
|
76
|
+
} catch {
|
|
77
|
+
/* fall through to comma-split */
|
|
78
|
+
}
|
|
74
79
|
}
|
|
75
|
-
return s
|
|
80
|
+
return s
|
|
81
|
+
.split(',')
|
|
82
|
+
.map((x) => x.trim())
|
|
83
|
+
.filter(Boolean);
|
|
76
84
|
}
|
|
77
85
|
return v;
|
|
78
86
|
},
|
|
79
87
|
// D#N (deferred_work) requires the `#` — bare "D92" is prose, not a token.
|
|
80
88
|
// Round-trip rule: `defer list` renders "(D#92)", so mem_get must accept it
|
|
81
89
|
// back (tests/schema-roundtrip.test.mjs). Delete/timeline keep rejecting D#.
|
|
82
|
-
z
|
|
90
|
+
z
|
|
91
|
+
.array(z.string().regex(/^(?:[Dd]#|[EePpSs]?#?)\d+$/, 'Expected N, #N, P#N, S#N, E#N, or D#N'))
|
|
92
|
+
.min(1)
|
|
93
|
+
.max(20),
|
|
83
94
|
);
|
|
84
95
|
|
|
85
96
|
export const memSearchSchema = {
|
|
86
97
|
query: z.string().optional().describe('Search query (FTS5 syntax supported)'),
|
|
87
|
-
type: z
|
|
98
|
+
type: z
|
|
99
|
+
.enum(['observations', 'sessions', 'prompts', 'events'])
|
|
100
|
+
.optional()
|
|
101
|
+
.describe(
|
|
102
|
+
'Limit to one source table (default: all). events = the canonical bugfix/feature/decision/lesson history (auto-captured event-typed memories)',
|
|
103
|
+
),
|
|
88
104
|
obs_type: OBS_TYPE_ENUM.optional().describe('Filter observation type'),
|
|
89
105
|
project: z.string().optional().describe('Filter by project name'),
|
|
90
106
|
date_from: z.string().optional().describe('Start date (ISO 8601 or YYYY-MM-DD)'),
|
|
91
|
-
date_to: z
|
|
92
|
-
|
|
93
|
-
|
|
107
|
+
date_to: z
|
|
108
|
+
.string()
|
|
109
|
+
.optional()
|
|
110
|
+
.describe('End date (ISO 8601 or YYYY-MM-DD). Date-only format is inclusive (covers full day)'),
|
|
111
|
+
date_since: z
|
|
112
|
+
.string()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe(
|
|
115
|
+
'Relative lower bound from now: 7d/24h/90m/2w/30s. Use for "recent" queries instead of computing a date_from; ignored when date_from is set',
|
|
116
|
+
),
|
|
117
|
+
importance: coerceInt
|
|
118
|
+
.pipe(z.number().int().min(1).max(3))
|
|
119
|
+
.optional()
|
|
120
|
+
.describe('Minimum importance (1=routine, 2=notable, 3=critical)'),
|
|
94
121
|
branch: z.string().optional().describe('Filter by git branch name'),
|
|
95
|
-
tier: z
|
|
122
|
+
tier: z
|
|
123
|
+
.enum(['working', 'active', 'archive'])
|
|
124
|
+
.optional()
|
|
125
|
+
.describe(
|
|
126
|
+
'Filter by memory tier (working=current session, active=within decay window, archive=old/compressed)',
|
|
127
|
+
),
|
|
96
128
|
limit: coerceInt.pipe(z.number().int().min(1).max(100)).optional().describe('Max results (default 20)'),
|
|
97
129
|
offset: coerceInt.pipe(z.number().int().min(0)).optional().describe('Offset for pagination'),
|
|
98
|
-
sort: z
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
130
|
+
sort: z
|
|
131
|
+
.enum(['relevance', 'time', 'importance'])
|
|
132
|
+
.optional()
|
|
133
|
+
.describe('Sort order: relevance (default, BM25), time (newest first), importance (highest first)'),
|
|
134
|
+
include_noise: coerceBool
|
|
135
|
+
.optional()
|
|
136
|
+
.describe(
|
|
137
|
+
'Include hook-llm fallback titles ("Modified X", "Worked on X", raw error logs) — hidden by default as they have ~3% access rate',
|
|
138
|
+
),
|
|
139
|
+
or: coerceBool
|
|
140
|
+
.optional()
|
|
141
|
+
.describe(
|
|
142
|
+
'Force OR semantics between query terms from the start (default: AND with automatic OR-fallback when AND returns 0). Aligns with CLI --or.',
|
|
143
|
+
),
|
|
144
|
+
deep: coerceBool
|
|
145
|
+
.optional()
|
|
146
|
+
.describe(
|
|
147
|
+
'Tri-state LLM multi-query/HyDE deep search (observations-only). true=force; false=never; omit=AUTO (default ON for mem_search): a normal search that returns weak/few results auto-escalates with ONE Haiku call (query rewritten to keyword/concept/HyDE variants, RRF-fused). Set CLAUDE_MEM_AUTO_DEEP=0 to disable AUTO. Passive recall stays single-query.',
|
|
148
|
+
),
|
|
149
|
+
rerank: coerceBool
|
|
150
|
+
.optional()
|
|
151
|
+
.describe(
|
|
152
|
+
'Opt-in: LLM-rerank the deep-search candidates for ranking precision (one extra Haiku call, ~1.4s). Requires deep=true (no effect on AUTO/normal). Reserve for hard, ranking-sensitive queries where the right memory is likely retrieved but mis-ranked — skip for routine search. Default off.',
|
|
153
|
+
),
|
|
103
154
|
// ── CLI-flag aliases (v3.61.0) ──────────────────────────────────────────────
|
|
104
155
|
// A property the schema doesn't declare is STRIPPED by the validator, so a caller
|
|
105
156
|
// using the CLI vocabulary (`--source` / `--from` / `--to` / `--since`) previously
|
|
@@ -107,7 +158,12 @@ export const memSearchSchema = {
|
|
|
107
158
|
// result set that reads as filtered. Declaring the aliases makes the filter apply;
|
|
108
159
|
// the canonical name wins when both are supplied. Mirror of v3.59.0, which taught
|
|
109
160
|
// the CLI to accept MCP field names.
|
|
110
|
-
source: z
|
|
161
|
+
source: z
|
|
162
|
+
.enum(['observations', 'sessions', 'prompts', 'events'])
|
|
163
|
+
.optional()
|
|
164
|
+
.describe(
|
|
165
|
+
'Alias for `type` (CLI `search --source`). Note: CLI `--type` is the OBSERVATION type — that is `obs_type` here.',
|
|
166
|
+
),
|
|
111
167
|
from: z.string().optional().describe('Alias for `date_from` (CLI `search --from`)'),
|
|
112
168
|
to: z.string().optional().describe('Alias for `date_to` (CLI `search --to`)'),
|
|
113
169
|
since: z.string().optional().describe('Alias for `date_since` (CLI `search --since`)'),
|
|
@@ -116,8 +172,15 @@ export const memSearchSchema = {
|
|
|
116
172
|
export const memRecentSchema = {
|
|
117
173
|
limit: coerceInt.pipe(z.number().int().min(1).max(100)).optional().describe('Max results (default 10)'),
|
|
118
174
|
project: z.string().optional().describe('Filter by project (default: inferred from CWD)'),
|
|
119
|
-
obs_type: OBS_TYPE_ENUM.optional().describe(
|
|
120
|
-
|
|
175
|
+
obs_type: OBS_TYPE_ENUM.optional().describe(
|
|
176
|
+
'Filter observation type (e.g. bugfix, decision) — CLI `recent --type` parity',
|
|
177
|
+
),
|
|
178
|
+
date_since: z
|
|
179
|
+
.string()
|
|
180
|
+
.optional()
|
|
181
|
+
.describe(
|
|
182
|
+
'Relative lower bound from now: 7d/24h/90m/2w/30s. Only items newer than the window (pair with a high limit for "everything since X")',
|
|
183
|
+
),
|
|
121
184
|
// CLI-flag aliases — see the note on memSearchSchema. `recent --type` IS the
|
|
122
185
|
// observation type here (unlike `search --type`, which the CLI spells `--source`),
|
|
123
186
|
// so `type` maps to obs_type on this tool.
|
|
@@ -138,17 +201,27 @@ const coerceAnchor = z.preprocess(
|
|
|
138
201
|
}
|
|
139
202
|
return v;
|
|
140
203
|
},
|
|
141
|
-
z.union([
|
|
142
|
-
z.number().int(),
|
|
143
|
-
z.string().regex(/^[EePpSs]?#?\d+$/, 'Expected N, #N, P#N, S#N, or E#N'),
|
|
144
|
-
])
|
|
204
|
+
z.union([z.number().int(), z.string().regex(/^[EePpSs]?#?\d+$/, 'Expected N, #N, P#N, S#N, or E#N')]),
|
|
145
205
|
);
|
|
146
206
|
|
|
147
207
|
export const memTimelineSchema = {
|
|
148
|
-
anchor: coerceAnchor
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
208
|
+
anchor: coerceAnchor
|
|
209
|
+
.optional()
|
|
210
|
+
.describe(
|
|
211
|
+
'Anchor as observation ID (int) or prefixed token string: "#123", "P#123" (prompt → nearest obs), "S#123" (session → nearest obs), "E#123" (event → nearest obs). Takes precedence over query.',
|
|
212
|
+
),
|
|
213
|
+
query: z
|
|
214
|
+
.string()
|
|
215
|
+
.optional()
|
|
216
|
+
.describe('FTS5 query to auto-find anchor. Ignored when anchor is also given; use one or the other.'),
|
|
217
|
+
before: coerceInt
|
|
218
|
+
.pipe(z.number().int().min(0).max(50))
|
|
219
|
+
.optional()
|
|
220
|
+
.describe('Items before anchor (default 5)'),
|
|
221
|
+
after: coerceInt
|
|
222
|
+
.pipe(z.number().int().min(0).max(50))
|
|
223
|
+
.optional()
|
|
224
|
+
.describe('Items after anchor (default 5)'),
|
|
152
225
|
project: z.string().optional().describe('Filter by project'),
|
|
153
226
|
};
|
|
154
227
|
|
|
@@ -156,9 +229,20 @@ export const memGetSchema = {
|
|
|
156
229
|
// Accepts mixed tokens so pasted search results work verbatim: [1], [1, "P#2"], "1,P#2,S#3",
|
|
157
230
|
// or the JSON-stringified form ["1","P#2"]. Each token's prefix routes to its source bucket
|
|
158
231
|
// in server.mjs via lib/id-routing.bucketIdTokens. An explicit `source` override still wins.
|
|
159
|
-
ids: coerceMixedIdTokens.describe(
|
|
160
|
-
|
|
161
|
-
|
|
232
|
+
ids: coerceMixedIdTokens.describe(
|
|
233
|
+
'Mixed observation/prompt/session/event/deferred IDs — accepts N, #N, P#N, S#N, E#N, D#N; comma-strings and JSON arrays also coerced. D#N reads a deferred_work item with FULL detail (defer list is title-only)',
|
|
234
|
+
),
|
|
235
|
+
source: z
|
|
236
|
+
.enum(['obs', 'session', 'prompt', 'event'])
|
|
237
|
+
.optional()
|
|
238
|
+
.describe(
|
|
239
|
+
'Force all IDs to this source (overrides per-token prefixes). Omit to let P#/S#/E#/# prefixes route individually. D#N tokens are exempt — they always read deferred_work.',
|
|
240
|
+
),
|
|
241
|
+
fields: coerceStringArray
|
|
242
|
+
.optional()
|
|
243
|
+
.describe(
|
|
244
|
+
'Specific fields to return (default: all; validated against obs schema — session/prompt sources ignore this filter)',
|
|
245
|
+
),
|
|
162
246
|
};
|
|
163
247
|
|
|
164
248
|
export const memDeleteSchema = {
|
|
@@ -173,7 +257,7 @@ export const memDeleteSchema = {
|
|
|
173
257
|
const coerceDeferredTokens = z.preprocess(
|
|
174
258
|
(v) => {
|
|
175
259
|
if (Array.isArray(v)) {
|
|
176
|
-
return v.map(x => {
|
|
260
|
+
return v.map((x) => {
|
|
177
261
|
if (typeof x === 'number') return x;
|
|
178
262
|
if (typeof x === 'string') {
|
|
179
263
|
const s = x.trim();
|
|
@@ -185,10 +269,15 @@ const coerceDeferredTokens = z.preprocess(
|
|
|
185
269
|
}
|
|
186
270
|
return v;
|
|
187
271
|
},
|
|
188
|
-
z
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
272
|
+
z
|
|
273
|
+
.array(
|
|
274
|
+
z.union([
|
|
275
|
+
z.number().int().positive(),
|
|
276
|
+
z.string().regex(/^D#\d+$/, 'expected D#N (raw id) or positive integer (ordinal)'),
|
|
277
|
+
]),
|
|
278
|
+
)
|
|
279
|
+
.min(1)
|
|
280
|
+
.max(20),
|
|
192
281
|
);
|
|
193
282
|
|
|
194
283
|
// Coerce supersedes input — positive observation ids, plus `E#<n>` for an EVENT row
|
|
@@ -198,13 +287,19 @@ const coerceDeferredTokens = z.preprocess(
|
|
|
198
287
|
// a number here would lose the only thing that says which table it names.
|
|
199
288
|
// Empty/other shapes reject.
|
|
200
289
|
const coerceSupersedes = z.preprocess(
|
|
201
|
-
(v) =>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
290
|
+
(v) =>
|
|
291
|
+
Array.isArray(v)
|
|
292
|
+
? v.map((x) => (typeof x === 'string' && /^\d+$/.test(x.trim()) ? parseInt(x.trim(), 10) : x))
|
|
293
|
+
: v,
|
|
294
|
+
z
|
|
295
|
+
.array(
|
|
296
|
+
z.union([
|
|
297
|
+
z.number().int().positive(),
|
|
298
|
+
z.string().regex(/^[Ee]#?\d+$/, 'expected a positive observation id or E#<n> for an event'),
|
|
299
|
+
]),
|
|
300
|
+
)
|
|
301
|
+
.min(1)
|
|
302
|
+
.max(20),
|
|
208
303
|
);
|
|
209
304
|
|
|
210
305
|
export const memSaveSchema = {
|
|
@@ -219,50 +314,122 @@ export const memSaveSchema = {
|
|
|
219
314
|
// every `--type bugfix` filter, so it must not be a silent coercion.
|
|
220
315
|
obs_type: OBS_TYPE_ENUM.optional().describe('Alias for `type` (parity with mem_search/mem_recent)'),
|
|
221
316
|
project: z.string().optional().describe('Project name (default: inferred from CWD)'),
|
|
222
|
-
importance: coerceInt
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
317
|
+
importance: coerceInt
|
|
318
|
+
.pipe(z.number().int().min(1).max(3))
|
|
319
|
+
.optional()
|
|
320
|
+
.describe('Importance level: 1=routine, 2=notable, 3=critical (default: 2 for explicit saves)'),
|
|
321
|
+
files: coerceStringArray
|
|
322
|
+
.optional()
|
|
323
|
+
.describe(
|
|
324
|
+
'File paths associated with this observation. Stored in the `files_modified` column and rendered as `files` — passing a path here does not assert the file was edited; a file you only read belongs here too',
|
|
325
|
+
),
|
|
326
|
+
lesson_learned: z
|
|
327
|
+
.string()
|
|
328
|
+
.max(500)
|
|
329
|
+
.optional()
|
|
330
|
+
.describe('Key lesson or takeaway, ≤500 chars (for bugfix: root cause & fix; for decision: rationale)'),
|
|
331
|
+
closes_deferred: coerceDeferredTokens
|
|
332
|
+
.optional()
|
|
333
|
+
.describe(
|
|
334
|
+
'Close one or more deferred_work items in the same project. Mixed array: bare integer = ordinal-within-project, "D#<n>" string = raw id. Transactional with the obs insert — a single invalid id rolls back the whole save.',
|
|
335
|
+
),
|
|
336
|
+
supersedes: coerceSupersedes
|
|
337
|
+
.optional()
|
|
338
|
+
.describe(
|
|
339
|
+
'Ids (same project) that this save overturns: a bare number for an observation, or E#<n> for an event — the same prefix events are shown with in the injected lessons block, so you can retire one by typing back what you read. They are marked superseded (dropped from live search); observations are also linked to the new row via superseded_by, events are not, because that column can only reference another event. Use ONLY when this genuinely replaces a prior conclusion; do NOT use for merely-related or updated-but-still-valid memories.',
|
|
340
|
+
),
|
|
227
341
|
};
|
|
228
342
|
|
|
229
343
|
export const memStatsSchema = {
|
|
230
344
|
project: z.string().optional().describe('Filter by project'),
|
|
231
345
|
days: coerceInt.pipe(z.number().int().min(1).max(365)).optional().describe('Look back N days (default 30)'),
|
|
232
|
-
quality: coerceBool
|
|
346
|
+
quality: coerceBool
|
|
347
|
+
.optional()
|
|
348
|
+
.describe(
|
|
349
|
+
'Return quality dashboard (lesson rate, LOW_SIGNAL rate, per-type hit/lesson %, top-accessed lessons, R-2 watchdog) instead of default stats. Aligns with CLI --quality.',
|
|
350
|
+
),
|
|
233
351
|
};
|
|
234
352
|
|
|
235
353
|
export const memCompressSchema = {
|
|
236
354
|
preview: coerceBool.optional().describe('true=count candidates, false=execute compression (default: true)'),
|
|
237
|
-
age_days: coerceInt
|
|
355
|
+
age_days: coerceInt
|
|
356
|
+
.pipe(z.number().int().min(30).max(365))
|
|
357
|
+
.optional()
|
|
358
|
+
.describe('Min age in days (default: 30, minimum: 30)'),
|
|
238
359
|
project: z.string().optional().describe('Filter by project'),
|
|
239
360
|
};
|
|
240
361
|
|
|
241
362
|
export const memOptimizeSchema = {
|
|
242
|
-
action: z
|
|
363
|
+
action: z
|
|
364
|
+
.enum(['preview', 'run', 'run_all'])
|
|
365
|
+
.optional()
|
|
366
|
+
.default('preview')
|
|
243
367
|
.describe('preview=scan candidates, run=execute with limits, run_all=bypass gates'),
|
|
244
|
-
tasks: z
|
|
368
|
+
tasks: z
|
|
369
|
+
.array(z.enum(['re-enrich', 'normalize', 'cluster-merge', 'smart-compress']))
|
|
370
|
+
.optional()
|
|
245
371
|
.describe('Which optimization tasks to run (default: all)'),
|
|
246
|
-
max_items: coerceInt
|
|
372
|
+
max_items: coerceInt
|
|
373
|
+
.pipe(z.number().int().min(1).max(100))
|
|
374
|
+
.optional()
|
|
375
|
+
.default(15)
|
|
247
376
|
.describe('Maximum LLM calls across all tasks (default: 15)'),
|
|
248
|
-
scope: z
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
377
|
+
scope: z
|
|
378
|
+
.enum(['narrow', 'wide'])
|
|
379
|
+
.optional()
|
|
380
|
+
.default('narrow')
|
|
381
|
+
.describe(
|
|
382
|
+
"Re-enrich scope: narrow=narrative-only candidates (default); wide=R-7 backfill (bugfix/refactor/feature/decision with narrative but lesson_learned='none'). CLI parity: --scope wide.",
|
|
383
|
+
),
|
|
384
|
+
project: z
|
|
385
|
+
.string()
|
|
386
|
+
.optional()
|
|
387
|
+
.describe('Filter all 4 tasks to a single project. Default: scan all projects.'),
|
|
388
|
+
detail: coerceBool
|
|
389
|
+
.optional()
|
|
390
|
+
.describe(
|
|
391
|
+
'preview action only — include cluster contents + re-enrich/compress sample arrays alongside aggregate counts.',
|
|
392
|
+
),
|
|
252
393
|
};
|
|
253
394
|
|
|
254
395
|
export const memMaintainSchema = {
|
|
255
396
|
action: z.enum(['scan', 'execute']).describe('scan=analyze candidates, execute=apply changes'),
|
|
256
|
-
operations: z
|
|
257
|
-
.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
397
|
+
operations: z
|
|
398
|
+
.array(
|
|
399
|
+
z.enum([
|
|
400
|
+
'dedup',
|
|
401
|
+
'decay',
|
|
402
|
+
'cleanup',
|
|
403
|
+
'boost',
|
|
404
|
+
'demote_pinned',
|
|
405
|
+
'purge_stale',
|
|
406
|
+
'rebuild_vectors',
|
|
407
|
+
'vacuum',
|
|
408
|
+
]),
|
|
409
|
+
)
|
|
410
|
+
.optional()
|
|
411
|
+
.describe(
|
|
412
|
+
'Operations: dedup=find/merge duplicate observations, decay=reduce importance of old low-value obs, cleanup=remove orphaned records, boost=promote frequently-accessed obs, demote_pinned=floor importance for obs injected>=8 times but never cited — to 1 with no lesson_learned, to 2 with one (v3.76.1: a lesson-bearing row keeps eligibility on every importance>=2 injection face) (clears pinned noise the decay op cannot reach; in the default set since v3.76.0 and ordered after boost, since boost would otherwise raise the row straight back — set CLAUDE_MEM_SKIP_DEMOTE_PINNED=1 to drop it from the DEFAULT set only), purge_stale=DELETE pending-purge obs older than retain_days (requires confirm=true; first call previews), rebuild_vectors=rebuild TF-IDF vocabulary and all observation vectors, vacuum=reclaim freelist dead space (whole-DB)',
|
|
413
|
+
),
|
|
414
|
+
merge_ids: z
|
|
415
|
+
.preprocess(
|
|
416
|
+
(v) =>
|
|
417
|
+
Array.isArray(v)
|
|
418
|
+
? v.map((g) => (Array.isArray(g) ? g.map((x) => (typeof x === 'string' ? parseInt(x, 10) : x)) : g))
|
|
419
|
+
: v,
|
|
420
|
+
z.array(z.array(z.number().int()).min(2)),
|
|
421
|
+
)
|
|
422
|
+
.optional()
|
|
423
|
+
.describe('For dedup: [[keepId, removeId1, removeId2], ...] — first ID in each group is kept'),
|
|
424
|
+
retain_days: coerceInt
|
|
425
|
+
.pipe(z.number().int().min(7).max(365))
|
|
426
|
+
.optional()
|
|
263
427
|
.describe('For purge_stale: keep observations newer than N days (default 30)'),
|
|
264
|
-
confirm: coerceBool
|
|
265
|
-
.
|
|
428
|
+
confirm: coerceBool
|
|
429
|
+
.optional()
|
|
430
|
+
.describe(
|
|
431
|
+
'Required for destructive ops in `execute` mode (currently: purge_stale). Omit/false → dry-run preview; true → actually delete.',
|
|
432
|
+
),
|
|
266
433
|
project: z.string().optional().describe('Filter by project'),
|
|
267
434
|
};
|
|
268
435
|
|
|
@@ -270,10 +437,18 @@ export const memUpdateSchema = {
|
|
|
270
437
|
id: coerceInt.pipe(z.number().int().positive()).describe('Observation ID to update'),
|
|
271
438
|
// CLI parity (cmdUpdate): empty/whitespace title would render as `(untitled)`
|
|
272
439
|
// in every listing — reject here like the CLI does, instead of persisting it.
|
|
273
|
-
title: z
|
|
440
|
+
title: z
|
|
441
|
+
.string()
|
|
442
|
+
.refine((s) => s.trim() !== '', 'title cannot be empty')
|
|
443
|
+
.optional()
|
|
444
|
+
.describe('New title'),
|
|
274
445
|
// Reject empty content fields (parity with `title` above + cmdUpdate): an explicit
|
|
275
446
|
// '' would blank narrative/lesson/concepts irrecoverably (mem_update takes no snapshot).
|
|
276
|
-
narrative: z
|
|
447
|
+
narrative: z
|
|
448
|
+
.string()
|
|
449
|
+
.refine((s) => s.trim() !== '', 'narrative cannot be empty')
|
|
450
|
+
.optional()
|
|
451
|
+
.describe('New narrative/content'),
|
|
277
452
|
type: OBS_TYPE_ENUM.optional().describe('New observation type'),
|
|
278
453
|
// Same alias as memSaveSchema, for the same reason and found by the same review: without
|
|
279
454
|
// it `mem_update({id, importance: 3, obs_type: 'bugfix'})` reported
|
|
@@ -283,8 +458,17 @@ export const memUpdateSchema = {
|
|
|
283
458
|
importance: coerceInt.pipe(z.number().int().min(1).max(3)).optional().describe('New importance (1-3)'),
|
|
284
459
|
// 500-char cap mirrors memSaveSchema + cmdUpdate — update was the one path
|
|
285
460
|
// that let overlong lessons leak into the DB via MCP.
|
|
286
|
-
lesson_learned: z
|
|
287
|
-
|
|
461
|
+
lesson_learned: z
|
|
462
|
+
.string()
|
|
463
|
+
.max(500)
|
|
464
|
+
.refine((s) => s.trim() !== '', 'lesson_learned cannot be empty')
|
|
465
|
+
.optional()
|
|
466
|
+
.describe('Add or update lesson learned'),
|
|
467
|
+
concepts: z
|
|
468
|
+
.string()
|
|
469
|
+
.refine((s) => s.trim() !== '', 'concepts cannot be empty')
|
|
470
|
+
.optional()
|
|
471
|
+
.describe('Space-separated concept tags'),
|
|
288
472
|
};
|
|
289
473
|
|
|
290
474
|
export const memExportSchema = {
|
|
@@ -305,13 +489,22 @@ export const memExportSchema = {
|
|
|
305
489
|
// The default stays 200 because an MCP result is model context — a bare exploratory call
|
|
306
490
|
// must not dump a whole store into the transcript — but a capped result now announces
|
|
307
491
|
// itself as PARTIAL and names the limit that would return all of it.
|
|
308
|
-
limit: coerceInt
|
|
492
|
+
limit: coerceInt
|
|
493
|
+
.pipe(z.number().int().min(1))
|
|
494
|
+
.optional()
|
|
495
|
+
.describe(
|
|
496
|
+
'Max observations to export (default: 200 — a capped result is flagged PARTIAL and names the total; pass that total for a complete backup, no upper bound)',
|
|
497
|
+
),
|
|
309
498
|
};
|
|
310
499
|
|
|
311
500
|
export const memRecallSchema = {
|
|
312
501
|
file: z.string().min(1).describe('File path or filename to recall observations for'),
|
|
313
502
|
limit: coerceInt.pipe(z.number().int().min(1).max(50)).optional().describe('Max results (default 10)'),
|
|
314
|
-
include_noise: coerceBool
|
|
503
|
+
include_noise: coerceBool
|
|
504
|
+
.optional()
|
|
505
|
+
.describe(
|
|
506
|
+
'Include hook-llm fallback titles ("Modified X", "Worked on X", raw error logs) — hidden by default for parity with mem_search',
|
|
507
|
+
),
|
|
315
508
|
};
|
|
316
509
|
|
|
317
510
|
export const memFtsCheckSchema = {
|
|
@@ -319,12 +512,17 @@ export const memFtsCheckSchema = {
|
|
|
319
512
|
};
|
|
320
513
|
|
|
321
514
|
export const memRegistrySchema = {
|
|
322
|
-
action: z
|
|
515
|
+
action: z
|
|
516
|
+
.enum(['list', 'stats', 'search', 'import', 'remove', 'reindex', 'import_url', 'enrich'])
|
|
517
|
+
.describe('Registry operation'),
|
|
323
518
|
query: z.string().optional().describe('Search query — keywords describing what you need (for search)'),
|
|
324
519
|
type: z.enum(['skill', 'agent']).optional().describe('Filter by resource type (for list/search)'),
|
|
325
520
|
name: z.string().optional().describe('Resource name (for import/remove)'),
|
|
326
521
|
resource_type: z.enum(['skill', 'agent']).optional().describe('Resource type (for import/remove)'),
|
|
327
|
-
source: z
|
|
522
|
+
source: z
|
|
523
|
+
.enum(['preinstalled', 'user', 'github'])
|
|
524
|
+
.optional()
|
|
525
|
+
.describe('Source (for import, default: user)'),
|
|
328
526
|
repo_url: z.string().optional().describe('GitHub repository URL (for import)'),
|
|
329
527
|
local_path: z.string().optional().describe('Local file path (for import)'),
|
|
330
528
|
invocation_name: z.string().optional().describe('Invocation name like "plugin:skill" (for import)'),
|
|
@@ -337,8 +535,14 @@ export const memRegistrySchema = {
|
|
|
337
535
|
use_cases: z.string().optional().describe('Usage scenarios (for import)'),
|
|
338
536
|
url: z.string().optional().describe('GitHub repository URL (for import_url action)'),
|
|
339
537
|
enrich: coerceBool.optional().describe('Auto-enrich imported resources (for import_url action)'),
|
|
340
|
-
category: z
|
|
341
|
-
|
|
538
|
+
category: z
|
|
539
|
+
.string()
|
|
540
|
+
.optional()
|
|
541
|
+
.describe("Filter by category (e.g., 'testing', 'code-quality', 'debugging')"),
|
|
542
|
+
quality: z
|
|
543
|
+
.enum(['installed', 'verified', 'community'])
|
|
544
|
+
.optional()
|
|
545
|
+
.describe('Filter by quality tier (default: all)'),
|
|
342
546
|
};
|
|
343
547
|
|
|
344
548
|
export const memUseSchema = {
|
|
@@ -349,12 +553,18 @@ export const memUseSchema = {
|
|
|
349
553
|
export const memBrowseSchema = {
|
|
350
554
|
project: z.string().optional().describe('Filter by project (default: inferred from CWD)'),
|
|
351
555
|
tier: z.enum(['working', 'active', 'archive']).optional().describe('Show only this tier'),
|
|
352
|
-
limit: coerceInt
|
|
556
|
+
limit: coerceInt
|
|
557
|
+
.pipe(z.number().int().min(1).max(100))
|
|
558
|
+
.optional()
|
|
559
|
+
.describe('Max entries per tier (default 5, or 20 when filtering by tier)'),
|
|
353
560
|
};
|
|
354
561
|
|
|
355
562
|
export const memDeferSchema = {
|
|
356
563
|
title: z.string().min(1).max(200).describe('One-line subject of the deferred item'),
|
|
357
|
-
priority: coerceInt
|
|
564
|
+
priority: coerceInt
|
|
565
|
+
.pipe(z.number().int().min(1).max(3))
|
|
566
|
+
.optional()
|
|
567
|
+
.describe('1=low, 2=normal, 3=urgent (default: 2)'),
|
|
358
568
|
detail: z.string().max(2000).optional().describe('Optional longer description / constraint / why deferred'),
|
|
359
569
|
files: coerceStringArray.optional().describe('Optional file paths this deferred item concerns'),
|
|
360
570
|
project: z.string().optional().describe('Project name (default: inferred from CWD)'),
|
|
@@ -366,10 +576,12 @@ export const memDeferListSchema = {
|
|
|
366
576
|
};
|
|
367
577
|
|
|
368
578
|
export const memDeferDropSchema = {
|
|
369
|
-
id: z
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
579
|
+
id: z
|
|
580
|
+
.union([
|
|
581
|
+
coerceInt.pipe(z.number().int().positive()),
|
|
582
|
+
z.string().regex(/^D#\d+$/, 'expected D#N or positive integer'),
|
|
583
|
+
])
|
|
584
|
+
.describe('Deferred item id — accepts D#N (raw id) or positive integer (ordinal-within-project)'),
|
|
373
585
|
reason: z.string().min(1).max(500).describe('Why this item is being dropped (required for audit trail)'),
|
|
374
586
|
project: z.string().optional().describe('Project name (default: inferred from CWD)'),
|
|
375
587
|
};
|
|
@@ -418,7 +630,9 @@ export const tools = [
|
|
|
418
630
|
' - Looking for prior art on a module/feature before refactoring\n' +
|
|
419
631
|
' - A normal search missed — weak results auto-escalate to deep (set deep=false to opt out)\n' +
|
|
420
632
|
'\n' +
|
|
421
|
-
'Equivalent CLI: ' +
|
|
633
|
+
'Equivalent CLI: ' +
|
|
634
|
+
CLI_INVOKE +
|
|
635
|
+
' search "<query>" [--type bugfix] [--deep]',
|
|
422
636
|
inputSchema: memSearchSchema,
|
|
423
637
|
},
|
|
424
638
|
{
|
|
@@ -436,7 +650,9 @@ export const tools = [
|
|
|
436
650
|
' - User asks "what did we do yesterday / last" with no topic keyword\n' +
|
|
437
651
|
' - Verifying that a just-made change was captured as an observation\n' +
|
|
438
652
|
'\n' +
|
|
439
|
-
'Equivalent CLI: ' +
|
|
653
|
+
'Equivalent CLI: ' +
|
|
654
|
+
CLI_INVOKE +
|
|
655
|
+
' recent [N]',
|
|
440
656
|
inputSchema: memRecentSchema,
|
|
441
657
|
},
|
|
442
658
|
{
|
|
@@ -456,7 +672,9 @@ export const tools = [
|
|
|
456
672
|
' - A search hit is interesting and you want its chronological neighbours\n' +
|
|
457
673
|
' - Replaying a session narrative around a known observation ID\n' +
|
|
458
674
|
'\n' +
|
|
459
|
-
'Equivalent CLI: ' +
|
|
675
|
+
'Equivalent CLI: ' +
|
|
676
|
+
CLI_INVOKE +
|
|
677
|
+
' timeline --anchor <ID> [--before N --after N]',
|
|
460
678
|
inputSchema: memTimelineSchema,
|
|
461
679
|
},
|
|
462
680
|
{
|
|
@@ -476,7 +694,9 @@ export const tools = [
|
|
|
476
694
|
'\n' +
|
|
477
695
|
'On miss, response includes "Try: …" hint listing other sources the ID lives in.\n' +
|
|
478
696
|
'\n' +
|
|
479
|
-
'Equivalent CLI: ' +
|
|
697
|
+
'Equivalent CLI: ' +
|
|
698
|
+
CLI_INVOKE +
|
|
699
|
+
' get <id>[,<id>,...] — accepts P#/S#/# prefix.',
|
|
480
700
|
inputSchema: memGetSchema,
|
|
481
701
|
},
|
|
482
702
|
{
|
|
@@ -494,7 +714,9 @@ export const tools = [
|
|
|
494
714
|
' - Cleaning up an observation saved from a test run or incorrect save\n' +
|
|
495
715
|
' - Always run once with confirm=false, then again with confirm=true\n' +
|
|
496
716
|
'\n' +
|
|
497
|
-
'Equivalent CLI: ' +
|
|
717
|
+
'Equivalent CLI: ' +
|
|
718
|
+
CLI_INVOKE +
|
|
719
|
+
' delete <id>[,<id>,...] [--confirm]',
|
|
498
720
|
inputSchema: memDeleteSchema,
|
|
499
721
|
hidden: true,
|
|
500
722
|
},
|
|
@@ -513,7 +735,9 @@ export const tools = [
|
|
|
513
735
|
' - After a non-obvious architecture/tradeoff decision — set type="decision", lesson_learned="<constraint + why>"\n' +
|
|
514
736
|
' - User explicitly asks "remember this" or "save a note that ..."\n' +
|
|
515
737
|
'\n' +
|
|
516
|
-
'Equivalent CLI: ' +
|
|
738
|
+
'Equivalent CLI: ' +
|
|
739
|
+
CLI_INVOKE +
|
|
740
|
+
' save --type bugfix --lesson "..." "<content>"',
|
|
517
741
|
inputSchema: memSaveSchema,
|
|
518
742
|
},
|
|
519
743
|
{
|
|
@@ -531,7 +755,9 @@ export const tools = [
|
|
|
531
755
|
' - Diagnosing why search feels sparse or noisy at a macro level\n' +
|
|
532
756
|
' - Auditing a project before major compression/maintenance\n' +
|
|
533
757
|
'\n' +
|
|
534
|
-
'Equivalent CLI: ' +
|
|
758
|
+
'Equivalent CLI: ' +
|
|
759
|
+
CLI_INVOKE +
|
|
760
|
+
' stats [--project X] [--days 30]',
|
|
535
761
|
inputSchema: memStatsSchema,
|
|
536
762
|
hidden: true,
|
|
537
763
|
},
|
|
@@ -550,7 +776,9 @@ export const tools = [
|
|
|
550
776
|
' - After a major project phase completes and old per-file observations are noise\n' +
|
|
551
777
|
' - Stats show thousands of low-importance rows dragging search quality\n' +
|
|
552
778
|
'\n' +
|
|
553
|
-
'Equivalent CLI: ' +
|
|
779
|
+
'Equivalent CLI: ' +
|
|
780
|
+
CLI_INVOKE +
|
|
781
|
+
' compress [--execute] [--age-days 90] (preview is default)',
|
|
554
782
|
inputSchema: memCompressSchema,
|
|
555
783
|
hidden: true,
|
|
556
784
|
},
|
|
@@ -569,7 +797,9 @@ export const tools = [
|
|
|
569
797
|
' - After bulk imports or a long offline period\n' +
|
|
570
798
|
' - User asks for periodic maintenance / cleanup\n' +
|
|
571
799
|
'\n' +
|
|
572
|
-
'Equivalent CLI: ' +
|
|
800
|
+
'Equivalent CLI: ' +
|
|
801
|
+
CLI_INVOKE +
|
|
802
|
+
' maintain scan --ops dedup,decay',
|
|
573
803
|
inputSchema: memMaintainSchema,
|
|
574
804
|
hidden: true,
|
|
575
805
|
},
|
|
@@ -588,7 +818,9 @@ export const tools = [
|
|
|
588
818
|
' - stats show many degraded (title-only, no lesson) observations\n' +
|
|
589
819
|
' - Start with action="preview" to see candidates before spending tokens\n' +
|
|
590
820
|
'\n' +
|
|
591
|
-
'Equivalent CLI: ' +
|
|
821
|
+
'Equivalent CLI: ' +
|
|
822
|
+
CLI_INVOKE +
|
|
823
|
+
' optimize [--run|--run-all] [--task re-enrich,normalize,cluster-merge,smart-compress] [--max N] (preview is default)',
|
|
592
824
|
inputSchema: memOptimizeSchema,
|
|
593
825
|
hidden: true,
|
|
594
826
|
},
|
|
@@ -607,7 +839,9 @@ export const tools = [
|
|
|
607
839
|
' - Looking for a tool by capability → action="search" with keywords\n' +
|
|
608
840
|
' - User explicitly asks to import a GitHub repo → action="import_url"\n' +
|
|
609
841
|
'\n' +
|
|
610
|
-
'Equivalent CLI: ' +
|
|
842
|
+
'Equivalent CLI: ' +
|
|
843
|
+
CLI_INVOKE +
|
|
844
|
+
' registry <list|search|import|...> [args]',
|
|
611
845
|
inputSchema: memRegistrySchema,
|
|
612
846
|
hidden: true,
|
|
613
847
|
},
|
|
@@ -615,7 +849,7 @@ export const tools = [
|
|
|
615
849
|
name: 'mem_use',
|
|
616
850
|
description:
|
|
617
851
|
'Load and activate a skill or agent from the registry by EXACT name or invocation_name.\n' +
|
|
618
|
-
|
|
852
|
+
"A name that matches nothing returns closest-match names to pick from — never another resource's content.\n" +
|
|
619
853
|
'\n' +
|
|
620
854
|
'DO NOT use when:\n' +
|
|
621
855
|
' - You have not confirmed the skill exists (run mem_registry action="list" first)\n' +
|
|
@@ -646,7 +880,9 @@ export const tools = [
|
|
|
646
880
|
' - You later discover additional context worth appending to lesson_learned\n' +
|
|
647
881
|
' - Reclassifying an observation after its true type becomes clear\n' +
|
|
648
882
|
'\n' +
|
|
649
|
-
'Equivalent CLI: ' +
|
|
883
|
+
'Equivalent CLI: ' +
|
|
884
|
+
CLI_INVOKE +
|
|
885
|
+
' update <id> [--title ...] [--lesson ...]',
|
|
650
886
|
inputSchema: memUpdateSchema,
|
|
651
887
|
hidden: true,
|
|
652
888
|
},
|
|
@@ -663,9 +899,11 @@ export const tools = [
|
|
|
663
899
|
'USE when:\n' +
|
|
664
900
|
' - Backing up memory before a migration or reinstall\n' +
|
|
665
901
|
' - Moving observations between machines or projects\n' +
|
|
666
|
-
|
|
902
|
+
" - User asks for a JSON snapshot of a project's memories\n" +
|
|
667
903
|
'\n' +
|
|
668
|
-
'Equivalent CLI: ' +
|
|
904
|
+
'Equivalent CLI: ' +
|
|
905
|
+
CLI_INVOKE +
|
|
906
|
+
' export [--format jsonl] [--project X] [--limit 500]',
|
|
669
907
|
inputSchema: memExportSchema,
|
|
670
908
|
hidden: true,
|
|
671
909
|
},
|
|
@@ -684,7 +922,9 @@ export const tools = [
|
|
|
684
922
|
' - User asks "what do we know about <file>"\n' +
|
|
685
923
|
' - Investigating a recurring issue in a file you have not touched recently\n' +
|
|
686
924
|
'\n' +
|
|
687
|
-
'Equivalent CLI: ' +
|
|
925
|
+
'Equivalent CLI: ' +
|
|
926
|
+
CLI_INVOKE +
|
|
927
|
+
' recall "<file>" [--limit 10]',
|
|
688
928
|
inputSchema: memRecallSchema,
|
|
689
929
|
},
|
|
690
930
|
{
|
|
@@ -702,7 +942,9 @@ export const tools = [
|
|
|
702
942
|
' - After a crash, power loss, or manual DB edit\n' +
|
|
703
943
|
' - doctor / stats flags FTS integrity problems\n' +
|
|
704
944
|
'\n' +
|
|
705
|
-
'Equivalent CLI: ' +
|
|
945
|
+
'Equivalent CLI: ' +
|
|
946
|
+
CLI_INVOKE +
|
|
947
|
+
' fts-check <check|rebuild>',
|
|
706
948
|
inputSchema: memFtsCheckSchema,
|
|
707
949
|
hidden: true,
|
|
708
950
|
},
|
|
@@ -721,7 +963,9 @@ export const tools = [
|
|
|
721
963
|
' - Triaging what to compress or clean up before running maintenance\n' +
|
|
722
964
|
' - Scanning for interesting anchors to follow up with mem_timeline\n' +
|
|
723
965
|
'\n' +
|
|
724
|
-
'Equivalent CLI: ' +
|
|
966
|
+
'Equivalent CLI: ' +
|
|
967
|
+
CLI_INVOKE +
|
|
968
|
+
' browse [--tier active] [--project X]',
|
|
725
969
|
inputSchema: memBrowseSchema,
|
|
726
970
|
hidden: true,
|
|
727
971
|
},
|
|
@@ -738,9 +982,11 @@ export const tools = [
|
|
|
738
982
|
'USE when:\n' +
|
|
739
983
|
' - User says "下次/next session/留给下个会话/defer to next round"\n' +
|
|
740
984
|
' - Wrap-up phase enumerates follow-up items for the next session\n' +
|
|
741
|
-
|
|
985
|
+
" - Bug surfaces but root cause is out of this session's scope\n" +
|
|
742
986
|
'\n' +
|
|
743
|
-
'Equivalent CLI: ' +
|
|
987
|
+
'Equivalent CLI: ' +
|
|
988
|
+
CLI_INVOKE +
|
|
989
|
+
' defer add "<title>" [--priority 1|2|3] [--detail "..."] [--files a.mjs,b.mjs]',
|
|
744
990
|
inputSchema: memDeferSchema,
|
|
745
991
|
},
|
|
746
992
|
{
|
|
@@ -757,7 +1003,9 @@ export const tools = [
|
|
|
757
1003
|
' - About to refer to "item N" and need to confirm what N points to\n' +
|
|
758
1004
|
' - Auditing carry-forward state across multiple sessions\n' +
|
|
759
1005
|
'\n' +
|
|
760
|
-
'Equivalent CLI: ' +
|
|
1006
|
+
'Equivalent CLI: ' +
|
|
1007
|
+
CLI_INVOKE +
|
|
1008
|
+
' defer list [--project X] [--limit 10]',
|
|
761
1009
|
inputSchema: memDeferListSchema,
|
|
762
1010
|
},
|
|
763
1011
|
{
|
|
@@ -775,7 +1023,9 @@ export const tools = [
|
|
|
775
1023
|
' - Scope changed and the work is no longer needed\n' +
|
|
776
1024
|
' - User explicitly says "drop the deferred X, never mind"\n' +
|
|
777
1025
|
'\n' +
|
|
778
|
-
'Equivalent CLI: ' +
|
|
1026
|
+
'Equivalent CLI: ' +
|
|
1027
|
+
CLI_INVOKE +
|
|
1028
|
+
' defer drop <D#N|ordinal> --reason "..."',
|
|
779
1029
|
inputSchema: memDeferDropSchema,
|
|
780
1030
|
},
|
|
781
1031
|
];
|