@cachly-dev/mcp-server 0.5.1 → 0.5.3

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/dist/index.js CHANGED
@@ -3291,62 +3291,144 @@ async function handleTool(name, args) {
3291
3291
  // Generate copilot-instructions.md content
3292
3292
  const tier = inst.tier.toUpperCase();
3293
3293
  const smartRecallNote = hasVector
3294
- ? '3. Call `smart_recall` with keywords for semantic context search'
3295
- : '3. Call `recall_context` with relevant key patterns like "arch:*" or "file:*"';
3294
+ ? '- `smart_recall("natural language query")` — semantic search (finds by meaning)'
3295
+ : '- `recall_context("arch:*")` — exact/glob key lookup (upgrade to Speed/Business for smart_recall)';
3296
3296
  const layerNote = hasVector
3297
- ? `Layer 3 (Semantic): smart_recall uses pgvector HNSW on your ${tier} instance for meaning-based search`
3298
- : `Layer 3 (Autopilot): This file — upgrade to Speed/Business tier to unlock smart_recall`;
3299
- const copilotInstructions = `# cachly AI Memory — ${project_description}
3297
+ ? `Layer 3 (Semantic): smart_recall uses pgvector HNSW on your ${tier} instance`
3298
+ : `Layer 3 (Autopilot): this file — upgrade to Speed/Business tier to unlock smart_recall`;
3299
+ const copilotInstructions = `# cachly AI Brain — ${project_description}
3300
3300
 
3301
- ## 3-Layer AI Memory System
3301
+ > AI memory system powered by [cachly.dev](https://cachly.dev). Works with GitHub Copilot, Claude Code, Cursor, Windsurf, Continue.dev, and any MCP-compatible editor.
3302
3302
 
3303
- - **Layer 1 — Storage:** cachly instance \`${inst.name}\` (${tier}) — persistent Valkey brain, survives restarts
3304
- - **Layer 2 — Tools:** learn_from_attempts · recall_best_solution · smart_recall — the memory API
3305
- - **Layer 3 — Autopilot (this file):** instructs the AI to run memory tools automatically
3303
+ ---
3306
3304
 
3307
- ## BEFORE every task — check memory first
3305
+ ## 🔴 MANDATORY RULES — Always enforced, no exceptions
3308
3306
 
3309
- 1. Call \`recall_best_solution\` with the task topic to get known solutions
3310
- - \`recall_best_solution("deploy:web")\` → returns working deploy command
3311
- - \`recall_best_solution("debug:redis-tls")\` → returns root cause + fix
3312
- - If a solution exists, use it — do NOT re-discover from scratch
3307
+ ### Rule 1 — Session Start (FIRST thing in every window)
3308
+ Call \`session_start\` **before reading any source files or making any changes**:
3313
3309
 
3314
- 2. Call \`recall_context\` with the area you're working in
3315
- - \`recall_context("arch:*")\` → cached architecture decisions
3316
- - \`recall_context("file:src/auth*")\` → cached file summaries
3310
+ \`\`\`
3311
+ session_start(
3312
+ instance_id = "${instance_id}",
3313
+ focus = "<what you plan to work on today>",
3314
+ )
3315
+ \`\`\`
3316
+
3317
+ Returns: last session summary, handoff from previous window, recent lessons, open failures, brain health.
3318
+
3319
+ **If \`session_start\` is unavailable**, fall back:
3320
+ 1. \`smart_recall("recent lessons <topic>")\`
3321
+ 2. \`recall_best_solution("<topic>")\`
3322
+
3323
+ ### Rule 2 — Learn After Every Fix/Deploy/Discovery (AUTOMATIC — no prompting needed)
3324
+ After **every** bug fix, deployment, failed attempt, or discovery — call \`learn_from_attempts\` immediately:
3325
+
3326
+ \`\`\`
3327
+ learn_from_attempts(
3328
+ instance_id = "${instance_id}",
3329
+ topic = "category:keyword", # deploy:web | fix:auth | debug:db | infra:docker
3330
+ outcome = "success" | "failure" | "partial",
3331
+ what_worked = "exact approach or command that worked",
3332
+ what_failed = "what did NOT work (prevents future wasted time)",
3333
+ severity = "critical" | "major" | "minor",
3334
+ commands = ["exact command that worked"],
3335
+ file_paths = ["path/to/changed/file"],
3336
+ )
3337
+ \`\`\`
3338
+
3339
+ **These events MUST automatically trigger \`learn_from_attempts\`:**
3340
+ - ✅ Successfully deployed → \`topic="deploy:<component>", outcome="success"\`
3341
+ - ✅ Fixed a bug → \`topic="fix:<description>", outcome="success"\`
3342
+ - ✅ Discovered root cause → \`topic="debug:<area>", outcome="partial"\`
3343
+ - ✅ Tried something that failed → \`topic="<area>", outcome="failure", what_failed="..."\`
3344
+ - ✅ Found a config quirk → \`topic="infra:<topic>", outcome="success"\`
3345
+ - ✅ Ran a working command → store it in \`commands=[...]\`
3346
+
3347
+ ### Rule 3 — Session Handoff (when closing window or task list > 3 items)
3348
+ \`\`\`
3349
+ session_handoff(
3350
+ instance_id = "${instance_id}",
3351
+ completed_tasks = ["what was done"],
3352
+ remaining_tasks = ["what is still to do"],
3353
+ files_changed = [{"path": "src/...", "status": "modified", "description": "what changed"}],
3354
+ instructions = "IMPORTANT: what the next window MUST know to not break things",
3355
+ context_summary = "current state of the system",
3356
+ )
3357
+ \`\`\`
3358
+
3359
+ The next \`session_start\` automatically includes this handoff. The new window sees exactly what's done, what remains, and what NOT to break.
3317
3360
 
3318
- ${smartRecallNote}
3319
- - \`smart_recall("how does authentication work")\` → returns cached auth notes
3361
+ ### Rule 4 — Session End (LAST thing before closing)
3362
+ \`\`\`
3363
+ session_end(
3364
+ instance_id = "${instance_id}",
3365
+ summary = "What was accomplished (2-3 sentences with specifics)",
3366
+ files_changed = ["list", "of", "changed", "files"],
3367
+ lessons_learned = <number of learn_from_attempts calls made>,
3368
+ )
3369
+ \`\`\`
3320
3370
 
3321
- 4. If nothing is found, proceed normally — then save (see below)
3371
+ > \`session_end\` **automatically extracts and stores lessons** from the summary text — no manual calls needed.
3322
3372
 
3323
- ## AFTER every non-trivial task — save to memory
3373
+ ### Rule 5 — Code Index Invalidation (AUTOMATIC)
3374
+ - \`index_project\` tracks MD5 hashes per file — **skips unchanged files automatically**
3375
+ - After code changes: next \`index_project\` run re-indexes only modified files
3376
+ - After delete/rename: run \`index_project\` again to sync
3377
+ - No manual invalidation needed
3324
3378
 
3325
- 1. Call \`learn_from_attempts\` for deploy/debug/fix tasks:
3326
- - Always set outcome = "success" | "failure" | "partial"
3327
- - Always include what_failed — it prevents future wasted time
3328
- - Example: \`learn_from_attempts(topic="deploy:api", outcome="success", what_worked="nohup docker compose up -d --build", what_failed="direct docker compose hangs on SSH timeout")\`
3379
+ ---
3329
3380
 
3330
- 2. Call \`remember_context\` for code/architecture findings:
3331
- - \`remember_context("arch:auth", "Uses Keycloak with JWT...", category="architecture")\`
3332
- - \`remember_context("file:src/index.ts", "Entry point, registers routes...", category="file_summary")\`
3381
+ ## Task-type trigger table
3382
+
3383
+ | You're about to... | Call BEFORE | Call AFTER |
3384
+ |---|---|---|
3385
+ | Deploy anything | \`recall_best_solution("deploy:<component>")\` | \`learn_from_attempts(topic="deploy:...")\` |
3386
+ | Fix a bug | \`recall_best_solution("fix:<area>")\` | \`learn_from_attempts(topic="fix:...")\` |
3387
+ | Add a feature | \`session_start(focus="feat:<area>")\` | \`learn_from_attempts(topic="feat:...")\` |
3388
+ | Infra/server work | \`recall_best_solution("infra:<topic>")\` | \`learn_from_attempts(topic="infra:...")\` |
3389
+ | Debug an issue | \`smart_recall("<error message or symptom>")\` | \`learn_from_attempts(topic="debug:...")\` |
3390
+
3391
+ ---
3392
+
3393
+ ## Available Brain Tools
3394
+
3395
+ | Tool | When to use |
3396
+ |------|-------------|
3397
+ | \`session_start\` | **FIRST** — mandatory at start of every session |
3398
+ | \`session_end\` | **LAST** — mandatory at end, auto-learns from summary |
3399
+ | \`session_handoff\` | When closing window with remaining tasks |
3400
+ | \`learn_from_attempts\` | **AUTOMATIC** after every fix/deploy/discovery |
3401
+ | \`recall_best_solution\` | Before any non-trivial task |
3402
+ | \`remember_context\` | After analyzing code — save findings for future sessions |
3403
+ ${smartRecallNote ? `| \`smart_recall\` | Search brain by meaning/keywords |\n` : ''}\
3404
+ | \`recall_context\` | Get exact key (supports glob: \`arch:*\`, \`file:*\`) |
3405
+ | \`brain_search\` | BM25+ full-text search over all brain data |
3406
+ | \`auto_learn_session\` | Batch-learn from a list of observations (optional) |
3407
+ | \`index_project\` | Index source files (smart hash, skips unchanged files) |
3408
+ | \`list_remembered\` | See what's cached in the brain |
3409
+ | \`forget_context\` | Remove stale context |
3410
+
3411
+ ---
3333
3412
 
3334
3413
  ## Instance Details
3335
3414
 
3336
- - Instance ID: \`${instance_id}\`
3337
- - Instance name: ${inst.name}
3338
- - Tier: ${tier}
3339
- - ${layerNote}
3340
- - Embedding provider: ${providerArg}
3415
+ - **Instance ID:** \`${instance_id}\`
3416
+ - **Instance name:** ${inst.name}
3417
+ - **Tier:** ${tier}
3418
+ - **${layerNote}**
3419
+ - **Embedding provider:** ${providerArg}
3420
+
3421
+ ---
3341
3422
 
3342
- ## Quick reference
3423
+ ## How the 3-layer system works
3343
3424
 
3344
3425
  \`\`\`
3345
- recall_best_solution("topic") # before task
3346
- smart_recall("natural language") # find by meaning
3347
- learn_from_attempts(...) # after task
3348
- remember_context("key", "content") # save analysis
3426
+ Layer 1 — Storage: Your cachly Valkey instance (${inst.name}) — persists forever
3427
+ Layer 2 — Tools: learn_from_attempts · recall_best_solution · brain_search · session_start/end
3428
+ Layer 3 — Autopilot: This file — AI reads it and runs tools automatically every session
3349
3429
  \`\`\`
3430
+
3431
+ Result: Your AI **never solves the same problem twice** and always picks up exactly where it left off. 🚀
3350
3432
  `;
3351
3433
  const lines = [
3352
3434
  `🧠 **cachly AI Memory Setup Complete**`,
@@ -3524,8 +3606,74 @@ async function handleBulkLockStream(name, args) {
3524
3606
  // ── Server setup ──────────────────────────────────────────────────────────────
3525
3607
  const server = new Server({ name: 'cachly-mcp', version: '0.4.0' }, { capabilities: { tools: {} } });
3526
3608
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
3609
+ // ── Auto-session management ───────────────────────────────────────────────────
3610
+ // Transparently starts a Brain session on the first tool call of a process
3611
+ // and saves a session_end summary on SIGTERM/exit so users never have to call
3612
+ // session_start/session_end manually.
3613
+ let _autoSessionInstanceId = null;
3614
+ let _autoSessionStarted = false;
3615
+ let _autoSessionToolCount = 0;
3616
+ async function autoStartSession(instanceId) {
3617
+ if (_autoSessionStarted)
3618
+ return;
3619
+ _autoSessionStarted = true;
3620
+ _autoSessionInstanceId = instanceId;
3621
+ try {
3622
+ await handleTool('session_start', { instance_id: instanceId, focus: 'auto (MCP session)' });
3623
+ }
3624
+ catch { /* non-fatal — session tracking is a best-effort feature */ }
3625
+ // Auto-index the project if it hasn't been indexed in the last 24h.
3626
+ // This is the main lever for growing token savings: more indexed code →
3627
+ // more semantic cache hits → fewer LLM calls for repeated questions.
3628
+ if (process.env.CACHLY_AUTO_INDEX !== 'false') {
3629
+ try {
3630
+ const redis = await getConnection(instanceId);
3631
+ const lastIndexed = await redis.get(`cachly:index:last_indexed:${instanceId}`);
3632
+ const staleMs = 24 * 60 * 60 * 1000;
3633
+ const isStale = !lastIndexed || (Date.now() - parseInt(lastIndexed, 10)) > staleMs;
3634
+ if (isStale) {
3635
+ // Mark as indexing now to prevent concurrent re-runs.
3636
+ await redis.set(`cachly:index:last_indexed:${instanceId}`, String(Date.now()), 'EX', 90000);
3637
+ // Run in background — don't block the first tool call.
3638
+ handleTool('index_project', {
3639
+ instance_id: instanceId,
3640
+ dir: process.cwd(),
3641
+ max_files: 150,
3642
+ ttl: 86400 * 7, // cache for 7 days
3643
+ namespace: 'cachly:sem:code',
3644
+ }).catch(() => undefined);
3645
+ }
3646
+ }
3647
+ catch { /* never block the session on indexing errors */ }
3648
+ }
3649
+ }
3650
+ async function autoEndSession() {
3651
+ if (!_autoSessionStarted || !_autoSessionInstanceId)
3652
+ return;
3653
+ _autoSessionStarted = false;
3654
+ try {
3655
+ await handleTool('session_end', {
3656
+ instance_id: _autoSessionInstanceId,
3657
+ summary: `Auto-ended after ${_autoSessionToolCount} tool call(s). Session was started automatically.`,
3658
+ files_changed: [],
3659
+ lessons_learned: 0,
3660
+ });
3661
+ }
3662
+ catch { /* non-fatal */ }
3663
+ }
3527
3664
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
3528
3665
  const { name, arguments: args } = request.params;
3666
+ // Auto-start brain session on first tool call that has an instance_id.
3667
+ // Skip session management tools to avoid recursion.
3668
+ const sessionTools = new Set(['session_start', 'session_end', 'auto_learn_session']);
3669
+ if (!sessionTools.has(name) && !_autoSessionStarted) {
3670
+ const instanceId = args?.instance_id;
3671
+ if (instanceId) {
3672
+ await autoStartSession(instanceId).catch(() => undefined);
3673
+ }
3674
+ }
3675
+ if (!sessionTools.has(name))
3676
+ _autoSessionToolCount++;
3529
3677
  try {
3530
3678
  const text = await handleTool(name, (args ?? {}));
3531
3679
  return { content: [{ type: 'text', text }] };
@@ -3536,8 +3684,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3536
3684
  throw new McpError(ErrorCode.InternalError, err.message);
3537
3685
  }
3538
3686
  });
3539
- // Graceful shutdown – close all Redis connections
3687
+ // Graceful shutdown – close all Redis connections + auto-end brain session
3540
3688
  process.on('SIGTERM', async () => {
3689
+ await autoEndSession().catch(() => undefined);
3690
+ for (const [, client] of pool)
3691
+ await client.quit().catch(() => undefined);
3692
+ process.exit(0);
3693
+ });
3694
+ process.on('SIGINT', async () => {
3695
+ await autoEndSession().catch(() => undefined);
3541
3696
  for (const [, client] of pool)
3542
3697
  await client.quit().catch(() => undefined);
3543
3698
  process.exit(0);