Skip to content

Commit 775fa44

Browse files
committed
Move into init command
1 parent 8633645 commit 775fa44

File tree

5 files changed

+56
-188
lines changed

5 files changed

+56
-188
lines changed

src/commands/ai/ai.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/commands/ai/index.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/commands/ai/ai-start.ts renamed to src/commands/init/ai-rules.ts

Lines changed: 46 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { OptionValues } from 'commander'
21
import { resolve } from 'node:path'
32
import { promises as fs } from 'node:fs'
43
import type { NetlifyAPI } from '@netlify/api'
@@ -7,11 +6,11 @@ import { chalk, log, logAndThrowError, type APIError } from '../../utils/command
76
import { normalizeRepoUrl } from '../../utils/normalize-repo-url.js'
87
import { runGit } from '../../utils/run-git.js'
98
import { startSpinner } from '../../lib/spinner.js'
10-
import type BaseCommand from '../base-command.js'
11-
import type { SiteInfo } from '../../utils/types.js'
129
import { getContextConsumers, type ConsumerConfig } from '../../recipes/ai-context/context.js'
1310
import execa from '../../utils/execa.js'
1411
import { version } from '../../utils/command-helpers.js'
12+
import type BaseCommand from '../base-command.js'
13+
import type { SiteInfo } from '../../utils/types.js'
1514
import inquirer from 'inquirer'
1615

1716
interface ProjectInfo {
@@ -20,10 +19,6 @@ interface ProjectInfo {
2019
prompt: string
2120
}
2221

23-
interface AIStartOptions extends OptionValues {
24-
debug?: boolean
25-
}
26-
2722
// Check if a command belongs to a known IDE (reusing ai-context logic)
2823
const getConsumerKeyFromCommand = (command: string, consumers: ConsumerConfig[]): string | null => {
2924
const match = consumers.find(
@@ -143,50 +138,6 @@ const generateMcpConfig = (ide: ConsumerConfig): string => {
143138
)
144139
}
145140

146-
// Trigger IDE-specific MCP configuration
147-
const triggerMcpConfiguration = async (ide: ConsumerConfig, projectPath: string): Promise<boolean> => {
148-
log(`\n${chalk.blue('🔧 MCP Configuration for')} ${chalk.cyan(ide.presentedName)}`)
149-
150-
const { shouldConfigure } = await inquirer.prompt<{ shouldConfigure: boolean }>([
151-
{
152-
type: 'confirm',
153-
name: 'shouldConfigure',
154-
message: `Would you like to automatically configure MCP server for ${ide.presentedName}?`,
155-
default: true,
156-
},
157-
])
158-
159-
if (!shouldConfigure) {
160-
log(chalk.gray('Skipped MCP configuration. You can set it up manually later.'))
161-
return false
162-
}
163-
164-
try {
165-
const config = generateMcpConfig(ide)
166-
167-
// IDE-specific configuration actions
168-
switch (ide.key) {
169-
case 'vscode':
170-
await configureMcpForVSCode(config, projectPath)
171-
break
172-
case 'cursor':
173-
await configureMcpForCursor(config, projectPath)
174-
break
175-
case 'windsurf':
176-
await configureMcpForWindsurf(config, projectPath)
177-
break
178-
default:
179-
showGenericMcpConfig(config, ide.presentedName)
180-
}
181-
182-
log(`${chalk.green('✅')} MCP configuration completed for ${chalk.cyan(ide.presentedName)}`)
183-
return true
184-
} catch (error) {
185-
log(`${chalk.red('❌')} Failed to configure MCP: ${error instanceof Error ? error.message : 'Unknown error'}`)
186-
return false
187-
}
188-
}
189-
190141
// VS Code specific MCP configuration
191142
const configureMcpForVSCode = async (config: string, projectPath: string): Promise<void> => {
192143
const configPath = resolve(projectPath, '.vscode', 'mcp.json')
@@ -276,79 +227,51 @@ const showGenericMcpConfig = (config: string, ideName: string): void => {
276227
log(`${chalk.gray('--- End Configuration ---')}\n`)
277228
}
278229

279-
// Try to automatically activate MCP in the detected IDE
280-
const tryActivateMcp = async (ide: ConsumerConfig, projectPath: string): Promise<boolean> => {
230+
// Trigger IDE-specific MCP configuration
231+
const triggerMcpConfiguration = async (ide: ConsumerConfig, projectPath: string): Promise<boolean> => {
232+
log(`\n${chalk.blue('🔧 MCP Configuration for')} ${chalk.cyan(ide.presentedName)}`)
233+
234+
const { shouldConfigure } = await inquirer.prompt<{ shouldConfigure: boolean }>([
235+
{
236+
type: 'confirm',
237+
name: 'shouldConfigure',
238+
message: `Would you like to automatically configure MCP server for ${ide.presentedName}?`,
239+
default: true,
240+
},
241+
])
242+
243+
if (!shouldConfigure) {
244+
log(chalk.gray('Skipped MCP configuration. You can set it up manually later.'))
245+
return false
246+
}
247+
281248
try {
249+
const config = generateMcpConfig(ide)
250+
251+
// IDE-specific configuration actions
282252
switch (ide.key) {
283253
case 'vscode':
284-
return await activateMcpInVSCode(projectPath)
254+
await configureMcpForVSCode(config, projectPath)
255+
break
285256
case 'cursor':
286-
return await activateMcpInCursor(projectPath)
257+
await configureMcpForCursor(config, projectPath)
258+
break
287259
case 'windsurf':
288-
return await activateMcpInWindsurf(projectPath)
260+
await configureMcpForWindsurf(config, projectPath)
261+
break
289262
default:
290-
return false
291-
}
292-
} catch (_) {
293-
// Silent fail - activation is best effort
294-
return false
295-
}
296-
}
297-
298-
// Activate MCP in VS Code
299-
const activateMcpInVSCode = async (projectPath: string): Promise<boolean> => {
300-
try {
301-
// Try to reload VS Code window via command palette
302-
// This uses VS Code's command line interface
303-
await execa('code', ['--command', 'workbench.action.reloadWindow'], {
304-
cwd: projectPath,
305-
timeout: 5000,
306-
})
307-
return true
308-
} catch {
309-
// Try alternative: send reload command via VS Code extension API
310-
try {
311-
await execa('code', ['--command', 'developer.reloadWindow'], {
312-
cwd: projectPath,
313-
timeout: 5000,
314-
})
315-
return true
316-
} catch {
317-
return false
263+
showGenericMcpConfig(config, ide.presentedName)
318264
}
319-
}
320-
}
321265

322-
// Activate MCP in Cursor
323-
const activateMcpInCursor = async (projectPath: string): Promise<boolean> => {
324-
try {
325-
// Cursor might support similar command line interface
326-
await execa('cursor', ['--command', 'workbench.action.reloadWindow'], {
327-
cwd: projectPath,
328-
timeout: 5000,
329-
})
330-
return true
331-
} catch {
332-
return false
333-
}
334-
}
335-
336-
// Activate MCP in Windsurf
337-
const activateMcpInWindsurf = async (projectPath: string): Promise<boolean> => {
338-
try {
339-
// Windsurf-specific activation (placeholder - would need actual API)
340-
// For now, try to signal the IDE to reload configuration
341-
await execa('windsurf', ['--reload-config'], {
342-
cwd: projectPath,
343-
timeout: 5000,
344-
})
266+
log(`${chalk.green('✅')} MCP configuration completed for ${chalk.cyan(ide.presentedName)}`)
345267
return true
346-
} catch {
268+
} catch (error) {
269+
log(`${chalk.red('❌')} Failed to configure MCP: ${error instanceof Error ? error.message : 'Unknown error'}`)
347270
return false
348271
}
349272
}
350273

351-
// Move helper functions to a separate utils file
274+
// Helper functions reused from ai-start.ts
352275
const decodeHash = (hash: string): string => {
353276
try {
354277
return atob(hash)
@@ -413,21 +336,16 @@ const cloneRepo = async (repoUrl: string, targetDir: string, debug: boolean): Pr
413336
}
414337
}
415338

416-
export const aiStartCommand = async (options: AIStartOptions, command: BaseCommand): Promise<void> => {
417-
const hash = command.args[0]
418-
419-
// Validate hash parameter
420-
if (!hash) {
421-
log(`${chalk.red('Error:')} Hash parameter is required`)
422-
log(`${chalk.gray('Usage:')} netlify ai:start <hash>`)
423-
return
424-
}
425-
339+
/**
340+
* Handles AI rules initialization workflow
341+
* This is the experimental --ai-rules functionality for the init command
342+
*/
343+
export const initWithAiRules = async (hash: string, command: BaseCommand): Promise<void> => {
426344
// Authenticate first before any API operations
427345
await command.authenticate()
428346
const { api } = command.netlify
429347

430-
log(`${chalk.blue('🤖 AI Start')} - Initializing AI project...`)
348+
log(`${chalk.blue('🤖 Initializing AI project')} with rules...`)
431349
log(`${chalk.gray('Hash:')} ${hash}`)
432350
log(`${chalk.gray('User:')} ${api.accessToken ? 'Authenticated ✅' : 'Not authenticated ❌'}`)
433351

@@ -453,7 +371,7 @@ export const aiStartCommand = async (options: AIStartOptions, command: BaseComma
453371

454372
const cloneSpinner = startSpinner({ text: `Cloning repository to ${chalk.cyan(targetDir)}` })
455373

456-
await cloneRepo(repoUrl, targetDir, Boolean(options.debug))
374+
await cloneRepo(repoUrl, targetDir, false)
457375
cloneSpinner.success({ text: `Cloned repository to ${chalk.cyan(targetDir)}` })
458376

459377
// Step 4: Save AI instructions to file
@@ -475,6 +393,7 @@ export const aiStartCommand = async (options: AIStartOptions, command: BaseComma
475393
// Update working directory to cloned repo
476394
process.chdir(targetDir)
477395
command.workingDir = targetDir
396+
478397
// Success message with next steps
479398
log()
480399
log(chalk.green('✔ Your AI project is ready to go!'))
@@ -486,25 +405,20 @@ export const aiStartCommand = async (options: AIStartOptions, command: BaseComma
486405
log()
487406
log(chalk.yellowBright(`📁 Step 1: Enter your project directory`))
488407
log(` ${chalk.cyanBright(`cd ${targetDir}`)}`)
408+
489409
if (detectedIDE) {
490410
if (mcpConfigured) {
491411
log(chalk.yellowBright(`🔧 Step 2: MCP Server Configured`))
492412
log(` ${chalk.green('✅')} ${chalk.cyan(detectedIDE.presentedName)} is ready with Netlify MCP server`)
493-
494-
// Try to automatically activate MCP in the IDE
495-
const activated = await tryActivateMcp(detectedIDE, targetDir)
496-
if (activated) {
497-
log(` ${chalk.green('🚀')} MCP server automatically activated`)
498-
} else {
499-
log(` ${chalk.gray('💡 MCP will activate when you reload/restart your IDE window')}`)
500-
}
413+
log(` ${chalk.gray('💡 MCP will activate when you reload/restart your IDE window')}`)
501414
} else {
502415
log(chalk.yellowBright(`🔧 Step 2: Manual MCP Configuration`))
503416
log(` ${chalk.cyan(detectedIDE.presentedName)} detected - MCP setup was skipped`)
504417
log(` ${chalk.gray('You can configure MCP manually later for enhanced AI capabilities')}`)
505418
}
506419
log()
507420
}
421+
508422
if (projectInfo.prompt) {
509423
const stepNumber = detectedIDE ? '3' : '2'
510424
log(chalk.yellowBright(`🤖 Step ${stepNumber}: Ask your AI assistant to process the instructions`))
@@ -515,4 +429,4 @@ export const aiStartCommand = async (options: AIStartOptions, command: BaseComma
515429
} catch (error) {
516430
return logAndThrowError(error)
517431
}
518-
}
432+
}

src/commands/init/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { OptionValues } from 'commander'
1+
import { OptionValues, Option } from 'commander'
22
import terminalLink from 'terminal-link'
33

44
import BaseCommand from '../base-command.js'
@@ -11,13 +11,22 @@ export const createInitCommand = (program: BaseCommand) =>
1111
)
1212
.option('-m, --manual', 'Manually configure a git remote for CI')
1313
.option('--git-remote-name <name>', 'Name of Git remote to use. e.g. "origin"')
14+
.addOption(new Option('--ai-rules <hash>', 'Initialize with AI project configuration (experimental)').hideHelp())
1415
.addHelpText('after', () => {
1516
const docsUrl = 'https://docs.netlify.com/cli/get-started/'
1617
return `
1718
For more information about getting started with Netlify CLI, see ${terminalLink(docsUrl, docsUrl, { fallback: false })}
1819
`
1920
})
2021
.action(async (options: OptionValues, command: BaseCommand) => {
22+
// Check for experimental AI rules flag
23+
if (options.aiRules) {
24+
const { initWithAiRules } = await import('./ai-rules.js')
25+
await initWithAiRules(options.aiRules, command)
26+
return
27+
}
28+
29+
// Standard init flow
2130
const { init } = await import('./init.js')
2231
await init(options, command)
2332
})

src/commands/main.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import getCLIPackageJson from '../utils/get-cli-package-json.js'
2222
import { didEnableCompileCache } from '../utils/nodejs-compile-cache.js'
2323
import { track, reportError } from '../utils/telemetry/index.js'
2424

25-
import { createAiCommand } from './ai/index.js'
2625
import { createApiCommand } from './api/index.js'
2726
import BaseCommand from './base-command.js'
2827
import { createBlobsCommand } from './blobs/blobs.js'
@@ -215,7 +214,6 @@ export const createMainCommand = (): BaseCommand => {
215214
const program = new BaseCommand('netlify')
216215

217216
// register all the commands
218-
createAiCommand(program)
219217
createApiCommand(program)
220218
createBlobsCommand(program)
221219
createBuildCommand(program)

0 commit comments

Comments
 (0)