1515 */
1616
1717import { logger } from '@genkit-ai/tools-common/utils' ;
18- import { mkdir } from 'fs/promises' ;
18+ import { select } from '@inquirer/prompts' ;
19+ import { existsSync , readFileSync } from 'fs' ;
20+ import { mkdir , writeFile } from 'fs/promises' ;
1921import path from 'path' ;
2022import { AIToolConfigResult , AIToolModule , InitConfigOptions } from '../types' ;
2123import {
2224 GENKIT_PROMPT_PATH ,
23- getGenkitContext ,
25+ initGenkitFile ,
2426 initOrReplaceFile ,
27+ updateContentInPlace ,
2528} from '../utils' ;
2629
27- // Define constants at the module level for clarity and reuse.
28- const GENKIT_EXT_DIR = path . join ( '.gemini' , 'extensions' , 'genkit' ) ;
30+ // GEMINI specific paths
31+ const GEMINI_DIR = '.gemini' ;
32+ const GEMINI_SETTINGS_PATH = path . join ( GEMINI_DIR , 'settings.json' ) ;
33+ const GEMINI_MD_PATH = path . join ( 'GEMINI.md' ) ;
34+
35+ // GENKIT specific constants
36+ const GENKIT_EXT_DIR = path . join ( GEMINI_DIR , 'extensions' , 'genkit' ) ;
2937const GENKIT_MD_REL_PATH = path . join ( '..' , '..' , '..' , GENKIT_PROMPT_PATH ) ;
3038const GENKIT_EXTENSION_CONFIG = {
3139 name : 'genkit' ,
@@ -46,6 +54,10 @@ const GENKIT_EXTENSION_CONFIG = {
4654 contextFileName : GENKIT_MD_REL_PATH ,
4755} ;
4856
57+ const EXT_INSTALLATION = 'extension' ;
58+ const MD_INSTALLATION = 'geminimd' ;
59+ type InstallationType = typeof EXT_INSTALLATION | typeof MD_INSTALLATION ;
60+
4961/** Configuration module for Gemini CLI */
5062export const gemini : AIToolModule = {
5163 name : 'gemini' ,
@@ -55,42 +67,115 @@ export const gemini: AIToolModule = {
5567 * Configures the Gemini CLI extension for Genkit.
5668 */
5769 async configure ( options ?: InitConfigOptions ) : Promise < AIToolConfigResult > {
58- // TODO(ssbushi): Support option to install as file import vs extension
59- const files : AIToolConfigResult [ 'files' ] = [ ] ;
70+ let installationMethod : InstallationType = EXT_INSTALLATION ;
71+ if ( ! options ?. yesMode ) {
72+ installationMethod = await select ( {
73+ message : 'Select your preferred installation method' ,
74+ choices : [
75+ {
76+ name : 'Gemini CLI Extension' ,
77+ value : 'extension' ,
78+ description :
79+ 'Use Gemini Extension to install Genkit context in a modular fashion' ,
80+ } ,
81+ {
82+ name : 'GEMINI.md' ,
83+ value : 'geminimd' ,
84+ description : 'Incorporate Genkit context within the GEMINI.md file' ,
85+ } ,
86+ ] ,
87+ } ) ;
88+ }
89+
90+ if ( installationMethod === EXT_INSTALLATION ) {
91+ logger . info ( 'Installing as part of GEMINI.md' ) ;
92+ return await installAsExtension ( ) ;
93+ } else {
94+ logger . info ( 'Installing as Gemini CLI extension' ) ;
95+ return await installInMdFile ( ) ;
96+ }
97+ } ,
98+ } ;
6099
61- // Part 1: Generate GENKIT.md file.
100+ async function installInMdFile ( ) : Promise < AIToolConfigResult > {
101+ const files : AIToolConfigResult [ 'files' ] = [ ] ;
102+ // Part 1: Generate GENKIT.md file.
62103
63- logger . info ( 'Copying the GENKIT.md file...' ) ;
64- const genkitContext = getGenkitContext ( ) ;
65- const baseResult = await initOrReplaceFile (
66- GENKIT_PROMPT_PATH ,
67- genkitContext
104+ logger . info ( 'Installing the Genkit MCP server for Gemini CLI' ) ;
105+ // Handle MCP configuration - merge with existing if present
106+ let existingConfig : any = { } ;
107+ let settingsUpdated = false ;
108+ try {
109+ const fileExists = existsSync ( GEMINI_SETTINGS_PATH ) ;
110+ if ( fileExists ) {
111+ existingConfig = JSON . parse ( readFileSync ( GEMINI_SETTINGS_PATH , 'utf-8' ) ) ;
112+ } else {
113+ await mkdir ( GEMINI_DIR , { recursive : true } ) ;
114+ }
115+ } catch ( e ) {
116+ // File doesn't exist or is invalid JSON, start fresh
117+ }
118+
119+ // Check if genkit server already exists
120+ if ( ! existingConfig . mcpServers ?. genkit ) {
121+ if ( ! existingConfig . mcpServers ) {
122+ existingConfig . mcpServers = { } ;
123+ }
124+ existingConfig . mcpServers . genkit =
125+ GENKIT_EXTENSION_CONFIG . mcpServers . genkit ;
126+ await writeFile (
127+ GEMINI_SETTINGS_PATH ,
128+ JSON . stringify ( existingConfig , null , 2 )
68129 ) ;
69- files . push ( { path : GENKIT_PROMPT_PATH , updated : baseResult . updated } ) ;
70-
71- // Part 2: Configure the main gemini-extension.json file, and gemini config directory if needed.
72- logger . info ( 'Configuring extentions files in user workspace...' ) ;
73- await mkdir ( GENKIT_EXT_DIR , { recursive : true } ) ;
74- const extensionPath = path . join ( GENKIT_EXT_DIR , 'gemini-extension.json' ) ;
75-
76- let extensionUpdated = false ;
77- try {
78- const { updated } = await initOrReplaceFile (
79- extensionPath ,
80- JSON . stringify ( GENKIT_EXTENSION_CONFIG , null , 2 )
130+ settingsUpdated = true ;
131+ }
132+ files . push ( { path : GEMINI_SETTINGS_PATH , updated : settingsUpdated } ) ;
133+
134+ // Copy GENKIT.md file
135+ logger . info ( 'Copying the GENKIT.md file...' ) ;
136+ const baseResult = await initGenkitFile ( ) ;
137+ files . push ( { path : GENKIT_PROMPT_PATH , updated : baseResult . updated } ) ;
138+
139+ logger . info ( 'Updating GEMINI.md to include Genkit context' ) ;
140+ const geminiImportTag = `\nGenkit Framework Instructions:\n - @./GENKIT.md\n` ;
141+ const { updated : mdUpdated } = await updateContentInPlace (
142+ GEMINI_MD_PATH ,
143+ geminiImportTag ,
144+ { hash : baseResult . hash }
145+ ) ;
146+ files . push ( { path : GEMINI_MD_PATH , updated : mdUpdated } ) ;
147+
148+ return { files } ;
149+ }
150+
151+ async function installAsExtension ( ) : Promise < AIToolConfigResult > {
152+ const files : AIToolConfigResult [ 'files' ] = [ ] ;
153+ // Part 1: Generate GENKIT.md file.
154+ const baseResult = await initGenkitFile ( ) ;
155+ files . push ( { path : GENKIT_PROMPT_PATH , updated : baseResult . updated } ) ;
156+
157+ // Part 2: Configure the main gemini-extension.json file, and gemini config directory if needed.
158+ logger . info ( 'Configuring extentions files in user workspace' ) ;
159+ await mkdir ( GENKIT_EXT_DIR , { recursive : true } ) ;
160+ const extensionPath = path . join ( GENKIT_EXT_DIR , 'gemini-extension.json' ) ;
161+
162+ let extensionUpdated = false ;
163+ try {
164+ const { updated } = await initOrReplaceFile (
165+ extensionPath ,
166+ JSON . stringify ( GENKIT_EXTENSION_CONFIG , null , 2 )
167+ ) ;
168+ extensionUpdated = updated ;
169+ if ( extensionUpdated ) {
170+ logger . info (
171+ `Genkit extension for Gemini CLI initialized at ${ extensionPath } `
81172 ) ;
82- extensionUpdated = updated ;
83- if ( extensionUpdated ) {
84- logger . info (
85- `Genkit extension for Gemini CLI initialized at ${ extensionPath } `
86- ) ;
87- }
88- } catch ( err ) {
89- logger . error ( err ) ;
90- process . exit ( 1 ) ;
91173 }
92- files . push ( { path : extensionPath , updated : extensionUpdated } ) ;
174+ } catch ( err ) {
175+ logger . error ( err ) ;
176+ process . exit ( 1 ) ;
177+ }
178+ files . push ( { path : extensionPath , updated : extensionUpdated } ) ;
93179
94- return { files } ;
95- } ,
96- } ;
180+ return { files } ;
181+ }
0 commit comments