@cachly-dev/mcp-server 0.9.0 β†’ 0.9.1

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/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.9.1] – 2026-05-05
11
+
12
+ ### πŸ’Ž Memory Crystals β€” Properly surfaced
13
+
14
+ Memory Crystals were implemented but invisible. Now they're first-class:
15
+
16
+ - **`crystal_view`** β€” New tool: inspect the current Crystal (patterns by category, freshness status, when to refresh). Previously there was no way to see Crystal contents without triggering a full `session_start`.
17
+ - **`session_end` Crystal reminder** β€” After saving a session, `session_end` now checks Crystal age. If no Crystal exists, or it's β‰₯ 30 days old, it reminds you to run `memory_crystalize`. Zero friction β€” the Brain tells you when it needs a refresh.
18
+ - **`autopilot` updated** β€” Generated CLAUDE.md now includes: `compact_recover` rule (context compaction), monthly crystal schedule, `syndicate_search` rule (unknown errors), and full Cognitive Tools table. Updated to v0.9.
19
+
20
+ ---
21
+
10
22
  ## [0.9.0] – 2026-05-05
11
23
 
12
24
  ### ⚑ compact_recover + 🌱 brain_from_git β€” The two most-requested missing tools
package/README.md CHANGED
@@ -229,7 +229,7 @@ Top lessons extracted:
229
229
 
230
230
  ---
231
231
 
232
- ## 67 MCP Tools
232
+ ## 68 MCP Tools
233
233
 
234
234
  ### 🧠 Cognitive Cache
235
235
 
@@ -275,6 +275,38 @@ Top lessons extracted:
275
275
  | **`sync_file_changes`** | Track file change history, surface related lessons per file |
276
276
  | **`setup_ai_memory`** | Interactive wizard to configure AI memory |
277
277
 
278
+ ### πŸ’Ž Memory Crystals
279
+
280
+ Memory Crystals distill **everything** your AI brain has learned into a permanent, compact snapshot β€” injected into every `session_start` automatically.
281
+
282
+ ```
283
+ memory_crystalize(instance_id="...", label="May 2026")
284
+
285
+ πŸ’Ž Memory Crystal created: May 2026
286
+
287
+ πŸ“Š Compressed: 47 sessions + 89 lessons β†’ 8 top patterns
288
+
289
+ Top patterns by category:
290
+ β€’ auth (12Γ—): Use httpOnly cookie for refresh token β€” Safari blocks third-party localStorage
291
+ β€’ k8s (9Γ—): PollUntilContextTimeout 3min before namespace recreate
292
+ β€’ deploy (8Γ—): readinessProbe.failureThreshold must be β‰₯ 10 on cold starts
293
+ β€’ redis (6Γ—): pipeline() for N writes, 10Γ— faster than sequential
294
+ …and 4 more
295
+
296
+ πŸ’‘ This crystal will appear in every future session_start briefing.
297
+ πŸ’‘ Re-run memory_crystalize monthly to keep it fresh.
298
+ ```
299
+
300
+ | Tool | What it does |
301
+ |------|-------------|
302
+ | **`memory_crystalize`** | Distill all sessions + lessons into a Crystal. Run monthly or after every major milestone. |
303
+ | **`crystal_view`** | Inspect the current Crystal: all patterns, freshness status, when to refresh. |
304
+
305
+ **`session_end` reminds you** automatically when the Crystal is missing or 30+ days old.
306
+ **`autopilot`** now generates CLAUDE.md with the monthly crystal schedule pre-wired.
307
+
308
+ ---
309
+
278
310
  ### πŸ—ΊοΈ Roadmap
279
311
 
280
312
  | Tool | What it does |
package/dist/index.js CHANGED
@@ -2976,6 +2976,21 @@ const TOOLS = [
2976
2976
  required: ['instance_id'],
2977
2977
  },
2978
2978
  },
2979
+ // ── crystal_view β€” inspect current Memory Crystal ──────────────────────
2980
+ {
2981
+ name: 'crystal_view',
2982
+ description: 'View the current Memory Crystal β€” the distilled snapshot of everything your AI brain has learned. ' +
2983
+ 'Shows all top patterns grouped by category, lesson count, crystal age, and when to refresh. ' +
2984
+ 'A crystal is created by memory_crystalize and injected into every session_start automatically. ' +
2985
+ 'Call this to review what institutional knowledge is preserved, or to decide if a refresh is due.',
2986
+ inputSchema: {
2987
+ type: 'object',
2988
+ properties: {
2989
+ instance_id: { type: 'string', description: 'UUID of the cache instance' },
2990
+ },
2991
+ required: ['instance_id'],
2992
+ },
2993
+ },
2979
2994
  // ── Roadmap β€” Persistent project plan tracker ───────────────────────────
2980
2995
  {
2981
2996
  name: 'roadmap_add',
@@ -5240,6 +5255,22 @@ async function handleTool(name, args) {
5240
5255
  }
5241
5256
  catch { /* git not available or not a repo β€” silent skip */ }
5242
5257
  }
5258
+ // ── Crystal age check β€” remind if > 30 days old or missing ──────────────
5259
+ let crystalReminder = '';
5260
+ try {
5261
+ const crystalRaw = await redis.get('cachly:crystal:latest');
5262
+ if (!crystalRaw) {
5263
+ crystalReminder = `πŸ’Ž **Memory Crystal:** None exists yet. Run \`memory_crystalize(instance_id="${instance_id}")\` to distill your brain into a permanent snapshot β€” injected into every future session automatically.`;
5264
+ }
5265
+ else {
5266
+ const crystal = JSON.parse(crystalRaw);
5267
+ const crystalAgeDays = Math.round((Date.now() - new Date(crystal.ts).getTime()) / 86_400_000);
5268
+ if (crystalAgeDays >= 30) {
5269
+ crystalReminder = `πŸ’Ž **Memory Crystal is ${crystalAgeDays} days old** (${crystal.label ?? 'unnamed'}). Run \`memory_crystalize(instance_id="${instance_id}")\` to refresh it with your latest ${crystal.lesson_count ?? '?'} lessons.`;
5270
+ }
5271
+ }
5272
+ }
5273
+ catch { /* non-critical */ }
5243
5274
  const durationStr = durationMin !== undefined ? ` Β· ${durationMin} min` : '';
5244
5275
  return [
5245
5276
  `βœ… **Session saved**${durationStr}`,
@@ -5249,6 +5280,7 @@ async function handleTool(name, args) {
5249
5280
  lessons_learned !== undefined ? `🧠 **Lessons stored:** ${lessons_learned}` : '',
5250
5281
  autoLearned.length > 0 ? `πŸ€– **Auto-learned:** ${autoLearned.length} lessons extracted from summary (${autoLearned.slice(0, 3).map(t => `\`${t}\``).join(', ')}${autoLearned.length > 3 ? '…' : ''})` : '',
5251
5282
  ambientLearned.length > 0 ? `🌿 **Ambient git learning:** ${ambientLearned.length} commit${ambientLearned.length > 1 ? 's' : ''} auto-learned (${ambientLearned.slice(0, 3).map(t => `\`${t}\``).join(', ')}${ambientLearned.length > 3 ? '…' : ''})` : '',
5283
+ crystalReminder,
5252
5284
  ``,
5253
5285
  `πŸ’‘ Next session: \`session_start(focus="...")\` to see this summary.`,
5254
5286
  ].filter(l => l !== '').join('\n');
@@ -5637,6 +5669,52 @@ async function handleTool(name, args) {
5637
5669
  ];
5638
5670
  return lines.join('\n');
5639
5671
  }
5672
+ // ── crystal_view β€” show current Memory Crystal ───────────────────────────
5673
+ case 'crystal_view': {
5674
+ const { instance_id } = args;
5675
+ const redis = await getConnection(instance_id);
5676
+ const crystalRaw = await redis.get('cachly:crystal:latest');
5677
+ if (!crystalRaw) {
5678
+ return [
5679
+ `πŸ’Ž **No Memory Crystal yet**`,
5680
+ ``,
5681
+ `Your brain has no crystal β€” all knowledge lives in raw lessons only.`,
5682
+ ``,
5683
+ `Run \`memory_crystalize(instance_id="${instance_id}")\` to:`,
5684
+ ` β€’ Distill everything learned into a permanent, compact snapshot`,
5685
+ ` β€’ Inject it into every future \`session_start\` automatically`,
5686
+ ` β€’ Preserve team knowledge even after lesson TTLs expire`,
5687
+ ``,
5688
+ `πŸ’‘ Recommended: run monthly or after any major milestone.`,
5689
+ ].join('\n');
5690
+ }
5691
+ const crystal = JSON.parse(crystalRaw);
5692
+ const ageDays = Math.round((Date.now() - new Date(crystal.ts).getTime()) / 86_400_000);
5693
+ const ageStr = ageDays === 0 ? 'today' : ageDays === 1 ? '1 day ago' : `${ageDays} days ago`;
5694
+ const freshness = ageDays < 14 ? '🟒 Fresh' : ageDays < 30 ? '🟑 Aging' : 'πŸ”΄ Stale β€” refresh recommended';
5695
+ const lines = [
5696
+ `πŸ’Ž **Memory Crystal: ${crystal.label}**`,
5697
+ ``,
5698
+ `πŸ“… Created: **${ageStr}** Β· Freshness: ${freshness}`,
5699
+ `πŸ“Š Compressed: **${crystal.session_count} sessions** + **${crystal.lesson_count} lessons** β†’ ${crystal.top_patterns.length} patterns`,
5700
+ `πŸ—‚οΈ Categories: ${crystal.categories.slice(0, 10).join(', ')}${crystal.categories.length > 10 ? `… +${crystal.categories.length - 10}` : ''}`,
5701
+ ``,
5702
+ `**Top patterns by category:**`,
5703
+ ``,
5704
+ ];
5705
+ for (const p of crystal.top_patterns) {
5706
+ lines.push(`**${p.category}** (${p.count} lesson${p.count > 1 ? 's' : ''})`);
5707
+ lines.push(` β†’ ${p.insight.slice(0, 150)}${p.insight.length > 150 ? '…' : ''}`);
5708
+ lines.push('');
5709
+ }
5710
+ if (ageDays >= 30) {
5711
+ lines.push(`---`, `⚠️ Crystal is ${ageDays} days old. Run \`memory_crystalize(instance_id="${instance_id}")\` to refresh with your latest lessons.`);
5712
+ }
5713
+ else {
5714
+ lines.push(`---`, `πŸ’‘ This crystal is injected automatically into every \`session_start\`. Refresh monthly with \`memory_crystalize\`.`);
5715
+ }
5716
+ return lines.filter(l => l !== undefined).join('\n');
5717
+ }
5640
5718
  case 'brain_doctor': {
5641
5719
  const { instance_id, workspace_path: drWorkspacePath = '' } = args;
5642
5720
  const redis = await getConnection(instance_id);
@@ -7036,9 +7114,11 @@ async function handleBulkLockStream(name, args) {
7036
7114
  > Sessions are fully automatic. The Brain starts when you call any tool and saves a summary when you close.
7037
7115
 
7038
7116
  ### MANDATORY hooks (always fire, no exceptions):
7117
+ - **Context compacted** β†’ call \`compact_recover(instance_id="${instance_id}", focus="<current task>")\` FIRST
7039
7118
  - **Window opens** β†’ call \`session_start(instance_id="${instance_id}", focus="<today's goal>")\`
7040
7119
  - **Bug fixed / deployed / discovered** β†’ call \`learn_from_attempts(instance_id="${instance_id}", topic="fix:<area>", outcome="success", what_worked="...")\`
7041
7120
  - **Window closes** β†’ call \`session_end(instance_id="${instance_id}", summary="...", files_changed=[...])\`
7121
+ - **Monthly** β†’ call \`memory_crystalize(instance_id="${instance_id}")\` to distill knowledge into a permanent Crystal
7042
7122
  `;
7043
7123
  const full = `## Cachly AI Brain β€” Autopilot for ${project_name}
7044
7124
 
@@ -7098,18 +7178,40 @@ smart_recall(
7098
7178
 
7099
7179
  ---
7100
7180
 
7101
- ## ⚑ Cognitive Tools (v0.6)
7181
+ ## ⚑ Context Recovery (MANDATORY after compaction)
7182
+
7183
+ **If your context window was compacted**, call this FIRST β€” before reading any file:
7184
+ \`\`\`
7185
+ compact_recover(
7186
+ instance_id = "${instance_id}",
7187
+ focus = "<what you were working on>",
7188
+ )
7189
+ \`\`\`
7190
+ Returns: last checkpoint, pending tasks, top lessons. Instant β€” no full brain scan.
7191
+
7192
+ ---
7193
+
7194
+ ## πŸ” Scheduled Maintenance (run on schedule)
7195
+
7196
+ | When | Call |
7197
+ |---|---|
7198
+ | **Monthly** | \`memory_crystalize(instance_id="${instance_id}")\` β€” distill brain into a permanent snapshot |
7199
+ | Weekly review | \`brain_diff(instance_id="${instance_id}", since="7d")\` β€” see what your AI learned this week |
7200
+ | Before big refactor | \`knowledge_decay(instance_id="${instance_id}")\` β€” see which lessons to still trust |
7201
+ | After major milestone | \`memory_crystalize(instance_id="${instance_id}", label="v2 launch")\` β€” preserve milestone knowledge |
7202
+
7203
+ ## 🧠 Cognitive Tools (use anytime)
7102
7204
 
7103
7205
  | When | Call |
7104
7206
  |---|---|
7105
7207
  | Brain feeling cluttered | \`memory_consolidate(instance_id="${instance_id}")\` |
7106
- | Weekly review | \`brain_diff(instance_id="${instance_id}", since="7d")\` |
7107
7208
  | Weird bug, no idea why | \`causal_trace(instance_id="${instance_id}", problem="<symptom>")\` |
7108
- | Before big refactor | \`knowledge_decay(instance_id="${instance_id}")\` |
7209
+ | Unknown error | \`syndicate_search(q="<error message>")\` β€” global community solutions |
7210
+ | Unknown error that you solved | \`syndicate(instance_id="${instance_id}", topic="fix:<area>", what_worked="...")\` |
7109
7211
 
7110
7212
  ---
7111
7213
 
7112
- *Cachly v0.6 Β· Generated ${new Date().toISOString().slice(0, 10)}*
7214
+ *Cachly v0.9 Β· Generated ${new Date().toISOString().slice(0, 10)}*
7113
7215
  `;
7114
7216
  const content = style === 'minimal' ? minimal : full;
7115
7217
  const filename = editor === 'copilot'