Skip to content

Commit 8edaf5f

Browse files
committed
Improvements
1 parent eb21fb0 commit 8edaf5f

File tree

1 file changed

+124
-94
lines changed

1 file changed

+124
-94
lines changed

src/commands/ai/ai-start.ts

Lines changed: 124 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,94 @@ import { runGit } from '../../utils/run-git.js'
66
import { startSpinner } from '../../lib/spinner.js'
77
import BaseCommand from '../base-command.js'
88

9-
interface MockHashDecodeResult {
10-
siteId?: string
11-
repoUrl?: string
12-
targetDir?: string
13-
branch?: string
9+
// Decode hash to get the encoded URL
10+
const decodeHash = (hash: string): string => {
11+
// In real implementation, this would decode the hash to get the actual URL
12+
// For now, return a mock URL
13+
return 'https://api.netlify.com/api/v1/ai/projects/mock-endpoint'
1414
}
1515

16-
// Mock hash decoding - in real implementation this would decode the actual hash
17-
const mockDecodeHash = (hash: string): MockHashDecodeResult => {
18-
// Mock: If hash starts with 'site_', treat it as a site ID
19-
// Otherwise, treat it as a direct repo URL
20-
if (hash.startsWith('site_')) {
21-
return {
22-
siteId: hash.replace('site_', ''),
23-
targetDir: `ai-project-${hash.substring(0, 8)}`,
24-
branch: 'main'
25-
}
26-
} else {
16+
interface ProjectInfo {
17+
success: boolean
18+
projectId: string
19+
aiInstructions: string
20+
}
21+
22+
// Call the decoded URL to get project information
23+
const fetchProjectInfo = async (url: string, authToken: string): Promise<ProjectInfo> => {
24+
try {
25+
// Mock response for now - in real implementation, fetch from the decoded URL
26+
await new Promise(resolve => setTimeout(resolve, 1000))
27+
28+
// Mock response with project ID and AI instructions (no repository URL)
2729
return {
28-
repoUrl: 'https://github.com/netlify/netlify-cli.git', // Mock repo
29-
targetDir: `ai-project-${hash.substring(0, 8)}`,
30-
branch: 'main'
30+
success: true,
31+
projectId: '4d6c8c75-2278-409e-bcb7-06e07b79e1bc',
32+
aiInstructions: `# AI Project Instructions
33+
34+
This is your AI-powered project setup guide.
35+
36+
## Getting Started
37+
38+
1. Review the project structure
39+
2. Install dependencies: \`npm install\`
40+
3. Start development: \`netlify dev\`
41+
4. Deploy your project: \`netlify deploy\`
42+
43+
## AI Features
44+
45+
- Automated code analysis
46+
- Smart deployment optimizations
47+
- Performance monitoring
48+
- Error detection and suggestions
49+
50+
## Next Steps
51+
52+
- Configure your build settings
53+
- Set up environment variables
54+
- Explore the AI dashboard for insights
55+
56+
Happy coding! 🚀`
3157
}
58+
} catch (error) {
59+
throw new Error(`Failed to fetch project information: ${error instanceof Error ? error.message : 'Unknown error'}`)
3260
}
3361
}
3462

35-
// Get repository URL from site ID using existing API functionality
36-
const getRepoUrlFromSiteId = async (api: any, siteId: string): Promise<string> => {
63+
// Get repository URL from project ID using existing site API functionality
64+
const getRepoUrlFromProjectId = async (api: any, projectId: string): Promise<string> => {
3765
try {
38-
const siteData = await api.getSite({ siteId })
66+
// Use project ID as site ID to get site data
67+
const siteData = await api.getSite({ siteId: projectId })
3968

4069
const repoUrl = siteData.build_settings?.repo_url
4170

4271
if (!repoUrl) {
43-
throw new Error(`No repository URL found for site ID: ${siteId}`)
72+
throw new Error(`No repository URL found for project ID: ${projectId}`)
4473
}
4574

4675
return repoUrl
4776

4877
} catch (error: any) {
4978
if (error.status === 404) {
50-
throw new Error(`Project with ID ${siteId} not found`)
79+
throw new Error(`Project with ID ${projectId} not found`)
5180
}
5281
throw new Error(`Failed to fetch project data: ${error.message}`)
5382
}
5483
}
5584

56-
// Mock server response for now
57-
const mockServerRequest = async (hash: string, _authToken: string) => {
58-
// Simulate API call delay
59-
await new Promise(resolve => setTimeout(resolve, 1500))
85+
// Save AI instructions to markdown file
86+
const saveAiInstructions = async (instructions: string, targetDir: string): Promise<void> => {
87+
const fs = await import('node:fs/promises')
88+
const path = await import('node:path')
6089

61-
// Mock successful response
62-
return {
63-
success: true,
64-
message: 'AI project initialization started successfully',
65-
projectId: '4d6c8c75-2278-409e-bcb7-06e07b79e1bc',
66-
status: 'processing',
67-
estimatedTime: '2-3 minutes',
68-
dashboardUrl: `https://app.netlify.com/ai/projects/proj_${hash.substring(0, 8)}`
90+
try {
91+
const filePath = path.resolve(targetDir, 'AI-instructions.md')
92+
await fs.writeFile(filePath, instructions, 'utf-8')
93+
log(`${chalk.green('✅')} AI instructions saved to ${chalk.cyan('AI-instructions.md')}`)
94+
} catch (error) {
95+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
96+
log(`${chalk.yellow('⚠️')} Warning: Failed to save AI instructions: ${errorMessage}`)
6997
}
7098
}
7199

@@ -96,43 +124,44 @@ export const aiStartCommand = async (options: OptionValues, command: BaseCommand
96124
log(`${chalk.gray('Hash:')} ${hash}`)
97125
log(`${chalk.gray('User:')} ${api.accessToken ? 'Authenticated ✅' : 'Not authenticated ❌'}`)
98126

99-
// Step 1: Decode hash to get repository information
127+
// Step 1: Decode hash and fetch project information
100128
log('\n📋 Decoding project hash...')
101-
const hashResult = mockDecodeHash(hash)
102-
103-
let finalRepoUrl: string
129+
const decodedUrl = decodeHash(hash)
130+
log(`${chalk.cyan('Decoded URL:')} ${decodedUrl}`)
104131

105-
// Step 1a: If hash contains site ID, fetch repository URL from Netlify API
106-
if (hashResult.siteId) {
107-
log(`${chalk.cyan('Site ID:')} ${hashResult.siteId}`)
108-
log('🔍 Fetching repository information from Netlify...')
109-
110-
try {
111-
finalRepoUrl = await getRepoUrlFromSiteId(api, hashResult.siteId)
112-
log(`${chalk.cyan('Repository:')} ${finalRepoUrl}`)
113-
} catch (error) {
114-
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
115-
log(chalk.red('❌ Error:'), errorMessage)
116-
return
117-
}
118-
} else if (hashResult.repoUrl) {
119-
// Direct repository URL provided
120-
finalRepoUrl = hashResult.repoUrl
121-
log(`${chalk.cyan('Repository:')} ${finalRepoUrl}`)
122-
} else {
123-
log(chalk.red('❌ Error: No repository information found in hash'))
132+
log('\n🔍 Fetching project information...')
133+
let projectInfo: ProjectInfo
134+
try {
135+
projectInfo = await fetchProjectInfo(decodedUrl, api.accessToken ?? '')
136+
} catch (error) {
137+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
138+
log(chalk.red('❌ Error:'), errorMessage)
124139
return
125140
}
126-
127-
log(`${chalk.cyan('Target Directory:')} ${hashResult.targetDir ?? 'auto-generated'}`)
128-
if (hashResult.branch) {
129-
log(`${chalk.cyan('Branch:')} ${hashResult.branch}`)
141+
142+
if (!projectInfo.success) {
143+
log(chalk.red('❌ Failed to fetch project information'))
144+
return
130145
}
131146

132-
// Step 2: Clone repository using existing functionality
147+
log(`${chalk.cyan('Project ID:')} ${projectInfo.projectId}`)
148+
149+
// Step 2: Get repository URL from project ID via Netlify site API
150+
log('\n🔗 Linking to Netlify site and fetching repository...')
151+
let repositoryUrl: string
133152
try {
134-
const { repoUrl, repoName } = normalizeRepoUrl(finalRepoUrl)
135-
const targetDir = hashResult.targetDir ?? `ai-project-${repoName}-${hash.substring(0, 8)}`
153+
repositoryUrl = await getRepoUrlFromProjectId(api, projectInfo.projectId)
154+
log(`${chalk.cyan('Repository:')} ${repositoryUrl}`)
155+
} catch (error) {
156+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
157+
log(chalk.red('❌ Error:'), errorMessage)
158+
return
159+
}
160+
161+
// Step 3: Clone repository using existing functionality
162+
try {
163+
const { repoUrl, repoName } = normalizeRepoUrl(repositoryUrl)
164+
const targetDir = `ai-project-${repoName}-${hash.substring(0, 8)}`
136165

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

@@ -148,36 +177,37 @@ export const aiStartCommand = async (options: OptionValues, command: BaseCommand
148177
command.workingDir = targetDir
149178
process.chdir(targetDir)
150179

151-
// Step 3: Send request to AI server for project setup
152-
log('\n🚀 Sending request to AI server...')
153-
const response = await mockServerRequest(hash, api.accessToken ?? '')
154-
155-
if (response.success) {
156-
log(`\n${chalk.green('✅ Success!')} ${response.message}`)
157-
log(`${chalk.cyan('Project ID:')} ${response.projectId}`)
158-
log(`${chalk.cyan('Status:')} ${response.status}`)
159-
log(`${chalk.cyan('Estimated Time:')} ${response.estimatedTime}`)
160-
161-
if (response.dashboardUrl) {
162-
log(`${chalk.cyan('Dashboard:')} ${response.dashboardUrl}`)
163-
}
164-
165-
// Success message with next steps
166-
log()
167-
log(chalk.green('✔ Your AI project is ready to go!'))
168-
log(`→ Project cloned to: ${chalk.cyanBright(targetDir)}`)
169-
log()
170-
log(chalk.yellowBright(`📁 Next: Enter your project directory`))
171-
log(` ${chalk.cyanBright(`cd ${targetDir}`)}`)
172-
log()
173-
log(`→ AI setup is processing in the background`)
174-
log(`→ Check progress at: ${chalk.cyanBright(response.dashboardUrl)}`)
175-
log(`→ Then you can run: ${chalk.cyanBright('netlify dev')} or ${chalk.cyanBright('netlify deploy')}`)
176-
log()
180+
// Step 4: Save AI instructions to file
181+
if (projectInfo.aiInstructions) {
182+
log('\n📝 Saving AI instructions...')
183+
// Use command working directory which is now set to the cloned repo
184+
await saveAiInstructions(projectInfo.aiInstructions, command.workingDir)
185+
}
177186

178-
} else {
179-
log(chalk.red('❌ Failed to start AI project'))
187+
// Success message with next steps
188+
log()
189+
log(chalk.green('✔ Your AI project is ready to go!'))
190+
log(`→ Project ID: ${chalk.cyanBright(projectInfo.projectId)}`)
191+
log(`→ Project cloned to: ${chalk.cyanBright(targetDir)}`)
192+
if (projectInfo.aiInstructions) {
193+
log(`→ AI instructions saved: ${chalk.cyanBright('AI-instructions.md')}`)
194+
}
195+
log()
196+
log(chalk.yellowBright(`📁 Step 1: Enter your project directory`))
197+
log(` ${chalk.cyanBright(`cd ${targetDir}`)}`)
198+
log()
199+
if (projectInfo.aiInstructions) {
200+
log(chalk.yellowBright(`🤖 Step 2: Ask your AI assistant to process the instructions`))
201+
log(` ${chalk.gray('Tell your AI:')} ${chalk.cyanBright('"Please read and follow the AI-instructions.md file"')}`)
202+
log()
180203
}
204+
log(chalk.yellowBright(`🚀 Step 3: Start development`))
205+
log(` ${chalk.cyanBright('netlify dev')} ${chalk.gray('- Start local development server')}`)
206+
log(` ${chalk.cyanBright('netlify deploy')} ${chalk.gray('- Deploy your project')}`)
207+
log()
208+
log(chalk.gray(`💡 Pro tip: Your AI assistant can help you understand and implement`))
209+
log(chalk.gray(` the project-specific instructions in AI-instructions.md`))
210+
log()
181211

182212
} catch (error) {
183213
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'

0 commit comments

Comments
 (0)