@cachly-dev/mcp-server 0.5.35 โ 0.5.36
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 +8 -2
- package/dist/index.js +281 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +299 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ๐ง cachly AI Brain โ MCP Server
|
|
2
2
|
|
|
3
|
-
> **Persistent memory for Claude Code, Cursor, GitHub Copilot &
|
|
3
|
+
> **Persistent memory for Claude Code, Cursor, GitHub Copilot, Windsurf, Cline & Zed.**
|
|
4
4
|
> Your AI remembers every lesson, every fix, every architecture decision โ forever.
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
@@ -45,9 +45,15 @@ npx @cachly-dev/mcp-server@latest setup
|
|
|
45
45
|
The interactive wizard:
|
|
46
46
|
1. Signs you in (free, no credit card required)
|
|
47
47
|
2. Picks or creates your AI Brain instance
|
|
48
|
-
3. **Auto-detects** Cursor, Windsurf, VS Code, Claude Code, Continue.dev
|
|
48
|
+
3. **Auto-detects** Cursor, Windsurf, VS Code, Claude Code, Continue.dev, **Cline & Zed**
|
|
49
49
|
4. Writes the correct MCP config for every detected editor
|
|
50
50
|
5. Creates `CLAUDE.md` with memory rules pre-filled
|
|
51
|
+
6. Shows your Brain Health snapshot right in the terminal
|
|
52
|
+
|
|
53
|
+
**Index your project** into the Brain for instant semantic search:
|
|
54
|
+
```bash
|
|
55
|
+
npx @cachly-dev/mcp-server@latest index .
|
|
56
|
+
```
|
|
51
57
|
|
|
52
58
|
**Result:** Your AI remembers everything. Always.
|
|
53
59
|
|
package/dist/index.js
CHANGED
|
@@ -1534,6 +1534,100 @@ const TOOLS = [
|
|
|
1534
1534
|
required: ['instance_id'],
|
|
1535
1535
|
},
|
|
1536
1536
|
},
|
|
1537
|
+
// โโ Roadmap โ Persistent project plan tracker โโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1538
|
+
{
|
|
1539
|
+
name: 'roadmap_add',
|
|
1540
|
+
description: 'Add a new item to the persistent project roadmap stored in the Brain. ' +
|
|
1541
|
+
'Items survive across sessions and editors โ the roadmap is always up to date. ' +
|
|
1542
|
+
'Use for features, bugs, refactors, or any planned work. ' +
|
|
1543
|
+
'Call roadmap_list to see all open items, roadmap_next to get the next actionable item.',
|
|
1544
|
+
inputSchema: {
|
|
1545
|
+
type: 'object',
|
|
1546
|
+
properties: {
|
|
1547
|
+
instance_id: { type: 'string', description: 'UUID of the cache instance' },
|
|
1548
|
+
title: { type: 'string', description: 'Short title of the task/feature (3โ10 words)' },
|
|
1549
|
+
description: { type: 'string', description: 'What needs to be done, acceptance criteria, context' },
|
|
1550
|
+
priority: {
|
|
1551
|
+
type: 'string',
|
|
1552
|
+
enum: ['critical', 'high', 'medium', 'low'],
|
|
1553
|
+
description: 'Priority level (default: medium)',
|
|
1554
|
+
},
|
|
1555
|
+
tags: {
|
|
1556
|
+
type: 'array',
|
|
1557
|
+
items: { type: 'string' },
|
|
1558
|
+
description: 'Tags for filtering (e.g. ["api", "web", "sdk", "infra"])',
|
|
1559
|
+
},
|
|
1560
|
+
milestone: { type: 'string', description: 'Milestone/epic this belongs to (optional)' },
|
|
1561
|
+
},
|
|
1562
|
+
required: ['instance_id', 'title'],
|
|
1563
|
+
},
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
name: 'roadmap_update',
|
|
1567
|
+
description: 'Update the status, priority, or details of a roadmap item. ' +
|
|
1568
|
+
'Use to move items through the lifecycle: planned โ in-progress โ done (or blocked/cancelled). ' +
|
|
1569
|
+
'Also use to add notes/findings while working on an item.',
|
|
1570
|
+
inputSchema: {
|
|
1571
|
+
type: 'object',
|
|
1572
|
+
properties: {
|
|
1573
|
+
instance_id: { type: 'string', description: 'UUID of the cache instance' },
|
|
1574
|
+
id: { type: 'string', description: 'Item ID returned by roadmap_add or roadmap_list' },
|
|
1575
|
+
status: {
|
|
1576
|
+
type: 'string',
|
|
1577
|
+
enum: ['planned', 'in-progress', 'done', 'blocked', 'cancelled'],
|
|
1578
|
+
description: 'New status for the item',
|
|
1579
|
+
},
|
|
1580
|
+
priority: {
|
|
1581
|
+
type: 'string',
|
|
1582
|
+
enum: ['critical', 'high', 'medium', 'low'],
|
|
1583
|
+
description: 'Updated priority (optional)',
|
|
1584
|
+
},
|
|
1585
|
+
notes: { type: 'string', description: 'Progress notes, findings, or blockers (appended to existing notes)' },
|
|
1586
|
+
title: { type: 'string', description: 'Updated title (optional)' },
|
|
1587
|
+
description: { type: 'string', description: 'Updated description (optional)' },
|
|
1588
|
+
},
|
|
1589
|
+
required: ['instance_id', 'id'],
|
|
1590
|
+
},
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
name: 'roadmap_list',
|
|
1594
|
+
description: 'List all roadmap items, optionally filtered by status, priority, tag, or milestone. ' +
|
|
1595
|
+
'Returns items sorted by priority then creation date. ' +
|
|
1596
|
+
'Called automatically by session_start to show open work.',
|
|
1597
|
+
inputSchema: {
|
|
1598
|
+
type: 'object',
|
|
1599
|
+
properties: {
|
|
1600
|
+
instance_id: { type: 'string', description: 'UUID of the cache instance' },
|
|
1601
|
+
status: {
|
|
1602
|
+
type: 'string',
|
|
1603
|
+
enum: ['planned', 'in-progress', 'done', 'blocked', 'cancelled', 'open'],
|
|
1604
|
+
description: 'Filter by status. Use \'open\' to see planned+in-progress+blocked (default: open)',
|
|
1605
|
+
},
|
|
1606
|
+
tag: { type: 'string', description: 'Filter by tag (optional)' },
|
|
1607
|
+
milestone: { type: 'string', description: 'Filter by milestone (optional)' },
|
|
1608
|
+
priority: {
|
|
1609
|
+
type: 'string',
|
|
1610
|
+
enum: ['critical', 'high', 'medium', 'low'],
|
|
1611
|
+
description: 'Filter by minimum priority (optional)',
|
|
1612
|
+
},
|
|
1613
|
+
},
|
|
1614
|
+
required: ['instance_id'],
|
|
1615
|
+
},
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
name: 'roadmap_next',
|
|
1619
|
+
description: 'Get the single most important next actionable roadmap item. ' +
|
|
1620
|
+
'Returns the highest-priority in-progress item first, then planned items, sorted by priority. ' +
|
|
1621
|
+
'Call at session start to immediately know what to work on next.',
|
|
1622
|
+
inputSchema: {
|
|
1623
|
+
type: 'object',
|
|
1624
|
+
properties: {
|
|
1625
|
+
instance_id: { type: 'string', description: 'UUID of the cache instance' },
|
|
1626
|
+
tag: { type: 'string', description: 'Filter by tag (optional)' },
|
|
1627
|
+
},
|
|
1628
|
+
required: ['instance_id'],
|
|
1629
|
+
},
|
|
1630
|
+
},
|
|
1537
1631
|
{
|
|
1538
1632
|
name: 'brain_doctor',
|
|
1539
1633
|
description: 'Check the health of your AI Brain and get actionable recommendations. ' +
|
|
@@ -3201,6 +3295,38 @@ async function handleTool(name, args) {
|
|
|
3201
3295
|
}
|
|
3202
3296
|
}
|
|
3203
3297
|
catch { /* non-critical */ }
|
|
3298
|
+
// โโ ๐บ๏ธ Roadmap โ open items at session start โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3299
|
+
try {
|
|
3300
|
+
const roadmapAll = await redis.hgetall(`cachly:roadmap:${instance_id}`);
|
|
3301
|
+
if (roadmapAll && Object.keys(roadmapAll).length > 0) {
|
|
3302
|
+
const PRIORITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
3303
|
+
const PRIORITY_ICON = { critical: '๐ด', high: '๐ ', medium: '๐ก', low: '๐ต' };
|
|
3304
|
+
const openStatuses = new Set(['planned', 'in-progress', 'blocked']);
|
|
3305
|
+
const allItems = Object.values(roadmapAll).map(v => JSON.parse(v));
|
|
3306
|
+
const openItems = allItems
|
|
3307
|
+
.filter(i => openStatuses.has(i.status))
|
|
3308
|
+
.sort((a, b) => {
|
|
3309
|
+
if (a.status === 'in-progress' && b.status !== 'in-progress')
|
|
3310
|
+
return -1;
|
|
3311
|
+
if (b.status === 'in-progress' && a.status !== 'in-progress')
|
|
3312
|
+
return 1;
|
|
3313
|
+
return (PRIORITY_ORDER[a.priority] ?? 99) - (PRIORITY_ORDER[b.priority] ?? 99);
|
|
3314
|
+
});
|
|
3315
|
+
const doneCount = allItems.filter(i => i.status === 'done').length;
|
|
3316
|
+
if (openItems.length > 0) {
|
|
3317
|
+
lines.push(`๐บ๏ธ **Roadmap** (${openItems.length} open ยท ${doneCount} done):`);
|
|
3318
|
+
for (const it of openItems.slice(0, 5)) {
|
|
3319
|
+
const statusIcon = it.status === 'in-progress' ? 'โก' : it.status === 'blocked' ? '๐ซ' : '๐';
|
|
3320
|
+
lines.push(` ${statusIcon} ${PRIORITY_ICON[it.priority] ?? 'โช'} \`${it.id}\` **${it.title}**`);
|
|
3321
|
+
}
|
|
3322
|
+
if (openItems.length > 5)
|
|
3323
|
+
lines.push(` โฆ and ${openItems.length - 5} more`);
|
|
3324
|
+
lines.push(` _Use \`roadmap_next\` for the top priority item ยท \`roadmap_list\` for full view_`);
|
|
3325
|
+
lines.push('');
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
catch { /* non-critical */ }
|
|
3204
3330
|
return lines.join('\n');
|
|
3205
3331
|
}
|
|
3206
3332
|
// โโ session_end โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -4587,6 +4713,161 @@ async function handleBulkLockStream(name, args) {
|
|
|
4587
4713
|
return (`โ
**cache_stream_get** โ ${len} chunk(s) retrieved for \`${key}\`.\n\n` +
|
|
4588
4714
|
`**Preview** (first 500 chars):\n\`\`\`\n${preview}${preview.length < chunks.join('').length ? 'โฆ' : ''}\n\`\`\``);
|
|
4589
4715
|
}
|
|
4716
|
+
// โโ Roadmap โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
4717
|
+
case 'roadmap_add': {
|
|
4718
|
+
const { instance_id: rid, title, description: desc = '', priority = 'medium', tags: rtags = [], milestone = '', } = args;
|
|
4719
|
+
const redis = await getConnection(rid);
|
|
4720
|
+
const id = `rm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
4721
|
+
const item = {
|
|
4722
|
+
id, title, description: desc, priority, tags: rtags, milestone,
|
|
4723
|
+
status: 'planned',
|
|
4724
|
+
created: new Date().toISOString(),
|
|
4725
|
+
updated: new Date().toISOString(),
|
|
4726
|
+
notes: '',
|
|
4727
|
+
};
|
|
4728
|
+
await redis.hset(`cachly:roadmap:${rid}`, id, JSON.stringify(item));
|
|
4729
|
+
const PRIORITY_ICON = { critical: '๐ด', high: '๐ ', medium: '๐ก', low: '๐ต' };
|
|
4730
|
+
return [
|
|
4731
|
+
`๐ **Roadmap item added**`,
|
|
4732
|
+
``,
|
|
4733
|
+
` ID: \`${id}\``,
|
|
4734
|
+
` Title: ${title}`,
|
|
4735
|
+
` Priority: ${PRIORITY_ICON[priority] ?? 'โช'} ${priority}`,
|
|
4736
|
+
` Status: planned`,
|
|
4737
|
+
milestone ? ` Milestone: ${milestone}` : '',
|
|
4738
|
+
rtags.length ? ` Tags: ${rtags.join(', ')}` : '',
|
|
4739
|
+
``,
|
|
4740
|
+
`๐ก Use \`roadmap_update(id: "${id}", status: "in-progress")\` when you start working on it.`,
|
|
4741
|
+
].filter(Boolean).join('\n');
|
|
4742
|
+
}
|
|
4743
|
+
case 'roadmap_update': {
|
|
4744
|
+
const { instance_id: rid, id: itemId, status: newStatus, priority: newPriority, notes: newNotes, title: newTitle, description: newDesc, } = args;
|
|
4745
|
+
const redis = await getConnection(rid);
|
|
4746
|
+
const raw = await redis.hget(`cachly:roadmap:${rid}`, itemId);
|
|
4747
|
+
if (!raw)
|
|
4748
|
+
return `โ ๏ธ **roadmap_update** โ Item \`${itemId}\` not found. Use \`roadmap_list\` to see all items.`;
|
|
4749
|
+
const item = JSON.parse(raw);
|
|
4750
|
+
const oldStatus = item.status;
|
|
4751
|
+
if (newStatus)
|
|
4752
|
+
item.status = newStatus;
|
|
4753
|
+
if (newPriority)
|
|
4754
|
+
item.priority = newPriority;
|
|
4755
|
+
if (newTitle)
|
|
4756
|
+
item.title = newTitle;
|
|
4757
|
+
if (newDesc)
|
|
4758
|
+
item.description = newDesc;
|
|
4759
|
+
if (newNotes)
|
|
4760
|
+
item.notes = item.notes ? `${item.notes}\n[${new Date().toISOString().slice(0, 10)}] ${newNotes}` : `[${new Date().toISOString().slice(0, 10)}] ${newNotes}`;
|
|
4761
|
+
item.updated = new Date().toISOString();
|
|
4762
|
+
await redis.hset(`cachly:roadmap:${rid}`, itemId, JSON.stringify(item));
|
|
4763
|
+
const statusEmoji = { planned: '๐', 'in-progress': 'โก', done: 'โ
', blocked: '๐ซ', cancelled: '๐๏ธ' };
|
|
4764
|
+
return [
|
|
4765
|
+
`${statusEmoji[newStatus ?? oldStatus] ?? '๐'} **Roadmap updated** \`${itemId}\``,
|
|
4766
|
+
``,
|
|
4767
|
+
` Title: ${item.title}`,
|
|
4768
|
+
oldStatus !== newStatus ? ` Status: ${oldStatus} โ ${newStatus}` : ` Status: ${item.status}`,
|
|
4769
|
+
newNotes ? ` Notes: ${newNotes}` : '',
|
|
4770
|
+
].filter(Boolean).join('\n');
|
|
4771
|
+
}
|
|
4772
|
+
case 'roadmap_list': {
|
|
4773
|
+
const { instance_id: rid, status: filterStatus = 'open', tag: filterTag, milestone: filterMilestone, priority: filterPriority, } = args;
|
|
4774
|
+
const redis = await getConnection(rid);
|
|
4775
|
+
const all = await redis.hgetall(`cachly:roadmap:${rid}`);
|
|
4776
|
+
if (!all || Object.keys(all).length === 0) {
|
|
4777
|
+
return '๐ **Roadmap is empty.**\n\nUse `roadmap_add` to create your first item.';
|
|
4778
|
+
}
|
|
4779
|
+
const PRIORITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
4780
|
+
const PRIORITY_ICON = { critical: '๐ด', high: '๐ ', medium: '๐ก', low: '๐ต' };
|
|
4781
|
+
const STATUS_ICON = { planned: '๐', 'in-progress': 'โก', done: 'โ
', blocked: '๐ซ', cancelled: '๐๏ธ' };
|
|
4782
|
+
const openStatuses = new Set(['planned', 'in-progress', 'blocked']);
|
|
4783
|
+
let items = Object.values(all).map(v => JSON.parse(v));
|
|
4784
|
+
// Filter
|
|
4785
|
+
if (filterStatus === 'open')
|
|
4786
|
+
items = items.filter(i => openStatuses.has(i.status));
|
|
4787
|
+
else if (filterStatus)
|
|
4788
|
+
items = items.filter(i => i.status === filterStatus);
|
|
4789
|
+
if (filterTag)
|
|
4790
|
+
items = items.filter(i => i.tags.includes(filterTag));
|
|
4791
|
+
if (filterMilestone)
|
|
4792
|
+
items = items.filter(i => i.milestone === filterMilestone);
|
|
4793
|
+
if (filterPriority)
|
|
4794
|
+
items = items.filter(i => (PRIORITY_ORDER[i.priority] ?? 99) <= (PRIORITY_ORDER[filterPriority] ?? 99));
|
|
4795
|
+
// Sort: priority asc, then created asc
|
|
4796
|
+
items.sort((a, b) => {
|
|
4797
|
+
const pa = PRIORITY_ORDER[a.priority] ?? 99;
|
|
4798
|
+
const pb = PRIORITY_ORDER[b.priority] ?? 99;
|
|
4799
|
+
return pa !== pb ? pa - pb : a.created.localeCompare(b.created);
|
|
4800
|
+
});
|
|
4801
|
+
if (items.length === 0)
|
|
4802
|
+
return `๐ **No roadmap items** match the current filter (status: ${filterStatus}).`;
|
|
4803
|
+
const grouped = {};
|
|
4804
|
+
for (const it of items) {
|
|
4805
|
+
const st = it.status;
|
|
4806
|
+
if (!grouped[st])
|
|
4807
|
+
grouped[st] = [];
|
|
4808
|
+
grouped[st].push(it);
|
|
4809
|
+
}
|
|
4810
|
+
const lines = [`๐ **Roadmap** (${items.length} item${items.length !== 1 ? 's' : ''})`, ''];
|
|
4811
|
+
for (const [st, grp] of Object.entries(grouped)) {
|
|
4812
|
+
lines.push(`**${STATUS_ICON[st] ?? 'โข'} ${st.toUpperCase()}** (${grp.length})`);
|
|
4813
|
+
for (const it of grp) {
|
|
4814
|
+
const tags = it.tags.length ? ` [${it.tags.join(', ')}]` : '';
|
|
4815
|
+
const milestone = it.milestone ? ` ยท ${it.milestone}` : '';
|
|
4816
|
+
lines.push(` ${PRIORITY_ICON[it.priority] ?? 'โช'} \`${it.id}\` **${it.title}**${tags}${milestone}`);
|
|
4817
|
+
if (it.description)
|
|
4818
|
+
lines.push(` ${it.description.slice(0, 120)}`);
|
|
4819
|
+
if (it.notes)
|
|
4820
|
+
lines.push(` ๐ ${it.notes.split('\n').pop()?.slice(0, 100)}`);
|
|
4821
|
+
}
|
|
4822
|
+
lines.push('');
|
|
4823
|
+
}
|
|
4824
|
+
lines.push(`๐ก \`roadmap_update(id, status: "in-progress")\` to start ยท \`roadmap_next\` for top priority item`);
|
|
4825
|
+
return lines.join('\n');
|
|
4826
|
+
}
|
|
4827
|
+
case 'roadmap_next': {
|
|
4828
|
+
const { instance_id: rid, tag: filterTag } = args;
|
|
4829
|
+
const redis = await getConnection(rid);
|
|
4830
|
+
const all = await redis.hgetall(`cachly:roadmap:${rid}`);
|
|
4831
|
+
if (!all || Object.keys(all).length === 0) {
|
|
4832
|
+
return '๐ **Roadmap is empty.** Use `roadmap_add` to plan your first task.';
|
|
4833
|
+
}
|
|
4834
|
+
const PRIORITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
4835
|
+
const PRIORITY_ICON = { critical: '๐ด', high: '๐ ', medium: '๐ก', low: '๐ต' };
|
|
4836
|
+
let items = Object.values(all)
|
|
4837
|
+
.map(v => JSON.parse(v))
|
|
4838
|
+
.filter(i => i.status === 'in-progress' || i.status === 'planned')
|
|
4839
|
+
.filter(i => !filterTag || i.tags.includes(filterTag));
|
|
4840
|
+
if (items.length === 0)
|
|
4841
|
+
return '๐ **No open roadmap items!** All tasks are done (or use `roadmap_list` to check).';
|
|
4842
|
+
// in-progress first, then by priority
|
|
4843
|
+
items.sort((a, b) => {
|
|
4844
|
+
if (a.status === 'in-progress' && b.status !== 'in-progress')
|
|
4845
|
+
return -1;
|
|
4846
|
+
if (b.status === 'in-progress' && a.status !== 'in-progress')
|
|
4847
|
+
return 1;
|
|
4848
|
+
return (PRIORITY_ORDER[a.priority] ?? 99) - (PRIORITY_ORDER[b.priority] ?? 99);
|
|
4849
|
+
});
|
|
4850
|
+
const next = items[0];
|
|
4851
|
+
const remaining = items.length - 1;
|
|
4852
|
+
const tags = next.tags.length ? `\nTags: ${next.tags.join(', ')}` : '';
|
|
4853
|
+
const milestone = next.milestone ? `\nMilestone: ${next.milestone}` : '';
|
|
4854
|
+
const notes = next.notes ? `\nNotes: ${next.notes.split('\n').pop()?.slice(0, 120)}` : '';
|
|
4855
|
+
return [
|
|
4856
|
+
`${next.status === 'in-progress' ? 'โก' : '๐'} **Next up: ${next.title}**`,
|
|
4857
|
+
``,
|
|
4858
|
+
`ID: \`${next.id}\``,
|
|
4859
|
+
`Priority: ${PRIORITY_ICON[next.priority] ?? 'โช'} ${next.priority}`,
|
|
4860
|
+
`Status: ${next.status}`,
|
|
4861
|
+
next.description ? `\nWhat to do:\n${next.description}` : '',
|
|
4862
|
+
tags, milestone, notes,
|
|
4863
|
+
``,
|
|
4864
|
+
remaining > 0 ? `(+${remaining} more open item${remaining !== 1 ? 's' : ''} in roadmap)` : '(last open item)',
|
|
4865
|
+
``,
|
|
4866
|
+
next.status === 'planned'
|
|
4867
|
+
? `๐ก Start with: \`roadmap_update(id: "${next.id}", status: "in-progress")\``
|
|
4868
|
+
: `๐ก Finish with: \`roadmap_update(id: "${next.id}", status: "done", notes: "...")\``,
|
|
4869
|
+
].filter(s => s !== undefined).join('\n');
|
|
4870
|
+
}
|
|
4590
4871
|
default:
|
|
4591
4872
|
return null;
|
|
4592
4873
|
}
|