@cachly-dev/mcp-server 0.10.129 → 0.10.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.js +1 -7
- package/dist/packages/telegram-notify/client.js +2 -75
- package/dist/src/ambient-cli.js +7 -253
- package/dist/src/ambient-deps.js +1 -55
- package/dist/src/ambient-hooks.js +5 -278
- package/dist/src/ambient-ledger.js +6 -95
- package/dist/src/ambient-memory.js +1 -67
- package/dist/src/ambient-recall.js +1 -221
- package/dist/src/aussetzer.js +1 -225
- package/dist/src/auth.js +9 -140
- package/dist/src/autor-abzeichen.js +1 -56
- package/dist/src/autor.js +1 -136
- package/dist/src/bauabdruck.js +1 -93
- package/dist/src/bedeutung.js +2 -323
- package/dist/src/bench/cachly-bench.js +4 -249
- package/dist/src/bench/external-corpus.js +4 -135
- package/dist/src/bench/fixtures.js +1 -225
- package/dist/src/brain-export.js +16 -119
- package/dist/src/brain-watch-hook.js +8 -86
- package/dist/src/cachly-url.js +1 -21
- package/dist/src/ckg.js +1 -108
- package/dist/src/cls-hook.js +8 -94
- package/dist/src/confidence.js +1 -50
- package/dist/src/credentials.js +1 -85
- package/dist/src/deutsch.js +1 -223
- package/dist/src/doctor.js +2 -248
- package/dist/src/editor.js +1 -63
- package/dist/src/eingaenge-nachruesten.js +1 -290
- package/dist/src/eingaenge.js +2 -260
- package/dist/src/embeddings.js +13 -223
- package/dist/src/feldreparatur.js +3 -113
- package/dist/src/first-contact.js +4 -108
- package/dist/src/first-recall-proof.js +4 -41
- package/dist/src/funnel-milestones.js +2 -48
- package/dist/src/geliefert.js +1 -114
- package/dist/src/handlers/advanced.js +38 -537
- package/dist/src/handlers/brain.js +69 -4397
- package/dist/src/handlers/cache.js +59 -638
- package/dist/src/handlers/context.js +17 -182
- package/dist/src/handlers/fedbrain.js +40 -1436
- package/dist/src/handlers/instances.js +16 -201
- package/dist/src/handlers/roadmap.js +13 -175
- package/dist/src/handlers/share.js +19 -530
- package/dist/src/handlers/syndicate.js +38 -904
- package/dist/src/handlers/team.js +89 -1865
- package/dist/src/handlers/viz.js +3 -148
- package/dist/src/index.js +321 -4281
- package/dist/src/interferenz.js +1 -141
- package/dist/src/kostprobe.js +7 -125
- package/dist/src/lesson-preview.js +2 -133
- package/dist/src/namespace.js +1 -128
- package/dist/src/notifier.js +1 -6
- package/dist/src/rangfolge-stellschrauben.js +1 -0
- package/dist/src/rangfolge.js +1 -249
- package/dist/src/raumkorrektur.js +1 -197
- package/dist/src/recall-tiefe.js +1 -42
- package/dist/src/rerank.js +1 -154
- package/dist/src/resonanz.js +1 -140
- package/dist/src/search.js +1 -2104
- package/dist/src/seltenheitsbestand.js +1 -92
- package/dist/src/speicherlage.js +1 -84
- package/dist/src/spuren.js +1 -150
- package/dist/src/starter-corpus.js +1 -165
- package/dist/src/telemetry-types.js +0 -13
- package/dist/src/tools.js +10 -2465
- package/dist/src/toolspecs.js +1 -121
- package/dist/src/upgrade-nudge.js +1 -29
- package/dist/src/utils.js +1 -89
- package/dist/src/versuch.js +1 -264
- package/dist/src/vorspann.js +1 -146
- package/dist/src/wartung/vektoren-verdichten.js +1 -200
- package/dist/src/werkzeug-nutzung.js +1 -72
- package/dist/src/wertbeitrag.js +1 -203
- package/dist/src/wirkungsspur.js +1 -0
- package/glama.json +1 -1
- package/package.json +8 -7
- package/server.json +3 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.js.map +0 -1
- package/dist/packages/telegram-notify/client.d.ts +0 -19
- package/dist/packages/telegram-notify/client.js.map +0 -1
- package/dist/src/ambient-cli.d.ts +0 -101
- package/dist/src/ambient-cli.js.map +0 -1
- package/dist/src/ambient-deps.d.ts +0 -26
- package/dist/src/ambient-deps.js.map +0 -1
- package/dist/src/ambient-hooks.d.ts +0 -137
- package/dist/src/ambient-hooks.js.map +0 -1
- package/dist/src/ambient-ledger.d.ts +0 -19
- package/dist/src/ambient-ledger.js.map +0 -1
- package/dist/src/ambient-memory.d.ts +0 -12
- package/dist/src/ambient-memory.js.map +0 -1
- package/dist/src/ambient-recall.d.ts +0 -127
- package/dist/src/ambient-recall.js.map +0 -1
- package/dist/src/aussetzer.d.ts +0 -156
- package/dist/src/aussetzer.js.map +0 -1
- package/dist/src/auth.d.ts +0 -43
- package/dist/src/auth.js.map +0 -1
- package/dist/src/autor-abzeichen.d.ts +0 -41
- package/dist/src/autor-abzeichen.js.map +0 -1
- package/dist/src/autor.d.ts +0 -101
- package/dist/src/autor.js.map +0 -1
- package/dist/src/bauabdruck-daten.generiert.json +0 -5
- package/dist/src/bauabdruck.d.ts +0 -38
- package/dist/src/bauabdruck.js.map +0 -1
- package/dist/src/bedeutung.d.ts +0 -168
- package/dist/src/bedeutung.js.map +0 -1
- package/dist/src/bench/cachly-bench.d.ts +0 -61
- package/dist/src/bench/cachly-bench.js.map +0 -1
- package/dist/src/bench/external-corpus.d.ts +0 -44
- package/dist/src/bench/external-corpus.js.map +0 -1
- package/dist/src/bench/fixtures.d.ts +0 -37
- package/dist/src/bench/fixtures.js.map +0 -1
- package/dist/src/brain-export.d.ts +0 -64
- package/dist/src/brain-export.js.map +0 -1
- package/dist/src/brain-watch-hook.d.ts +0 -21
- package/dist/src/brain-watch-hook.js.map +0 -1
- package/dist/src/cachly-url.d.ts +0 -68
- package/dist/src/cachly-url.js.map +0 -1
- package/dist/src/ckg.d.ts +0 -54
- package/dist/src/ckg.js.map +0 -1
- package/dist/src/cls-hook.d.ts +0 -15
- package/dist/src/cls-hook.js.map +0 -1
- package/dist/src/confidence.d.ts +0 -19
- package/dist/src/confidence.js.map +0 -1
- package/dist/src/credentials.d.ts +0 -37
- package/dist/src/credentials.js.map +0 -1
- package/dist/src/deutsch.d.ts +0 -98
- package/dist/src/deutsch.js.map +0 -1
- package/dist/src/doctor.d.ts +0 -47
- package/dist/src/doctor.js.map +0 -1
- package/dist/src/editor.d.ts +0 -9
- package/dist/src/editor.js.map +0 -1
- package/dist/src/eingaenge-nachruesten.d.ts +0 -41
- package/dist/src/eingaenge-nachruesten.js.map +0 -1
- package/dist/src/eingaenge.d.ts +0 -159
- package/dist/src/eingaenge.js.map +0 -1
- package/dist/src/embeddings.d.ts +0 -48
- package/dist/src/embeddings.js.map +0 -1
- package/dist/src/feldreparatur.d.ts +0 -58
- package/dist/src/feldreparatur.js.map +0 -1
- package/dist/src/first-contact.d.ts +0 -56
- package/dist/src/first-contact.js.map +0 -1
- package/dist/src/first-recall-proof.d.ts +0 -19
- package/dist/src/first-recall-proof.js.map +0 -1
- package/dist/src/funnel-milestones.d.ts +0 -5
- package/dist/src/funnel-milestones.js.map +0 -1
- package/dist/src/geliefert.d.ts +0 -63
- package/dist/src/geliefert.js.map +0 -1
- package/dist/src/handlers/advanced.d.ts +0 -6
- package/dist/src/handlers/advanced.js.map +0 -1
- package/dist/src/handlers/brain.d.ts +0 -41
- package/dist/src/handlers/brain.js.map +0 -1
- package/dist/src/handlers/cache.d.ts +0 -6
- package/dist/src/handlers/cache.js.map +0 -1
- package/dist/src/handlers/context.d.ts +0 -6
- package/dist/src/handlers/context.js.map +0 -1
- package/dist/src/handlers/fedbrain.d.ts +0 -18
- package/dist/src/handlers/fedbrain.js.map +0 -1
- package/dist/src/handlers/instances.d.ts +0 -33
- package/dist/src/handlers/instances.js.map +0 -1
- package/dist/src/handlers/roadmap.d.ts +0 -6
- package/dist/src/handlers/roadmap.js.map +0 -1
- package/dist/src/handlers/share.d.ts +0 -8
- package/dist/src/handlers/share.js.map +0 -1
- package/dist/src/handlers/syndicate.d.ts +0 -6
- package/dist/src/handlers/syndicate.js.map +0 -1
- package/dist/src/handlers/team.d.ts +0 -45
- package/dist/src/handlers/team.js.map +0 -1
- package/dist/src/handlers/viz.d.ts +0 -6
- package/dist/src/handlers/viz.js.map +0 -1
- package/dist/src/index.d.ts +0 -93
- package/dist/src/index.js.map +0 -1
- package/dist/src/interferenz.d.ts +0 -111
- package/dist/src/interferenz.js.map +0 -1
- package/dist/src/kostprobe.d.ts +0 -25
- package/dist/src/kostprobe.js.map +0 -1
- package/dist/src/lesson-preview.d.ts +0 -81
- package/dist/src/lesson-preview.js.map +0 -1
- package/dist/src/namespace.d.ts +0 -20
- package/dist/src/namespace.js.map +0 -1
- package/dist/src/notifier.d.ts +0 -5
- package/dist/src/notifier.js.map +0 -1
- package/dist/src/rangfolge.d.ts +0 -205
- package/dist/src/rangfolge.js.map +0 -1
- package/dist/src/raumkorrektur.d.ts +0 -100
- package/dist/src/raumkorrektur.js.map +0 -1
- package/dist/src/recall-tiefe.d.ts +0 -48
- package/dist/src/recall-tiefe.js.map +0 -1
- package/dist/src/rerank.d.ts +0 -48
- package/dist/src/rerank.js.map +0 -1
- package/dist/src/resonanz.d.ts +0 -85
- package/dist/src/resonanz.js.map +0 -1
- package/dist/src/search.d.ts +0 -153
- package/dist/src/search.js.map +0 -1
- package/dist/src/seltenheitsbestand.d.ts +0 -53
- package/dist/src/seltenheitsbestand.js.map +0 -1
- package/dist/src/speicherlage.d.ts +0 -51
- package/dist/src/speicherlage.js.map +0 -1
- package/dist/src/spuren.d.ts +0 -97
- package/dist/src/spuren.js.map +0 -1
- package/dist/src/starter-corpus.d.ts +0 -28
- package/dist/src/starter-corpus.js.map +0 -1
- package/dist/src/telemetry-types.d.ts +0 -87
- package/dist/src/telemetry-types.js.map +0 -1
- package/dist/src/tools.d.ts +0 -2933
- package/dist/src/tools.js.map +0 -1
- package/dist/src/toolspecs.d.ts +0 -43
- package/dist/src/toolspecs.js.map +0 -1
- package/dist/src/upgrade-nudge.d.ts +0 -15
- package/dist/src/upgrade-nudge.js.map +0 -1
- package/dist/src/utils.d.ts +0 -34
- package/dist/src/utils.js.map +0 -1
- package/dist/src/versuch.d.ts +0 -187
- package/dist/src/versuch.js.map +0 -1
- package/dist/src/vorspann.d.ts +0 -73
- package/dist/src/vorspann.js.map +0 -1
- package/dist/src/wartung/vektoren-verdichten.d.ts +0 -50
- package/dist/src/wartung/vektoren-verdichten.js.map +0 -1
- package/dist/src/werkzeug-nutzung.d.ts +0 -62
- package/dist/src/werkzeug-nutzung.js.map +0 -1
- package/dist/src/wertbeitrag.d.ts +0 -148
- package/dist/src/wertbeitrag.js.map +0 -1
|
@@ -1,530 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
]);
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
continue;
|
|
21
|
-
const lesson = safeJsonParse(raw, null);
|
|
22
|
-
if (!lesson || !lesson.topic)
|
|
23
|
-
continue;
|
|
24
|
-
if (topicFilter && topicFilter.length > 0) {
|
|
25
|
-
const matches = topicFilter.some(f => lesson.topic.includes(f));
|
|
26
|
-
if (!matches)
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
// Strip fields that should not be shared (internal audit trails, etc.)
|
|
30
|
-
lessons.push({
|
|
31
|
-
topic: lesson.topic,
|
|
32
|
-
outcome: lesson.outcome,
|
|
33
|
-
...(lesson.what_worked ? { what_worked: lesson.what_worked } : {}),
|
|
34
|
-
...(lesson.what_failed ? { what_failed: lesson.what_failed } : {}),
|
|
35
|
-
...(lesson.ctx ? { ctx: lesson.ctx } : {}),
|
|
36
|
-
...(lesson.tags?.length ? { tags: lesson.tags } : {}),
|
|
37
|
-
...(lesson.author ? { author: lesson.author } : {}),
|
|
38
|
-
...(lesson.service ? { service: lesson.service } : {}),
|
|
39
|
-
confidence: lesson.confidence ?? 1.0,
|
|
40
|
-
ts: lesson.ts,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
return lessons;
|
|
44
|
-
}
|
|
45
|
-
export async function handleShareTool(name, args, getConnection, apiFetch) {
|
|
46
|
-
switch (name) {
|
|
47
|
-
// ── brain_share ────────────────────────────────────────────────────────────
|
|
48
|
-
case 'brain_share': {
|
|
49
|
-
const { instance_id, title = 'My Brain Snapshot', description = '', topic_filter, visibility = 'unlisted', max_lessons = 100, dry_run = false, } = args;
|
|
50
|
-
if (!instance_id)
|
|
51
|
-
return '⚠️ `brain_share` requires `instance_id`.';
|
|
52
|
-
const redis = await getConnection(instance_id);
|
|
53
|
-
const topicFilter = Array.isArray(topic_filter) && topic_filter.length > 0 ? topic_filter : undefined;
|
|
54
|
-
const allLessons = await scanAllLessons(redis, topicFilter);
|
|
55
|
-
const lessons = allLessons.slice(0, max_lessons);
|
|
56
|
-
if (lessons.length === 0) {
|
|
57
|
-
return [
|
|
58
|
-
`📤 **brain_share** — nothing to share yet`,
|
|
59
|
-
``,
|
|
60
|
-
`No lessons found in Brain \`${instance_id}\`${topicFilter ? ` matching topics: ${topicFilter.join(', ')}` : ''}.`,
|
|
61
|
-
``,
|
|
62
|
-
`Store lessons first with \`learn_from_attempts(topic="...", what_worked="...")\`.`,
|
|
63
|
-
].join('\n');
|
|
64
|
-
}
|
|
65
|
-
if (dry_run) {
|
|
66
|
-
const sample = lessons.slice(0, 5).map(l => {
|
|
67
|
-
const icon = l.outcome === 'success' ? '✅' : l.outcome === 'failure' ? '❌' : '⚠️';
|
|
68
|
-
return ` ${icon} \`${l.topic}\` — ${(l.what_worked ?? l.what_failed ?? '').slice(0, 80)}`;
|
|
69
|
-
});
|
|
70
|
-
return [
|
|
71
|
-
`📤 **brain_share (DRY RUN)** — would share ${lessons.length} lesson${lessons.length !== 1 ? 's' : ''}`,
|
|
72
|
-
``,
|
|
73
|
-
`**Title:** ${title}`,
|
|
74
|
-
`**Visibility:** ${visibility}`,
|
|
75
|
-
topicFilter ? `**Topic filter:** ${topicFilter.join(', ')}` : '',
|
|
76
|
-
``,
|
|
77
|
-
`**Sample lessons (first 5):**`,
|
|
78
|
-
...sample,
|
|
79
|
-
lessons.length > 5 ? ` _...and ${lessons.length - 5} more_` : '',
|
|
80
|
-
``,
|
|
81
|
-
`_Remove \`dry_run=true\` to create the public share._`,
|
|
82
|
-
].filter(l => l !== '').join('\n');
|
|
83
|
-
}
|
|
84
|
-
// POST to API
|
|
85
|
-
let shareId = null;
|
|
86
|
-
let shareUrl = null;
|
|
87
|
-
try {
|
|
88
|
-
const result = await apiFetch('/api/v1/brains/share', {
|
|
89
|
-
method: 'POST',
|
|
90
|
-
headers: { 'Content-Type': 'application/json' },
|
|
91
|
-
body: JSON.stringify({ title, description, visibility, lessons }),
|
|
92
|
-
});
|
|
93
|
-
shareId = result.share_id ?? null;
|
|
94
|
-
if (shareId)
|
|
95
|
-
shareUrl = cachlyUrl(`/brain/share/${shareId}`, 'brain-share');
|
|
96
|
-
}
|
|
97
|
-
catch (e) {
|
|
98
|
-
// API endpoint may not be live yet — store locally and return JSON
|
|
99
|
-
const err = e.message ?? '';
|
|
100
|
-
if (err.includes('404') || err.includes('not found') || err.includes('ECONNREFUSED')) {
|
|
101
|
-
const exportJson = JSON.stringify({ title, description, visibility, lessons, exported_at: new Date().toISOString() }, null, 2);
|
|
102
|
-
return [
|
|
103
|
-
`📤 **brain_share** — exported ${lessons.length} lesson${lessons.length !== 1 ? 's' : ''}`,
|
|
104
|
-
``,
|
|
105
|
-
`⚠️ The public share API is not yet available. Here is your portable snapshot:`,
|
|
106
|
-
``,
|
|
107
|
-
`\`\`\`json`,
|
|
108
|
-
exportJson.slice(0, 3000) + (exportJson.length > 3000 ? '\n... (truncated)' : ''),
|
|
109
|
-
`\`\`\``,
|
|
110
|
-
``,
|
|
111
|
-
`_To import this on another Brain, use \`brain_import\` with the JSON payload._`,
|
|
112
|
-
].join('\n');
|
|
113
|
-
}
|
|
114
|
-
return `❌ brain_share failed: ${e.message}`;
|
|
115
|
-
}
|
|
116
|
-
const sample = lessons.slice(0, 5).map(l => {
|
|
117
|
-
const icon = l.outcome === 'success' ? '✅' : l.outcome === 'failure' ? '❌' : '⚠️';
|
|
118
|
-
return ` ${icon} \`${l.topic}\``;
|
|
119
|
-
});
|
|
120
|
-
// Record provenance
|
|
121
|
-
try {
|
|
122
|
-
const prov = JSON.stringify({ share_id: shareId, title, visibility, lesson_count: lessons.length, created_at: new Date().toISOString() });
|
|
123
|
-
await redis.rpush('cachly:brain:shares', prov);
|
|
124
|
-
await redis.ltrim('cachly:brain:shares', -20, -1);
|
|
125
|
-
}
|
|
126
|
-
catch { /* non-critical */ }
|
|
127
|
-
return [
|
|
128
|
-
`📤 **brain_share** — ${lessons.length} lesson${lessons.length !== 1 ? 's' : ''} shared`,
|
|
129
|
-
``,
|
|
130
|
-
`**Share ID:** \`${shareId}\``,
|
|
131
|
-
`**URL:** ${shareUrl}`,
|
|
132
|
-
`**Title:** ${title}`,
|
|
133
|
-
`**Visible:** ${visibility}`,
|
|
134
|
-
``,
|
|
135
|
-
`**Sample lessons:**`,
|
|
136
|
-
...sample,
|
|
137
|
-
lessons.length > 5 ? ` _...and ${lessons.length - 5} more_` : '',
|
|
138
|
-
``,
|
|
139
|
-
`**Anyone can import this Brain with:**`,
|
|
140
|
-
`\`brain_import(instance_id="<their-id>", share_id="${shareId}")\``,
|
|
141
|
-
``,
|
|
142
|
-
`_To list all your shares, run \`brain_share_list\`._`,
|
|
143
|
-
].filter(l => l !== '').join('\n');
|
|
144
|
-
}
|
|
145
|
-
// ── brain_import ───────────────────────────────────────────────────────────
|
|
146
|
-
case 'brain_import': {
|
|
147
|
-
const { instance_id, share_id, topic_prefix = '', min_confidence = 0.0, dry_run = false, overwrite = false, } = args;
|
|
148
|
-
if (!instance_id)
|
|
149
|
-
return '⚠️ `brain_import` requires `instance_id`.';
|
|
150
|
-
if (!share_id)
|
|
151
|
-
return '⚠️ `brain_import` requires `share_id`. Get one from `brain_share`.';
|
|
152
|
-
// Extract share ID from URL if passed as URL
|
|
153
|
-
const cleanShareId = share_id.replace(/^.*\/brain\/share\//, '').trim();
|
|
154
|
-
// Fetch the public share
|
|
155
|
-
let payload;
|
|
156
|
-
try {
|
|
157
|
-
payload = await apiFetch(`/api/v1/brains/share/${cleanShareId}`);
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
const err = e.message ?? '';
|
|
161
|
-
if (err.includes('404') || err.includes('not found')) {
|
|
162
|
-
return [
|
|
163
|
-
`❌ **brain_import** — share not found`,
|
|
164
|
-
``,
|
|
165
|
-
`Share ID \`${cleanShareId}\` does not exist or has been removed.`,
|
|
166
|
-
``,
|
|
167
|
-
`Ask the Brain owner to re-run \`brain_share\` and share the new ID with you.`,
|
|
168
|
-
].join('\n');
|
|
169
|
-
}
|
|
170
|
-
return `❌ brain_import failed: ${e.message}`;
|
|
171
|
-
}
|
|
172
|
-
const lessons = payload.lessons ?? [];
|
|
173
|
-
if (lessons.length === 0) {
|
|
174
|
-
return `⚠️ Share \`${cleanShareId}\` exists but contains no lessons.`;
|
|
175
|
-
}
|
|
176
|
-
const redis = await getConnection(instance_id);
|
|
177
|
-
let imported = 0;
|
|
178
|
-
let skipped = 0;
|
|
179
|
-
let conflicts = 0;
|
|
180
|
-
const importLog = [];
|
|
181
|
-
for (const lesson of lessons) {
|
|
182
|
-
if ((lesson.confidence ?? 1.0) < min_confidence) {
|
|
183
|
-
skipped++;
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
const topic = topic_prefix ? `${topic_prefix}:${lesson.topic}` : lesson.topic;
|
|
187
|
-
const bestKey = `cachly:lesson:best:${topic}`;
|
|
188
|
-
// Check for existing lesson
|
|
189
|
-
if (!overwrite) {
|
|
190
|
-
const existing = await redis.get(bestKey);
|
|
191
|
-
if (existing) {
|
|
192
|
-
conflicts++;
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const lessonToStore = JSON.stringify({
|
|
197
|
-
...lesson,
|
|
198
|
-
topic,
|
|
199
|
-
imported_from: cleanShareId,
|
|
200
|
-
imported_at: new Date().toISOString(),
|
|
201
|
-
});
|
|
202
|
-
if (!dry_run) {
|
|
203
|
-
await redis.set(bestKey, lessonToStore);
|
|
204
|
-
const listKey = `cachly:lessons:${topic}`;
|
|
205
|
-
await redis.rpush(listKey, lessonToStore);
|
|
206
|
-
await redis.ltrim(listKey, -100, -1);
|
|
207
|
-
await redis.expire(listKey, 90 * 86400);
|
|
208
|
-
}
|
|
209
|
-
imported++;
|
|
210
|
-
if (importLog.length < 6) {
|
|
211
|
-
const icon = lesson.outcome === 'success' ? '✅' : lesson.outcome === 'failure' ? '❌' : '⚠️';
|
|
212
|
-
importLog.push(` ${icon} \`${topic}\` — ${(lesson.what_worked ?? lesson.what_failed ?? '').slice(0, 80)}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// Record import provenance
|
|
216
|
-
if (!dry_run && imported > 0) {
|
|
217
|
-
try {
|
|
218
|
-
const prov = JSON.stringify({
|
|
219
|
-
share_id: cleanShareId, title: payload.title,
|
|
220
|
-
imported: imported, skipped, conflicts,
|
|
221
|
-
topic_prefix, imported_at: new Date().toISOString(),
|
|
222
|
-
});
|
|
223
|
-
await redis.rpush('cachly:brain:imports', prov);
|
|
224
|
-
await redis.ltrim('cachly:brain:imports', -20, -1);
|
|
225
|
-
}
|
|
226
|
-
catch { /* non-critical */ }
|
|
227
|
-
}
|
|
228
|
-
const dryTag = dry_run ? ' (DRY RUN — no writes)' : '';
|
|
229
|
-
const shareTitle = payload.title ?? cleanShareId;
|
|
230
|
-
const lines = [
|
|
231
|
-
`📥 **brain_import${dryTag}** — from "${shareTitle}"`,
|
|
232
|
-
``,
|
|
233
|
-
`**Share ID:** \`${cleanShareId}\``,
|
|
234
|
-
`**Lessons imported:** ${imported}`,
|
|
235
|
-
...(skipped > 0 ? [`**Skipped (low confidence):** ${skipped}`] : []),
|
|
236
|
-
...(conflicts > 0 ? [`**Skipped (already exist):** ${conflicts} _(pass \`overwrite=true\` to replace)_`] : []),
|
|
237
|
-
``,
|
|
238
|
-
];
|
|
239
|
-
if (importLog.length > 0) {
|
|
240
|
-
lines.push(`**Sample lessons imported:**`, ...importLog);
|
|
241
|
-
if (imported > 6)
|
|
242
|
-
lines.push(` _...and ${imported - 6} more_`);
|
|
243
|
-
}
|
|
244
|
-
if (imported > 0 && !dry_run) {
|
|
245
|
-
lines.push(``, `✅ Brain updated — use \`smart_recall(query="...")\` to query the new knowledge.`);
|
|
246
|
-
}
|
|
247
|
-
else if (dry_run) {
|
|
248
|
-
lines.push(``, `_Remove \`dry_run=true\` to actually import these lessons._`);
|
|
249
|
-
}
|
|
250
|
-
else if (imported === 0 && conflicts > 0) {
|
|
251
|
-
lines.push(``, `ℹ️ All lessons already exist in your Brain. Pass \`overwrite=true\` to update them.`);
|
|
252
|
-
}
|
|
253
|
-
return lines.join('\n');
|
|
254
|
-
}
|
|
255
|
-
// ── brain_share_list ───────────────────────────────────────────────────────
|
|
256
|
-
case 'brain_share_list': {
|
|
257
|
-
const { instance_id } = args;
|
|
258
|
-
if (!instance_id)
|
|
259
|
-
return '⚠️ `brain_share_list` requires `instance_id`.';
|
|
260
|
-
const redis = await getConnection(instance_id);
|
|
261
|
-
// Load from local provenance log
|
|
262
|
-
let localShares = [];
|
|
263
|
-
try {
|
|
264
|
-
const raw = await redis.lrange('cachly:brain:shares', 0, -1);
|
|
265
|
-
localShares = raw.map(r => {
|
|
266
|
-
try {
|
|
267
|
-
return JSON.parse(r);
|
|
268
|
-
}
|
|
269
|
-
catch {
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
}).filter(Boolean);
|
|
273
|
-
}
|
|
274
|
-
catch { /* non-critical */ }
|
|
275
|
-
// Also try API for authoritative list
|
|
276
|
-
let apiShares = [];
|
|
277
|
-
try {
|
|
278
|
-
const result = await apiFetch('/api/v1/brains/share');
|
|
279
|
-
apiShares = result.shares ?? [];
|
|
280
|
-
}
|
|
281
|
-
catch { /* API may not be live yet */ }
|
|
282
|
-
// Merge: API is authoritative; local fills gaps
|
|
283
|
-
const merged = apiShares.length > 0 ? apiShares : localShares;
|
|
284
|
-
if (merged.length === 0) {
|
|
285
|
-
return [
|
|
286
|
-
`📤 **brain_share_list** — no shares yet`,
|
|
287
|
-
``,
|
|
288
|
-
`You haven't shared any Brain snapshots from this instance.`,
|
|
289
|
-
``,
|
|
290
|
-
`Create one with: \`brain_share(instance_id="${instance_id}", title="My Patterns")\``,
|
|
291
|
-
].join('\n');
|
|
292
|
-
}
|
|
293
|
-
const lines = [
|
|
294
|
-
`📤 **Your Brain Shares** (${merged.length})`,
|
|
295
|
-
``,
|
|
296
|
-
`| # | Title | Lessons | Visibility | Created |`,
|
|
297
|
-
`|---|-------|---------|------------|---------|`,
|
|
298
|
-
];
|
|
299
|
-
for (const [i, s] of merged.entries()) {
|
|
300
|
-
const d = new Date(s.created_at);
|
|
301
|
-
const created = isNaN(d.getTime()) ? s.created_at : d.toISOString().slice(0, 10);
|
|
302
|
-
const vis = s.visibility === 'public' ? '🌐 public' : '🔗 unlisted';
|
|
303
|
-
lines.push(`| ${i + 1} | ${s.title ?? '—'} | ${s.lesson_count ?? '?'} | ${vis} | ${created} |`);
|
|
304
|
-
}
|
|
305
|
-
const newest = merged[merged.length - 1];
|
|
306
|
-
lines.push(``, `**Import command:** \`brain_import(instance_id="<their-id>", share_id="${newest?.share_id ?? '...'}")\``, `**Revoke:** \`brain_unshare(instance_id="${instance_id}", share_id="<id>")\``);
|
|
307
|
-
return lines.join('\n');
|
|
308
|
-
}
|
|
309
|
-
// ── brain_unshare ──────────────────────────────────────────────────────────
|
|
310
|
-
case 'brain_unshare': {
|
|
311
|
-
const { instance_id, share_id } = args;
|
|
312
|
-
if (!instance_id)
|
|
313
|
-
return '⚠️ `brain_unshare` requires `instance_id`.';
|
|
314
|
-
if (!share_id)
|
|
315
|
-
return '⚠️ `brain_unshare` requires `share_id`.';
|
|
316
|
-
// Call API to delete
|
|
317
|
-
try {
|
|
318
|
-
await apiFetch(`/api/v1/brains/share/${share_id}`, { method: 'DELETE' });
|
|
319
|
-
}
|
|
320
|
-
catch (e) {
|
|
321
|
-
const msg = e.message ?? '';
|
|
322
|
-
if (msg.includes('404') || msg.includes('not found')) {
|
|
323
|
-
return `❌ Share \`${share_id}\` not found — it may have already been removed.`;
|
|
324
|
-
}
|
|
325
|
-
// If API is down, just remove from local log and warn
|
|
326
|
-
if (msg.includes('ECONNREFUSED') || msg.includes('fetch failed')) {
|
|
327
|
-
const redis = await getConnection(instance_id);
|
|
328
|
-
try {
|
|
329
|
-
const raw = await redis.lrange('cachly:brain:shares', 0, -1);
|
|
330
|
-
const kept = raw.filter(r => { try {
|
|
331
|
-
return JSON.parse(r).share_id !== share_id;
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
return true;
|
|
335
|
-
} });
|
|
336
|
-
await redis.del('cachly:brain:shares');
|
|
337
|
-
for (const entry of kept)
|
|
338
|
-
await redis.rpush('cachly:brain:shares', entry);
|
|
339
|
-
}
|
|
340
|
-
catch { /* non-critical */ }
|
|
341
|
-
return [
|
|
342
|
-
`⚠️ **brain_unshare** — removed from local log`,
|
|
343
|
-
``,
|
|
344
|
-
`API unreachable — removed \`${share_id}\` from your local share registry.`,
|
|
345
|
-
`The public link may still be accessible until the API confirms deletion.`,
|
|
346
|
-
].join('\n');
|
|
347
|
-
}
|
|
348
|
-
return `❌ brain_unshare failed: ${msg}`;
|
|
349
|
-
}
|
|
350
|
-
// Remove from local log
|
|
351
|
-
const redis = await getConnection(instance_id);
|
|
352
|
-
try {
|
|
353
|
-
const raw = await redis.lrange('cachly:brain:shares', 0, -1);
|
|
354
|
-
const kept = raw.filter(r => { try {
|
|
355
|
-
return JSON.parse(r).share_id !== share_id;
|
|
356
|
-
}
|
|
357
|
-
catch {
|
|
358
|
-
return true;
|
|
359
|
-
} });
|
|
360
|
-
await redis.del('cachly:brain:shares');
|
|
361
|
-
for (const entry of kept)
|
|
362
|
-
await redis.rpush('cachly:brain:shares', entry);
|
|
363
|
-
}
|
|
364
|
-
catch { /* non-critical */ }
|
|
365
|
-
return [
|
|
366
|
-
`🗑️ **brain_unshare** — share revoked`,
|
|
367
|
-
``,
|
|
368
|
-
`Share \`${share_id}\` has been deleted. The public link is no longer accessible.`,
|
|
369
|
-
``,
|
|
370
|
-
`_Anyone who already imported this Brain keeps their local copy._`,
|
|
371
|
-
].join('\n');
|
|
372
|
-
}
|
|
373
|
-
// ── brain_discover ─────────────────────────────────────────────────────────
|
|
374
|
-
case 'brain_discover': {
|
|
375
|
-
const { query = '', topic = '', limit = 10, } = args;
|
|
376
|
-
// Search public Brain marketplace
|
|
377
|
-
let results = [];
|
|
378
|
-
try {
|
|
379
|
-
const params = new URLSearchParams();
|
|
380
|
-
if (query)
|
|
381
|
-
params.set('q', query);
|
|
382
|
-
if (topic)
|
|
383
|
-
params.set('topic', topic);
|
|
384
|
-
params.set('limit', String(limit));
|
|
385
|
-
results = await apiFetch(`/api/v1/brains/discover?${params.toString()}`);
|
|
386
|
-
}
|
|
387
|
-
catch (e) {
|
|
388
|
-
const msg = e.message ?? '';
|
|
389
|
-
if (msg.includes('404') || msg.includes('ECONNREFUSED') || msg.includes('fetch failed')) {
|
|
390
|
-
return [
|
|
391
|
-
`🔍 **brain_discover** — marketplace not yet live`,
|
|
392
|
-
``,
|
|
393
|
-
`The public Brain marketplace is coming soon.`,
|
|
394
|
-
``,
|
|
395
|
-
`**In the meantime:**`,
|
|
396
|
-
` • Share your Brain: \`brain_share(instance_id="...", visibility="public")\``,
|
|
397
|
-
` • Import by ID: \`brain_import(instance_id="...", share_id="<id>")\``,
|
|
398
|
-
` • Community index: ${cachlyUrl('/brains', 'brain-discover')}`,
|
|
399
|
-
].join('\n');
|
|
400
|
-
}
|
|
401
|
-
return `❌ brain_discover failed: ${msg}`;
|
|
402
|
-
}
|
|
403
|
-
if (results.length === 0) {
|
|
404
|
-
const hint = query ? `matching "${query}"` : topic ? `in topic "${topic}"` : '';
|
|
405
|
-
return [
|
|
406
|
-
`🔍 **brain_discover** — no public Brains found ${hint}`.trim(),
|
|
407
|
-
``,
|
|
408
|
-
`Be the first! \`brain_share(instance_id="...", visibility="public")\``,
|
|
409
|
-
].join('\n');
|
|
410
|
-
}
|
|
411
|
-
const lines = [
|
|
412
|
-
`🔍 **brain_discover**${query ? ` — "${query}"` : ''}${topic ? ` · topic: ${topic}` : ''} (${results.length} result${results.length !== 1 ? 's' : ''})`,
|
|
413
|
-
``,
|
|
414
|
-
];
|
|
415
|
-
for (const [i, r] of results.entries()) {
|
|
416
|
-
const d = new Date(r.created_at);
|
|
417
|
-
const created = isNaN(d.getTime()) ? '' : ` · ${d.toISOString().slice(0, 10)}`;
|
|
418
|
-
const importCount = r.imports != null ? ` · ${r.imports} imports` : '';
|
|
419
|
-
const topicStr = r.topics?.length ? ` · topics: ${r.topics.slice(0, 4).join(', ')}` : '';
|
|
420
|
-
const by = r.author ? ` by **${r.author}**` : '';
|
|
421
|
-
lines.push(`**${i + 1}. ${r.title}**${by}`, ` 📚 ${r.lesson_count} lessons${topicStr}${importCount}${created}`, r.description ? ` _${r.description.slice(0, 120)}_` : '', ` \`brain_import(instance_id="...", share_id="${r.share_id}")\``, ``);
|
|
422
|
-
}
|
|
423
|
-
lines.push(`_Browse more at ${cachlyUrl('/brains', 'brain-discover')}_`);
|
|
424
|
-
return lines.filter(l => l !== ' ').join('\n');
|
|
425
|
-
}
|
|
426
|
-
// ── brain_seed_starter ─────────────────────────────────────────────────────
|
|
427
|
-
// Seeds a fresh Brain with curated, universal engineering lessons so the very
|
|
428
|
-
// first query returns a hit — drives time-to-first-recall under 2 minutes.
|
|
429
|
-
case 'brain_seed_starter': {
|
|
430
|
-
const { instance_id, topic_filter, dry_run = false, force = false, } = args;
|
|
431
|
-
if (!instance_id)
|
|
432
|
-
return '⚠️ `brain_seed_starter` requires `instance_id`.';
|
|
433
|
-
const redis = await getConnection(instance_id);
|
|
434
|
-
// Idempotency guard: don't double-seed unless force=true.
|
|
435
|
-
const seededMarker = await redis.get(`cachly:brain:starter_seeded:${instance_id}`);
|
|
436
|
-
if (seededMarker && !force && !dry_run) {
|
|
437
|
-
return [
|
|
438
|
-
`🌱 **brain_seed_starter** — already seeded`,
|
|
439
|
-
``,
|
|
440
|
-
`This Brain was seeded with the starter corpus on ${seededMarker}.`,
|
|
441
|
-
``,
|
|
442
|
-
`Pass \`force=true\` to re-seed (existing starter lessons will be refreshed).`,
|
|
443
|
-
].join('\n');
|
|
444
|
-
}
|
|
445
|
-
const topicFilter = Array.isArray(topic_filter) && topic_filter.length > 0 ? topic_filter : undefined;
|
|
446
|
-
const selected = topicFilter
|
|
447
|
-
? STARTER_CORPUS.filter(l => topicFilter.some(f => l.topic.includes(f) || l.tags.some(t => t.includes(f))))
|
|
448
|
-
: STARTER_CORPUS;
|
|
449
|
-
if (selected.length === 0) {
|
|
450
|
-
return [
|
|
451
|
-
`🌱 **brain_seed_starter** — no matching starter lessons`,
|
|
452
|
-
``,
|
|
453
|
-
`No starter lessons match filter: ${topicFilter?.join(', ')}.`,
|
|
454
|
-
`Available topics: ${STARTER_CORPUS.map(l => `\`${l.topic}\``).join(', ')}`,
|
|
455
|
-
].join('\n');
|
|
456
|
-
}
|
|
457
|
-
if (dry_run) {
|
|
458
|
-
const sample = selected.slice(0, 8).map(l => ` ✅ \`${l.topic}\` — ${l.what_worked.slice(0, 70)}…`);
|
|
459
|
-
return [
|
|
460
|
-
`🌱 **brain_seed_starter (DRY RUN)** — would seed ${selected.length} lesson${selected.length !== 1 ? 's' : ''}`,
|
|
461
|
-
``,
|
|
462
|
-
...sample,
|
|
463
|
-
selected.length > 8 ? ` _...and ${selected.length - 8} more_` : '',
|
|
464
|
-
``,
|
|
465
|
-
`_Remove \`dry_run=true\` to seed these into the Brain._`,
|
|
466
|
-
].filter(l => l !== '').join('\n');
|
|
467
|
-
}
|
|
468
|
-
const now = new Date().toISOString();
|
|
469
|
-
let seeded = 0;
|
|
470
|
-
let skipped = 0;
|
|
471
|
-
for (const lesson of selected) {
|
|
472
|
-
const bestKey = `cachly:lesson:best:${lesson.topic}`;
|
|
473
|
-
// Never clobber a user's own lesson on the same topic (unless force).
|
|
474
|
-
if (!force) {
|
|
475
|
-
const existing = await redis.get(bestKey);
|
|
476
|
-
if (existing) {
|
|
477
|
-
const parsed = safeJsonParse(existing, null);
|
|
478
|
-
// Only skip if it's a real user lesson (not a prior starter seed).
|
|
479
|
-
if (parsed && parsed.source !== 'starter') {
|
|
480
|
-
skipped++;
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
const lessonToStore = JSON.stringify({
|
|
486
|
-
topic: lesson.topic,
|
|
487
|
-
outcome: lesson.outcome,
|
|
488
|
-
what_worked: lesson.what_worked,
|
|
489
|
-
what_failed: lesson.what_failed,
|
|
490
|
-
ctx: lesson.ctx,
|
|
491
|
-
tags: lesson.tags,
|
|
492
|
-
confidence: lesson.confidence,
|
|
493
|
-
recall_count: 0,
|
|
494
|
-
source: 'starter',
|
|
495
|
-
ts: now,
|
|
496
|
-
verified_at: now,
|
|
497
|
-
version: 3,
|
|
498
|
-
});
|
|
499
|
-
await redis.set(bestKey, lessonToStore);
|
|
500
|
-
const listKey = `cachly:lessons:${lesson.topic}`;
|
|
501
|
-
await redis.rpush(listKey, lessonToStore);
|
|
502
|
-
await redis.ltrim(listKey, -100, -1);
|
|
503
|
-
await redis.expire(listKey, 90 * 86400);
|
|
504
|
-
seeded++;
|
|
505
|
-
}
|
|
506
|
-
// Stamp born_at so time-to-first-recall starts counting from the seed moment.
|
|
507
|
-
await redis.set(`cachly:stats:born_at:${instance_id}`, now, 'EX', 365 * 86400, 'NX').catch(() => { });
|
|
508
|
-
// Mark seeded for idempotency.
|
|
509
|
-
await redis.set(`cachly:brain:starter_seeded:${instance_id}`, now, 'EX', 365 * 86400);
|
|
510
|
-
return [
|
|
511
|
-
`🌱 **brain_seed_starter** — Brain seeded`,
|
|
512
|
-
``,
|
|
513
|
-
`**${seeded}** universal engineering lesson${seeded !== 1 ? 's' : ''} added${skipped > 0 ? ` (${skipped} skipped — your own lessons take priority)` : ''}.`,
|
|
514
|
-
``,
|
|
515
|
-
`Your Brain now answers common questions out of the box:`,
|
|
516
|
-
` • \`smart_recall(query="docker build slow")\``,
|
|
517
|
-
` • \`smart_recall(query="pod OOMKilled")\``,
|
|
518
|
-
` • \`smart_recall(query="jwt token rejected")\``,
|
|
519
|
-
``,
|
|
520
|
-
`_Starter lessons are tagged \`source: "starter"\` and never override your own._`,
|
|
521
|
-
`_They're replaced as you learn project-specific lessons on the same topics._`,
|
|
522
|
-
].join('\n');
|
|
523
|
-
}
|
|
524
|
-
default:
|
|
525
|
-
return null;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
/** Exposed for the empty-brain welcome nudge in session_start. */
|
|
529
|
-
export { STARTER_CORPUS_SIZE };
|
|
530
|
-
//# sourceMappingURL=share.js.map
|
|
1
|
+
import{safeJsonParse as T}from"../utils.js";import{STARTER_CORPUS as k,STARTER_CORPUS_SIZE as x}from"../starter-corpus.js";import{cachlyUrl as j}from"../cachly-url.js";const E=new Set(["brain_share","brain_import","brain_share_list","brain_unshare","brain_discover","brain_seed_starter"]);async function R(v,w){const y=[],$=v.scanStream({match:"cachly:lesson:best:*",count:200});await new Promise((s,h)=>{$.on("data",e=>y.push(...e)),$.on("end",s),$.on("error",h)});const o=[];for(const s of y.slice(0,500)){const h=await v.get(s);if(!h)continue;const e=T(h,null);!e||!e.topic||w&&w.length>0&&!w.some(n=>e.topic.includes(n))||o.push({topic:e.topic,outcome:e.outcome,...e.what_worked?{what_worked:e.what_worked}:{},...e.what_failed?{what_failed:e.what_failed}:{},...e.ctx?{ctx:e.ctx}:{},...e.tags?.length?{tags:e.tags}:{},...e.author?{author:e.author}:{},...e.service?{service:e.service}:{},confidence:e.confidence??1,ts:e.ts})}return o}async function A(v,w,y,$){switch(v){case"brain_share":{const{instance_id:o,title:s="My Brain Snapshot",description:h="",topic_filter:e,visibility:t="unlisted",max_lessons:n=100,dry_run:r=!1}=w;if(!o)return"\u26A0\uFE0F `brain_share` requires `instance_id`.";const l=await y(o),c=Array.isArray(e)&&e.length>0?e:void 0,i=(await R(l,c)).slice(0,n);if(i.length===0)return["\u{1F4E4} **brain_share** \u2014 nothing to share yet","",`No lessons found in Brain \`${o}\`${c?` matching topics: ${c.join(", ")}`:""}.`,"",'Store lessons first with `learn_from_attempts(topic="...", what_worked="...")`.'].join(`
|
|
2
|
+
`);if(r){const u=i.slice(0,5).map(p=>` ${p.outcome==="success"?"\u2705":p.outcome==="failure"?"\u274C":"\u26A0\uFE0F"} \`${p.topic}\` \u2014 ${(p.what_worked??p.what_failed??"").slice(0,80)}`);return[`\u{1F4E4} **brain_share (DRY RUN)** \u2014 would share ${i.length} lesson${i.length!==1?"s":""}`,"",`**Title:** ${s}`,`**Visibility:** ${t}`,c?`**Topic filter:** ${c.join(", ")}`:"","","**Sample lessons (first 5):**",...u,i.length>5?` _...and ${i.length-5} more_`:"","","_Remove `dry_run=true` to create the public share._"].filter(p=>p!=="").join(`
|
|
3
|
+
`)}let a=null,d=null;try{a=(await $("/api/v1/brains/share",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:s,description:h,visibility:t,lessons:i})})).share_id??null,a&&(d=j(`/brain/share/${a}`,"brain-share"))}catch(u){const p=u.message??"";if(p.includes("404")||p.includes("not found")||p.includes("ECONNREFUSED")){const f=JSON.stringify({title:s,description:h,visibility:t,lessons:i,exported_at:new Date().toISOString()},null,2);return[`\u{1F4E4} **brain_share** \u2014 exported ${i.length} lesson${i.length!==1?"s":""}`,"","\u26A0\uFE0F The public share API is not yet available. Here is your portable snapshot:","","```json",f.slice(0,3e3)+(f.length>3e3?`
|
|
4
|
+
... (truncated)`:""),"```","","_To import this on another Brain, use `brain_import` with the JSON payload._"].join(`
|
|
5
|
+
`)}return`\u274C brain_share failed: ${u.message}`}const b=i.slice(0,5).map(u=>` ${u.outcome==="success"?"\u2705":u.outcome==="failure"?"\u274C":"\u26A0\uFE0F"} \`${u.topic}\``);try{const u=JSON.stringify({share_id:a,title:s,visibility:t,lesson_count:i.length,created_at:new Date().toISOString()});await l.rpush("cachly:brain:shares",u),await l.ltrim("cachly:brain:shares",-20,-1)}catch{}return[`\u{1F4E4} **brain_share** \u2014 ${i.length} lesson${i.length!==1?"s":""} shared`,"",`**Share ID:** \`${a}\``,`**URL:** ${d}`,`**Title:** ${s}`,`**Visible:** ${t}`,"","**Sample lessons:**",...b,i.length>5?` _...and ${i.length-5} more_`:"","","**Anyone can import this Brain with:**",`\`brain_import(instance_id="<their-id>", share_id="${a}")\``,"","_To list all your shares, run `brain_share_list`._"].filter(u=>u!=="").join(`
|
|
6
|
+
`)}case"brain_import":{const{instance_id:o,share_id:s,topic_prefix:h="",min_confidence:e=0,dry_run:t=!1,overwrite:n=!1}=w;if(!o)return"\u26A0\uFE0F `brain_import` requires `instance_id`.";if(!s)return"\u26A0\uFE0F `brain_import` requires `share_id`. Get one from `brain_share`.";const r=s.replace(/^.*\/brain\/share\//,"").trim();let l;try{l=await $(`/api/v1/brains/share/${r}`)}catch(m){const g=m.message??"";return g.includes("404")||g.includes("not found")?["\u274C **brain_import** \u2014 share not found","",`Share ID \`${r}\` does not exist or has been removed.`,"","Ask the Brain owner to re-run `brain_share` and share the new ID with you."].join(`
|
|
7
|
+
`):`\u274C brain_import failed: ${m.message}`}const c=l.lessons??[];if(c.length===0)return`\u26A0\uFE0F Share \`${r}\` exists but contains no lessons.`;const _=await y(o);let i=0,a=0,d=0;const b=[];for(const m of c){if((m.confidence??1)<e){a++;continue}const g=h?`${h}:${m.topic}`:m.topic,N=`cachly:lesson:best:${g}`;if(!n&&await _.get(N)){d++;continue}const O=JSON.stringify({...m,topic:g,imported_from:r,imported_at:new Date().toISOString()});if(!t){await _.set(N,O);const S=`cachly:lessons:${g}`;await _.rpush(S,O),await _.ltrim(S,-100,-1),await _.expire(S,90*86400)}if(i++,b.length<6){const S=m.outcome==="success"?"\u2705":m.outcome==="failure"?"\u274C":"\u26A0\uFE0F";b.push(` ${S} \`${g}\` \u2014 ${(m.what_worked??m.what_failed??"").slice(0,80)}`)}}if(!t&&i>0)try{const m=JSON.stringify({share_id:r,title:l.title,imported:i,skipped:a,conflicts:d,topic_prefix:h,imported_at:new Date().toISOString()});await _.rpush("cachly:brain:imports",m),await _.ltrim("cachly:brain:imports",-20,-1)}catch{}const u=t?" (DRY RUN \u2014 no writes)":"",p=l.title??r,f=[`\u{1F4E5} **brain_import${u}** \u2014 from "${p}"`,"",`**Share ID:** \`${r}\``,`**Lessons imported:** ${i}`,...a>0?[`**Skipped (low confidence):** ${a}`]:[],...d>0?[`**Skipped (already exist):** ${d} _(pass \`overwrite=true\` to replace)_`]:[],""];return b.length>0&&(f.push("**Sample lessons imported:**",...b),i>6&&f.push(` _...and ${i-6} more_`)),i>0&&!t?f.push("",'\u2705 Brain updated \u2014 use `smart_recall(query="...")` to query the new knowledge.'):t?f.push("","_Remove `dry_run=true` to actually import these lessons._"):i===0&&d>0&&f.push("","\u2139\uFE0F All lessons already exist in your Brain. Pass `overwrite=true` to update them."),f.join(`
|
|
8
|
+
`)}case"brain_share_list":{const{instance_id:o}=w;if(!o)return"\u26A0\uFE0F `brain_share_list` requires `instance_id`.";const s=await y(o);let h=[];try{h=(await s.lrange("cachly:brain:shares",0,-1)).map(c=>{try{return JSON.parse(c)}catch{return null}}).filter(Boolean)}catch{}let e=[];try{e=(await $("/api/v1/brains/share")).shares??[]}catch{}const t=e.length>0?e:h;if(t.length===0)return["\u{1F4E4} **brain_share_list** \u2014 no shares yet","","You haven't shared any Brain snapshots from this instance.","",`Create one with: \`brain_share(instance_id="${o}", title="My Patterns")\``].join(`
|
|
9
|
+
`);const n=[`\u{1F4E4} **Your Brain Shares** (${t.length})`,"","| # | Title | Lessons | Visibility | Created |","|---|-------|---------|------------|---------|"];for(const[l,c]of t.entries()){const _=new Date(c.created_at),i=isNaN(_.getTime())?c.created_at:_.toISOString().slice(0,10),a=c.visibility==="public"?"\u{1F310} public":"\u{1F517} unlisted";n.push(`| ${l+1} | ${c.title??"\u2014"} | ${c.lesson_count??"?"} | ${a} | ${i} |`)}const r=t[t.length-1];return n.push("",`**Import command:** \`brain_import(instance_id="<their-id>", share_id="${r?.share_id??"..."}")\``,`**Revoke:** \`brain_unshare(instance_id="${o}", share_id="<id>")\``),n.join(`
|
|
10
|
+
`)}case"brain_unshare":{const{instance_id:o,share_id:s}=w;if(!o)return"\u26A0\uFE0F `brain_unshare` requires `instance_id`.";if(!s)return"\u26A0\uFE0F `brain_unshare` requires `share_id`.";try{await $(`/api/v1/brains/share/${s}`,{method:"DELETE"})}catch(e){const t=e.message??"";if(t.includes("404")||t.includes("not found"))return`\u274C Share \`${s}\` not found \u2014 it may have already been removed.`;if(t.includes("ECONNREFUSED")||t.includes("fetch failed")){const n=await y(o);try{const l=(await n.lrange("cachly:brain:shares",0,-1)).filter(c=>{try{return JSON.parse(c).share_id!==s}catch{return!0}});await n.del("cachly:brain:shares");for(const c of l)await n.rpush("cachly:brain:shares",c)}catch{}return["\u26A0\uFE0F **brain_unshare** \u2014 removed from local log","",`API unreachable \u2014 removed \`${s}\` from your local share registry.`,"The public link may still be accessible until the API confirms deletion."].join(`
|
|
11
|
+
`)}return`\u274C brain_unshare failed: ${t}`}const h=await y(o);try{const t=(await h.lrange("cachly:brain:shares",0,-1)).filter(n=>{try{return JSON.parse(n).share_id!==s}catch{return!0}});await h.del("cachly:brain:shares");for(const n of t)await h.rpush("cachly:brain:shares",n)}catch{}return["\u{1F5D1}\uFE0F **brain_unshare** \u2014 share revoked","",`Share \`${s}\` has been deleted. The public link is no longer accessible.`,"","_Anyone who already imported this Brain keeps their local copy._"].join(`
|
|
12
|
+
`)}case"brain_discover":{const{query:o="",topic:s="",limit:h=10}=w;let e=[];try{const n=new URLSearchParams;o&&n.set("q",o),s&&n.set("topic",s),n.set("limit",String(h)),e=await $(`/api/v1/brains/discover?${n.toString()}`)}catch(n){const r=n.message??"";return r.includes("404")||r.includes("ECONNREFUSED")||r.includes("fetch failed")?["\u{1F50D} **brain_discover** \u2014 marketplace not yet live","","The public Brain marketplace is coming soon.","","**In the meantime:**",' \u2022 Share your Brain: `brain_share(instance_id="...", visibility="public")`',' \u2022 Import by ID: `brain_import(instance_id="...", share_id="<id>")`',` \u2022 Community index: ${j("/brains","brain-discover")}`].join(`
|
|
13
|
+
`):`\u274C brain_discover failed: ${r}`}if(e.length===0)return[`\u{1F50D} **brain_discover** \u2014 no public Brains found ${o?`matching "${o}"`:s?`in topic "${s}"`:""}`.trim(),"",'Be the first! `brain_share(instance_id="...", visibility="public")`'].join(`
|
|
14
|
+
`);const t=[`\u{1F50D} **brain_discover**${o?` \u2014 "${o}"`:""}${s?` \xB7 topic: ${s}`:""} (${e.length} result${e.length!==1?"s":""})`,""];for(const[n,r]of e.entries()){const l=new Date(r.created_at),c=isNaN(l.getTime())?"":` \xB7 ${l.toISOString().slice(0,10)}`,_=r.imports!=null?` \xB7 ${r.imports} imports`:"",i=r.topics?.length?` \xB7 topics: ${r.topics.slice(0,4).join(", ")}`:"",a=r.author?` by **${r.author}**`:"";t.push(`**${n+1}. ${r.title}**${a}`,` \u{1F4DA} ${r.lesson_count} lessons${i}${_}${c}`,r.description?` _${r.description.slice(0,120)}_`:"",` \`brain_import(instance_id="...", share_id="${r.share_id}")\``,"")}return t.push(`_Browse more at ${j("/brains","brain-discover")}_`),t.filter(n=>n!==" ").join(`
|
|
15
|
+
`)}case"brain_seed_starter":{const{instance_id:o,topic_filter:s,dry_run:h=!1,force:e=!1}=w;if(!o)return"\u26A0\uFE0F `brain_seed_starter` requires `instance_id`.";const t=await y(o),n=await t.get(`cachly:brain:starter_seeded:${o}`);if(n&&!e&&!h)return["\u{1F331} **brain_seed_starter** \u2014 already seeded","",`This Brain was seeded with the starter corpus on ${n}.`,"","Pass `force=true` to re-seed (existing starter lessons will be refreshed)."].join(`
|
|
16
|
+
`);const r=Array.isArray(s)&&s.length>0?s:void 0,l=r?k.filter(a=>r.some(d=>a.topic.includes(d)||a.tags.some(b=>b.includes(d)))):k;if(l.length===0)return["\u{1F331} **brain_seed_starter** \u2014 no matching starter lessons","",`No starter lessons match filter: ${r?.join(", ")}.`,`Available topics: ${k.map(a=>`\`${a.topic}\``).join(", ")}`].join(`
|
|
17
|
+
`);if(h){const a=l.slice(0,8).map(d=>` \u2705 \`${d.topic}\` \u2014 ${d.what_worked.slice(0,70)}\u2026`);return[`\u{1F331} **brain_seed_starter (DRY RUN)** \u2014 would seed ${l.length} lesson${l.length!==1?"s":""}`,"",...a,l.length>8?` _...and ${l.length-8} more_`:"","","_Remove `dry_run=true` to seed these into the Brain._"].filter(d=>d!=="").join(`
|
|
18
|
+
`)}const c=new Date().toISOString();let _=0,i=0;for(const a of l){const d=`cachly:lesson:best:${a.topic}`;if(!e){const p=await t.get(d);if(p){const f=T(p,null);if(f&&f.source!=="starter"){i++;continue}}}const b=JSON.stringify({topic:a.topic,outcome:a.outcome,what_worked:a.what_worked,what_failed:a.what_failed,ctx:a.ctx,tags:a.tags,confidence:a.confidence,recall_count:0,source:"starter",ts:c,verified_at:c,version:3});await t.set(d,b);const u=`cachly:lessons:${a.topic}`;await t.rpush(u,b),await t.ltrim(u,-100,-1),await t.expire(u,90*86400),_++}return await t.set(`cachly:stats:born_at:${o}`,c,"EX",365*86400,"NX").catch(()=>{}),await t.set(`cachly:brain:starter_seeded:${o}`,c,"EX",365*86400),["\u{1F331} **brain_seed_starter** \u2014 Brain seeded","",`**${_}** universal engineering lesson${_!==1?"s":""} added${i>0?` (${i} skipped \u2014 your own lessons take priority)`:""}.`,"","Your Brain now answers common questions out of the box:",' \u2022 `smart_recall(query="docker build slow")`',' \u2022 `smart_recall(query="pod OOMKilled")`',' \u2022 `smart_recall(query="jwt token rejected")`',"",'_Starter lessons are tagged `source: "starter"` and never override your own._',"_They're replaced as you learn project-specific lessons on the same topics._"].join(`
|
|
19
|
+
`)}default:return null}}export{E as SHARE_TOOL_NAMES,x as STARTER_CORPUS_SIZE,A as handleShareTool};
|