@everstateai/mcp 1.3.11 → 1.3.13

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/src/setup.ts CHANGED
@@ -24,6 +24,8 @@ import {
24
24
  import {
25
25
  getSessionStartHook,
26
26
  getSessionEndHook,
27
+ getPreCompactHook,
28
+ getSubagentContextHook,
27
29
  getSyncTodosHook,
28
30
  } from "./setup/hooks/templates.js";
29
31
 
@@ -336,11 +338,13 @@ function cleanupOldHooks(projectDir: string) {
336
338
 
337
339
  const oldHookFiles = [
338
340
  "everstate-session-start.sh",
339
- "everstate-session-stop.sh",
341
+ "everstate-session-end.sh",
342
+ "everstate-session-stop.sh", // legacy name
340
343
  "everstate-sync-todos.js",
341
344
  "everstate-pre-compact.sh",
342
345
  "session-start.sh",
343
- "session-stop.sh",
346
+ "session-end.sh",
347
+ "session-stop.sh", // legacy name
344
348
  "sync-todos.js",
345
349
  "pre-compact.sh",
346
350
  ];
@@ -400,7 +404,9 @@ async function installHooks(projectDir: string, force: boolean = false) {
400
404
 
401
405
  // Use versioned templates for all hooks
402
406
  const sessionStartHook = getSessionStartHook(hookConfig);
403
- const sessionStopHook = getSessionEndHook(hookConfig);
407
+ const sessionEndHook = getSessionEndHook(hookConfig);
408
+ const preCompactHook = getPreCompactHook(hookConfig);
409
+ const subagentContextHook = getSubagentContextHook(hookConfig);
404
410
 
405
411
  // Sync-todos hook: try download from server, fallback to embedded template
406
412
  let syncTodosHook: string;
@@ -412,10 +418,26 @@ async function installHooks(projectDir: string, force: boolean = false) {
412
418
 
413
419
  // Write project-local hook files
414
420
  const startHookPath = path.join(projectHooksDir, "everstate-session-start.sh");
415
- const stopHookPath = path.join(projectHooksDir, "everstate-session-stop.sh");
421
+ const endHookPath = path.join(projectHooksDir, "everstate-session-end.sh");
422
+ const preCompactPath = path.join(projectHooksDir, "everstate-pre-compact.js");
423
+ const subagentContextPath = path.join(projectHooksDir, "everstate-subagent-context.js");
416
424
 
417
425
  fs.writeFileSync(startHookPath, sessionStartHook, { mode: 0o755 });
418
- fs.writeFileSync(stopHookPath, sessionStopHook, { mode: 0o755 });
426
+ fs.writeFileSync(endHookPath, sessionEndHook, { mode: 0o755 });
427
+ fs.writeFileSync(preCompactPath, preCompactHook, { mode: 0o755 });
428
+ fs.writeFileSync(subagentContextPath, subagentContextHook, { mode: 0o755 });
429
+
430
+ // Clean up old bash-based pre-compact hook (replaced by JS version)
431
+ const oldPreCompactShPath = path.join(projectHooksDir, "everstate-pre-compact.sh");
432
+ if (fs.existsSync(oldPreCompactShPath)) {
433
+ fs.unlinkSync(oldPreCompactShPath);
434
+ }
435
+
436
+ // Clean up old session-stop.sh if it exists (renamed to session-end.sh)
437
+ const oldStopPath = path.join(projectHooksDir, "everstate-session-stop.sh");
438
+ if (fs.existsSync(oldStopPath)) {
439
+ fs.unlinkSync(oldStopPath);
440
+ }
419
441
 
420
442
  // Write sync-todos to global location
421
443
  const globalSyncTodosPath = path.join(globalHooksDir, "sync-todos.js");
@@ -483,7 +505,24 @@ async function installHooks(projectDir: string, force: boolean = false) {
483
505
  }
484
506
 
485
507
  addHookIfNotExists("SessionStart", "*", startHookPath);
486
- addHookIfNotExists("Stop", "*", stopHookPath);
508
+ addHookIfNotExists("SessionEnd", "*", endHookPath);
509
+ addHookIfNotExists("PreCompact", "*", `node ${preCompactPath}`);
510
+ addHookIfNotExists("SubagentStart", "*", `node ${subagentContextPath}`);
511
+
512
+ // Migrate: remove stale Stop hooks from older installations
513
+ if (settings.hooks.Stop) {
514
+ const everstateStopEntries = settings.hooks.Stop.filter((entry: any) =>
515
+ entry.hooks?.some((h: any) => h.command?.includes("everstate"))
516
+ );
517
+ if (everstateStopEntries.length > 0) {
518
+ settings.hooks.Stop = settings.hooks.Stop.filter((entry: any) =>
519
+ !entry.hooks?.some((h: any) => h.command?.includes("everstate"))
520
+ );
521
+ if (settings.hooks.Stop.length === 0) {
522
+ settings.hooks.Stop = [];
523
+ }
524
+ }
525
+ }
487
526
 
488
527
  // Also register PostToolUse for TodoWrite at the project level as fallback
489
528
  // This ensures sync works even if global ~/.claude.json is missing the hook
@@ -498,8 +537,18 @@ async function installHooks(projectDir: string, force: boolean = false) {
498
537
  }
499
538
  }
500
539
 
540
+ // Add Everstate MCP tools to permissions allow list
541
+ if (!settings.permissions) settings.permissions = {};
542
+ if (!settings.permissions.allow) settings.permissions.allow = [];
543
+
544
+ const everstatePermission = "mcp__everstate__*";
545
+ if (!settings.permissions.allow.includes(everstatePermission)) {
546
+ settings.permissions.allow.push(everstatePermission);
547
+ }
548
+
501
549
  writeJsonFile(settingsPath, settings);
502
550
  logSuccess("Session hooks installed");
551
+ logSuccess("Everstate tools added to approved commands");
503
552
  }
504
553
 
505
554
  // ============================================================================
@@ -555,7 +604,7 @@ async function createProjectConfig(
555
604
 
556
605
  function updateGitignore(projectDir: string) {
557
606
  const gitignorePath = path.join(projectDir, ".gitignore");
558
- const entries = ["# Everstate", ".claude/settings.local.json", ".claude/hooks/"];
607
+ const entries = ["# Everstate", ".claude/settings.local.json", ".claude/hooks/", ".claude/rules/"];
559
608
 
560
609
  let content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf8") : "";
561
610
  const toAdd = entries.filter((e) => !content.includes(e.replace("# Everstate", "")));
@@ -566,6 +615,56 @@ function updateGitignore(projectDir: string) {
566
615
  }
567
616
  }
568
617
 
618
+ // ============================================================================
619
+ // Everstate Rules (.claude/rules/everstate.md)
620
+ // ============================================================================
621
+ // Claude Code auto-loads all .md files from .claude/rules/ — this keeps
622
+ // Everstate instructions separate from the user's CLAUDE.md.
623
+
624
+ const EVERSTATE_CLAUDE_MD_MARKER = "<!-- everstate -->";
625
+
626
+ const EVERSTATE_RULES_CONTENT = `# Everstate - Project Memory
627
+
628
+ This project uses [Everstate](https://www.everstate.ai) for persistent memory across sessions.
629
+
630
+ - Sessions start and end automatically via hooks — no manual calls needed
631
+ - Use \`recall("query")\` to search past session context
632
+ - Use \`log({ type: "achievement", message: "..." })\` to record progress
633
+ - Use \`everstate({ action: "help" })\` to discover all available actions
634
+ - Call \`done()\` before ending for a detailed summary (auto-done runs otherwise)
635
+ `;
636
+
637
+ function ensureClaudeMd(projectDir: string) {
638
+ // Write instructions to .claude/rules/everstate.md (auto-loaded by Claude Code)
639
+ const rulesDir = path.join(projectDir, ".claude", "rules");
640
+ const rulesPath = path.join(rulesDir, "everstate.md");
641
+
642
+ if (!fs.existsSync(rulesDir)) {
643
+ fs.mkdirSync(rulesDir, { recursive: true });
644
+ }
645
+ fs.writeFileSync(rulesPath, EVERSTATE_RULES_CONTENT);
646
+ logSuccess("Created .claude/rules/everstate.md");
647
+
648
+ // Clean up: remove old <!-- everstate --> section from CLAUDE.md if present
649
+ const claudeMdPath = path.join(projectDir, "CLAUDE.md");
650
+ if (fs.existsSync(claudeMdPath)) {
651
+ const content = fs.readFileSync(claudeMdPath, "utf8");
652
+ if (content.includes(EVERSTATE_CLAUDE_MD_MARKER)) {
653
+ // Remove the old Everstate section (marker through end of section)
654
+ const markerIdx = content.indexOf(EVERSTATE_CLAUDE_MD_MARKER);
655
+ const cleaned = content.substring(0, markerIdx).trimEnd();
656
+ if (cleaned.length > 0) {
657
+ fs.writeFileSync(claudeMdPath, cleaned + "\n");
658
+ logSuccess("Removed old Everstate section from CLAUDE.md (moved to .claude/rules/)");
659
+ } else {
660
+ // CLAUDE.md was entirely the Everstate section — remove the file
661
+ fs.unlinkSync(claudeMdPath);
662
+ logSuccess("Removed CLAUDE.md (was only Everstate content, now in .claude/rules/)");
663
+ }
664
+ }
665
+ }
666
+ }
667
+
569
668
  // ============================================================================
570
669
  // Setup summary
571
670
  // ============================================================================
@@ -679,6 +778,19 @@ async function runUpgrade(args: SetupArgs) {
679
778
  logInfo("Updating hooks...");
680
779
  await installHooks(projectDir, true);
681
780
 
781
+ // Also run auto-update to install any new hook types (gotcha-check, implicit-progress)
782
+ try {
783
+ const { checkAndApplyUpdates } = await import('./setup/auto-update.js');
784
+ const updateResult = await checkAndApplyUpdates();
785
+ if (updateResult.updates.length > 0) {
786
+ for (const update of updateResult.updates) {
787
+ logSuccess(update);
788
+ }
789
+ }
790
+ } catch {
791
+ // Auto-update is optional - don't fail upgrade if it errors
792
+ }
793
+
682
794
  logInfo("Updating version file...");
683
795
  const configData = readJsonFile(configPath);
684
796
  const projectId = configData?.projectId;
@@ -833,7 +945,10 @@ export async function runSetup(argv: string[]) {
833
945
  // ── Step 7: Update .gitignore ────────────────────────────────────
834
946
  updateGitignore(projectDir);
835
947
 
836
- // ── Step 8: Write version file ───────────────────────────────────
948
+ // ── Step 8: Create .claude/rules/everstate.md ─────────────────────
949
+ ensureClaudeMd(projectDir);
950
+
951
+ // ── Step 9: Write version file ───────────────────────────────────
837
952
  const versionFile = createVersionFile("setup");
838
953
  versionFile.projects[projectConfig.projectId] = {
839
954
  projectId: projectConfig.projectId,