Skip to content

Commit 63c4096

Browse files
authored
feat(genkit-tools/cli): Support cursor config (#3397)
1 parent d512249 commit 63c4096

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { existsSync, readFileSync } from 'fs';
18+
import { mkdir, writeFile } from 'fs/promises';
19+
import * as path from 'path';
20+
import { AIToolConfigResult, AIToolModule, InitConfigOptions } from '../types';
21+
import {
22+
GENKIT_PROMPT_PATH,
23+
getGenkitContext,
24+
initOrReplaceFile,
25+
} from '../utils';
26+
27+
const CURSOR_MCP_PATH = path.join('.cursor', 'mcp.json');
28+
const CURSOR_RULES_DIR = '.cursor/rules';
29+
const GENKIT_MDC_PATH = path.join(CURSOR_RULES_DIR, 'GENKIT.mdc');
30+
31+
const CURSOR_RULES_HEADER = `---
32+
description: Genkit project development guidelines
33+
---
34+
`;
35+
36+
export const cursor: AIToolModule = {
37+
name: 'cursor',
38+
displayName: 'Cursor',
39+
40+
/**
41+
* Configures Cursor with Genkit context files.
42+
*
43+
* This function sets up the necessary files for Cursor to understand the
44+
* Genkit app and interact with Genkit MCP tools. It creates
45+
* a `.cursor` directory with the following:
46+
*
47+
* - `mcp.json`: Configures the Genkit MCP server for direct Genkit operations from Cursor.
48+
* - `rules/GENKIT.mdc`: The main entry point for project-specific context, importing the base GENKIT.md file.
49+
*
50+
* File ownership:
51+
* - .cursor/mcp.json: Merges with existing config (preserves user settings)
52+
* - .cursor/rules/GENKIT.mdc: Fully managed by us (replaced on each update)
53+
54+
*/
55+
async configure(options?: InitConfigOptions): Promise<AIToolConfigResult> {
56+
const files: AIToolConfigResult['files'] = [];
57+
58+
// Create the base GENKIT context file (GENKIT.md).
59+
// This file contains fundamental details about the GENKIT project.
60+
const genkitContext = getGenkitContext();
61+
const baseResult = await initOrReplaceFile(
62+
GENKIT_PROMPT_PATH,
63+
genkitContext
64+
);
65+
files.push({ path: GENKIT_PROMPT_PATH, updated: baseResult.updated });
66+
67+
// Handle MCP configuration - merge with existing if present.
68+
// This allows Cursor to communicate with Genkit tools.
69+
let mcpUpdated = false;
70+
let existingConfig: any = {};
71+
72+
try {
73+
const fileExists = existsSync(CURSOR_MCP_PATH);
74+
if (fileExists) {
75+
existingConfig = JSON.parse(readFileSync(CURSOR_MCP_PATH, 'utf-8'));
76+
} else {
77+
await mkdir('.cursor', { recursive: true });
78+
}
79+
} catch (e) {
80+
// File doesn't exist or is invalid JSON, start fresh
81+
}
82+
83+
if (!existingConfig.mcpServers?.genkit) {
84+
if (!existingConfig.mcpServers) {
85+
existingConfig.mcpServers = {};
86+
}
87+
existingConfig.mcpServers.genkit = {
88+
command: 'npx',
89+
args: ['genkit', 'mcp'],
90+
};
91+
await writeFile(CURSOR_MCP_PATH, JSON.stringify(existingConfig, null, 2));
92+
mcpUpdated = true;
93+
}
94+
files.push({ path: CURSOR_MCP_PATH, updated: mcpUpdated });
95+
96+
// Create the main `GENKIT.mdc` file, which acts as an entry point
97+
// for Cursor's AI and imports the other context files.
98+
await mkdir(path.join('.cursor', 'rules'), { recursive: true });
99+
const genkitImport = '@' + path.join('..', '..', GENKIT_PROMPT_PATH);
100+
const importContent = `# Genkit Context\n\n${genkitImport}\n`;
101+
102+
const mdcContent = CURSOR_RULES_HEADER + '\n' + importContent;
103+
const { updated } = await initOrReplaceFile(GENKIT_MDC_PATH, mdcContent);
104+
files.push({ path: GENKIT_MDC_PATH, updated: updated });
105+
106+
return { files };
107+
},
108+
};

genkit-tools/cli/src/commands/init-ai-tools/command.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { checkbox } from '@inquirer/prompts';
1919
import * as clc from 'colorette';
2020
import { Command } from 'commander';
2121
import { claude } from './ai-tools/claude';
22+
import { cursor } from './ai-tools/cursor';
2223
import { gemini } from './ai-tools/gemini';
2324
import { generic } from './ai-tools/generic';
2425
import { AIToolChoice, AIToolModule, InitConfigOptions } from './types';
@@ -28,6 +29,7 @@ import { AIToolChoice, AIToolModule, InitConfigOptions } from './types';
2829
export const AI_TOOLS: Record<string, AIToolModule> = {
2930
gemini,
3031
claude,
32+
cursor,
3133
generic,
3234
};
3335

0 commit comments

Comments
 (0)