@cachly-dev/mcp-server 0.10.117 → 0.10.119
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 +9 -5
- package/dist/src/ambient-cli.d.ts +85 -0
- package/dist/src/ambient-cli.js +199 -0
- package/dist/src/ambient-cli.js.map +1 -0
- package/dist/src/ambient-hooks.d.ts +128 -0
- package/dist/src/ambient-hooks.js +269 -0
- package/dist/src/ambient-hooks.js.map +1 -0
- package/dist/src/ambient-ledger.d.ts +19 -0
- package/dist/src/ambient-ledger.js +95 -0
- package/dist/src/ambient-ledger.js.map +1 -0
- package/dist/src/ambient-recall.d.ts +59 -0
- package/dist/src/ambient-recall.js +96 -0
- package/dist/src/ambient-recall.js.map +1 -0
- package/dist/src/doctor.d.ts +47 -0
- package/dist/src/doctor.js +247 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/editor.d.ts +9 -0
- package/dist/src/editor.js +63 -0
- package/dist/src/editor.js.map +1 -0
- package/dist/src/first-contact.d.ts +56 -0
- package/dist/src/first-contact.js +108 -0
- package/dist/src/first-contact.js.map +1 -0
- package/dist/src/funnel-milestones.d.ts +5 -0
- package/dist/src/funnel-milestones.js +48 -0
- package/dist/src/funnel-milestones.js.map +1 -0
- package/dist/src/handlers/brain.d.ts +1 -0
- package/dist/src/handlers/brain.js +45 -8
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/handlers/fedbrain.js +48 -16
- package/dist/src/handlers/fedbrain.js.map +1 -1
- package/dist/src/handlers/syndicate.js +91 -1
- package/dist/src/handlers/syndicate.js.map +1 -1
- package/dist/src/handlers/team.d.ts +10 -0
- package/dist/src/handlers/team.js +45 -1
- package/dist/src/handlers/team.js.map +1 -1
- package/dist/src/index.d.ts +27 -5
- package/dist/src/index.js +293 -28
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools.d.ts +5 -0
- package/dist/src/tools.js +12 -1
- package/dist/src/tools.js.map +1 -1
- package/glama.json +1 -1
- package/package.json +2 -2
- package/server.json +4 -4
|
@@ -2,13 +2,20 @@ import { execSync } from 'node:child_process';
|
|
|
2
2
|
import { calculateConfidence, confidenceBadge, STRUCTURED_TEMPLATES, CONFIDENCE_WARN_VALUE, CONFIDENCE_STALE_VALUE, CONFIDENCE_WARN_DAYS } from '../confidence.js';
|
|
3
3
|
import { ckgSlug, extractProblemConcept, ckgUpsertNode, ckgUpdateEdge, ckgUpsertPersonNode, ckgUpsertFileNode, ckgRecordCollaboration, ckgUpsertServiceNode } from '../ckg.js';
|
|
4
4
|
import { safeJsonParse, scanKeys } from '../utils.js';
|
|
5
|
-
import { getRole, ROLE_BADGE, getScopes, lessonVisibleToScope } from './team.js';
|
|
5
|
+
import { getRole, ROLE_BADGE, getScopes, lessonVisibleToScope, reviewModeEnabled, storeLessonProposal } from './team.js';
|
|
6
6
|
import { keywordSearch, tokenize, splitMultiQuery, levenshtein, indexVocab as _indexVocab } from '../search.js';
|
|
7
7
|
import { rerankByQuality } from '../rerank.js';
|
|
8
8
|
import { computeEmbedding, hasEmbedProvider } from '../embeddings.js';
|
|
9
9
|
// ── Changelog (shown once per version in session_start) ──────────────────────
|
|
10
|
-
const MCP_VERSION = '0.10.
|
|
10
|
+
const MCP_VERSION = '0.10.119';
|
|
11
11
|
const WHATS_NEW = {
|
|
12
|
+
'0.10.119': [
|
|
13
|
+
`🔍 **Explainable briefings — provenance in every warning**`,
|
|
14
|
+
` 📎 \`/briefing\` warnings now carry \`outcome\`, \`author\`, \`learned_at\` and \`matched_on\` (the context tokens that triggered the warning) — editors can show WHY a fix hint fired, not just what it claims.`,
|
|
15
|
+
` 🧰 VS Code 0.12.0 uses this: severity + confidence in the toast, full lesson card with provenance, "Not helpful" per-file suppression, reviewable CLS auto-learns.`,
|
|
16
|
+
` 🧹 CI docs now describe exactly what ships: scan mode uses the REST \`/scan\` endpoint, the GitLab template covers learn/scan/confirm.`,
|
|
17
|
+
` 📊 126 MCP tools`,
|
|
18
|
+
],
|
|
12
19
|
'0.10.115': [
|
|
13
20
|
`🔧 **\`brain_get_pref\` + \`brain_set_pref\` — persistent Brain preferences**`,
|
|
14
21
|
` 📖 \`brain_get_pref\` reads back any stored preference (omit key to list all).`,
|
|
@@ -402,6 +409,21 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
402
409
|
const { instance_id, topic, outcome, what_worked, what_failed = '', context: ctx = '', severity = 'major', file_paths = [], commands = [], tags = [], depends_on = [], author = '', visibility = 'team', service = '', service_kind = 'service', group = '', } = args;
|
|
403
410
|
const redis = await getConnection(instance_id);
|
|
404
411
|
const ts = new Date().toISOString();
|
|
412
|
+
// ── Lessons-Review workflow (P1-1) ─────────────────────────────────────
|
|
413
|
+
// When the org enabled pending-mode on this (shared) brain, team-visible
|
|
414
|
+
// lessons become proposals awaiting owner/admin approval instead of
|
|
415
|
+
// landing in the recall keyspace. Private notes stay direct — they never
|
|
416
|
+
// surface in team recall anyway. Flag absent = direct write, as before.
|
|
417
|
+
if (visibility !== 'private' && await reviewModeEnabled(redis, instance_id)) {
|
|
418
|
+
return storeLessonProposal(redis, topic, {
|
|
419
|
+
topic, outcome, what_worked, what_failed, context: ctx, severity,
|
|
420
|
+
file_paths, commands, tags, depends_on,
|
|
421
|
+
...(author ? { author } : {}),
|
|
422
|
+
...(service ? { service } : {}),
|
|
423
|
+
...(group ? { group: String(group).toLowerCase().trim() } : {}),
|
|
424
|
+
visibility, recall_count: 0, ts, confidence: 1.0, version: 3,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
405
427
|
// ── Structured template hints ──────────────────────────────────────────
|
|
406
428
|
const category = topic.split(':')[0];
|
|
407
429
|
const template = STRUCTURED_TEMPLATES[category];
|
|
@@ -416,12 +438,14 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
416
438
|
// ── Deduplication + audit trail ────────────────────────────────────────
|
|
417
439
|
let isUpdate = false;
|
|
418
440
|
let recallCount = 0;
|
|
441
|
+
let lastRecalledAt;
|
|
419
442
|
let auditTrail = [];
|
|
420
443
|
const existingRaw = await redis.get(`cachly:lesson:best:${topic}`);
|
|
421
444
|
if (existingRaw) {
|
|
422
445
|
try {
|
|
423
446
|
const prev = JSON.parse(existingRaw);
|
|
424
447
|
recallCount = prev.recall_count ?? 0;
|
|
448
|
+
lastRecalledAt = prev.last_recalled_at; // learn never stamps it — only recall does
|
|
425
449
|
auditTrail = prev.audit_trail ?? [];
|
|
426
450
|
auditTrail.push({ ts, action: 'updated', prev_outcome: prev.outcome });
|
|
427
451
|
if (auditTrail.length > 20)
|
|
@@ -521,6 +545,7 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
521
545
|
...(group ? { group: String(group).toLowerCase().trim() } : {}),
|
|
522
546
|
visibility,
|
|
523
547
|
recall_count: recallCount,
|
|
548
|
+
...(lastRecalledAt ? { last_recalled_at: lastRecalledAt } : {}),
|
|
524
549
|
ts,
|
|
525
550
|
verified_at: outcome === 'success' || outcome === 'partial' ? ts : undefined,
|
|
526
551
|
confidence: 1.0,
|
|
@@ -788,11 +813,14 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
788
813
|
const ref = lesson.verified_at ?? lesson.ts;
|
|
789
814
|
const ageDays = (Date.now() - new Date(ref).getTime()) / 86400000;
|
|
790
815
|
const badge = confidenceBadge(confidence, ageDays);
|
|
791
|
-
// Recall resets verified_at (confidence clock restart)
|
|
816
|
+
// Recall resets verified_at (confidence clock restart) and stamps
|
|
817
|
+
// last_recalled_at so the recall-quality dashboard can measure recency.
|
|
818
|
+
const recalledAt = new Date().toISOString();
|
|
792
819
|
const updatedLesson = {
|
|
793
820
|
...lesson,
|
|
794
821
|
recall_count: (lesson.recall_count ?? 0) + 1,
|
|
795
|
-
verified_at:
|
|
822
|
+
verified_at: recalledAt,
|
|
823
|
+
last_recalled_at: recalledAt,
|
|
796
824
|
confidence: 1.0,
|
|
797
825
|
};
|
|
798
826
|
await redis.set(`cachly:lesson:best:${topic}`, JSON.stringify(updatedLesson));
|
|
@@ -837,7 +865,9 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
837
865
|
trustBadge,
|
|
838
866
|
`${badge} **Best solution for \`${topic}\`** ${sevEmoji}${lesson.severity ? ` (${lesson.severity})` : ''} · recalled ${updatedLesson.recall_count}×`,
|
|
839
867
|
``,
|
|
840
|
-
|
|
868
|
+
// what_worked may be empty for unresolved failure lessons (the API
|
|
869
|
+
// accepts empty what_worked when outcome=failure) — skip the line.
|
|
870
|
+
lesson.what_worked ? `**What worked:** ${lesson.what_worked}` : '',
|
|
841
871
|
lesson.what_failed ? `**What failed (avoid this):** ${lesson.what_failed}` : '',
|
|
842
872
|
lesson.context ? `**Context:** ${lesson.context}` : '',
|
|
843
873
|
(lesson.file_paths ?? []).length > 0 ? `**Files:** ${(lesson.file_paths ?? []).map((f) => `\`${f}\``).join(', ')}` : '',
|
|
@@ -906,6 +936,7 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
906
936
|
// Exclude archived lessons — they are kept for audit but must not surface in recall.
|
|
907
937
|
const lessonMatches = kwMatches.filter(m => m.key.startsWith('cachly:lesson:best:'));
|
|
908
938
|
let crossAuthorThisCall = 0;
|
|
939
|
+
let successRecallsThisCall = 0;
|
|
909
940
|
for (const m of lessonMatches.slice(0, 5)) {
|
|
910
941
|
const existing = await redis.get(m.key).catch(() => null);
|
|
911
942
|
if (existing) {
|
|
@@ -914,7 +945,8 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
914
945
|
if (lesson?.state === 'archived')
|
|
915
946
|
continue;
|
|
916
947
|
if (lesson) {
|
|
917
|
-
const
|
|
948
|
+
const recalledAt = new Date().toISOString();
|
|
949
|
+
const updated = { ...lesson, recall_count: (lesson.recall_count ?? 0) + 1, verified_at: recalledAt, last_recalled_at: recalledAt };
|
|
918
950
|
redis.set(m.key, JSON.stringify(updated)).catch(() => { });
|
|
919
951
|
const sev = lesson.severity;
|
|
920
952
|
const savedMins = sev === 'critical' ? 240 : sev === 'major' ? 60 : 30;
|
|
@@ -934,6 +966,8 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
934
966
|
ckgUpdateEdge(redis, reqPersonId, 'collaborates', authPersonId, true).catch(() => { });
|
|
935
967
|
ckgUpdateEdge(redis, authPersonId, 'collaborates', reqPersonId, true).catch(() => { });
|
|
936
968
|
}
|
|
969
|
+
if (lesson.outcome === 'success')
|
|
970
|
+
successRecallsThisCall++;
|
|
937
971
|
// Surface banner for proven successes (recall_count >= 1 means it's been validated)
|
|
938
972
|
if (lesson.outcome === 'success' && (lesson.recall_count ?? 0) >= 1) {
|
|
939
973
|
savedHere.push({
|
|
@@ -949,8 +983,11 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
949
983
|
}
|
|
950
984
|
}
|
|
951
985
|
// Metric 1 (time-to-first-recall): stamp the first time recall returned a
|
|
952
|
-
//
|
|
953
|
-
|
|
986
|
+
// successful lesson. SET NX so only the first successful recall wins.
|
|
987
|
+
// Deliberately NOT gated on savedHere: savedHere requires a pre-existing
|
|
988
|
+
// recall_count >= 1, which would delay the stamp until the SECOND recall
|
|
989
|
+
// and inflate every TTFR percentile derived from it.
|
|
990
|
+
if (successRecallsThisCall > 0) {
|
|
954
991
|
redis.set(`cachly:stats:first_recall_at:${instance_id}`, new Date().toISOString(), 'EX', 365 * 86400, 'NX').catch(() => { });
|
|
955
992
|
}
|
|
956
993
|
// ── Layer 2: Semantic search (parallel, optional) ────────────────────────
|