@@ -7,7 +7,10 @@ import (
77 "os"
88 "os/exec"
99 "path/filepath"
10+ "regexp"
1011 "runtime"
12+ "sort"
13+ "strings"
1114
1215 "github.com/mendixlabs/mxcli/cmd/mxcli/docker"
1316 "github.com/spf13/cobra"
@@ -28,13 +31,26 @@ Examples:
2831 mxcli new MyApp
2932 mxcli new MyApp --version 11.8.0
3033 mxcli new MyApp --version 10.24.0 --output-dir ./projects/my-app
34+ mxcli new --list-versions
3135` ,
32- Args : cobra .ExactArgs (1 ),
36+ Args : cobra .MaximumNArgs (1 ),
3337 Run : func (cmd * cobra.Command , args []string ) {
38+ listVersions , _ := cmd .Flags ().GetBool ("list-versions" )
39+ if listVersions {
40+ listMendixVersions ()
41+ return
42+ }
43+
44+ if len (args ) < 1 {
45+ fmt .Fprintln (os .Stderr , "Error: app name is required" )
46+ fmt .Fprintln (os .Stderr , "Usage: mxcli new <app-name> [--version X.Y.Z]" )
47+ os .Exit (1 )
48+ }
3449 appName := args [0 ]
3550 mendixVersion , _ := cmd .Flags ().GetString ("version" )
3651 outputDir , _ := cmd .Flags ().GetString ("output-dir" )
3752 skipInit , _ := cmd .Flags ().GetBool ("skip-init" )
53+ force , _ := cmd .Flags ().GetBool ("force" )
3854
3955 if mendixVersion == "" {
4056 fmt .Fprintln (os .Stderr , "Error: --version is required (e.g., --version 11.8.0)" )
@@ -53,8 +69,13 @@ Examples:
5369
5470 // Check if directory already exists and has content
5571 if entries , err := os .ReadDir (absDir ); err == nil && len (entries ) > 0 {
56- fmt .Fprintf (os .Stderr , "Error: directory %s already exists and is not empty\n " , absDir )
57- os .Exit (1 )
72+ if force {
73+ fmt .Printf (" Directory %s is not empty (--force), proceeding...\n " , absDir )
74+ } else {
75+ fmt .Fprintf (os .Stderr , "Error: directory %s already exists and is not empty\n " , absDir )
76+ fmt .Fprintf (os .Stderr , " Use --force to override, or choose a different --output-dir\n " )
77+ os .Exit (1 )
78+ }
5879 }
5980
6081 // Step 1: Resolve mx binary.
@@ -154,6 +175,96 @@ Examples:
154175 },
155176}
156177
178+ // listMendixVersions lists available Mendix versions from all sources.
179+ func listMendixVersions () {
180+ all := map [string ]string {} // version → source label
181+ add := func (version , source string ) {
182+ if version != "" {
183+ all [version ] = source
184+ }
185+ }
186+
187+ // 1. Cached downloads (~/.mxcli/mxbuild/<version>/)
188+ for _ , v := range allCachedMxBuildVersions () {
189+ add (v , "cached download" )
190+ }
191+
192+ // 2. Windows: C:\Program Files\Mendix\<version>\modeler\mxbuild.exe
193+ if runtime .GOOS == "windows" {
194+ for _ , env := range []string {"PROGRAMFILES" , "PROGRAMW6432" , "PROGRAMFILES(X86)" } {
195+ if d := os .Getenv (env ); d != "" {
196+ entries , _ := os .ReadDir (filepath .Join (d , "Mendix" ))
197+ for _ , e := range entries {
198+ if e .IsDir () {
199+ if _ , err := os .Stat (filepath .Join (d , "Mendix" , e .Name (), "modeler" , "mxbuild.exe" )); err == nil {
200+ add (e .Name (), "Studio Pro" )
201+ }
202+ }
203+ }
204+ }
205+ }
206+ if sd := os .Getenv ("SystemDrive" ); sd != "" {
207+ root := sd + string (os .PathSeparator )
208+ for _ , dir := range []string {"Program Files" , "Program Files (x86)" } {
209+ entries , _ := os .ReadDir (filepath .Join (root , dir , "Mendix" ))
210+ for _ , e := range entries {
211+ if e .IsDir () {
212+ if _ , err := os .Stat (filepath .Join (root , dir , "Mendix" , e .Name (), "modeler" , "mxbuild.exe" )); err == nil {
213+ add (e .Name (), "Studio Pro" )
214+ }
215+ }
216+ }
217+ }
218+ }
219+ }
220+
221+ // 3. macOS: /Applications/Mendix Studio Pro *.app
222+ if runtime .GOOS == "darwin" {
223+ matches , _ := filepath .Glob ("/Applications/Mendix Studio Pro *.app" )
224+ re := regexp .MustCompile (`^Mendix Studio Pro (\d+\.\d+\.\d+)` )
225+ for _ , match := range matches {
226+ base := strings .TrimSuffix (filepath .Base (match ), ".app" )
227+ if m := re .FindStringSubmatch (base ); m != nil {
228+ add (m [1 ], "Studio Pro" )
229+ }
230+ }
231+ }
232+
233+ if len (all ) == 0 {
234+ fmt .Println ("No Mendix versions found." )
235+ fmt .Println ()
236+ fmt .Println ("To find available Mendix versions, visit:" )
237+ fmt .Println (" https://docs.mendix.com/releasenotes/studio-pro/" )
238+ fmt .Println ()
239+ fmt .Println ("Usage:" )
240+ fmt .Println (" mxcli new MyApp --version X.Y.Z" )
241+ fmt .Println ()
242+ fmt .Println ("mxcli automatically downloads the required version on first use." )
243+ return
244+ }
245+
246+ var versions []string
247+ for v := range all {
248+ versions = append (versions , v )
249+ }
250+ sort .Slice (versions , func (i , j int ) bool {
251+ // Prefer newer versions first (simple semver comparison by string).
252+ // This works for X.Y.Z format when all are the same length.
253+ return versions [i ] > versions [j ]
254+ })
255+
256+ fmt .Println ("Available Mendix versions:" )
257+ for _ , v := range versions {
258+ fmt .Printf (" %-12s (%s)\n " , v , all [v ])
259+ }
260+ fmt .Println ()
261+ fmt .Println ("Usage:" )
262+ fmt .Println (" mxcli new MyApp --version X.Y.Z" )
263+ fmt .Println ()
264+ fmt .Println ("Cached downloads are stored in ~/.mxcli/mxbuild/<version>/." )
265+ fmt .Println ("mxcli automatically downloads the required version on first use." )
266+ }
267+
157268// cleanupDuplicateLocaleFiles removes duplicate locale files that mx create-project
158269// generates in themesource/atlas_core/. MxBuild crashes when multiple translation.json
159270// files map to the same locale key (e.g., "en-US").
@@ -201,9 +312,11 @@ func cleanupDuplicateLocaleFiles(projectDir string) int {
201312}
202313
203314func init () {
315+ newCmd .Flags ().Bool ("list-versions" , false , "List cached Mendix versions and show how to find available ones" )
204316 newCmd .Flags ().String ("version" , "" , "Mendix version (e.g., 11.8.0) — required" )
205317 newCmd .Flags ().String ("output-dir" , "" , "Output directory (default: ./<app-name>)" )
206318 newCmd .Flags ().Bool ("skip-init" , false , "Skip AI tooling initialization (mxcli init)" )
319+ newCmd .Flags ().Bool ("force" , false , "Allow creating project in a non-empty directory" )
207320
208321 rootCmd .AddCommand (newCmd )
209322}
0 commit comments