@everstateai/mcp 1.3.14 → 1.3.15

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
@@ -551,6 +551,81 @@ async function installHooks(projectDir: string, force: boolean = false) {
551
551
  logSuccess("Everstate tools added to approved commands");
552
552
  }
553
553
 
554
+ // ============================================================================
555
+ // Stack detection
556
+ // ============================================================================
557
+
558
+ const STACK_DETECT: Array<{ id: string; name: string; checks: Array<{ file: string; contains?: string }> }> = [
559
+ {
560
+ id: 'react',
561
+ name: 'React',
562
+ checks: [
563
+ { file: 'package.json', contains: '"react"' },
564
+ { file: 'vite.config.ts' },
565
+ { file: 'vite.config.js' },
566
+ { file: 'next.config.js' },
567
+ { file: 'next.config.mjs' },
568
+ { file: 'next.config.ts' },
569
+ ],
570
+ },
571
+ {
572
+ id: 'node',
573
+ name: 'Node.js',
574
+ checks: [
575
+ { file: 'package.json', contains: '"express"' },
576
+ { file: 'package.json', contains: '"fastify"' },
577
+ { file: 'package.json', contains: '"@nestjs/core"' },
578
+ ],
579
+ },
580
+ {
581
+ id: 'python',
582
+ name: 'Python',
583
+ checks: [
584
+ { file: 'pyproject.toml' },
585
+ { file: 'requirements.txt' },
586
+ { file: 'setup.py' },
587
+ { file: 'Pipfile' },
588
+ { file: 'manage.py' },
589
+ ],
590
+ },
591
+ ];
592
+
593
+ function detectProjectStack(projectDir: string): { id: string; name: string } | null {
594
+ for (const stack of STACK_DETECT) {
595
+ for (const check of stack.checks) {
596
+ const filePath = path.join(projectDir, check.file);
597
+ if (!fs.existsSync(filePath)) continue;
598
+
599
+ if (check.contains) {
600
+ try {
601
+ const content = fs.readFileSync(filePath, 'utf8');
602
+ if (content.includes(check.contains)) {
603
+ return { id: stack.id, name: stack.name };
604
+ }
605
+ } catch {
606
+ continue;
607
+ }
608
+ } else {
609
+ return { id: stack.id, name: stack.name };
610
+ }
611
+ }
612
+ }
613
+ return null;
614
+ }
615
+
616
+ async function applyStackTemplate(apiKey: string, projectId: string, stackId: string): Promise<boolean> {
617
+ try {
618
+ const response = await httpPost(
619
+ "https://www.everstate.ai/api/projects/apply-stack-template",
620
+ { projectId, stack: stackId },
621
+ apiKey
622
+ );
623
+ return response && !response.error;
624
+ } catch {
625
+ return false;
626
+ }
627
+ }
628
+
554
629
  // ============================================================================
555
630
  // Project config
556
631
  // ============================================================================
@@ -559,7 +634,7 @@ async function createProjectConfig(
559
634
  projectDir: string,
560
635
  apiKey: string,
561
636
  existingProjectId: string | null
562
- ): Promise<{ projectId: string; projectName: string }> {
637
+ ): Promise<{ projectId: string; projectName: string; stack?: string }> {
563
638
  const configPath = path.join(projectDir, ".everstate.json");
564
639
 
565
640
  const existingConfig = readJsonFile(configPath);
@@ -586,16 +661,26 @@ async function createProjectConfig(
586
661
  }
587
662
  }
588
663
 
589
- const config = {
664
+ // Detect project stack
665
+ const detectedStack = detectProjectStack(projectDir);
666
+
667
+ const config: Record<string, unknown> = {
590
668
  projectId: projectId || `proj_${Date.now()}`,
591
669
  projectName,
592
670
  createdAt: new Date().toISOString(),
593
671
  version: "1.0.0",
594
672
  };
595
673
 
674
+ if (detectedStack) {
675
+ config.stack = detectedStack.id;
676
+ }
677
+
596
678
  writeJsonFile(configPath, config);
597
679
  logSuccess(`Project configured: ${config.projectName}`);
598
- return config;
680
+ if (detectedStack) {
681
+ logSuccess(`Detected stack: ${detectedStack.name}`);
682
+ }
683
+ return config as { projectId: string; projectName: string; stack?: string };
599
684
  }
600
685
 
601
686
  // ============================================================================
@@ -604,7 +689,7 @@ async function createProjectConfig(
604
689
 
605
690
  function updateGitignore(projectDir: string) {
606
691
  const gitignorePath = path.join(projectDir, ".gitignore");
607
- const entries = ["# Everstate", ".claude/settings.local.json", ".claude/hooks/", ".claude/rules/"];
692
+ const entries = ["# Everstate", ".claude/settings.local.json", ".claude/hooks/", ".claude/rules/", ".cursor/rules/everstate.mdc", ".windsurf/rules/everstate.md"];
608
693
 
609
694
  let content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf8") : "";
610
695
  const toAdd = entries.filter((e) => !content.includes(e.replace("# Everstate", "")));
@@ -665,6 +750,104 @@ function ensureClaudeMd(projectDir: string) {
665
750
  }
666
751
  }
667
752
 
753
+ // ============================================================================
754
+ // Cursor Rules (.cursor/rules/everstate.mdc)
755
+ // ============================================================================
756
+
757
+ const CURSOR_RULES_CONTENT = `---
758
+ description: Everstate project memory - persistent context across sessions
759
+ globs: "**/*"
760
+ alwaysApply: true
761
+ ---
762
+
763
+ # Everstate - Project Memory
764
+
765
+ This project uses [Everstate](https://www.everstate.ai) for persistent memory across sessions.
766
+
767
+ ## MCP Tools Available
768
+
769
+ You have Everstate MCP tools. Use them:
770
+
771
+ | Tool | When to Use |
772
+ |------|-------------|
773
+ | \`sync\` | **Call at the START of every conversation** to load project context |
774
+ | \`done\` | **Call BEFORE ending** to save session summary for next time |
775
+ | \`recall\` | Search past sessions: \`recall({ query: "what did we try for X?" })\` |
776
+ | \`log\` | Record progress: \`log({ type: "achievement", message: "..." })\` |
777
+ | \`everstate\` | All 100+ actions: \`everstate({ action: "help" })\` |
778
+
779
+ ## Session Workflow
780
+
781
+ 1. **Start**: Call \`sync()\` first thing to load context (gotchas, tasks, last session)
782
+ 2. **During**: Use \`log()\` for progress, \`recall()\` to search memory
783
+ 3. **End**: Call \`done({ summary: "...", nextSteps: [...] })\` to save context
784
+ `;
785
+
786
+ function ensureCursorRules(projectDir: string) {
787
+ const rulesDir = path.join(projectDir, ".cursor", "rules");
788
+ const rulesPath = path.join(rulesDir, "everstate.mdc");
789
+
790
+ if (!fs.existsSync(rulesDir)) {
791
+ fs.mkdirSync(rulesDir, { recursive: true });
792
+ }
793
+ fs.writeFileSync(rulesPath, CURSOR_RULES_CONTENT);
794
+ logSuccess("Created .cursor/rules/everstate.mdc");
795
+ }
796
+
797
+ // ============================================================================
798
+ // Windsurf Rules (.windsurf/rules/everstate.md)
799
+ // ============================================================================
800
+
801
+ const WINDSURF_RULES_CONTENT = `# Everstate - Project Memory
802
+
803
+ This project uses [Everstate](https://www.everstate.ai) for persistent memory across sessions.
804
+
805
+ ## MCP Tools Available
806
+
807
+ You have Everstate MCP tools. Use them:
808
+
809
+ | Tool | When to Use |
810
+ |------|-------------|
811
+ | \`sync\` | **Call at the START of every conversation** to load project context |
812
+ | \`done\` | **Call BEFORE ending** to save session summary for next time |
813
+ | \`recall\` | Search past sessions: \`recall({ query: "what did we try for X?" })\` |
814
+ | \`log\` | Record progress: \`log({ type: "achievement", message: "..." })\` |
815
+ | \`everstate\` | All 100+ actions: \`everstate({ action: "help" })\` |
816
+
817
+ ## Session Workflow
818
+
819
+ 1. **Start**: Call \`sync()\` first thing to load context (gotchas, tasks, last session)
820
+ 2. **During**: Use \`log()\` for progress, \`recall()\` to search memory
821
+ 3. **End**: Call \`done({ summary: "...", nextSteps: [...] })\` to save context
822
+ `;
823
+
824
+ function ensureWindsurfRules(projectDir: string) {
825
+ const rulesDir = path.join(projectDir, ".windsurf", "rules");
826
+ const rulesPath = path.join(rulesDir, "everstate.md");
827
+
828
+ if (!fs.existsSync(rulesDir)) {
829
+ fs.mkdirSync(rulesDir, { recursive: true });
830
+ }
831
+ fs.writeFileSync(rulesPath, WINDSURF_RULES_CONTENT);
832
+ logSuccess("Created .windsurf/rules/everstate.md");
833
+ }
834
+
835
+ // ============================================================================
836
+ // Install rules for detected non-Claude tools
837
+ // ============================================================================
838
+
839
+ function installToolRules(projectDir: string, configResults: ConfigureResult[]) {
840
+ for (const result of configResults) {
841
+ if (result.status !== "configured") continue;
842
+
843
+ if (result.id === "cursor") {
844
+ ensureCursorRules(projectDir);
845
+ } else if (result.id === "windsurf") {
846
+ ensureWindsurfRules(projectDir);
847
+ }
848
+ }
849
+ }
850
+
668
851
  // ============================================================================
669
852
  // Setup summary
670
853
  // ============================================================================
@@ -673,6 +856,7 @@ function printSetupSummary(
673
856
  configResults: ConfigureResult[],
674
857
  projectDir: string,
675
858
  apiKey: string,
859
+ detectedStack?: string,
676
860
  ) {
677
861
  console.log("");
678
862
  console.log(c("green", "╔═══════════════════════════════════════════════════════════╗"));
@@ -708,6 +892,19 @@ function printSetupSummary(
708
892
  console.log(c("green", " ✓ ") + "Project config".padEnd(22) + c("dim", ".everstate.json"));
709
893
  console.log(c("green", " ✓ ") + "Session hooks".padEnd(22) + c("dim", ".claude/hooks/"));
710
894
  console.log(c("green", " ✓ ") + "Permissions".padEnd(22) + c("dim", ".claude/settings.local.json"));
895
+ // Show rules files for detected tools
896
+ for (const result of configResults) {
897
+ if (result.status !== "configured") continue;
898
+ if (result.id === "cursor") {
899
+ console.log(c("green", " ✓ ") + "Cursor rules".padEnd(22) + c("dim", ".cursor/rules/everstate.mdc"));
900
+ } else if (result.id === "windsurf") {
901
+ console.log(c("green", " ✓ ") + "Windsurf rules".padEnd(22) + c("dim", ".windsurf/rules/everstate.md"));
902
+ }
903
+ }
904
+ if (detectedStack) {
905
+ const stackName = STACK_DETECT.find(s => s.id === detectedStack)?.name || detectedStack;
906
+ console.log(c("green", " ✓ ") + "Stack template".padEnd(22) + c("dim", `${stackName} (gotchas + tasks)`));
907
+ }
711
908
  console.log("");
712
909
 
713
910
  // Next steps
@@ -775,8 +972,9 @@ async function runUpgrade(args: SetupArgs) {
775
972
  }
776
973
  }
777
974
 
778
- logInfo("Updating hooks...");
975
+ logInfo("Updating hooks and rules...");
779
976
  await installHooks(projectDir, true);
977
+ installToolRules(projectDir, configResults);
780
978
 
781
979
  // Also run auto-update to install any new hook types (gotcha-check, implicit-progress)
782
980
  try {
@@ -937,6 +1135,30 @@ export async function runSetup(argv: string[]) {
937
1135
  logInfo(`Project directory: ${shortenPath(projectDir)}`);
938
1136
  const projectConfig = await createProjectConfig(projectDir, apiKey, args.projectId);
939
1137
 
1138
+ // ── Step 5b: Apply stack template if detected ──────────────────
1139
+ if (projectConfig.stack) {
1140
+ const stackName = STACK_DETECT.find(s => s.id === projectConfig.stack)?.name || projectConfig.stack;
1141
+ let shouldApply = true;
1142
+
1143
+ if (!args.nonInteractive) {
1144
+ const answer = await askQuestion(
1145
+ ` Apply ${c("bold", stackName)} starter template (gotchas + tasks)? [Y/n] `,
1146
+ "y"
1147
+ );
1148
+ shouldApply = answer.toLowerCase() !== "n";
1149
+ }
1150
+
1151
+ if (shouldApply) {
1152
+ logInfo(`Applying ${stackName} stack template...`);
1153
+ const applied = await applyStackTemplate(apiKey, projectConfig.projectId, projectConfig.stack);
1154
+ if (applied) {
1155
+ logSuccess(`${stackName} gotchas and task template seeded`);
1156
+ } else {
1157
+ logSkip("Stack template will be available via: everstate({ action: 'project.apply_stack_template', params: { stack: '" + projectConfig.stack + "' } })");
1158
+ }
1159
+ }
1160
+ }
1161
+
940
1162
  // ── Step 6: Install hooks ────────────────────────────────────────
941
1163
  if (!args.skipHooks) {
942
1164
  await installHooks(projectDir, args.force);
@@ -945,8 +1167,9 @@ export async function runSetup(argv: string[]) {
945
1167
  // ── Step 7: Update .gitignore ────────────────────────────────────
946
1168
  updateGitignore(projectDir);
947
1169
 
948
- // ── Step 8: Create .claude/rules/everstate.md ─────────────────────
1170
+ // ── Step 8: Create rules files for detected tools ──────────────────
949
1171
  ensureClaudeMd(projectDir);
1172
+ installToolRules(projectDir, configResults);
950
1173
 
951
1174
  // ── Step 9: Write version file ───────────────────────────────────
952
1175
  const versionFile = createVersionFile("setup");
@@ -974,7 +1197,7 @@ export async function runSetup(argv: string[]) {
974
1197
  }
975
1198
 
976
1199
  // ── Summary ──────────────────────────────────────────────────────
977
- printSetupSummary(configResults, projectDir, apiKey);
1200
+ printSetupSummary(configResults, projectDir, apiKey, projectConfig.stack);
978
1201
  } catch (error: any) {
979
1202
  logError(`Setup failed: ${error.message}`);
980
1203
  process.exit(1);