@cachly-dev/mcp-server 0.5.36 → 0.5.38
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/.github/workflows/publish.yml +28 -0
- package/Dockerfile +15 -12
- package/LICENSE +202 -0
- package/README.md +2 -8
- package/dist/index.d.ts +2 -7
- package/dist/index.js +319 -2096
- package/dist/index.js.map +1 -1
- package/glama.json +6 -0
- package/package.json +1 -1
- package/smithery.yaml +5 -10
- package/src/index.ts +328 -2202
- package/CHANGELOG.md +0 -65
- package/dist/__tests__/namespace.test.d.ts +0 -1
- package/dist/__tests__/namespace.test.js +0 -114
- package/dist/__tests__/namespace.test.js.map +0 -1
- package/run-mcp.sh +0 -57
- package/scripts/postinstall.js +0 -36
- package/src/__tests__/namespace.test.ts +0 -126
- package/test-features.cjs +0 -23
package/CHANGELOG.md
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Changelog – cachly SDK (mcp)
|
|
2
|
-
|
|
3
|
-
**Language:** MCP (Model Context Protocol)
|
|
4
|
-
**Package:** `@cachly-dev/mcp-server` on **npm**
|
|
5
|
-
|
|
6
|
-
> Full cross-SDK release notes: [../CHANGELOG.md](../CHANGELOG.md)
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## [0.2.0] – 2026-04-17
|
|
11
|
-
|
|
12
|
-
### Added
|
|
13
|
-
|
|
14
|
-
- **`setup` command** — interactive zero-arg CLI wizard (`npx @cachly-dev/mcp-server setup`):
|
|
15
|
-
- Reads `CACHLY_JWT` from env or prompts interactively via readline
|
|
16
|
-
- Fetches instances from API; presents list when multiple exist
|
|
17
|
-
- Auto-detects installed editors (Cursor, Windsurf, VS Code, Continue.dev) by checking for their config directories
|
|
18
|
-
- Writes the correct MCP config file for each detected editor in one step
|
|
19
|
-
- Always writes/updates `CLAUDE.md` (idempotent via `<!-- cachly-brain-start/end -->` markers)
|
|
20
|
-
- **`init` command** now idempotent — re-running `npx @cachly-dev/mcp-server init` updates the brain block in `CLAUDE.md` instead of appending a duplicate
|
|
21
|
-
- Shared helpers: `buildMcpConfig()`, `buildClaudeMdBlock()`, `writeClaudeMd()` — used by both `setup` and `init`
|
|
22
|
-
|
|
23
|
-
### Fixed
|
|
24
|
-
|
|
25
|
-
- `init` no longer duplicates the brain block in `CLAUDE.md` when run multiple times
|
|
26
|
-
- Correct package name `@cachly-dev/mcp-server` used consistently (was `@cachly-dev/mcp` in generated configs)
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## [0.1.1] – 2026-04-07
|
|
31
|
-
|
|
32
|
-
### Fixed
|
|
33
|
-
|
|
34
|
-
- Broken `index_project` tool schema – properties were accidentally placed outside the `TOOLS` array
|
|
35
|
-
- Unused `openai` variable removed from `cache_warmup` handler
|
|
36
|
-
- `readdir` type mismatch (`Dirent<string>` vs. `NonSharedBuffer`) fixed
|
|
37
|
-
- Version bumped to `0.3.0` in server metadata
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## [0.1.0] – 2026-04-07
|
|
42
|
-
|
|
43
|
-
Initial release.
|
|
44
|
-
|
|
45
|
-
### Added
|
|
46
|
-
|
|
47
|
-
- MCP tool: `cache_set` – store a value with optional TTL
|
|
48
|
-
- MCP tool: `cache_get` – retrieve a cached value
|
|
49
|
-
- MCP tool: `cache_delete` – remove a key
|
|
50
|
-
- MCP tool: `semantic_search` – vector-similarity lookup for LLM response caching
|
|
51
|
-
- MCP tool: `cache_clear` – flush namespace or entire cache
|
|
52
|
-
- Compatible with Claude Desktop, Cursor, Windsurf, and any MCP-capable host
|
|
53
|
-
- API-key-based authentication
|
|
54
|
-
- EU data residency (German servers, DSGVO compliant)
|
|
55
|
-
|
|
56
|
-
### Known limitations
|
|
57
|
-
|
|
58
|
-
- ~~Streaming tools not yet supported~~ (tracked for a future release)
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## [Unreleased]
|
|
63
|
-
|
|
64
|
-
See [../CHANGELOG.md](../CHANGELOG.md) for upcoming features.
|
|
65
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { detectNamespace } from '../namespace.js';
|
|
3
|
-
// ── Namespace Auto-Detection ───────────────────────────────────────────────
|
|
4
|
-
describe('detectNamespace', () => {
|
|
5
|
-
// ── Code ────────────────────────────────────────────────────────────────────
|
|
6
|
-
describe('code detection', () => {
|
|
7
|
-
it.each([
|
|
8
|
-
['python def', 'def process(data): return data * 2'],
|
|
9
|
-
['js const arrow', 'const handler = () => response.send(200)'],
|
|
10
|
-
['ts class', 'class UserService { constructor(private repo: Repo) {} }'],
|
|
11
|
-
['import statement', 'import { useState } from "react"'],
|
|
12
|
-
['shebang line', '#!/usr/bin/env python3\nprint("hello")'],
|
|
13
|
-
['go func', 'func main() { fmt.Println("hello") }'],
|
|
14
|
-
['cpp include', '#include <iostream>\nint main() { return 0; }'],
|
|
15
|
-
['interface block', 'interface ICache { get(key: string): void; }'],
|
|
16
|
-
['struct block', 'struct Config { host string }'],
|
|
17
|
-
['async def', 'async def fetch_data(url: str) -> dict:'],
|
|
18
|
-
['lambda', 'fn = lambda x: x * 2'],
|
|
19
|
-
['package decl', 'package com.example.service;'],
|
|
20
|
-
])('detects code: %s', (_, prompt) => {
|
|
21
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:code');
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
// ── Translation ─────────────────────────────────────────────────────────────
|
|
25
|
-
describe('translation detection', () => {
|
|
26
|
-
it.each([
|
|
27
|
-
['translate', 'translate this paragraph to Spanish'],
|
|
28
|
-
['übersetze', 'übersetze diesen Text bitte'],
|
|
29
|
-
['auf deutsch', 'Schreib das auf deutsch'],
|
|
30
|
-
['in english', 'Please write this in english'],
|
|
31
|
-
['ins deutsche', 'Bitte übersetze ins Deutsche'],
|
|
32
|
-
['traduce', 'traduce este texto al alemán'],
|
|
33
|
-
['traduis', 'traduis ce texte en anglais'],
|
|
34
|
-
['vertaal', 'vertaal dit naar het Nederlands'],
|
|
35
|
-
])('detects translation: %s', (_, prompt) => {
|
|
36
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:translation');
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
// ── Summary ──────────────────────────────────────────────────────────────────
|
|
40
|
-
describe('summary detection', () => {
|
|
41
|
-
it.each([
|
|
42
|
-
['summarize', 'summarize this article for me'],
|
|
43
|
-
['summarise', 'summarise the key points'],
|
|
44
|
-
['tl;dr', 'tl;dr of the following blog post:'],
|
|
45
|
-
['tldr', 'tldr of this document'],
|
|
46
|
-
['key points', 'what are the key points?'],
|
|
47
|
-
['zusammenfass', 'fasse zusammen worum es geht'],
|
|
48
|
-
['give me a brief', 'give me a brief overview'],
|
|
49
|
-
['in a nutshell', 'explain machine learning in a nutshell'],
|
|
50
|
-
['stichpunkte', 'Gib mir die wichtigsten Stichpunkte'],
|
|
51
|
-
])('detects summary: %s', (_, prompt) => {
|
|
52
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:summary');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
// ── Q&A ──────────────────────────────────────────────────────────────────────
|
|
56
|
-
describe('qa detection', () => {
|
|
57
|
-
it.each([
|
|
58
|
-
['what is', 'what is the capital of France?'],
|
|
59
|
-
['how does', 'how does photosynthesis work?'],
|
|
60
|
-
['why is', 'why is the sky blue?'],
|
|
61
|
-
['who invented', 'who invented the internet?'],
|
|
62
|
-
['where is', 'where is Mount Everest located?'],
|
|
63
|
-
['when was', 'when was the Eiffel Tower built?'],
|
|
64
|
-
['can you', 'can you explain recursion?'],
|
|
65
|
-
['is redis', 'is Redis single-threaded?'],
|
|
66
|
-
['does postgres', 'does postgres support JSON natively?'],
|
|
67
|
-
['wer ist', 'wer ist der aktuelle Bundeskanzler?'],
|
|
68
|
-
['wie funktioniert', 'wie funktioniert ein JWT Token?'],
|
|
69
|
-
['trailing ?', 'Redis vs Memcached?'],
|
|
70
|
-
])('detects q&a: %s', (_, prompt) => {
|
|
71
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:qa');
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
// ── Creative ─────────────────────────────────────────────────────────────────
|
|
75
|
-
describe('creative fallback', () => {
|
|
76
|
-
it.each([
|
|
77
|
-
['poem request', 'Write a short poem about autumn leaves'],
|
|
78
|
-
['story request', 'Tell me a fantasy story about dragons'],
|
|
79
|
-
['product copy', 'Generate a product description for running shoes'],
|
|
80
|
-
['greeting', 'Good morning, how are you today'],
|
|
81
|
-
['general task', 'Help me brainstorm names for my startup'],
|
|
82
|
-
])('falls back to creative: %s', (_, prompt) => {
|
|
83
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:creative');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
// ── Edge cases ────────────────────────────────────────────────────────────────
|
|
87
|
-
describe('edge cases', () => {
|
|
88
|
-
it('is case-insensitive', () => {
|
|
89
|
-
expect(detectNamespace('CONST x = 1')).toBe('cachly:sem:code');
|
|
90
|
-
expect(detectNamespace('TRANSLATE THIS')).toBe('cachly:sem:translation');
|
|
91
|
-
expect(detectNamespace('SUMMARIZE PLEASE')).toBe('cachly:sem:summary');
|
|
92
|
-
expect(detectNamespace('WHAT IS GRAVITY?')).toBe('cachly:sem:qa');
|
|
93
|
-
});
|
|
94
|
-
it('trims surrounding whitespace', () => {
|
|
95
|
-
expect(detectNamespace(' what is Redis? ')).toBe('cachly:sem:qa');
|
|
96
|
-
expect(detectNamespace(' const x = 1 ')).toBe('cachly:sem:code');
|
|
97
|
-
});
|
|
98
|
-
it('code keyword takes priority over translation', () => {
|
|
99
|
-
// Both "translate" and "def" present – code wins (checked first)
|
|
100
|
-
expect(detectNamespace('translate this function: def foo(): pass')).toBe('cachly:sem:code');
|
|
101
|
-
});
|
|
102
|
-
it('code keyword takes priority over summary', () => {
|
|
103
|
-
expect(detectNamespace('summarize this class MyService { }'))
|
|
104
|
-
.toBe('cachly:sem:code');
|
|
105
|
-
});
|
|
106
|
-
it('handles empty string as creative', () => {
|
|
107
|
-
expect(detectNamespace('')).toBe('cachly:sem:creative');
|
|
108
|
-
});
|
|
109
|
-
it('a single ? at end is qa', () => {
|
|
110
|
-
expect(detectNamespace('Interesting concept?')).toBe('cachly:sem:qa');
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
//# sourceMappingURL=namespace.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"namespace.test.js","sourceRoot":"","sources":["../../src/__tests__/namespace.test.ts"],"names":[],"mappings":"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"}
|
package/run-mcp.sh
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# MCP server wrapper – tees all stdin/stdout/stderr to /tmp/cachly-mcp.log
|
|
3
|
-
import subprocess, sys, os, threading, datetime
|
|
4
|
-
|
|
5
|
-
LOG = "/tmp/cachly-mcp.log"
|
|
6
|
-
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
7
|
-
|
|
8
|
-
def ts():
|
|
9
|
-
return datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3]
|
|
10
|
-
|
|
11
|
-
log = open(LOG, 'a')
|
|
12
|
-
|
|
13
|
-
def w(tag, data):
|
|
14
|
-
try:
|
|
15
|
-
text = data.decode(errors='replace').rstrip()
|
|
16
|
-
log.write(f'[{ts()}] {tag}: {text}\n')
|
|
17
|
-
log.flush()
|
|
18
|
-
except Exception:
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
proc = subprocess.Popen(
|
|
22
|
-
['node', os.path.join(SCRIPT_DIR, 'dist/index.js')],
|
|
23
|
-
stdin=subprocess.PIPE,
|
|
24
|
-
stdout=subprocess.PIPE,
|
|
25
|
-
stderr=subprocess.PIPE,
|
|
26
|
-
env=os.environ,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
log.write(f'[{ts()}] START pid={proc.pid}\n'); log.flush()
|
|
30
|
-
|
|
31
|
-
def pipe_in():
|
|
32
|
-
while True:
|
|
33
|
-
chunk = sys.stdin.buffer.read(4096)
|
|
34
|
-
if not chunk:
|
|
35
|
-
break
|
|
36
|
-
w('IN ', chunk)
|
|
37
|
-
proc.stdin.write(chunk)
|
|
38
|
-
proc.stdin.flush()
|
|
39
|
-
proc.stdin.close()
|
|
40
|
-
|
|
41
|
-
def pipe_out(src, dst, tag):
|
|
42
|
-
while True:
|
|
43
|
-
chunk = src.read(4096)
|
|
44
|
-
if not chunk:
|
|
45
|
-
break
|
|
46
|
-
w(tag, chunk)
|
|
47
|
-
dst.buffer.write(chunk)
|
|
48
|
-
dst.buffer.flush()
|
|
49
|
-
|
|
50
|
-
t1 = threading.Thread(target=pipe_in, daemon=True)
|
|
51
|
-
t2 = threading.Thread(target=pipe_out, args=(proc.stdout, sys.stdout, 'OUT'), daemon=True)
|
|
52
|
-
t3 = threading.Thread(target=pipe_out, args=(proc.stderr, sys.stderr, 'ERR'), daemon=True)
|
|
53
|
-
for t in (t1, t2, t3): t.start()
|
|
54
|
-
|
|
55
|
-
rc = proc.wait()
|
|
56
|
-
log.write(f'[{ts()}] EXIT code={rc}\n'); log.flush()
|
|
57
|
-
sys.exit(rc)
|
package/scripts/postinstall.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Shown once after: npm install @cachly-dev/mcp-server
|
|
3
|
-
|
|
4
|
-
const c = {
|
|
5
|
-
reset: "\x1b[0m",
|
|
6
|
-
bold: "\x1b[1m",
|
|
7
|
-
violet: "\x1b[35m",
|
|
8
|
-
green: "\x1b[32m",
|
|
9
|
-
cyan: "\x1b[36m",
|
|
10
|
-
yellow: "\x1b[33m",
|
|
11
|
-
dim: "\x1b[2m",
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const line = `${c.dim}─────────────────────────────────────────────────────${c.reset}`;
|
|
15
|
-
|
|
16
|
-
console.log(`
|
|
17
|
-
${line}
|
|
18
|
-
${c.bold}${c.violet}🧠 Cachly Brain MCP Server${c.reset} ${c.dim}v${process.env.npm_package_version ?? ""}${c.reset}
|
|
19
|
-
|
|
20
|
-
${c.bold}Give your AI assistant persistent memory in 30 seconds:${c.reset}
|
|
21
|
-
|
|
22
|
-
${c.cyan}1.${c.reset} Get a free Brain instance at ${c.bold}cachly.dev/setup-ai${c.reset}
|
|
23
|
-
${c.cyan}2.${c.reset} Add to Claude Code / Cursor / Copilot / Windsurf:
|
|
24
|
-
${c.dim}npx @cachly-dev/mcp-server@latest init${c.reset}
|
|
25
|
-
|
|
26
|
-
${c.green}✅ Free tier · GDPR · German servers · No credit card${c.reset}
|
|
27
|
-
|
|
28
|
-
${c.dim}──────────────────────────────────────────────────────${c.reset}
|
|
29
|
-
${c.yellow}📦 Related packages:${c.reset}
|
|
30
|
-
${c.cyan}@cachly-dev/openclaw${c.reset} Cut LLM costs 60–90% in JS/TS apps
|
|
31
|
-
${c.dim}npm install @cachly-dev/openclaw${c.reset}
|
|
32
|
-
${c.cyan}cachly${c.reset} CLI to manage your Brain instances
|
|
33
|
-
${c.dim}npm install -g cachly${c.reset}
|
|
34
|
-
|
|
35
|
-
${line}`);
|
|
36
|
-
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { detectNamespace } from '../namespace.js';
|
|
3
|
-
|
|
4
|
-
// ── Namespace Auto-Detection ───────────────────────────────────────────────
|
|
5
|
-
|
|
6
|
-
describe('detectNamespace', () => {
|
|
7
|
-
// ── Code ────────────────────────────────────────────────────────────────────
|
|
8
|
-
describe('code detection', () => {
|
|
9
|
-
it.each([
|
|
10
|
-
['python def', 'def process(data): return data * 2'],
|
|
11
|
-
['js const arrow', 'const handler = () => response.send(200)'],
|
|
12
|
-
['ts class', 'class UserService { constructor(private repo: Repo) {} }'],
|
|
13
|
-
['import statement', 'import { useState } from "react"'],
|
|
14
|
-
['shebang line', '#!/usr/bin/env python3\nprint("hello")'],
|
|
15
|
-
['go func', 'func main() { fmt.Println("hello") }'],
|
|
16
|
-
['cpp include', '#include <iostream>\nint main() { return 0; }'],
|
|
17
|
-
['interface block', 'interface ICache { get(key: string): void; }'],
|
|
18
|
-
['struct block', 'struct Config { host string }'],
|
|
19
|
-
['async def', 'async def fetch_data(url: str) -> dict:'],
|
|
20
|
-
['lambda', 'fn = lambda x: x * 2'],
|
|
21
|
-
['package decl', 'package com.example.service;'],
|
|
22
|
-
])('detects code: %s', (_, prompt) => {
|
|
23
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:code');
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// ── Translation ─────────────────────────────────────────────────────────────
|
|
28
|
-
describe('translation detection', () => {
|
|
29
|
-
it.each([
|
|
30
|
-
['translate', 'translate this paragraph to Spanish'],
|
|
31
|
-
['übersetze', 'übersetze diesen Text bitte'],
|
|
32
|
-
['auf deutsch', 'Schreib das auf deutsch'],
|
|
33
|
-
['in english', 'Please write this in english'],
|
|
34
|
-
['ins deutsche', 'Bitte übersetze ins Deutsche'],
|
|
35
|
-
['traduce', 'traduce este texto al alemán'],
|
|
36
|
-
['traduis', 'traduis ce texte en anglais'],
|
|
37
|
-
['vertaal', 'vertaal dit naar het Nederlands'],
|
|
38
|
-
])('detects translation: %s', (_, prompt) => {
|
|
39
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:translation');
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// ── Summary ──────────────────────────────────────────────────────────────────
|
|
44
|
-
describe('summary detection', () => {
|
|
45
|
-
it.each([
|
|
46
|
-
['summarize', 'summarize this article for me'],
|
|
47
|
-
['summarise', 'summarise the key points'],
|
|
48
|
-
['tl;dr', 'tl;dr of the following blog post:'],
|
|
49
|
-
['tldr', 'tldr of this document'],
|
|
50
|
-
['key points', 'what are the key points?'],
|
|
51
|
-
['zusammenfass', 'fasse zusammen worum es geht'],
|
|
52
|
-
['give me a brief', 'give me a brief overview'],
|
|
53
|
-
['in a nutshell', 'explain machine learning in a nutshell'],
|
|
54
|
-
['stichpunkte', 'Gib mir die wichtigsten Stichpunkte'],
|
|
55
|
-
])('detects summary: %s', (_, prompt) => {
|
|
56
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:summary');
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// ── Q&A ──────────────────────────────────────────────────────────────────────
|
|
61
|
-
describe('qa detection', () => {
|
|
62
|
-
it.each([
|
|
63
|
-
['what is', 'what is the capital of France?'],
|
|
64
|
-
['how does', 'how does photosynthesis work?'],
|
|
65
|
-
['why is', 'why is the sky blue?'],
|
|
66
|
-
['who invented', 'who invented the internet?'],
|
|
67
|
-
['where is', 'where is Mount Everest located?'],
|
|
68
|
-
['when was', 'when was the Eiffel Tower built?'],
|
|
69
|
-
['can you', 'can you explain recursion?'],
|
|
70
|
-
['is redis', 'is Redis single-threaded?'],
|
|
71
|
-
['does postgres', 'does postgres support JSON natively?'],
|
|
72
|
-
['wer ist', 'wer ist der aktuelle Bundeskanzler?'],
|
|
73
|
-
['wie funktioniert', 'wie funktioniert ein JWT Token?'],
|
|
74
|
-
['trailing ?', 'Redis vs Memcached?'],
|
|
75
|
-
])('detects q&a: %s', (_, prompt) => {
|
|
76
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:qa');
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// ── Creative ─────────────────────────────────────────────────────────────────
|
|
81
|
-
describe('creative fallback', () => {
|
|
82
|
-
it.each([
|
|
83
|
-
['poem request', 'Write a short poem about autumn leaves'],
|
|
84
|
-
['story request', 'Tell me a fantasy story about dragons'],
|
|
85
|
-
['product copy', 'Generate a product description for running shoes'],
|
|
86
|
-
['greeting', 'Good morning, how are you today'],
|
|
87
|
-
['general task', 'Help me brainstorm names for my startup'],
|
|
88
|
-
])('falls back to creative: %s', (_, prompt) => {
|
|
89
|
-
expect(detectNamespace(prompt)).toBe('cachly:sem:creative');
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// ── Edge cases ────────────────────────────────────────────────────────────────
|
|
94
|
-
describe('edge cases', () => {
|
|
95
|
-
it('is case-insensitive', () => {
|
|
96
|
-
expect(detectNamespace('CONST x = 1')).toBe('cachly:sem:code');
|
|
97
|
-
expect(detectNamespace('TRANSLATE THIS')).toBe('cachly:sem:translation');
|
|
98
|
-
expect(detectNamespace('SUMMARIZE PLEASE')).toBe('cachly:sem:summary');
|
|
99
|
-
expect(detectNamespace('WHAT IS GRAVITY?')).toBe('cachly:sem:qa');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('trims surrounding whitespace', () => {
|
|
103
|
-
expect(detectNamespace(' what is Redis? ')).toBe('cachly:sem:qa');
|
|
104
|
-
expect(detectNamespace(' const x = 1 ')).toBe('cachly:sem:code');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('code keyword takes priority over translation', () => {
|
|
108
|
-
// Both "translate" and "def" present – code wins (checked first)
|
|
109
|
-
expect(detectNamespace('translate this function: def foo(): pass')).toBe('cachly:sem:code');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('code keyword takes priority over summary', () => {
|
|
113
|
-
expect(detectNamespace('summarize this class MyService { }'))
|
|
114
|
-
.toBe('cachly:sem:code');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('handles empty string as creative', () => {
|
|
118
|
-
expect(detectNamespace('')).toBe('cachly:sem:creative');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('a single ? at end is qa', () => {
|
|
122
|
-
expect(detectNamespace('Interesting concept?')).toBe('cachly:sem:qa');
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
package/test-features.cjs
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
|
|
3
|
-
const src = fs.readFileSync('src/index.ts', 'utf8');
|
|
4
|
-
const initSrc = fs.readFileSync('../init/index.js', 'utf8');
|
|
5
|
-
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
6
|
-
|
|
7
|
-
let pass = 0, fail = 0;
|
|
8
|
-
function check(name, cond) {
|
|
9
|
-
if (cond) { console.log('PASS:', name); pass++; }
|
|
10
|
-
else { console.log('FAIL:', name); fail++; }
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
check('Test 1 - Ambient git learning in session_end', src.includes('git log --since') || src.includes('Ambient git'));
|
|
14
|
-
check('Test 2a - author param in learn_from_attempts schema', src.includes('"author"') && src.includes('learn_from_attempts'));
|
|
15
|
-
check('Test 2b - Team Telepathy in session_start', src.includes('Team Telepathy') || src.includes('team telepathy'));
|
|
16
|
-
check('Test 3 - memory_crystalize tool', src.includes('memory_crystalize'));
|
|
17
|
-
check('Test 4 - init team detection (~/.cachly-teams.json)', initSrc.includes('cachly-teams.json'));
|
|
18
|
-
check('Test 5 - brain_doctor workspace_path param', src.includes('workspace_path') && src.includes('brain_doctor'));
|
|
19
|
-
check('Bugfix - Pre-Warning uses splice(2,0)', src.includes('splice(2, 0'));
|
|
20
|
-
check('Bugfix - No broken unshift in pre-warning', !src.includes('lines.unshift('));
|
|
21
|
-
check('MCP version is 0.5.26', pkg.version === '0.5.26');
|
|
22
|
-
|
|
23
|
-
console.log(`\n${pass}/${pass+fail} tests passed`);
|