11import { OptionValues } from 'commander'
22
3- import { chalk , log } from '../../utils/command-helpers.js'
3+ import { chalk , log , logAndThrowError } from '../../utils/command-helpers.js'
4+ import { normalizeRepoUrl } from '../../utils/normalize-repo-url.js'
5+ import { runGit } from '../../utils/run-git.js'
6+ import { startSpinner } from '../../lib/spinner.js'
47import BaseCommand from '../base-command.js'
58
9+ interface MockHashDecodeResult {
10+ siteId ?: string
11+ repoUrl ?: string
12+ targetDir ?: string
13+ branch ?: string
14+ }
15+
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 {
27+ return {
28+ repoUrl : 'https://github.com/netlify/netlify-cli.git' , // Mock repo
29+ targetDir : `ai-project-${ hash . substring ( 0 , 8 ) } ` ,
30+ branch : 'main'
31+ }
32+ }
33+ }
34+
35+ // Get repository URL from site ID using existing API functionality
36+ const getRepoUrlFromSiteId = async ( api : any , siteId : string ) : Promise < string > => {
37+ try {
38+ const siteData = await api . getSite ( { siteId } )
39+
40+ const repoUrl = siteData . build_settings ?. repo_url
41+
42+ if ( ! repoUrl ) {
43+ throw new Error ( `No repository URL found for site ID: ${ siteId } ` )
44+ }
45+
46+ return repoUrl
47+
48+ } catch ( error : any ) {
49+ if ( error . status === 404 ) {
50+ throw new Error ( `Project with ID ${ siteId } not found` )
51+ }
52+ throw new Error ( `Failed to fetch project data: ${ error . message } ` )
53+ }
54+ }
55+
656// Mock server response for now
7- const mockServerRequest = async ( hash : string , authToken : string ) => {
57+ const mockServerRequest = async ( hash : string , _authToken : string ) => {
858 // Simulate API call delay
959 await new Promise ( resolve => setTimeout ( resolve , 1500 ) )
1060
1161 // Mock successful response
1262 return {
1363 success : true ,
1464 message : 'AI project initialization started successfully' ,
15- projectId : `proj_ ${ hash . substring ( 0 , 8 ) } ` ,
65+ projectId : '4d6c8c75-2278-409e-bcb7-06e07b79e1bc' ,
1666 status : 'processing' ,
1767 estimatedTime : '2-3 minutes' ,
1868 dashboardUrl : `https://app.netlify.com/ai/projects/proj_${ hash . substring ( 0 , 8 ) } `
1969 }
2070}
2171
22- export const aiStartCommand = async ( _options : OptionValues , command : BaseCommand ) => {
72+ const cloneRepo = async ( repoUrl : string , targetDir : string , debug : boolean ) : Promise < void > => {
73+ try {
74+ await runGit ( [ 'clone' , repoUrl , targetDir ] , ! debug )
75+ } catch ( error ) {
76+ throw new Error ( `Failed to clone repository: ${ error instanceof Error ? error . message : error ?. toString ( ) ?? '' } ` )
77+ }
78+ }
79+
80+ export const aiStartCommand = async ( options : OptionValues , command : BaseCommand ) => {
2381 const hash = command . args [ 0 ]
2482
2583 // Validate hash parameter
@@ -37,11 +95,62 @@ export const aiStartCommand = async (_options: OptionValues, command: BaseComman
3795 log ( `${ chalk . blue ( '🤖 AI Start' ) } - Initializing AI project...` )
3896 log ( `${ chalk . gray ( 'Hash:' ) } ${ hash } ` )
3997 log ( `${ chalk . gray ( 'User:' ) } ${ api . accessToken ? 'Authenticated ✅' : 'Not authenticated ❌' } ` )
40- log ( '\nSending request to AI server...' )
4198
99+ // Step 1: Decode hash to get repository information
100+ log ( '\n📋 Decoding project hash...' )
101+ const hashResult = mockDecodeHash ( hash )
102+
103+ let finalRepoUrl : string
104+
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' ) )
124+ return
125+ }
126+
127+ log ( `${ chalk . cyan ( 'Target Directory:' ) } ${ hashResult . targetDir ?? 'auto-generated' } ` )
128+ if ( hashResult . branch ) {
129+ log ( `${ chalk . cyan ( 'Branch:' ) } ${ hashResult . branch } ` )
130+ }
131+
132+ // Step 2: Clone repository using existing functionality
42133 try {
43- // Mock server request for now
44- const response = await mockServerRequest ( hash , api . accessToken || '' )
134+ const { repoUrl, repoName } = normalizeRepoUrl ( finalRepoUrl )
135+ const targetDir = hashResult . targetDir ?? `ai-project-${ repoName } -${ hash . substring ( 0 , 8 ) } `
136+
137+ const cloneSpinner = startSpinner ( { text : `Cloning repository to ${ chalk . cyan ( targetDir ) } ` } )
138+
139+ try {
140+ await cloneRepo ( repoUrl , targetDir , Boolean ( options . debug ) )
141+ cloneSpinner . success ( { text : `Cloned repository to ${ chalk . cyan ( targetDir ) } ` } )
142+ } catch ( error ) {
143+ cloneSpinner . error ( )
144+ return logAndThrowError ( error )
145+ }
146+
147+ // Update working directory to cloned repo
148+ command . workingDir = targetDir
149+ process . chdir ( targetDir )
150+
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 ?? '' )
45154
46155 if ( response . success ) {
47156 log ( `\n${ chalk . green ( '✅ Success!' ) } ${ response . message } ` )
@@ -53,16 +162,23 @@ export const aiStartCommand = async (_options: OptionValues, command: BaseComman
53162 log ( `${ chalk . cyan ( 'Dashboard:' ) } ${ response . dashboardUrl } ` )
54163 }
55164
56- log ( `\n${ chalk . gray ( '💡 Your AI project is being set up in the background.' ) } ` )
57- log ( `${ chalk . gray ( 'You can check the progress in your Netlify dashboard.' ) } ` )
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 ( `→ Enter your project directory: ${ chalk . cyanBright ( `cd ${ targetDir } ` ) } ` )
170+ log ( `→ AI setup is processing in the background` )
171+ log ( `→ Check progress at: ${ chalk . cyanBright ( response . dashboardUrl ) } ` )
172+ log ( )
173+
58174 } else {
59- log ( ` ${ chalk . red ( '❌ Failed to start AI project' ) } ` )
175+ log ( chalk . red ( '❌ Failed to start AI project' ) )
60176 }
61177
62178 } catch ( error ) {
63179 const errorMessage = error instanceof Error ? error . message : 'Unknown error occurred'
64180
65- log ( ` ${ chalk . red ( '❌ Error:' ) } ${ errorMessage } ` )
66- log ( ` ${ chalk . gray ( 'Please try again or contact support if the issue persists.' ) } ` )
181+ log ( chalk . red ( '❌ Error:' ) , errorMessage )
182+ log ( chalk . gray ( 'Please try again or contact support if the issue persists.' ) )
67183 }
68184}
0 commit comments