@cachly-dev/mcp-server 0.10.111 → 0.10.113

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.
@@ -0,0 +1,21 @@
1
+ /** Bumped whenever the hook script changes so installers can upgrade old hooks. */
2
+ export declare const BRAIN_WATCH_HOOK_VERSION = "v1";
3
+ export type BrainWatchHookResult = 'written' | 'upgraded' | 'appended' | 'unchanged' | 'skipped-no-git';
4
+ /**
5
+ * Idempotently install/upgrade the brain_watch post-commit hook in `projectDir`.
6
+ * Never throws — returns a status string the caller can report. Safe to run
7
+ * repeatedly; a second call with the same version returns 'unchanged'.
8
+ */
9
+ export declare function installBrainWatchHook(projectDir: string, instanceId: string, apiKey?: string): Promise<{
10
+ result: BrainWatchHookResult;
11
+ hookPath: string;
12
+ }>;
13
+ /**
14
+ * Build the `.git/hooks/post-commit` shell script for brain_watch.
15
+ * Uses curl (not Node) so it works without npx. Runs in the background and
16
+ * always exits 0 — never blocks a commit.
17
+ *
18
+ * @param instanceId Cachly Brain instance to learn into.
19
+ * @param apiKey Optional cky_… key; if absent, the script reads $CACHLY_JWT from env.
20
+ */
21
+ export declare function buildBrainWatchHook(instanceId: string, apiKey?: string): string;
@@ -0,0 +1,86 @@
1
+ // Ambient git post-commit hook builder for Brain auto-learning.
2
+ //
3
+ // Installs a .git/hooks/post-commit script that POSTs the commit data
4
+ // directly to the cachly API via curl — no Node required at hook runtime,
5
+ // so it works even without npx or the MCP server installed locally.
6
+ //
7
+ // Safety contract:
8
+ // • exit 0 is ALWAYS the last line — the hook never blocks a commit.
9
+ // • All network calls run in the background (&) with 2>/dev/null.
10
+ // • Commit message / sha / files are never interpolated into JS source;
11
+ // they're passed as shell variables in the JSON body (curl --data-raw).
12
+ import { existsSync } from 'node:fs';
13
+ import { readFile, writeFile, chmod, mkdir } from 'node:fs/promises';
14
+ import { resolve } from 'node:path';
15
+ /** Bumped whenever the hook script changes so installers can upgrade old hooks. */
16
+ export const BRAIN_WATCH_HOOK_VERSION = 'v1';
17
+ /**
18
+ * Matches any previously-installed cachly brain_watch block (from its marker
19
+ * comment down to the trailing `exit 0`), optionally including a shebang.
20
+ * Used to upgrade old hooks in place rather than duplicating them.
21
+ */
22
+ const BRAIN_WATCH_BLOCK_RE = /(?:#!\/bin\/sh\n)?# cachly brain_watch[\s\S]*?\nexit 0\n?/;
23
+ /**
24
+ * Idempotently install/upgrade the brain_watch post-commit hook in `projectDir`.
25
+ * Never throws — returns a status string the caller can report. Safe to run
26
+ * repeatedly; a second call with the same version returns 'unchanged'.
27
+ */
28
+ export async function installBrainWatchHook(projectDir, instanceId, apiKey) {
29
+ if (!existsSync(resolve(projectDir, '.git'))) {
30
+ return { result: 'skipped-no-git', hookPath: '' };
31
+ }
32
+ const hookDir = resolve(projectDir, '.git', 'hooks');
33
+ const hookPath = resolve(hookDir, 'post-commit');
34
+ await mkdir(hookDir, { recursive: true });
35
+ const script = buildBrainWatchHook(instanceId, apiKey);
36
+ let existing = '';
37
+ try {
38
+ existing = await readFile(hookPath, 'utf-8');
39
+ }
40
+ catch { /* no existing hook */ }
41
+ if (!existing) {
42
+ await writeFile(hookPath, script + '\n', 'utf-8');
43
+ await chmod(hookPath, 0o755).catch(() => { });
44
+ return { result: 'written', hookPath };
45
+ }
46
+ if (existing.includes(`cachly brain_watch — Auto-Learn ${BRAIN_WATCH_HOOK_VERSION}`)) {
47
+ return { result: 'unchanged', hookPath };
48
+ }
49
+ if (BRAIN_WATCH_BLOCK_RE.test(existing)) {
50
+ const replaced = existing.replace(BRAIN_WATCH_BLOCK_RE, script);
51
+ await writeFile(hookPath, replaced.endsWith('\n') ? replaced : replaced + '\n', 'utf-8');
52
+ await chmod(hookPath, 0o755).catch(() => { });
53
+ return { result: 'upgraded', hookPath };
54
+ }
55
+ // Foreign hook with no brain_watch block → append ours.
56
+ await writeFile(hookPath, existing.trimEnd() + '\n\n' + script + '\n', 'utf-8');
57
+ await chmod(hookPath, 0o755).catch(() => { });
58
+ return { result: 'appended', hookPath };
59
+ }
60
+ /**
61
+ * Build the `.git/hooks/post-commit` shell script for brain_watch.
62
+ * Uses curl (not Node) so it works without npx. Runs in the background and
63
+ * always exits 0 — never blocks a commit.
64
+ *
65
+ * @param instanceId Cachly Brain instance to learn into.
66
+ * @param apiKey Optional cky_… key; if absent, the script reads $CACHLY_JWT from env.
67
+ */
68
+ export function buildBrainWatchHook(instanceId, apiKey) {
69
+ return [
70
+ `#!/bin/sh`,
71
+ `# cachly brain_watch — Auto-Learn ${BRAIN_WATCH_HOOK_VERSION}`,
72
+ `# Silently teaches your Brain after every commit — no manual brain_from_git needed.`,
73
+ ...(apiKey ? [`CACHLY_JWT="${apiKey}"`] : []),
74
+ `_BW_SHA=$(git rev-parse HEAD 2>/dev/null || echo "")`,
75
+ `_BW_MSG=$(git log -1 --pretty=%B 2>/dev/null | head -1 | cut -c1-200)`,
76
+ `_BW_FILES=$(git diff-tree --no-commit-id -r --name-only HEAD 2>/dev/null | tr '\\n' '\\t' | sed 's/\\t$//')`,
77
+ `(curl -s -o /dev/null -m 8 \\`,
78
+ ` -X POST "https://api.cachly.dev/api/v1/instances/${instanceId}/learn" \\`,
79
+ ` -H "Authorization: Bearer \${CACHLY_JWT}" \\`,
80
+ ` -H "Content-Type: application/json" \\`,
81
+ ` --data-raw "{\\"message\\":\\"$(echo "$_BW_MSG" | sed 's/"/\\\\"/g')\\",\\"sha\\":\\"$_BW_SHA\\",\\"files\\":[$(echo "$_BW_FILES" | tr '\\t' '\\n' | grep -v '^$' | sed 's/.*/\\"&\\"/' | tr '\\n' ',' | sed 's/,$//')],\\"source\\":\\"brain_watch\\"}" \\`,
82
+ `) 2>/dev/null &`,
83
+ `exit 0`,
84
+ ].join('\n');
85
+ }
86
+ //# sourceMappingURL=brain-watch-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-watch-hook.js","sourceRoot":"","sources":["../../src/brain-watch-hook.ts"],"names":[],"mappings":"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"}
@@ -7,8 +7,21 @@ import { keywordSearch, tokenize, splitMultiQuery, levenshtein, indexVocab as _i
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.111';
10
+ const MCP_VERSION = '0.10.113';
11
11
  const WHATS_NEW = {
12
+ '0.10.113': [
13
+ `📈 **Week-over-week savings trend in \`cache_stats\`**`,
14
+ ` 📅 \`cache_stats\` now shows **7-day hits** + **WoW trend %** (e.g. \`+23.0% vs prev week\`) so ROI momentum is visible, not just the cumulative total.`,
15
+ ` ⬇️ Goes red (📉) on drops, green (📈) on growth — first week always shows +100% (new).`,
16
+ ` 🔧 Backed by new \`CountHitsInWindow\` repo method + \`hits_last_7d\`, \`hits_prev_7d\`, \`week_over_week_pct\` fields in \`SavingsEstimate\`.`,
17
+ ` 📊 137 MCP tools`,
18
+ ],
19
+ '0.10.112': [
20
+ `⚙️ **Configurable cost_per_call_usd — accurate ROI for any LLM**`,
21
+ ` 💡 New \`set_cost_per_call\` tool: set your real per-call cost (claude-opus-4.8 → $0.02, gpt-5.5 → $0.015, claude-sonnet-4.6 → $0.009, claude-haiku-4.5 → $0.001) so savings estimates in \`cache_stats\` reflect your actual bill.`,
22
+ ` 🔧 New API: \`PUT /api/v1/instances/:id/cost-per-call\` — persisted per instance, used by \`cache_stats\`, org savings, and SSE analytics stream.`,
23
+ ` 📊 137 MCP tools`,
24
+ ],
12
25
  '0.10.111': [
13
26
  `🏢 **Org-wide ROI + GitLab CI — the CFO number, aggregated**`,
14
27
  ` 💰 \`cache_org_stats\` now shows **org-wide Tokenmaxxing ROI**: total savings summed across every instance in your org, monthly projection, top-5 instances by savings. One number for the whole team.`,