Repository: pyenv-win/pyenv-win (language: python)

Issue:
pyenv install x.y:latest
#372 
feature: Support `pyenv install x.y:latest`, like pyenv does
Pyenv now support writing `pyenv install 3.10:latest`. Would be nice if pyenv-win also supported this. Useful for automated installation.


Discussion / hints:
Hi there!

I really would like to have this feature, so I began work on it! Currently, following your checkpoints from #372, I've written code that generates `latest_tags.xml` with all latest minor versions. Here is [my branch](https://github.com/Phanabani/pyenv-win/tree/feature/pyenv-install-latest) for this feature, and you can try it out by running `pyenv-win\bin\pyenv.bat update`.

I saw you removed the "help wanted" label from the other issue and assigned yourself to it. Would you like to work together with me on this issue? I admit, I hadn't written in VBScript before this, so your assistance would be helpful! Otherwise, I'd be happy to continue trying myself.

Also, I'd like to note that it appears `:latest` [has been deprecated](https://github.com/pyenv/pyenv/issues/2569) and has been superceded by [prefix auto-resolution](https://github.com/pyenv/pyenv#prefix-auto-resolution) ([PR for the feature](https://github.com/pyenv/pyenv/pull/2487)), where you can type `3.10` or `3` and it will resolve to the latest version for missing version pieces. I plan to support this functionality rather than `:latest`.
If anyone wants to work on this, here are the checkpoints

- [ ]  Create latest_tags.xml from .versions_cache.xml contains only latest version mappings
- [ ] The above code need to part of `pyenv update` 
- [ ] When latest tag given read from latest_tags.xml for right python version and pass them to pyenv install 
I agree this would be a great feature.  Here is what I am seeing:

```PowerShell
pyenv install 3.10:latest
:: [Info] ::  Mirror: https://www.python.org/ftp/python
pyenv-install: definition not found: 3.10:latest
```

In my opinion, the current message is slightly misleading, it implies something in one's set up might be incorrect.

If this `:latest` syntax isn't going to be supported, it may also be a good idea to change the printed message to "pyenv install of latest version is not yet supported in pyenv-win" or something similar.
@jamesbraza feel free to list the tasks to make such change and create a PR for the same.!

Fix patch:
diff --git a/README.md b/README.md
index 90d38bcf..3bf18566 100644
--- a/README.md
+++ b/README.md
@@ -69,9 +69,10 @@ This project was forked from [rbenv-win][3] and modified for [pyenv][1]. It is n
 ```yml
    commands     List all available pyenv commands
    local        Set or show the local application-specific Python version
+   latest       Print the latest installed or known version with the given prefix
    global       Set or show the global Python version
    shell        Set or show the shell-specific Python version
-   install      Install 1 or more versions of Python 
+   install      Install 1 or more versions of Python
    uninstall    Uninstall 1 or more versions of Python
    update       Update the cached version DB
    rehash       Rehash pyenv shims (run this after switching Python versions)
@@ -105,7 +106,7 @@ Please see the [Installation](./docs/installation.md) page for more details.
 
 If you are getting "**command not found**" error, check the below note and [manually check the settings](#manually-check-the-settings)
 
-For Visual Studio Code or another IDE with a built in terminal, restart it and check again  
+For Visual Studio Code or another IDE with a built in terminal, restart it and check again
 
 ***
 
@@ -114,10 +115,10 @@ For Visual Studio Code or another IDE with a built in terminal, restart it and c
 Ensure all environment variables are properly set via the GUI:
 
 ```plaintext
-This PC 
+This PC
    → Properties
-      → Advanced system settings 
-         → Advanced → Environment Variables... 
+      → Advanced system settings
+         → Advanced → Environment Variables...
             → PATH
 ```
 
@@ -195,7 +196,7 @@ Both releases can install 64bit and 32bit python versions; the difference is in
 3.8.2-amd64
 ....
 ```
-  
+
 Support for Python versions below 2.4 have been dropped since their installers don't install "cleanly" like versions from 2.4 onward and they're predominantly out of use/support in most environments now.
 
 ## FAQ
@@ -228,7 +229,7 @@ Please see the [Changelog](./docs/changelog.md) page.
 
 ## Author and Thanks
 
-pyenv-win was developed by [Kiran Kumar Kotari](https://github.com/kirankotari) and [Contributors](https://github.com/pyenv-win/pyenv-win/graphs/contributors)  
+pyenv-win was developed by [Kiran Kumar Kotari](https://github.com/kirankotari) and [Contributors](https://github.com/pyenv-win/pyenv-win/graphs/contributors)
 Thanks for all Contributors and Supports for patience for the latest major release.
 
 [1]: https://github.com/pyenv/pyenv
diff --git a/pyenv-win/libexec/libs/pyenv-install-lib.vbs b/pyenv-win/libexec/libs/pyenv-install-lib.vbs
index 172ef41d..bad7afe8 100644
--- a/pyenv-win/libexec/libs/pyenv-install-lib.vbs
+++ b/pyenv-win/libexec/libs/pyenv-install-lib.vbs
@@ -20,6 +20,7 @@ Const VRX_x64 = 5
 Const VRX_ARM = 6
 Const VRX_Web = 7
 Const VRX_Ext = 8
+Const VRX_Arch = 5
 
 ' Version definition array from LoadVersionsXML.
 Const LV_Code = 0
@@ -38,14 +39,21 @@ Const IP_Quiet = 9
 Const IP_Dev = 10
 
 Dim regexVer
+Dim regexVerArch
 Dim regexFile
 Set regexVer = New RegExp
+Set regexVerArch = New RegExp
 Set regexFile = New RegExp
 With regexVer
     .Pattern = "^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:([a-z]+)(\d*))?$"
     .Global = True
     .IgnoreCase = True
 End With
+With regexVerArch
+    .Pattern = "^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:([a-z]+)(\d*))?([\.-](?:amd64|arm64|win32))?$"
+    .Global = True
+    .IgnoreCase = True
+End With
 With regexFile
     .Pattern = "^python-(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:([a-z]+)(\d*))?([\.-]amd64)?([\.-]arm64)?(-webinstall)?\.(exe|msi)$"
     .Global = True
@@ -278,3 +286,202 @@ Sub SaveVersionsXML(xmlPath, versArray)
         .Close
     End With
 End Sub
+
+' Test if ver1 < ver2
+Function SymanticCompare(ver1, ver2)
+    Dim comp1, comp2
+
+    ' Major
+    comp1 = ver1(VRX_Major)
+    comp2 = ver2(VRX_Major)
+    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
+    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
+    SymanticCompare = comp1 < comp2
+    If comp1 <> comp2 Then Exit Function
+
+    ' Minor
+    comp1 = ver1(VRX_Minor)
+    comp2 = ver2(VRX_Minor)
+    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
+    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
+    SymanticCompare = comp1 < comp2
+    If comp1 <> comp2 Then Exit Function
+
+    ' Patch
+    comp1 = ver1(VRX_Patch)
+    comp2 = ver2(VRX_Patch)
+    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
+    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
+    SymanticCompare = comp1 < comp2
+    If comp1 <> comp2 Then Exit Function
+
+    ' Release
+    comp1 = ver1(VRX_Release)
+    comp2 = ver2(VRX_Release)
+    If Len(comp1) = 0 And Len(comp2) Then
+        SymanticCompare = False
+        Exit Function
+    ElseIf Len(comp1) And Len(comp2) = 0 Then
+        SymanticCompare = True
+        Exit Function
+    Else
+        SymanticCompare = comp1 < comp2
+    End If
+    If comp1 <> comp2 Then Exit Function
+
+    ' Release Number
+    comp1 = ver1(VRX_RelNumber)
+    comp2 = ver2(VRX_RelNumber)
+    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
+    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
+    SymanticCompare = comp1 < comp2
+    If comp1 <> comp2 Then Exit Function
+
+    ' x64
+    comp1 = ver1(VRX_x64)
+    comp2 = ver2(VRX_x64)
+    SymanticCompare = comp1 < comp2
+    If comp1 <> comp2 Then Exit Function
+
+    ' webinstall
+    comp1 = ver1(VRX_Web)
+    comp2 = ver2(VRX_Web)
+    SymanticCompare = comp1 < comp2
+    If comp1 <> comp2 Then Exit Function
+
+    ' ext
+    comp1 = ver1(VRX_Ext)
+    comp2 = ver2(VRX_Ext)
+    SymanticCompare = comp1 < comp2
+    If comp1 <> comp2 Then Exit Function
+End Function
+
+' Modified from code by "Reverend Jim" at:
+' https://www.daniweb.com/programming/code/515601/vbscript-implementation-of-quicksort
+Sub SymanticQuickSort(arr, arrMin, arrMax)
+    Dim middle  ' value of the element in the middle of the range
+    Dim swap    ' temporary item for the swapping of two elements
+    Dim arrFrst ' index of the first element in the range to check
+    Dim arrLast ' index of the last element in the range to check
+    Dim arrMid  ' index of the element in the middle of the range
+    If arrMax <= arrMin Then Exit Sub
+
+    ' Start the checks at the lower and upper limits of the Array
+    arrFrst = arrMin
+    arrLast = arrMax
+
+    ' Find the midpoint of the region to sort and the value of that element
+    arrMid = (arrMin + arrMax) \ 2
+    middle = arr(arrMid)
+    Do While (arrFrst <= arrLast)
+        ' Find the first element > the element at the midpoint
+        Do While SymanticCompare(arr(arrFrst)(SFV_Version), middle(SFV_Version))
+            arrFrst = arrFrst + 1
+            If arrFrst = arrMax Then Exit Do
+        Loop
+
+        ' Find the last element < the element at the midpoint
+        Do While SymanticCompare(middle(SFV_Version), arr(arrLast)(SFV_Version))
+            arrLast = arrLast - 1
+            If arrLast = arrMin Then Exit Do
+        Loop
+
+        ' Pivot the two elements around the midpoint if they are out of order
+        If (arrFrst <= arrLast) Then
+            swap = arr(arrFrst)
+            arr(arrFrst) = arr(arrLast)
+            arr(arrLast) = swap
+            arrFrst = arrFrst + 1
+            arrLast = arrLast - 1
+        End If
+    Loop
+
+    ' Sort sub-regions (recurse) if necessary
+    If arrMin  < arrLast Then SymanticQuickSort arr, arrMin,  arrLast
+    If arrFrst < arrMax  Then SymanticQuickSort arr, arrFrst, arrMax
+End Sub
+
+Function JoinVersionString(pieces)
+    ' WScript.echo "kkotari: pyenv-install-lib.vbs JoinVersionString..!"
+    JoinVersionString = ""
+    If Len(pieces(VRX_Major))     Then JoinVersionString = JoinVersionString & pieces(VRX_Major)
+    If Len(pieces(VRX_Minor))     Then JoinVersionString = JoinVersionString &"."& pieces(VRX_Minor)
+    If Len(pieces(VRX_Patch))     Then JoinVersionString = JoinVersionString &"."& pieces(VRX_Patch)
+    If Len(pieces(VRX_Release))   Then JoinVersionString = JoinVersionString & pieces(VRX_Release)
+    If Len(pieces(VRX_RelNumber)) Then JoinVersionString = JoinVersionString & pieces(VRX_RelNumber)
+    If Len(pieces(VRX_Arch))      Then JoinVersionString = JoinVersionString & pieces(VRX_Arch)
+End Function
+
+' Resolves latest python version by given prefix
+' known=False to find latest _installed_ version
+' See `pyenv latest --help`
+Function FindLatestVersion(prefix, known)
+    Dim candidates
+
+    if known Then
+        Dim cachedVersions
+        Set cachedVersions = LoadVersionsXML(strDBFile)
+
+        Dim cachedVersion
+
+        Dim convertor()
+        ReDim Preserve convertor(-1)
+
+        For Each cachedVersion In cachedVersions.Keys
+            ReDim Preserve convertor(UBound(convertor) + 1)
+            convertor(UBound(convertor)) = cachedVersion
+        Next
+
+        candidates = convertor
+    else
+        candidates = GetInstalledVersions()
+    end if
+
+    Dim x
+    Dim matches
+
+    Dim bestMatch
+    Dim arch
+
+    arch = GetArchPostfix()
+
+    For x = 0 To UBound(candidates) Step 1
+        ' startswith
+        If Left(candidates(x), Len(prefix)) = prefix Then
+            ' Full match OR prefix plus '.'
+            If candidates(x) = prefix & arch Or Mid(candidates(x), Len(prefix) + 1, 1) = "." Then
+                Set matches = regexVerArch.Execute(candidates(x))
+
+                if matches.Count = 1 Then
+                    ' Skip dev builds, releases and so on
+                    ' Comparing each version by <major>.<minor>.<patch>
+                    If matches(0).SubMatches(VRX_Release) = "" And matches(0).SubMatches(VRX_Arch) = arch Then
+                        If IsEmpty(bestMatch) Then
+                            Set bestMatch = matches(0).SubMatches
+                        Else
+                            If SymanticCompare(bestMatch, matches(0).SubMatches) Then
+                                Set bestMatch = matches(0).SubMatches
+                            End If
+                        End If
+                    End If
+                End If
+            End If
+        End If
+    Next
+
+    if IsEmpty(bestMatch) Then
+        FindLatestVersion = ""
+    else
+        FindLatestVersion = JoinVersionString(bestMatch)
+    end if
+End Function
+
+Function TryResolveVersion(prefix, known)
+    Dim resolved
+
+    resolved = FindLatestVersion(prefix, known)
+
+    If resolved = "" Then resolved = prefix
+
+    TryResolveVersion = resolved
+End Function
diff --git a/pyenv-win/libexec/libs/pyenv-lib.vbs b/pyenv-win/libexec/libs/pyenv-lib.vbs
index a11e26e4..aaf7acfc 100644
--- a/pyenv-win/libexec/libs/pyenv-lib.vbs
+++ b/pyenv-win/libexec/libs/pyenv-lib.vbs
@@ -214,13 +214,13 @@ Function GetCurrentVersionsNoError()
     str = GetCurrentVersionsShell
     If Not(IsNull(str)) Then
         For Each v1 in str
-            versions.Add v1(0), v1(1)
+            versions.Item(v1(0)) = v1(1)
         Next
     Else
         str = GetCurrentVersionsLocal(strCurrent)
         If Not(IsNull(str)) Then
             For Each v1 in str
-                versions.Add v1(0), v1(1)
+                versions.Item(TryResolveVersion(v1(0), False)) = v1(1)
             Next
         End If
     End If
@@ -228,7 +228,7 @@ Function GetCurrentVersionsNoError()
         str = GetCurrentVersionsGlobal
         If Not(IsNull(str)) Then
             For Each v1 in str
-                versions.Add v1(0), v1(1)
+                versions.Item(TryResolveVersion(v1(0), False)) = v1(1)
             Next
         End If
     End If
@@ -238,7 +238,7 @@ End Function
 Function GetInstalledVersions()
     ' WScript.echo "kkotari: pyenv-lib.vbs get installed versions..!"
     Dim rootBinDir, winBinDir, version, versions()
-    ReDim Preserve versions(0)
+    ReDim Preserve versions(-1)
     If objfs.FolderExists(strDirVers) Then
         Set rootBinDir = objfs.GetFolder(strDirVers)
         For Each winBinDir in rootBinDir.SubFolders
@@ -367,7 +367,7 @@ Sub WriteLinuxScript(baseName)
                 .Close
             End With
         End If
-        
+
     End If
 End Sub
 
@@ -423,6 +423,17 @@ Sub Rehash()
     Next
 End Sub
 
+Function GetArchPostfix()
+    Dim arch
+
+    arch = objws.Environment("Process")("PYENV_FORCE_ARCH")
+    If arch = "" Then arch = objws.Environment("System")("PROCESSOR_ARCHITECTURE")
+
+    If UCase(arch) = "AMD64" Then GetArchPostfix = ""
+    If UCase(arch) = "X86"   Then GetArchPostfix = "-win32"
+    If UCase(arch) = "ARM64" Then GetArchPostfix = "-arm64"  ' NOT TESTED
+End Function
+
 ' SYSTEM:PROCESSOR_ARCHITECTURE = AMD64 on 64-bit computers. (even when using 32-bit cmd.exe)
 Function Is32Bit()
     ' WScript.echo "kkotari: pyenv-lib.vbs is32bit..!"
diff --git a/pyenv-win/libexec/pyenv-help.bat b/pyenv-win/libexec/pyenv-help.bat
index 096cb105..0483df13 100644
--- a/pyenv-win/libexec/pyenv-help.bat
+++ b/pyenv-win/libexec/pyenv-help.bat
@@ -7,6 +7,7 @@ echo.
 echo Some useful pyenv commands are:
 echo    commands    List all available pyenv commands
 echo    local       Set or show the local application-specific Python version
+echo    latest      Print the latest installed or known version with the given prefix
 echo    global      Set or show the global Python version
 echo    shell       Set or show the shell-specific Python version
 echo    install     Install a Python version using python-build
diff --git a/pyenv-win/libexec/pyenv-install.vbs b/pyenv-win/libexec/pyenv-install.vbs
index 0cc66dd1..f7f93315 100644
--- a/pyenv-win/libexec/pyenv-install.vbs
+++ b/pyenv-win/libexec/pyenv-install.vbs
@@ -327,7 +327,6 @@ End Sub
 
 Sub main(arg)
     ' WScript.echo "kkotari: pyenv-install.vbs Main..!"
-    If arg.Count = 0 Then ShowHelp
 
     Dim idx
     Dim optForce
@@ -374,13 +373,13 @@ Sub main(arg)
             Case "-r"               optReg = True
             Case "--register"       optReg = True
             Case Else
-                installVersions.Item(Check32Bit(arg(idx))) = Empty
+                installVersions.Item(TryResolveVersion(arg(idx), True)) = Empty
         End Select
     Next
     If Is32Bit Then
         opt32 = False
         opt64 = False
-    End If    
+    End If
     If opt32 And opt64 Then
         WScript.Echo "pyenv-install: only --32only or --64only may be specified, not both."
         WScript.Quit 1
@@ -460,10 +459,10 @@ Sub main(arg)
             ' TODO Should we handle many versions here?
             ary = GetCurrentVersionNoError()
             If Not IsNull(ary) Then
-                installVersions.Item(ary(0)) = Empty
+                installVersions.Item(TryResolveVersion(ary(0), True)) = Empty
             Else
                 ShowHelp
-            End If    
+            End If
         End If
     End If
 
diff --git a/pyenv-win/libexec/pyenv-update.vbs b/pyenv-win/libexec/pyenv-update.vbs
index fc34670a..1bba6895 100644
--- a/pyenv-win/libexec/pyenv-update.vbs
+++ b/pyenv-win/libexec/pyenv-update.vbs
@@ -122,120 +122,6 @@ Function ScanForVersions(URL, optIgnore, ByRef pageCount)
     Next
 End Function
 
-' Test if ver1 < ver2
-Function SymanticCompare(ver1, ver2)
-    Dim comp1, comp2
-
-    ' Major
-    comp1 = ver1(VRX_Major)
-    comp2 = ver2(VRX_Major)
-    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
-    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
-    SymanticCompare = comp1 < comp2
-    If comp1 <> comp2 Then Exit Function
-
-    ' Minor
-    comp1 = ver1(VRX_Minor)
-    comp2 = ver2(VRX_Minor)
-    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
-    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
-    SymanticCompare = comp1 < comp2
-    If comp1 <> comp2 Then Exit Function
-
-    ' Patch
-    comp1 = ver1(VRX_Patch)
-    comp2 = ver2(VRX_Patch)
-    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
-    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
-    SymanticCompare = comp1 < comp2
-    If comp1 <> comp2 Then Exit Function
-
-    ' Release
-    comp1 = ver1(VRX_Release)
-    comp2 = ver2(VRX_Release)
-    If Len(comp1) = 0 And Len(comp2) Then
-        SymanticCompare = False
-        Exit Function
-    ElseIf Len(comp1) And Len(comp2) = 0 Then
-        SymanticCompare = True
-        Exit Function
-    Else
-        SymanticCompare = comp1 < comp2
-    End If
-    If comp1 <> comp2 Then Exit Function
-
-    ' Release Number
-    comp1 = ver1(VRX_RelNumber)
-    comp2 = ver2(VRX_RelNumber)
-    If Len(comp1) = 0 Then comp1 = 0: Else comp1 = CLng(comp1)
-    If Len(comp2) = 0 Then comp2 = 0: Else comp2 = CLng(comp2)
-    SymanticCompare = comp1 < comp2
-    If comp1 <> comp2 Then Exit Function
-
-    ' x64
-    comp1 = ver1(VRX_x64)
-    comp2 = ver2(VRX_x64)
-    SymanticCompare = comp1 < comp2
-    If comp1 <> comp2 Then Exit Function
-
-    ' webinstall
-    comp1 = ver1(VRX_Web)
-    comp2 = ver2(VRX_Web)
-    SymanticCompare = comp1 < comp2
-    If comp1 <> comp2 Then Exit Function
-
-    ' ext
-    comp1 = ver1(VRX_Ext)
-    comp2 = ver2(VRX_Ext)
-    SymanticCompare = comp1 < comp2
-    If comp1 <> comp2 Then Exit Function
-End Function
-
-' Modified from code by "Reverend Jim" at:
-' https://www.daniweb.com/programming/code/515601/vbscript-implementation-of-quicksort
-Sub SymanticQuickSort(arr, arrMin, arrMax)
-    Dim middle  ' value of the element in the middle of the range
-    Dim swap    ' temporary item for the swapping of two elements
-    Dim arrFrst ' index of the first element in the range to check
-    Dim arrLast ' index of the last element in the range to check
-    Dim arrMid  ' index of the element in the middle of the range
-    If arrMax <= arrMin Then Exit Sub
-
-    ' Start the checks at the lower and upper limits of the Array
-    arrFrst = arrMin
-    arrLast = arrMax
-
-    ' Find the midpoint of the region to sort and the value of that element
-    arrMid = (arrMin + arrMax) \ 2
-    middle = arr(arrMid)
-    Do While (arrFrst <= arrLast)
-        ' Find the first element > the element at the midpoint
-        Do While SymanticCompare(arr(arrFrst)(SFV_Version), middle(SFV_Version))
-            arrFrst = arrFrst + 1
-            If arrFrst = arrMax Then Exit Do
-        Loop
-
-        ' Find the last element < the element at the midpoint
-        Do While SymanticCompare(middle(SFV_Version), arr(arrLast)(SFV_Version))
-            arrLast = arrLast - 1
-            If arrLast = arrMin Then Exit Do
-        Loop
-
-        ' Pivot the two elements around the midpoint if they are out of order
-        If (arrFrst <= arrLast) Then
-            swap = arr(arrFrst)
-            arr(arrFrst) = arr(arrLast)
-            arr(arrLast) = swap
-            arrFrst = arrFrst + 1
-            arrLast = arrLast - 1
-        End If
-    Loop
-
-    ' Sort sub-regions (recurse) if necessary
-    If arrMin  < arrLast Then SymanticQuickSort arr, arrMin,  arrLast
-    If arrFrst < arrMax  Then SymanticQuickSort arr, arrFrst, arrMax
-End Sub
-
 Sub main(arg)
     Dim optIgnore
     optIgnore = False
diff --git a/pyenv-win/libexec/pyenv.vbs b/pyenv-win/libexec/pyenv.vbs
index 57220671..6170f9f9 100644
--- a/pyenv-win/libexec/pyenv.vbs
+++ b/pyenv-win/libexec/pyenv.vbs
@@ -18,6 +18,7 @@ Sub Import(importFile)
 End Sub
 
 Import "libs\pyenv-lib.vbs"
+Import "libs\pyenv-install-lib.vbs"
 ' WScript.echo "kkotari: pyenv.vbs Import called..!"
 
 Function GetCommandList()
@@ -262,6 +263,7 @@ Sub ShowHelp()
      WScript.Echo "   commands     List all available pyenv commands"
      WScript.Echo "   duplicate    Creates a duplicate python environment"
      WScript.Echo "   local        Set or show the local application-specific Python version"
+     WScript.Echo "   latest       Print the latest installed or known version with the given prefix"
      WScript.Echo "   global       Set or show the global Python version"
      WScript.Echo "   shell        Set or show the shell-specific Python version"
      WScript.Echo "   install      Install a Python version using python-build"
@@ -307,7 +309,7 @@ Sub CommandRehash(arg)
 
     Dim versions
     versions = GetInstalledVersions()
-    If UBound(versions) = 0 Then
+    If UBound(versions) = -1 Then
         WScript.Echo "No version installed. Please install one with 'pyenv install <version>'."
     Else
         Rehash
@@ -338,8 +340,8 @@ Sub CommandGlobal(arg)
             ReDim globalVersions(versionCount - 1)
             Dim i
             For i = 0 To versionCount - 1
-                globalVersions(i) = Check32Bit(arg(i + 1))
-                GetBinDir(globalVersions(i))
+                globalVersions(i) = arg(i + 1)
+                GetBinDir(TryResolveVersion(globalVersions(i), False))
             Next
         End If
 
@@ -380,8 +382,8 @@ Sub CommandLocal(arg)
             ReDim localVersions(versionCount - 1)
             Dim i
             For i = 0 To versionCount - 1
-                localVersions(i) = Check32Bit(arg(i + 1))
-                GetBinDir(localVersions(i))
+                localVersions(i) = arg(i + 1)
+                GetBinDir(TryResolveVersion(localVersions(i), False))
             Next
         End If
 


Test patch:
diff --git a/pyenv-win/libexec/pyenv-latest.vbs b/pyenv-win/libexec/pyenv-latest.vbs
new file mode 100644
index 00000000..b3bd8521
--- /dev/null
+++ b/pyenv-win/libexec/pyenv-latest.vbs
@@ -0,0 +1,86 @@
+Option Explicit
+
+Sub Import(importFile)
+    Dim fso, libFile
+    On Error Resume Next
+    Set fso = CreateObject("Scripting.FileSystemObject")
+    Set libFile = fso.OpenTextFile(fso.getParentFolderName(WScript.ScriptFullName) &"\"& importFile, 1)
+    ExecuteGlobal libFile.ReadAll
+    If Err.number <> 0 Then
+        WScript.Echo "Error importing library """& importFile &"""("& Err.Number &"): "& Err.Description
+        WScript.Quit 1
+    End If
+    libFile.Close
+End Sub
+
+Import "libs\pyenv-lib.vbs"
+Import "libs\pyenv-install-lib.vbs"
+
+Sub ShowHelp(exitcode)
+    ' WScript.echo "kkotari: pyenv-latest.vbs..!"
+    WScript.Echo "Usage: pyenv latest [-k|--known] [-q|--quiet] <prefix>"
+    WScript.Echo ""
+    WScript.Echo "  -k/--known      Select from all known versions instead of installed"
+    WScript.Echo "  -q/--quiet      Do not print an error message on resolution failure"
+    WScript.Echo ""
+    WScript.Quit exitcode
+End Sub
+
+Sub main(arg)
+    ' WScript.echo "kkotari: pyenv-latest.vbs Main..!"
+
+    Dim optKnown
+    Dim optQuiet
+    Dim optPrefix
+
+    optKnown = False
+    optQuiet = False
+    optPrefix = ""
+
+    Dim idx
+
+    For idx = 0 To arg.Count - 1
+        Select Case arg(idx)
+            Case "--help"           ShowHelp 0
+            Case "-k"               optKnown = True
+            Case "--known"          optKnown = True
+            Case "-q"               optQuiet = True
+            Case "--quiet"          optQuiet = True
+            Case Else
+                optPrefix = arg(idx)
+        End Select
+    Next
+
+    If arg.Count = 0 Then
+        If optQuiet <> True Then
+            ShowHelp 1
+        End If
+    End If
+
+    If optPrefix = "" Then
+        If optQuiet <> True Then
+            WScript.Echo "pyenv-latest: missing <prefix> argument"
+        End If
+
+        WScript.Quit 1
+    End If
+
+    Dim latest
+    latest = FindLatestVersion(optPrefix, optKnown)
+
+    If latest <> "" Then
+        WScript.Echo latest
+    Else
+        If optQuiet <> True Then
+            if optKnown Then
+                WScript.Echo "pyenv-latest: no known versions match the prefix '" & optPrefix & "'."
+            else
+                WScript.Echo "pyenv-latest: no installed versions match the prefix '" & optPrefix & "'."
+            end if
+        End If
+
+        WScript.Quit 1
+    End If
+End Sub
+
+main(WScript.Arguments)
diff --git a/tests/conftest.py b/tests/conftest.py
index 59f13f94..7134a84d 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -46,6 +46,10 @@ def bin_path(pyenv_path):
 def shims_path(pyenv_path):
     return pyenv_path / 'shims'
 
+@pytest.fixture()
+def current_arch():
+    return os.environ['PYENV_FORCE_ARCH']
+
 
 @pytest.fixture()
 def pyenv_file(shell, bin_path, shell_ext):
diff --git a/tests/test_pyenv_feature_global.py b/tests/test_pyenv_feature_global.py
index 9e1d9c37..2502e628 100644
--- a/tests/test_pyenv_feature_global.py
+++ b/tests/test_pyenv_feature_global.py
@@ -32,7 +32,13 @@ def test_global_version_defined(pyenv):
         'global_ver': Native("3.8.9")
     }])
 def test_global_set_installed_version(pyenv):
+    assert pyenv("global", Arch("3.7")) == ("", "")
+    assert pyenv("global") == (Arch("3.7"), "")
+
     assert pyenv("global", Arch("3.7.7")) == ("", "")
+    assert pyenv("global") == (Arch("3.7.7"), "")
+
+    assert pyenv("global", Native("3.7.7")) == ("", "")
     assert pyenv("global") == (Native("3.7.7"), "")
 
 
@@ -41,7 +47,9 @@ def test_global_set_installed_version(pyenv):
         'global_ver': Native("3.8.9"),
     }])
 def test_global_set_unknown_version(pyenv):
-    assert pyenv("global", Arch("3.7.8")) == (not_installed_output(Native("3.7.8")), "")
+    assert pyenv("global", Arch("3.7")) == (not_installed_output(Arch("3.7")), "")
+    assert pyenv("global", Arch("3.7.8")) == (not_installed_output(Arch("3.7.8")), "")
+    assert pyenv("global", Native("3.7.8")) == (not_installed_output(Native("3.7.8")), "")
 
 
 @pytest.mark.parametrize('settings', [lambda: {
@@ -56,12 +64,15 @@ def test_global_unset(pyenv):
 @pytest.mark.parametrize('settings', [lambda: {'versions': [Native("3.7.7"), Native("3.8.9")]}])
 def test_global_set_many_versions(pyenv_path, pyenv):
     assert pyenv('global', Arch("3.7.7"), Arch("3.8.9")) == ("", "")
-    assert global_python_versions(pyenv_path) == "\n".join([Native('3.7.7'), Native('3.8.9')])
+    assert global_python_versions(pyenv_path) == "\n".join([Arch('3.7.7'), Arch('3.8.9')])
+
+    assert pyenv('global', Arch("3.7.7"), Arch("3.8.9")) == ("", "")
+    assert global_python_versions(pyenv_path) == "\n".join([Arch('3.7.7'), Arch('3.8.9')])
 
 
 @pytest.mark.parametrize('settings', [lambda: {'versions': [Native("3.7.7")]}])
 def test_global_set_many_versions_one_not_installed(pyenv):
-    assert pyenv('global', Arch("3.7.7"), Arch("3.8.9")) == (not_installed_output(Native("3.8.9")), "")
+    assert pyenv('global', Arch("3.7.7"), Arch("3.8.9")) == (not_installed_output(Arch("3.8.9")), "")
 
 
 @pytest.mark.parametrize('settings', [lambda: {'global_ver': [Native('3.7.7'), Native('3.8.9')]}])
diff --git a/tests/test_pyenv_feature_latest.py b/tests/test_pyenv_feature_latest.py
new file mode 100644
index 00000000..c694b6b2
--- /dev/null
+++ b/tests/test_pyenv_feature_latest.py
@@ -0,0 +1,54 @@
+import pytest
+from test_pyenv_helpers import Native, X86, Arch
+
+
+def test_latest_help(pyenv):
+    assert pyenv("latest")
+
+    result, stderr = pyenv("latest")
+
+    assert "Usage:" in result
+    assert "--known" in result
+    assert "--quiet" in result
+
+
+@pytest.mark.parametrize('settings', [lambda: {
+        'versions': [
+            Native("3.1.4"), Native("3.11.0"),
+            Native("3.2.0"), Native("3.2.5"),
+            Native("3.9.1")
+        ]
+    }])
+def test_latest_edge_cases(pyenv):
+    assert pyenv.latest("1") == ("pyenv-latest: no installed versions match the prefix '1'.", "")
+    assert pyenv.latest("-k", "1") == ("pyenv-latest: no known versions match the prefix '1'.", "")
+    assert pyenv.latest("-k", "3.2.16") == ("pyenv-latest: no known versions match the prefix '3.2.16'.", "")
+    assert pyenv.latest("3.1") == (Native("3.1.4"), "")
+    assert pyenv.latest("3.2") == (Native("3.2.5"), "")
+    assert pyenv.latest("3.2.5") == (Native("3.2.5"), "")
+
+
+@pytest.mark.parametrize('settings', [lambda: {
+        'versions': [X86("3.1.0"), Arch("3.1.4")]
+    }])
+def test_latest_arch_cases(pyenv, current_arch):
+    if current_arch == 'X86':
+        assert pyenv.latest("3.1") == (X86("3.1.0"), "")
+    else:
+        assert pyenv.latest("3.1") == (Native("3.1.4"), "")
+
+
+def test_latest_quiet(pyenv):
+    assert pyenv.latest("-q") == ("", "")
+    assert pyenv.latest("-q", "-k") == ("", "")
+    assert pyenv.latest("-k", "-q") == ("", "")
+    assert pyenv.latest("-q", "-k", "1.") == ("", "")
+    assert pyenv.latest("-q", "-k", "www") == ("", "")
+
+
+@pytest.mark.parametrize('settings', [lambda: {
+        'versions': [Native("3.9.4"), Native("3.7.2"), Native("3.7.7"), Native("3.9.1")]
+    }])
+def test_latest_sort(pyenv):
+    assert pyenv.latest('3') == (Native('3.9.4'), '')
+    assert pyenv.latest('3.7') == (Native('3.7.7'), '')
diff --git a/tests/test_pyenv_feature_local.py b/tests/test_pyenv_feature_local.py
index 6ca8fe61..97c159c7 100644
--- a/tests/test_pyenv_feature_local.py
+++ b/tests/test_pyenv_feature_local.py
@@ -18,23 +18,37 @@ def test_local_version_defined(pyenv):
 }])
 def test_local_set_installed_version(pyenv):
     assert pyenv.local(Arch("3.7.7")) == ("", "")
-    assert pyenv.local() == (Native("3.7.7"), "")
+    assert pyenv.local() == (Arch("3.7.7"), "")
+
+
+@pytest.mark.parametrize('settings', [lambda: {
+    'versions': [Native("3.7.7"), Native("3.8.9")],
+    'local_ver': Native("3.8.9"),
+}])
+def test_local_set_minor_version(pyenv):
+    assert pyenv.local(Arch("3.7")) == ("", "")
+    assert pyenv.local() == (Arch("3.7"), "")
+    assert pyenv.vname() == (Native("3.7.7"), "")
+
+    assert pyenv.local(Arch("3")) == ("", "")
+    assert pyenv.local() == (Arch("3"), "")
+    assert pyenv.vname() == (Native("3.8.9"), "")
 
 
 @pytest.mark.parametrize('settings', [lambda: {'versions': [Native("3.8.9")]}])
 def test_local_set_unknown_version(pyenv):
-    assert pyenv.local(Arch("3.7.8")) == (not_installed_output(Native("3.7.8")), "")
+    assert pyenv.local(Arch("3.7.8")) == (not_installed_output(Arch("3.7.8")), "")
 
 
 @pytest.mark.parametrize('settings', [lambda: {'versions': [Native("3.7.7"), Native("3.8.9")]}])
 def test_local_set_many_versions(local_path, pyenv):
-    assert pyenv.local(Arch("3.7.7"), Arch("3.8.9")) == ("", "")
+    assert pyenv.local(Native("3.7.7"), Native("3.8.9")) == ("", "")
     assert local_python_versions(local_path) == "\n".join([Native('3.7.7'), Native('3.8.9')])
 
 
 @pytest.mark.parametrize('settings', [lambda: {'versions': [Native("3.7.7")]}])
 def test_local_set_many_versions_one_not_installed(pyenv):
-    assert pyenv.local(Arch("3.7.7"), Arch("3.8.9")) == (not_installed_output(Native("3.8.9")), "")
+    assert pyenv.local(Arch("3.7.7"), Arch("3.8.9")) == (not_installed_output(Arch("3.8.9")), "")
 
 
 @pytest.mark.parametrize('settings', [lambda: {'local_ver': [Native('3.7.7'), Native('3.8.9')]}])