@@ -3,6 +3,7 @@ package app
33import (
44 "fmt"
55 "os"
6+ "os/exec"
67 "path/filepath"
78
89 "github.com/charmbracelet/bubbles/key"
@@ -15,8 +16,6 @@ import (
1516 "github.com/spf13/cobra"
1617)
1718
18- const templateRepoURL = "https://github.com/major-technology/basic-template.git"
19-
2019// createCmd represents the create command
2120var createCmd = & cobra.Command {
2221 Use : "create" ,
@@ -68,11 +67,18 @@ func runCreate(cobraCmd *cobra.Command) error {
6867 return fmt .Errorf ("failed to collect application details: %w" , err )
6968 }
7069
71- cobraCmd .Printf ("\n Creating application '%s'...\n " , appName )
72-
7370 // Get the API client
7471 apiClient := singletons .GetAPIClient ()
7572
73+ // Fetch and select template
74+ cobraCmd .Println ("\n Fetching available templates..." )
75+ templateURL , templateName , err := selectTemplate (cobraCmd , apiClient )
76+ if err != nil {
77+ return fmt .Errorf ("failed to select template: %w" , err )
78+ }
79+
80+ cobraCmd .Printf ("\n Creating application '%s'...\n " , appName )
81+
7682 // Call POST /applications (token will be fetched automatically)
7783 createResp , err := apiClient .CreateApplication (appName , appDescription , orgID )
7884 if ok := api .CheckErr (cobraCmd , err ); ! ok {
@@ -110,7 +116,7 @@ func runCreate(cobraCmd *cobra.Command) error {
110116 cobraCmd .Printf ("\n Cloning template repository...\n " )
111117
112118 // Clone the template repository
113- if err := git .Clone (templateRepoURL , tempDir ); err != nil {
119+ if err := git .Clone (templateURL , tempDir ); err != nil {
114120 return fmt .Errorf ("failed to clone template repository: %w" , err )
115121 }
116122
@@ -137,7 +143,8 @@ func runCreate(cobraCmd *cobra.Command) error {
137143
138144 // Select resources for the application
139145 cobraCmd .Println ("\n Selecting resources for your application..." )
140- if err := selectApplicationResources (cobraCmd , orgID , createResp .ApplicationID ); err != nil {
146+ selectedResources , err := selectApplicationResources (cobraCmd , orgID , createResp .ApplicationID )
147+ if err != nil {
141148 errorStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("9" )) // Red
142149 cobraCmd .Println (errorStyle .Render ("Failed to configure resources. Please run 'major app resources' to configure them later." ))
143150 }
@@ -159,6 +166,15 @@ func runCreate(cobraCmd *cobra.Command) error {
159166 cobraCmd .Printf ("\n ✓ Application '%s' successfully created in ./%s\n " , appName , appName )
160167 cobraCmd .Printf (" Clone URL: %s\n " , cloneURL )
161168
169+ // If Vite template and resources were selected, add them using major-client
170+ if templateName == "Vite" && len (selectedResources ) > 0 {
171+ cobraCmd .Println ("\n Adding resources to Vite project..." )
172+ if err := addResourcesToViteProject (cobraCmd , targetDir , selectedResources , createResp .ApplicationID ); err != nil {
173+ cobraCmd .Printf ("Warning: Failed to add resources to project: %v\n " , err )
174+ cobraCmd .Println ("You can manually add them later using 'pnpm clients:add'" )
175+ }
176+ }
177+
162178 // Generate .env file
163179 cobraCmd .Println ("\n Generating .env file..." )
164180 envFilePath , _ , err := generateEnvFile (targetDir )
@@ -168,15 +184,6 @@ func runCreate(cobraCmd *cobra.Command) error {
168184 cobraCmd .Printf ("✓ Generated .env file at: %s\n " , envFilePath )
169185 }
170186
171- // Generate RESOURCES.md file
172- cobraCmd .Println ("\n Generating RESOURCES.md file..." )
173- resourcesFilePath , _ , err := generateResourcesFile (targetDir )
174- if err != nil {
175- cobraCmd .Printf ("Warning: Failed to generate RESOURCES.md file: %v\n " , err )
176- } else {
177- cobraCmd .Printf ("✓ Generated RESOURCES.md file at: %s\n " , resourcesFilePath )
178- }
179-
180187 printSuccessMessage (cobraCmd , appName )
181188
182189 return nil
@@ -251,21 +258,68 @@ func printSuccessMessage(cobraCmd *cobra.Command, appName string) {
251258 cobraCmd .Println (box )
252259}
253260
261+ // addResourcesToViteProject adds selected resources to a Vite project using pnpm clients:add
262+ func addResourcesToViteProject (cobraCmd * cobra.Command , projectDir string , resources []api.ResourceItem , applicationID string ) error {
263+ // First, install dependencies to make major-client available
264+ cobraCmd .Println (" Installing dependencies..." )
265+ installCmd := exec .Command ("pnpm" , "install" )
266+ installCmd .Dir = projectDir
267+ installCmd .Stdout = os .Stdout
268+ installCmd .Stderr = os .Stderr
269+
270+ if err := installCmd .Run (); err != nil {
271+ return fmt .Errorf ("failed to install dependencies: %w" , err )
272+ }
273+
274+ successCount := 0
275+ for _ , resource := range resources {
276+ // Convert resource name to a valid client name (kebab-case)
277+ // The major-client tool will convert it to camelCase for the actual client
278+ clientName := resource .Name
279+
280+ cobraCmd .Printf (" Adding resource: %s (%s)...\n " , resource .Name , resource .Type )
281+
282+ // Run: pnpm clients:add <resource_id> <name> <type> <description> <application_id>
283+ cmd := exec .Command ("pnpm" , "clients:add" , resource .ID , clientName , resource .Type , resource .Description , applicationID )
284+ cmd .Dir = projectDir
285+ cmd .Stdout = os .Stdout
286+ cmd .Stderr = os .Stderr
287+
288+ if err := cmd .Run (); err != nil {
289+ cobraCmd .Printf (" ⚠ Failed to add resource %s: %v\n " , resource .Name , err )
290+ continue
291+ }
292+
293+ successCount ++
294+ }
295+
296+ if successCount > 0 {
297+ cobraCmd .Printf ("✓ Successfully added %d/%d resource(s) to the project\n " , successCount , len (resources ))
298+ }
299+
300+ if successCount < len (resources ) {
301+ return fmt .Errorf ("failed to add %d resource(s)" , len (resources )- successCount )
302+ }
303+
304+ return nil
305+ }
306+
254307// selectApplicationResources prompts the user to select resources for the application
255- func selectApplicationResources (cobraCmd * cobra.Command , orgID , appID string ) error {
308+ // Returns the selected resources with their full details
309+ func selectApplicationResources (cobraCmd * cobra.Command , orgID , appID string ) ([]api.ResourceItem , error ) {
256310 // Get the API client
257311 apiClient := singletons .GetAPIClient ()
258312
259313 // Fetch available resources
260314 resourcesResp , err := apiClient .GetResources (orgID )
261315 if ok := api .CheckErr (cobraCmd , err ); ! ok {
262- return err
316+ return nil , err
263317 }
264318
265319 // Check if there are any resources available
266320 if len (resourcesResp .Resources ) == 0 {
267321 cobraCmd .Println ("No resources available in this organization." )
268- return nil
322+ return nil , nil
269323 }
270324
271325 // Create options for the multiselect
@@ -308,22 +362,89 @@ func selectApplicationResources(cobraCmd *cobra.Command, orgID, appID string) er
308362 ).WithKeyMap (customKeyMap )
309363
310364 if err := form .Run (); err != nil {
311- return fmt .Errorf ("failed to collect resource selection: %w" , err )
365+ return nil , fmt .Errorf ("failed to collect resource selection: %w" , err )
312366 }
313367
314368 // If no resources selected, just return
315369 if len (selectedResourceIDs ) == 0 {
316370 cobraCmd .Println ("No resources selected." )
317- return nil
371+ return nil , nil
318372 }
319373
320374 // Save the selected resources
321375 cobraCmd .Printf ("Saving %d selected resource(s)...\n " , len (selectedResourceIDs ))
322376 _ , err = apiClient .SaveApplicationResources (orgID , appID , selectedResourceIDs )
323377 if ok := api .CheckErr (cobraCmd , err ); ! ok {
324- return err
378+ return nil , err
325379 }
326380
327381 cobraCmd .Printf ("✓ Resources configured successfully\n " )
328- return nil
382+
383+ // Build and return the list of selected resources with full details
384+ var selectedResources []api.ResourceItem
385+ for _ , selectedID := range selectedResourceIDs {
386+ for _ , resource := range resourcesResp .Resources {
387+ if resource .ID == selectedID {
388+ selectedResources = append (selectedResources , resource )
389+ break
390+ }
391+ }
392+ }
393+
394+ return selectedResources , nil
395+ }
396+
397+ // selectTemplate prompts the user to select a template for the application
398+ // Returns the template URL and name
399+ func selectTemplate (cobraCmd * cobra.Command , apiClient * api.Client ) (string , string , error ) {
400+ // Fetch available templates
401+ templatesResp , err := apiClient .GetTemplates ()
402+ if ok := api .CheckErr (cobraCmd , err ); ! ok {
403+ return "" , "" , err
404+ }
405+
406+ // Check if there are any templates available
407+ if len (templatesResp .Templates ) == 0 {
408+ return "" , "" , fmt .Errorf ("no templates available" )
409+ }
410+
411+ // If only one template, use it automatically
412+ if len (templatesResp .Templates ) == 1 {
413+ template := templatesResp .Templates [0 ]
414+ cobraCmd .Printf ("Using template: %s\n " , template .Name )
415+ return template .TemplateURL , template .Name , nil
416+ }
417+
418+ // Create options for the select
419+ options := make ([]huh.Option [string ], len (templatesResp .Templates ))
420+ for i , template := range templatesResp .Templates {
421+ options [i ] = huh .NewOption (template .Name , template .TemplateURL )
422+ }
423+
424+ // Prompt user to select a template
425+ var selectedTemplateURL string
426+ form := huh .NewForm (
427+ huh .NewGroup (
428+ huh .NewSelect [string ]().
429+ Title ("Select a template for your application" ).
430+ Description ("Choose which template to use as a starting point" ).
431+ Options (options ... ).
432+ Value (& selectedTemplateURL ),
433+ ),
434+ )
435+
436+ if err := form .Run (); err != nil {
437+ return "" , "" , fmt .Errorf ("failed to select template: %w" , err )
438+ }
439+
440+ // Find the template name for the selected URL
441+ var selectedTemplateName string
442+ for _ , template := range templatesResp .Templates {
443+ if template .TemplateURL == selectedTemplateURL {
444+ selectedTemplateName = template .Name
445+ break
446+ }
447+ }
448+
449+ return selectedTemplateURL , selectedTemplateName , nil
329450}
0 commit comments