From f007e0beb6ec2d4793715bb1b608307f1c513d77 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 21:34:14 -0700
Subject: [PATCH 01/21] add ux improvements for deps and other minor changes

---
 src/nimble.nim             |  51 ++++++++++------
 src/nimblepkg/deps.nim     |   6 +-
 src/nimblepkg/download.nim | 117 +++++++++++++++++++++----------------
 src/nimblepkg/options.nim  |  29 ++++++---
 src/nimblepkg/publish.nim  |  82 +++++++++++++++++++++++++-
 src/nimblepkg/vcstools.nim |  16 +++++
 tests/testscommon.nim      |   3 +-
 tests/tissues.nim          |   2 +-
 8 files changed, 227 insertions(+), 79 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 639ccf778..64b199139 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -57,7 +57,7 @@ proc displaySatisfiedMsg(solvedPkgs: seq[SolvedPackage], pkgToInstall: seq[(stri
     for pkg in solvedPkgs:
       if pkg.pkgName notin pkgToInstall.mapIt(it[0]):
         for req in pkg.requirements:
-          displayInfo(pkgDepsAlreadySatisfiedMsg(req))
+          displayInfo(pkgDepsAlreadySatisfiedMsg(req), MediumPriority)
 
 proc displayUsingSpecialVersionWarning(solvedPkgs: seq[SolvedPackage], options: Options) =
   var messages = newSeq[string]()
@@ -182,7 +182,7 @@ proc processFreeDependencies(pkgInfo: PackageInfo,
 
   display("Verifying", "dependencies for $1@$2" %
           [pkgInfo.basicInfo.name, $pkgInfo.basicInfo.version],
-          priority = HighPriority)
+          priority = LowPriority)
 
   var reverseDependencies: seq[PackageBasicInfo] = @[]
 
@@ -208,7 +208,7 @@ proc processFreeDependencies(pkgInfo: PackageInfo,
                        resolvedDep.name)
 
     if not found:
-      display("Installing", $resolvedDep, priority = HighPriority)
+      display("Installing", $resolvedDep, priority = MediumPriority)
       let toInstall = @[(resolvedDep.name, resolvedDep.ver)]
       let (packages, installedPkg) = install(toInstall, options,
         doPrompt = false, first = false, fromLockFile = false,
@@ -230,7 +230,7 @@ proc processFreeDependencies(pkgInfo: PackageInfo,
       # This package has been installed so we add it to our pkgList.
       pkgList.add pkg
     else:
-      displayInfo(pkgDepsAlreadySatisfiedMsg(dep))
+      displayInfo(pkgDepsAlreadySatisfiedMsg(dep), MediumPriority)
       result.incl pkg
       # Process the dependencies of this dependency.
       let fullInfo = pkg.toFullInfo(options)
@@ -512,13 +512,13 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
 
   display("Installing", "$1@$2" %
     [pkginfo.basicInfo.name, $pkginfo.basicInfo.version],
-    priority = HighPriority)
+    priority = MediumPriority)
 
   let oldPkg = pkgInfo.packageExists(options)
   if oldPkg.isSome:
     # In the case we already have the same package in the cache then only merge
     # the new package special versions to the old one.
-    displayWarning(pkgAlreadyExistsInTheCacheMsg(pkgInfo))
+    displayWarning(pkgAlreadyExistsInTheCacheMsg(pkgInfo), MediumPriority)
     if not options.useSatSolver: #The dep path is not created when using the sat solver as packages are collected upfront
       var oldPkg = oldPkg.get
       oldPkg.metaData.specialVersions.incl pkgInfo.metaData.specialVersions
@@ -615,7 +615,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
 
   pkgInfo.isInstalled = true
 
-  displaySuccess(pkgInstalledMsg(pkgInfo.basicInfo.name))
+  displaySuccess(pkgInstalledMsg(pkgInfo.basicInfo.name), MediumPriority)
 
   result.deps.incl pkgInfo
   result.pkg = pkgInfo
@@ -1925,9 +1925,25 @@ proc lock(options: Options) =
   updateSyncFile(pkgInfo, options)
   displayLockOperationFinish(lockExists)
 
-proc depsTree(options: Options) =
+proc depsTree(options: Options,
+              pkgInfo: PackageInfo,
+              dependencies: seq[PackageInfo],
+              errors: ValidationErrors) =
   ## Prints the dependency tree
 
+  if options.action.format == "json":
+    if options.action.depsAction == "inverted":
+      raise nimbleError("Deps JSON format does not support inverted tree")
+    echo (%depsRecursive(pkgInfo, dependencies, errors, options)).pretty
+  elif options.action.depsAction == "inverted":
+    printDepsHumanReadableInverted(pkgInfo, dependencies, errors, options)
+  elif options.action.depsAction == "tree":
+    printDepsHumanReadable(pkgInfo, dependencies, errors, options)
+  else:
+    printDepsHumanReadable(pkgInfo, dependencies, errors, options, true)
+
+proc deps(options: Options) =
+  ## handles deps actions
   let pkgInfo = getPkgInfo(getCurrentDir(), options)
 
   var errors = validateDevModeDepsWorkingCopiesBeforeLock(pkgInfo, options)
@@ -1942,12 +1958,10 @@ proc depsTree(options: Options) =
     if not dependencyGraph.contains name:
       errors.del name
 
-  if options.action.format == "json":
-    echo (%depsRecursive(pkgInfo, dependencies, errors)).pretty
-  elif options.action.format == "inverted":
-    printDepsHumanReadableInverted(pkgInfo, dependencies, errors)
+  if options.action.depsAction in ["", "tree", "inverted"]:
+    depsTree(options, pkgInfo, dependencies, errors)
   else:
-    printDepsHumanReadable(pkgInfo, dependencies, errors)
+    raise nimbleError("Unknown deps flag: " & options.action.depsAction)
 
 proc syncWorkingCopy(name: string, path: Path, dependentPkg: PackageInfo,
                      options: Options) =
@@ -2331,7 +2345,10 @@ proc doAction(options: var Options) =
     init(options)
   of actionPublish:
     var pkgInfo = getPkgInfo(getCurrentDir(), options)
-    publish(pkgInfo, options)
+    if options.action.publishAction == "tags":
+      publishTags(pkgInfo, options)
+    else:
+      publish(pkgInfo, options)
   of actionDump:
     dump(options)
   of actionTasks:
@@ -2343,7 +2360,7 @@ proc doAction(options: var Options) =
   of actionLock:
     lock(options)
   of actionDeps:
-    depsTree(options)
+    deps(options)
   of actionSync:
     sync(options)
   of actionSetup:
@@ -2493,8 +2510,8 @@ when isMainModule:
   var opt: Options
   try:
     opt = parseCmdLine()
-    opt.setNimbleDir
-    opt.loadNimbleData
+    opt.setNimbleDir()
+    opt.loadNimbleData()
     if opt.action.typ in {actionTasks, actionRun, actionBuild, actionCompile, actionDevelop}:
       # Implicitly disable package validation for these commands.
       opt.disableValidation = true
diff --git a/src/nimblepkg/deps.nim b/src/nimblepkg/deps.nim
index 99819aca1..cb970800c 100644
--- a/src/nimblepkg/deps.nim
+++ b/src/nimblepkg/deps.nim
@@ -35,7 +35,8 @@ proc depsRecursive*(pkgInfo: PackageInfo,
 proc printDepsHumanReadable*(pkgInfo: PackageInfo,
                              dependencies: seq[PackageInfo],
                              errors: ValidationErrors,
-                             levelInfos: seq[tuple[skip: bool]] = @[]
+                             directOnly = false,
+                             levelInfos: seq[tuple[skip: bool]] = @[],
                              ) =
   ## print human readable tree deps
   ## 
@@ -82,7 +83,8 @@ proc printDepsHumanReadable*(pkgInfo: PackageInfo,
       displayFormatted(Error, fmt" - error: {errMsg}")
     if found:
       var levelInfos = levelInfos & @[(skip: isLast)]
-      printDepsHumanReadable(depPkgInfo, dependencies, errors, levelInfos)
+      if not directOnly:
+        printDepsHumanReadable(depPkgInfo, dependencies, errors, directOnly, levelInfos)
   if levelInfos.len() == 0:
     displayFormatted(Hint, "\n")
 
diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim
index 1e4106e85..9a93e4bbc 100644
--- a/src/nimblepkg/download.nim
+++ b/src/nimblepkg/download.nim
@@ -445,6 +445,31 @@ proc doDownload(url, downloadDir: string, verRange: VersionRange,
     result.vcsRevision = downloadDir.getVcsRevision
 {.warning[ProveInit]: on.}
 
+proc pkgDirHasNimble*(dir: string, options: Options): bool =
+  try:
+    discard findNimbleFile(dir, true, options)
+    return true
+  except NimbleError: 
+    #Continue with the download
+    discard
+
+proc downloadPkgDir*(url: string,
+                     verRange: VersionRange,
+                     subdir: string,
+                     options: Options,
+                     vcsRevision: Sha1Hash = notSetSha1Hash,
+                     downloadPath: string = ""
+): (string, string) =
+  let downloadDir =
+    if downloadPath == "":
+      (getNimbleTempDir() / getDownloadDirName(url, verRange, vcsRevision))
+    else:
+      downloadPath
+
+  createDir(downloadDir)
+
+  result = (downloadDir, downloadDir / subdir)
+
 proc downloadPkg*(url: string, verRange: VersionRange,
                   downMethod: DownloadMethod,
                   subdir: string,
@@ -464,38 +489,21 @@ proc downloadPkg*(url: string, verRange: VersionRange,
   ##   If specified this parameter will cause specific VCS revision to be
   ##   checked out.
 
+  let (downloadDir, pkgDir) = downloadPkgDir(url, verRange, subdir, options, vcsRevision, downloadPath)
+  result.dir = pkgDir
+
+  #when using a persistent download dir we can skip the download if it's already done
+  if pkgDirHasNimble(result.dir, options):
+    return # already downloaded, skipping
+
   if options.offline:
     raise nimbleError("Cannot download in offline mode.")
-  let downloadDir =
-    if downloadPath == "":
-      (getNimbleTempDir() / getDownloadDirName(url, verRange, vcsRevision))
-    else:
-      downloadPath
-    
-  createDir(downloadDir)
-  var modUrl =
-    if url.startsWith("git://") and options.config.cloneUsingHttps:
-      "https://" & url[6 .. ^1]
-    else: url
 
-  # Fixes issue #204
-  # github + https + trailing url slash causes a
-  # checkout/ls-remote to fail with Repository not found
-  if modUrl.contains("github.com") and modUrl.endswith("/"):
-    modUrl = modUrl[0 .. ^2]
+  let modUrl = modifyUrl(url, options.config.cloneUsingHttps)
 
   let downloadMethod = if downloadTarball(modUrl, options):
     "http" else: $downMethod
 
-  result.dir = downloadDir / subdir
-  #when using a persistent download dir we can skip the download if it's already done
-  try:
-    discard findNimbleFile(result.dir, true, options)
-    return
-  except NimbleError: 
-    #Continue with the download
-    discard    
-
   if subdir.len > 0:
     display("Downloading", "$1 using $2 (subdir is '$3')" %
                            [modUrl, downloadMethod, subdir],
@@ -584,35 +592,46 @@ proc refresh*(options: Options) =
     for name, list in options.config.packageLists:
       fetchList(list, options)
 
-proc getDownloadInfo*(pv: PkgTuple, options: Options,
-                      doPrompt: bool, ignorePackageCache = false): (DownloadMethod, string,
-                                        Table[string, string]) =
-  if pv.name.isURL:
-    let (url, metadata) = getUrlData(pv.name)
-    return (checkUrlType(url), url, metadata)
+proc getDownloadInfo*(
+    pv: PkgTuple, options: Options,
+    doPrompt: bool,
+    ignorePackageCache = false,
+): (DownloadMethod, string, Table[string, string]) =
+
+  # echo "getDownloadInfo:pv.name: ", $pv.name
+  var pkg = initPackage()
+  if getPackage(pv.name, options, pkg, ignorePackageCache):
+    let (url, metadata) = getUrlData(pkg.url)
+    result = (pkg.downloadMethod, url, metadata)
+    # echo "getDownloadInfo:getPackage: ", $result
+    return
+  elif pv.name.isURL:
+    # echo "getDownloadInfo:isURL:name: ", $pv.name
+    # echo "getDownloadInfo:isURL:options.nimbleData: ", $options.nimbleData
+    let (url, urlmeta) = getUrlData(pv.name)
+    var metadata = urlmeta
+    metadata["urlOnly"] = "true"
+    result = (checkUrlType(url), url, metadata)
+    # echo "getDownloadInfo:isURL: ", $result
+    return
   elif pv.name.isForgeAlias:
     let url = newForge(pv.name).expand()
     return (checkUrlType(url), url, default(Table[string, string]))
   else:
-    var pkg = initPackage()
-    if getPackage(pv.name, options, pkg, ignorePackageCache):
-      let (url, metadata) = getUrlData(pkg.url)
-      return (pkg.downloadMethod, url, metadata)
+    # If package is not found give the user a chance to refresh
+    # package.json
+    if doPrompt and not options.offline and
+        options.prompt(pv.name & " not found in any local packages.json, " &
+                        "check internet for updated packages?"):
+      refresh(options)
+
+      # Once we've refreshed, try again, but don't prompt if not found
+      # (as we've already refreshed and a failure means it really
+      # isn't there)
+      # Also ignore the package cache so the old info isn't used
+      return getDownloadInfo(pv, options, false, true)
     else:
-      # If package is not found give the user a chance to refresh
-      # package.json
-      if doPrompt and not options.offline and
-          options.prompt(pv.name & " not found in any local packages.json, " &
-                         "check internet for updated packages?"):
-        refresh(options)
-
-        # Once we've refreshed, try again, but don't prompt if not found
-        # (as we've already refreshed and a failure means it really
-        # isn't there)
-        # Also ignore the package cache so the old info isn't used
-        return getDownloadInfo(pv, options, false, true)
-      else:
-        raise nimbleError(pkgNotFoundMsg(pv))
+      raise nimbleError(pkgNotFoundMsg(pv))
 
 when isMainModule:
   import unittest
diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index 8f913b0e4..d9ff28cf8 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -79,7 +79,7 @@ type
 
   Action* = object
     case typ*: ActionType
-    of actionNil, actionList, actionPublish, actionTasks, actionCheck,
+    of actionNil, actionList, actionTasks, actionCheck,
        actionSetup, actionClean, actionManual: nil
     of actionSync:
       listOnly*: bool
@@ -115,6 +115,9 @@ type
       custRunFlags*: seq[string]
     of actionDeps:
       format*: string
+      depsAction*: string
+    of actionPublish:
+      publishAction*: string
     of actionShellEnv, actionShell:
       discard
 
@@ -239,9 +242,10 @@ Nimble Options:
       --ver                       Query remote server for package version
                                   information when searching or listing packages.
       --nimbleDir:dirname         Set the Nimble directory.
-      --nim:path                  Use specified path for Nim compiler
-      --silent                    Hide all Nimble and Nim output
-      --verbose                   Show all non-debug output.
+      --nim:path                  Use specified path for Nim compiler.
+      --silent                    Hide all Nimble and Nim output.
+      --info                      Show some informative output.
+      --verbose                   Show extra non-debugging output.
       --debug                     Show all output including debug messages.
       --offline                   Don't use network.
       --noColor                   Don't colorise output.
@@ -251,11 +255,11 @@ Nimble Options:
       --developFile               Specifies the name of the develop file which
                                   to be manipulated. If not present creates it.
       --useSystemNim              Use system nim and ignore nim from the lock
-                                  file if any
+                                  file if any.
       --solver:sat|legacy         Use the SAT solver (default) or the legacy for dependency resolution.
-      --requires                  Add extra packages to the dependency resolution. Uses the same syntax as the Nimble file. Example: nimble install --requires "pkg1; pkg2 >= 1.2"
+      --requires                  Add extra packages to the dependency resolution. Uses the same syntax as the Nimble file. Example: nimble install --requires "pkg1; pkg2 >= 1.2".
       --disableNimBinaries        Disable the use of nim precompiled binaries. Note in some platforms precompiled binaries are not available but the flag can still be used to avoid compile the Nim version once and reuse it.
-      --maximumTaggedVersions     Maximum number of tags to check for a package when discovering versions for the SAT solver. 0 means all. 
+      --maximumTaggedVersions     Maximum number of tags to check for a package when discovering versions for the SAT solver. 0 means all.
 For more information read the GitHub readme:
   https://github.com/nim-lang/nimble#readme
 """
@@ -614,6 +618,7 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
   of "reject", "n": result.forcePrompts = forcePromptNo
   of "nimbledir": result.nimbleDir = val
   of "silent": result.verbosity = SilentPriority
+  of "info": result.verbosity = MediumPriority
   of "verbose": result.verbosity = LowPriority
   of "debug": result.verbosity = DebugPriority
   of "offline": result.offline = true
@@ -736,10 +741,20 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
       result.action.listOnly = true
     else:
       wasFlagHandled = false
+  of actionPublish:
+    case f
+    of "tags":
+      result.action.publishAction = "tags"
+    else:
+      wasFlagHandled = false
   of actionDeps:
     case f
     of "format":
       result.action.format = val
+    of "tree":
+      result.action.depsAction = "tree"
+    of "inverted":
+      result.action.depsAction = "inverted"
     else:
       wasFlagHandled = false
   else:
diff --git a/src/nimblepkg/publish.nim b/src/nimblepkg/publish.nim
index 8e4c75875..f79d99687 100644
--- a/src/nimblepkg/publish.nim
+++ b/src/nimblepkg/publish.nim
@@ -6,8 +6,8 @@
 
 import system except TResult
 import httpclient, strutils, json, os, browsers, times, uri
-import common, tools, cli, config, options, packageinfotypes
-import strformat
+import common, tools, cli, config, options, packageinfotypes, sha1hashes, version, download
+import strformat, sequtils, pegs, sets
 {.warning[UnusedImport]: off.}
 from net import SslCVerifyMode, newContext
 
@@ -247,3 +247,81 @@ proc publish*(p: PackageInfo, o: Options) =
     doCmd("git push https://" & auth.token & "@github.com/" & auth.user & "/packages " & branchName)
     let prUrl = createPullRequest(auth, p, url, branchName)
     display("Success:", "Pull request successful, check at " & prUrl , Success, HighPriority)
+
+proc vcsFindCommits*(repoDir, nimbleFile: string, downloadMethod: DownloadMethod): seq[(Sha1Hash, string)] =
+  var output: string
+  case downloadMethod:
+    of DownloadMethod.git:
+      output = tryDoCmdEx(&"git -C {repoDir} log --format=\"%H %s\" -- $2")
+    of DownloadMethod.hg:
+      assert false, "hg not supported"
+  
+  for line in output.splitLines():
+    let line = line.strip()
+    if line != "":
+      result.add((line[0..39].initSha1Hash(), line[40..^1]))
+
+proc vcsDiff*(commit: Sha1Hash, repoDir, nimbleFile: string, downloadMethod: DownloadMethod): seq[string] =
+  case downloadMethod:
+    of DownloadMethod.git:
+      let (output, exitCode) = doCmdEx(&"git -C {repoDir} diff {commit}~ {commit} {nimbleFile}")
+      if exitCode != QuitSuccess:
+        return @[]
+      else:
+        return output.splitLines()
+    of DownloadMethod.hg:
+      assert false, "hg not supported"
+  
+proc createTag*(tag: string, commit: Sha1Hash, message, repoDir, nimbleFile: string, downloadMethod: DownloadMethod): bool =
+  case downloadMethod:
+    of DownloadMethod.git:
+      let (output, code) = doCmdEx(&"git -C {repoDir} tag -a {tag.quoteShell()} {commit} -m {message.quoteShell()}")
+      result = code == QuitSuccess
+      if not result:
+        displayError(&"Failed to create tag {tag.quoteShell()} with error {output}")
+    of DownloadMethod.hg:
+      assert false, "hg not supported"
+  
+proc findVersions(commits: seq[(Sha1Hash, string)], projdir, nimbleFile: string, downloadMethod: DownloadMethod) =
+  ## parse the versions
+  var
+    versions: HashSet[Version]
+    existingTags: HashSet[Version]
+  for tag in getTagsList(projdir, downloadMethod):
+    let tag = tag.strip(leading=true, chars={'v'})
+    try:
+      existingTags.incl(newVersion(tag))
+    except ParseVersionError:
+      discard
+
+  # adapted from @beef331's algorithm https://github.com/beef331/graffiti/blob/master/src/graffiti.nim
+  for (commit, message) in commits:
+    # echo "commit: ", commit
+    let diffs = vcsDiff(commit, projdir, nimbleFile, downloadMethod)
+    for line in diffs:
+      var matches: array[0..MaxSubpatterns, string]
+      if line.find(peg"'+version' \s* '=' \s* {[\34\39]} {@} $1", matches) > -1:
+        let version = newVersion(matches[1])
+        if version notin versions: 
+          versions.incl(version)
+          if version in existingTags:
+            displayInfo(&"Found existing tag for version {version}", MediumPriority)
+          else:
+            displayInfo(&"Found new version {version} at {commit}", MediumPriority)
+            let res = createTag(&"v{version}", commit, message, projdir, nimbleFile, downloadMethod)
+            if not res:
+              displayError(&"Unable to create tag {version}")
+
+proc publishTags*(p: PackageInfo, o: Options) =
+  discard
+  echo "publishTags:myPath: ", $p.myPath
+  echo "publishTags:basic: ", $p.basicInfo
+  # echo "publishTags: ", $p
+  let (projdir, file, ext) = p.myPath.splitFile()
+  let nimblefile = file & ext
+  let dlmethod = p.metadata.downloadMethod
+  let commits = vcsFindCommits(projdir, nimbleFile, dlmethod)
+  echo "publishTags:commits: ", $commits.len()
+
+  findVersions(commits, projdir, nimbleFile, dlmethod)
+  echo ""
diff --git a/src/nimblepkg/vcstools.nim b/src/nimblepkg/vcstools.nim
index 3defd1f2f..e2b921ed1 100644
--- a/src/nimblepkg/vcstools.nim
+++ b/src/nimblepkg/vcstools.nim
@@ -200,6 +200,22 @@ proc getVcsRevision*(dir: Path): Sha1Hash =
 
   return initSha1Hash(vcsRevision.strip(chars = Whitespace + {'+'}))
 
+proc getVcsRevisions*(dir: Path): Sha1Hash =
+  ## Returns current revision number if the directory `dir` is under version
+  ## control, or an invalid Sha1 checksum otherwise.
+  ##
+  ## Raises a `NimbleError` if:
+  ##   - the external command fails.
+  ##   - the directory does not exist.
+  ##   - there is no vcsRevisions in the repository.
+
+  let vcsRevision = tryDoVcsCmd(dir,
+    gitCmd = "rev-parse HEAD",
+    hgCmd  = "id -i --debug",
+    noVcsAction = $notSetSha1Hash)
+
+  return initSha1Hash(vcsRevision.strip(chars = Whitespace + {'+'}))
+
 proc getPackageFileListWithoutVcs(dir: Path): seq[string] =
   ## Recursively walks the directory `dir` and returns a list of files in it and
   ## its subdirectories.
diff --git a/tests/testscommon.nim b/tests/testscommon.nim
index b8e3c52c4..09ea1d2dc 100644
--- a/tests/testscommon.nim
+++ b/tests/testscommon.nim
@@ -29,9 +29,10 @@ let
 
 proc execNimble*(args: varargs[string]): ProcessOutput =
   var quotedArgs = @args
+  quotedArgs.insert("--info")
+  quotedArgs.insert("--noColor")
   if not args.anyIt("--nimbleDir:" in it or "-l"  == it or "--local" == it):
     quotedArgs.insert("--nimbleDir:" & installDir)
-  quotedArgs.insert("--noColor")
   quotedArgs.insert(nimblePath)
   quotedArgs = quotedArgs.map((x: string) => x.quoteShell)
 
diff --git a/tests/tissues.nim b/tests/tissues.nim
index 6514a730e..cd4457130 100644
--- a/tests/tissues.nim
+++ b/tests/tissues.nim
@@ -40,7 +40,7 @@ suite "issues":
         if line.contains("issue799"):
           let nimbleInstallDir = getPackageDir(
             pkgsDir, &"nimble-{nimbleVersion}")
-          let pkgInstalledPath = "--path:'" & nimble_install_dir & "'"
+          let pkgInstalledPath = "--path:" & nimbleInstallDir.quoteShell & ""
           check line.contains(pkgInstalledPath)
 
   test "issue 793":

From 08d786936c1b12cce100e22caf1e6de7751f6065 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 21:37:49 -0700
Subject: [PATCH 02/21] cleanup

---
 src/nimble.nim         |  8 ++++----
 src/nimblepkg/urls.nim | 14 +++++++++++++-
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 64b199139..5073a5b6d 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1934,13 +1934,13 @@ proc depsTree(options: Options,
   if options.action.format == "json":
     if options.action.depsAction == "inverted":
       raise nimbleError("Deps JSON format does not support inverted tree")
-    echo (%depsRecursive(pkgInfo, dependencies, errors, options)).pretty
+    echo (%depsRecursive(pkgInfo, dependencies, errors)).pretty
   elif options.action.depsAction == "inverted":
-    printDepsHumanReadableInverted(pkgInfo, dependencies, errors, options)
+    printDepsHumanReadableInverted(pkgInfo, dependencies, errors)
   elif options.action.depsAction == "tree":
-    printDepsHumanReadable(pkgInfo, dependencies, errors, options)
+    printDepsHumanReadable(pkgInfo, dependencies, errors)
   else:
-    printDepsHumanReadable(pkgInfo, dependencies, errors, options, true)
+    printDepsHumanReadable(pkgInfo, dependencies, errors, true)
 
 proc deps(options: Options) =
   ## handles deps actions
diff --git a/src/nimblepkg/urls.nim b/src/nimblepkg/urls.nim
index 4fca305c3..c56d72b75 100644
--- a/src/nimblepkg/urls.nim
+++ b/src/nimblepkg/urls.nim
@@ -1,4 +1,16 @@
-import std/pegs
+import std/pegs, std/strutils
 
 proc isURL*(name: string): bool =
   name.startsWith(peg" @'://' ") or name.startsWith(peg"\ident+'@'@':'.+")
+
+proc modifyUrl*(url: string, usingHttps: bool): string =
+  result =
+    if url.startsWith("git://") and usingHttps:
+      "https://" & url[6 .. ^1]
+    else:
+      url
+  # Fixes issue #204
+  # github + https + trailing url slash causes a
+  # checkout/ls-remote to fail with Repository not found
+  if result.contains("github.com") and result.endswith("/"):
+    result = result[0 .. ^2]

From 152850dbfbc446aa4be78642a9516d7a075f8441 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:00:43 -0700
Subject: [PATCH 03/21] cleanup

---
 src/nimble.nim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 5073a5b6d..ef536ab8a 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1925,7 +1925,7 @@ proc lock(options: Options) =
   updateSyncFile(pkgInfo, options)
   displayLockOperationFinish(lockExists)
 
-proc depsTree(options: Options,
+proc depsPrint(options: Options,
               pkgInfo: PackageInfo,
               dependencies: seq[PackageInfo],
               errors: ValidationErrors) =
@@ -1959,7 +1959,7 @@ proc deps(options: Options) =
       errors.del name
 
   if options.action.depsAction in ["", "tree", "inverted"]:
-    depsTree(options, pkgInfo, dependencies, errors)
+    depsPrint(options, pkgInfo, dependencies, errors)
   else:
     raise nimbleError("Unknown deps flag: " & options.action.depsAction)
 

From 742761c5bdbe95c1aba137dbabc9c66c1a245e44 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:09:49 -0700
Subject: [PATCH 04/21] revert getDownloadInfo

---
 src/nimblepkg/download.nim | 117 ++++++++++++++++---------------------
 1 file changed, 49 insertions(+), 68 deletions(-)

diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim
index 9a93e4bbc..1e4106e85 100644
--- a/src/nimblepkg/download.nim
+++ b/src/nimblepkg/download.nim
@@ -445,31 +445,6 @@ proc doDownload(url, downloadDir: string, verRange: VersionRange,
     result.vcsRevision = downloadDir.getVcsRevision
 {.warning[ProveInit]: on.}
 
-proc pkgDirHasNimble*(dir: string, options: Options): bool =
-  try:
-    discard findNimbleFile(dir, true, options)
-    return true
-  except NimbleError: 
-    #Continue with the download
-    discard
-
-proc downloadPkgDir*(url: string,
-                     verRange: VersionRange,
-                     subdir: string,
-                     options: Options,
-                     vcsRevision: Sha1Hash = notSetSha1Hash,
-                     downloadPath: string = ""
-): (string, string) =
-  let downloadDir =
-    if downloadPath == "":
-      (getNimbleTempDir() / getDownloadDirName(url, verRange, vcsRevision))
-    else:
-      downloadPath
-
-  createDir(downloadDir)
-
-  result = (downloadDir, downloadDir / subdir)
-
 proc downloadPkg*(url: string, verRange: VersionRange,
                   downMethod: DownloadMethod,
                   subdir: string,
@@ -489,21 +464,38 @@ proc downloadPkg*(url: string, verRange: VersionRange,
   ##   If specified this parameter will cause specific VCS revision to be
   ##   checked out.
 
-  let (downloadDir, pkgDir) = downloadPkgDir(url, verRange, subdir, options, vcsRevision, downloadPath)
-  result.dir = pkgDir
-
-  #when using a persistent download dir we can skip the download if it's already done
-  if pkgDirHasNimble(result.dir, options):
-    return # already downloaded, skipping
-
   if options.offline:
     raise nimbleError("Cannot download in offline mode.")
+  let downloadDir =
+    if downloadPath == "":
+      (getNimbleTempDir() / getDownloadDirName(url, verRange, vcsRevision))
+    else:
+      downloadPath
+    
+  createDir(downloadDir)
+  var modUrl =
+    if url.startsWith("git://") and options.config.cloneUsingHttps:
+      "https://" & url[6 .. ^1]
+    else: url
 
-  let modUrl = modifyUrl(url, options.config.cloneUsingHttps)
+  # Fixes issue #204
+  # github + https + trailing url slash causes a
+  # checkout/ls-remote to fail with Repository not found
+  if modUrl.contains("github.com") and modUrl.endswith("/"):
+    modUrl = modUrl[0 .. ^2]
 
   let downloadMethod = if downloadTarball(modUrl, options):
     "http" else: $downMethod
 
+  result.dir = downloadDir / subdir
+  #when using a persistent download dir we can skip the download if it's already done
+  try:
+    discard findNimbleFile(result.dir, true, options)
+    return
+  except NimbleError: 
+    #Continue with the download
+    discard    
+
   if subdir.len > 0:
     display("Downloading", "$1 using $2 (subdir is '$3')" %
                            [modUrl, downloadMethod, subdir],
@@ -592,46 +584,35 @@ proc refresh*(options: Options) =
     for name, list in options.config.packageLists:
       fetchList(list, options)
 
-proc getDownloadInfo*(
-    pv: PkgTuple, options: Options,
-    doPrompt: bool,
-    ignorePackageCache = false,
-): (DownloadMethod, string, Table[string, string]) =
-
-  # echo "getDownloadInfo:pv.name: ", $pv.name
-  var pkg = initPackage()
-  if getPackage(pv.name, options, pkg, ignorePackageCache):
-    let (url, metadata) = getUrlData(pkg.url)
-    result = (pkg.downloadMethod, url, metadata)
-    # echo "getDownloadInfo:getPackage: ", $result
-    return
-  elif pv.name.isURL:
-    # echo "getDownloadInfo:isURL:name: ", $pv.name
-    # echo "getDownloadInfo:isURL:options.nimbleData: ", $options.nimbleData
-    let (url, urlmeta) = getUrlData(pv.name)
-    var metadata = urlmeta
-    metadata["urlOnly"] = "true"
-    result = (checkUrlType(url), url, metadata)
-    # echo "getDownloadInfo:isURL: ", $result
-    return
+proc getDownloadInfo*(pv: PkgTuple, options: Options,
+                      doPrompt: bool, ignorePackageCache = false): (DownloadMethod, string,
+                                        Table[string, string]) =
+  if pv.name.isURL:
+    let (url, metadata) = getUrlData(pv.name)
+    return (checkUrlType(url), url, metadata)
   elif pv.name.isForgeAlias:
     let url = newForge(pv.name).expand()
     return (checkUrlType(url), url, default(Table[string, string]))
   else:
-    # If package is not found give the user a chance to refresh
-    # package.json
-    if doPrompt and not options.offline and
-        options.prompt(pv.name & " not found in any local packages.json, " &
-                        "check internet for updated packages?"):
-      refresh(options)
-
-      # Once we've refreshed, try again, but don't prompt if not found
-      # (as we've already refreshed and a failure means it really
-      # isn't there)
-      # Also ignore the package cache so the old info isn't used
-      return getDownloadInfo(pv, options, false, true)
+    var pkg = initPackage()
+    if getPackage(pv.name, options, pkg, ignorePackageCache):
+      let (url, metadata) = getUrlData(pkg.url)
+      return (pkg.downloadMethod, url, metadata)
     else:
-      raise nimbleError(pkgNotFoundMsg(pv))
+      # If package is not found give the user a chance to refresh
+      # package.json
+      if doPrompt and not options.offline and
+          options.prompt(pv.name & " not found in any local packages.json, " &
+                         "check internet for updated packages?"):
+        refresh(options)
+
+        # Once we've refreshed, try again, but don't prompt if not found
+        # (as we've already refreshed and a failure means it really
+        # isn't there)
+        # Also ignore the package cache so the old info isn't used
+        return getDownloadInfo(pv, options, false, true)
+      else:
+        raise nimbleError(pkgNotFoundMsg(pv))
 
 when isMainModule:
   import unittest

From e15bfd72dce197387b4f501d4bbff0356ca8a57d Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:41:47 -0700
Subject: [PATCH 05/21] Revert "revert getDownloadInfo"

This reverts commit 742761c5bdbe95c1aba137dbabc9c66c1a245e44.
---
 src/nimblepkg/download.nim | 117 +++++++++++++++++++++----------------
 1 file changed, 68 insertions(+), 49 deletions(-)

diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim
index 1e4106e85..9a93e4bbc 100644
--- a/src/nimblepkg/download.nim
+++ b/src/nimblepkg/download.nim
@@ -445,6 +445,31 @@ proc doDownload(url, downloadDir: string, verRange: VersionRange,
     result.vcsRevision = downloadDir.getVcsRevision
 {.warning[ProveInit]: on.}
 
+proc pkgDirHasNimble*(dir: string, options: Options): bool =
+  try:
+    discard findNimbleFile(dir, true, options)
+    return true
+  except NimbleError: 
+    #Continue with the download
+    discard
+
+proc downloadPkgDir*(url: string,
+                     verRange: VersionRange,
+                     subdir: string,
+                     options: Options,
+                     vcsRevision: Sha1Hash = notSetSha1Hash,
+                     downloadPath: string = ""
+): (string, string) =
+  let downloadDir =
+    if downloadPath == "":
+      (getNimbleTempDir() / getDownloadDirName(url, verRange, vcsRevision))
+    else:
+      downloadPath
+
+  createDir(downloadDir)
+
+  result = (downloadDir, downloadDir / subdir)
+
 proc downloadPkg*(url: string, verRange: VersionRange,
                   downMethod: DownloadMethod,
                   subdir: string,
@@ -464,38 +489,21 @@ proc downloadPkg*(url: string, verRange: VersionRange,
   ##   If specified this parameter will cause specific VCS revision to be
   ##   checked out.
 
+  let (downloadDir, pkgDir) = downloadPkgDir(url, verRange, subdir, options, vcsRevision, downloadPath)
+  result.dir = pkgDir
+
+  #when using a persistent download dir we can skip the download if it's already done
+  if pkgDirHasNimble(result.dir, options):
+    return # already downloaded, skipping
+
   if options.offline:
     raise nimbleError("Cannot download in offline mode.")
-  let downloadDir =
-    if downloadPath == "":
-      (getNimbleTempDir() / getDownloadDirName(url, verRange, vcsRevision))
-    else:
-      downloadPath
-    
-  createDir(downloadDir)
-  var modUrl =
-    if url.startsWith("git://") and options.config.cloneUsingHttps:
-      "https://" & url[6 .. ^1]
-    else: url
 
-  # Fixes issue #204
-  # github + https + trailing url slash causes a
-  # checkout/ls-remote to fail with Repository not found
-  if modUrl.contains("github.com") and modUrl.endswith("/"):
-    modUrl = modUrl[0 .. ^2]
+  let modUrl = modifyUrl(url, options.config.cloneUsingHttps)
 
   let downloadMethod = if downloadTarball(modUrl, options):
     "http" else: $downMethod
 
-  result.dir = downloadDir / subdir
-  #when using a persistent download dir we can skip the download if it's already done
-  try:
-    discard findNimbleFile(result.dir, true, options)
-    return
-  except NimbleError: 
-    #Continue with the download
-    discard    
-
   if subdir.len > 0:
     display("Downloading", "$1 using $2 (subdir is '$3')" %
                            [modUrl, downloadMethod, subdir],
@@ -584,35 +592,46 @@ proc refresh*(options: Options) =
     for name, list in options.config.packageLists:
       fetchList(list, options)
 
-proc getDownloadInfo*(pv: PkgTuple, options: Options,
-                      doPrompt: bool, ignorePackageCache = false): (DownloadMethod, string,
-                                        Table[string, string]) =
-  if pv.name.isURL:
-    let (url, metadata) = getUrlData(pv.name)
-    return (checkUrlType(url), url, metadata)
+proc getDownloadInfo*(
+    pv: PkgTuple, options: Options,
+    doPrompt: bool,
+    ignorePackageCache = false,
+): (DownloadMethod, string, Table[string, string]) =
+
+  # echo "getDownloadInfo:pv.name: ", $pv.name
+  var pkg = initPackage()
+  if getPackage(pv.name, options, pkg, ignorePackageCache):
+    let (url, metadata) = getUrlData(pkg.url)
+    result = (pkg.downloadMethod, url, metadata)
+    # echo "getDownloadInfo:getPackage: ", $result
+    return
+  elif pv.name.isURL:
+    # echo "getDownloadInfo:isURL:name: ", $pv.name
+    # echo "getDownloadInfo:isURL:options.nimbleData: ", $options.nimbleData
+    let (url, urlmeta) = getUrlData(pv.name)
+    var metadata = urlmeta
+    metadata["urlOnly"] = "true"
+    result = (checkUrlType(url), url, metadata)
+    # echo "getDownloadInfo:isURL: ", $result
+    return
   elif pv.name.isForgeAlias:
     let url = newForge(pv.name).expand()
     return (checkUrlType(url), url, default(Table[string, string]))
   else:
-    var pkg = initPackage()
-    if getPackage(pv.name, options, pkg, ignorePackageCache):
-      let (url, metadata) = getUrlData(pkg.url)
-      return (pkg.downloadMethod, url, metadata)
+    # If package is not found give the user a chance to refresh
+    # package.json
+    if doPrompt and not options.offline and
+        options.prompt(pv.name & " not found in any local packages.json, " &
+                        "check internet for updated packages?"):
+      refresh(options)
+
+      # Once we've refreshed, try again, but don't prompt if not found
+      # (as we've already refreshed and a failure means it really
+      # isn't there)
+      # Also ignore the package cache so the old info isn't used
+      return getDownloadInfo(pv, options, false, true)
     else:
-      # If package is not found give the user a chance to refresh
-      # package.json
-      if doPrompt and not options.offline and
-          options.prompt(pv.name & " not found in any local packages.json, " &
-                         "check internet for updated packages?"):
-        refresh(options)
-
-        # Once we've refreshed, try again, but don't prompt if not found
-        # (as we've already refreshed and a failure means it really
-        # isn't there)
-        # Also ignore the package cache so the old info isn't used
-        return getDownloadInfo(pv, options, false, true)
-      else:
-        raise nimbleError(pkgNotFoundMsg(pv))
+      raise nimbleError(pkgNotFoundMsg(pv))
 
 when isMainModule:
   import unittest

From 52a9aba0e17aa328e9edb83441dc245c6066d848 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:42:06 -0700
Subject: [PATCH 06/21] cleanup options

---
 src/nimble.nim            | 10 ++++++----
 src/nimblepkg/options.nim | 32 +++++++++++++++++++++++---------
 2 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index ef536ab8a..8768320df 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1023,7 +1023,7 @@ proc search(options: Options) =
   var found = false
   template onFound {.dirty.} =
     echoPackage(pkg)
-    if pkg.alias.len == 0 and options.queryVersions:
+    if pkg.alias.len == 0 and options.action.showVersions:
       echoPackageVersions(pkg)
     echo(" ")
     found = true
@@ -1049,7 +1049,7 @@ proc list(options: Options) =
   let pkgList = getPackageList(options)
   for pkg in pkgList:
     echoPackage(pkg)
-    if pkg.alias.len == 0 and options.queryVersions:
+    if pkg.alias.len == 0 and options.action.listVersions:
       echoPackageVersions(pkg)
     echo(" ")
 
@@ -2326,8 +2326,10 @@ proc doAction(options: var Options) =
   of actionSearch:
     search(options)
   of actionList:
-    if options.queryInstalled: listInstalled(options)
-    else: list(options)
+    if options.action.onlyInstalled:
+      listInstalled(options)
+    else:
+      list(options)
   of actionPath:
     listPaths(options)
   of actionBuild:
diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index d9ff28cf8..57bbe6d81 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -16,13 +16,13 @@ type
   NimBin* = object
     path*: string
     version*: Version
+
   DumpMode* = enum kdumpIni, kdumpJson
+
   Options* = object
     forcePrompts*: ForcePrompt
     depsOnly*: bool
     uninstallRevDeps*: bool
-    queryVersions*: bool
-    queryInstalled*: bool
     nimbleDir*: string
     verbosity*: cli.Priority
     action*: Action
@@ -79,8 +79,9 @@ type
 
   Action* = object
     case typ*: ActionType
-    of actionNil, actionList, actionTasks, actionCheck,
-       actionSetup, actionClean, actionManual: nil
+    of actionNil, actionTasks, actionCheck,
+       actionSetup, actionClean, actionManual:
+      discard
     of actionSync:
       listOnly*: bool
     of actionRefresh:
@@ -96,6 +97,11 @@ type
       global*: bool
     of actionSearch:
       search*: seq[string] # Search string.
+      showVersions*: bool
+    of actionList:
+      onlyNimBinaries*: bool
+      onlyInstalled*: bool
+      listVersions*: bool
     of actionInit, actionDump:
       projName*: string
       vcsOption*: string
@@ -658,12 +664,20 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
   var wasFlagHandled = true
   # Action-specific flags.
   case result.action.typ
-  of actionSearch, actionList:
+  of actionSearch:
+    case f
+    of "installed", "i":
+      result.action.onlyInstalled = true
+    of "versions", "ver":
+      result.action.showVersions = true
+    else:
+      wasFlagHandled = false
+  of actionList:
     case f
     of "installed", "i":
-      result.queryInstalled = true
-    of "ver":
-      result.queryVersions = true
+      result.action.onlyInstalled = true
+    of "versions":
+      result.action.showVersions = true
     else:
       wasFlagHandled = false
   of actionDump:
@@ -674,7 +688,7 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
       wasFlagHandled = false
   of actionInstall:
     case f
-    of "depsonly", "d":
+    of "depsonly", "deps", "d":
       result.depsOnly = true
     of "norebuild":
       result.action.noRebuild = true

From 3b899ae7aa11df9460adcf0e77ba07e9f9660617 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:42:38 -0700
Subject: [PATCH 07/21] cleanup options

---
 src/nimblepkg/options.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index 57bbe6d81..0338d9da6 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -676,7 +676,7 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
     case f
     of "installed", "i":
       result.action.onlyInstalled = true
-    of "versions":
+    of "versions", "ver":
       result.action.showVersions = true
     else:
       wasFlagHandled = false

From eb515c5f792da198565d5583e16cc341e2c8fb7e Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:44:27 -0700
Subject: [PATCH 08/21] cleanup options

---
 src/nimble.nim            | 4 ++--
 src/nimblepkg/options.nim | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 8768320df..1d96e416d 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1023,7 +1023,7 @@ proc search(options: Options) =
   var found = false
   template onFound {.dirty.} =
     echoPackage(pkg)
-    if pkg.alias.len == 0 and options.action.showVersions:
+    if pkg.alias.len == 0 and options.action.showSearchVersions:
       echoPackageVersions(pkg)
     echo(" ")
     found = true
@@ -1049,7 +1049,7 @@ proc list(options: Options) =
   let pkgList = getPackageList(options)
   for pkg in pkgList:
     echoPackage(pkg)
-    if pkg.alias.len == 0 and options.action.listVersions:
+    if pkg.alias.len == 0 and options.action.showListVersions:
       echoPackageVersions(pkg)
     echo(" ")
 
diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index 0338d9da6..0e1b5d7ab 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -97,11 +97,11 @@ type
       global*: bool
     of actionSearch:
       search*: seq[string] # Search string.
-      showVersions*: bool
+      showSearchVersions*: bool
     of actionList:
       onlyNimBinaries*: bool
       onlyInstalled*: bool
-      listVersions*: bool
+      showListVersions*: bool
     of actionInit, actionDump:
       projName*: string
       vcsOption*: string
@@ -669,7 +669,7 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
     of "installed", "i":
       result.action.onlyInstalled = true
     of "versions", "ver":
-      result.action.showVersions = true
+      result.action.showSearchVersions = true
     else:
       wasFlagHandled = false
   of actionList:
@@ -677,7 +677,7 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
     of "installed", "i":
       result.action.onlyInstalled = true
     of "versions", "ver":
-      result.action.showVersions = true
+      result.action.showListVersions = true
     else:
       wasFlagHandled = false
   of actionDump:

From 98573473ce6aaf7dc0d6cb5716c12430dbb79deb Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:46:38 -0700
Subject: [PATCH 09/21] cleanup options

---
 src/nimblepkg/options.nim | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index 0e1b5d7ab..e153a6e18 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -666,8 +666,6 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
   case result.action.typ
   of actionSearch:
     case f
-    of "installed", "i":
-      result.action.onlyInstalled = true
     of "versions", "ver":
       result.action.showSearchVersions = true
     else:

From c490e468ec4ffafcb2c56257a1d961da3909cc5e Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 22:56:14 -0700
Subject: [PATCH 10/21] print verify dependencies

---
 src/nimble.nim      | 2 +-
 tests/ttaskdeps.nim | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 1d96e416d..18e56f495 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -182,7 +182,7 @@ proc processFreeDependencies(pkgInfo: PackageInfo,
 
   display("Verifying", "dependencies for $1@$2" %
           [pkgInfo.basicInfo.name, $pkgInfo.basicInfo.version],
-          priority = LowPriority)
+          priority = MediumPriority)
 
   var reverseDependencies: seq[PackageBasicInfo] = @[]
 
diff --git a/tests/ttaskdeps.nim b/tests/ttaskdeps.nim
index 3a1e43f29..a459ebe95 100644
--- a/tests/ttaskdeps.nim
+++ b/tests/ttaskdeps.nim
@@ -119,6 +119,7 @@ suite "Task level dependencies":
   test "Dependencies aren't verified twice":
     inDir:
       let (output, _) = execNimbleYes("test")
+      checkpoint("Failed test output: \n>>>" & output.replace("\n", "\n>>> "))
       check output.count("dependencies for unittest2@0.0.4") == 1
 
   test "Requirements for tasks in dependencies aren't used":

From 5d7b34968454d592072d05d3a421b3c51a9a7cb9 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:17:52 -0700
Subject: [PATCH 11/21] prettify list

---
 src/nimble.nim | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 18e56f495..b87e30bc6 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1056,22 +1056,36 @@ proc list(options: Options) =
 proc listInstalled(options: Options) =
   type
     VersionChecksumTuple = tuple[version: Version, checksum: Sha1Hash]
-  var h: OrderedTable[string, seq[VersionChecksumTuple]]
+  var vers: OrderedTable[string, seq[VersionChecksumTuple]]
   let pkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
   for pkg in pkgs:
     let
       pName = pkg.basicInfo.name
       pVersion = pkg.basicInfo.version
       pChecksum = pkg.basicInfo.checksum
-    if not h.hasKey(pName): h[pName] = @[]
-    var s = h[pName]
+    if not vers.hasKey(pName): vers[pName] = @[]
+    var s = vers[pName]
     add(s, (pVersion, pChecksum))
-    h[pName] = s
+    vers[pName] = s
 
-  h.sort(proc (a, b: (string, seq[VersionChecksumTuple])): int =
+  vers.sort(proc (a, b: (string, seq[VersionChecksumTuple])): int =
     cmpIgnoreCase(a[0], b[0]))
-  for k in keys(h):
-    echo k & "  [" & h[k].join(", ") & "]"
+
+  displayInfo("Package list format: {PackageName} ")
+  displayInfo("                           {Version} ({CheckSum})")
+  for k in keys(vers):
+    displayFormatted(Message, k)
+    displayFormatted(Hint, "\n")
+    for idx, item in vers[k]:
+      if idx == vers[k].len() - 1:
+        displayFormatted(Hint, "└── ")
+      else:
+        displayFormatted(Hint, "├── ")
+      displayFormatted(Success, "@", $item.version)
+      displayFormatted(Hint, " ")
+      displayFormatted(Details, fmt"({item.checksum})")
+      displayFormatted(Hint, "\n")
+      # "  [" & vers[k].join(", ") & "]"
 
 type VersionAndPath = tuple[version: Version, path: string]
 

From 5eaa2d1d0a5a58077744433d5defa93462161c2d Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:23:13 -0700
Subject: [PATCH 12/21] prettify list

---
 src/nimble.nim | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index b87e30bc6..8be174b8c 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1076,16 +1076,17 @@ proc listInstalled(options: Options) =
   for k in keys(vers):
     displayFormatted(Message, k)
     displayFormatted(Hint, "\n")
-    for idx, item in vers[k]:
-      if idx == vers[k].len() - 1:
-        displayFormatted(Hint, "└── ")
-      else:
-        displayFormatted(Hint, "├── ")
-      displayFormatted(Success, "@", $item.version)
-      displayFormatted(Hint, " ")
-      displayFormatted(Details, fmt"({item.checksum})")
-      displayFormatted(Hint, "\n")
-      # "  [" & vers[k].join(", ") & "]"
+    if options.action.showListVersions:
+      for idx, item in vers[k]:
+        if idx == vers[k].len() - 1:
+          displayFormatted(Hint, "└── ")
+        else:
+          displayFormatted(Hint, "├── ")
+        displayFormatted(Success, "@", $item.version)
+        displayFormatted(Hint, " ")
+        displayFormatted(Details, fmt"({item.checksum})")
+        displayFormatted(Hint, "\n")
+        # "  [" & vers[k].join(", ") & "]"
 
 type VersionAndPath = tuple[version: Version, path: string]
 

From 2726a82e919681549bda8a6cb76e2faec70b83ca Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:25:10 -0700
Subject: [PATCH 13/21] prettify list

---
 src/nimblepkg/options.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index e153a6e18..f82c75333 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -202,8 +202,8 @@ Commands:
                                   performed by tag and by name.
                [--ver]            Queries remote server for package version.
   list                            Lists all packages.
-               [--ver]            Queries remote server for package version.
                [-i, --installed]  Lists all installed packages.
+               [--ver, --version] Also display versions for packages.
   tasks                           Lists the tasks specified in the Nimble
                                   package's Nimble file.
   path         pkgname ...        Shows absolute path to the installed packages

From 2302149c945fdfa765d399d5d1ba294d45fa0aff Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:25:19 -0700
Subject: [PATCH 14/21] prettify list

---
 src/nimblepkg/options.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index f82c75333..10c6ca6e9 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -200,7 +200,7 @@ Commands:
                                   can be optionally specified.
   search       pkg/tag            Searches for a specified package. Search is
                                   performed by tag and by name.
-               [--ver]            Queries remote server for package version.
+               [--ver, --version] Queries remote server for package version.
   list                            Lists all packages.
                [-i, --installed]  Lists all installed packages.
                [--ver, --version] Also display versions for packages.

From cd6f7b055b2eb1466028facd9eb957c0e2861faa Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:33:49 -0700
Subject: [PATCH 15/21] prettify list

---
 src/nimblepkg/packageinfo.nim | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim
index 0f9ee04d8..08e10c837 100644
--- a/src/nimblepkg/packageinfo.nim
+++ b/src/nimblepkg/packageinfo.nim
@@ -389,16 +389,24 @@ proc getOutputDir*(pkgInfo: PackageInfo, bin: string): string =
     result &= ".out"
 
 proc echoPackage*(pkg: Package) =
-  echo(pkg.name & ":")
+  displayFormatted(Message, pkg.name & ":")
+  displayFormatted(Hint, "\n")
+
+  proc displayInfoLine(field, msg: string) =
+      displayFormatted(Success, field)
+      displayFormatted(Details, msg)
+      displayFormatted(Hint, "\n")
+
   if pkg.alias.len > 0:
-    echo("  Alias for ", pkg.alias)
+    displayFormatted(Warning, "  Alias for ", pkg.alias)
+    displayFormatted(Hint, "\n")
   else:
-    echo("  url:         " & pkg.url & " (" & $pkg.downloadMethod & ")")
-    echo("  tags:        " & pkg.tags.join(", "))
-    echo("  description: " & pkg.description)
-    echo("  license:     " & pkg.license)
+    displayInfoLine("  url:         ", pkg.url & " (" & $pkg.downloadMethod & ")")
+    displayInfoLine("  tags:        ", pkg.tags.join(", "))
+    displayInfoLine("  description: ", pkg.description)
+    displayInfoLine("  license:     ", pkg.license)
     if pkg.web.len > 0:
-      echo("  website:     " & pkg.web)
+      displayInfoLine("  website:     ", pkg.web)
 
 proc getDownloadDirName*(pkg: Package, verRange: VersionRange): string =
   result = pkg.name

From 0ba360e2e93c72098fd3f78c071b8b7d98381b07 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:40:27 -0700
Subject: [PATCH 16/21] prettify list

---
 src/nimblepkg/download.nim | 17 ++++++++++++-----
 src/nimblepkg/options.nim  |  1 +
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim
index 9a93e4bbc..c3ed9285a 100644
--- a/src/nimblepkg/download.nim
+++ b/src/nimblepkg/download.nim
@@ -526,6 +526,11 @@ proc downloadPkg*(url: string, verRange: VersionRange,
         [$verRange, $pkginfo.basicInfo.version])
 
 proc echoPackageVersions*(pkg: Package) =
+  proc displayInfoLine(field, msg: string) =
+      displayFormatted(Success, field)
+      displayFormatted(Details, msg)
+      displayFormatted(Hint, "\n")
+
   let downMethod = pkg.downloadMethod
   case downMethod
   of DownloadMethod.git:
@@ -533,14 +538,16 @@ proc echoPackageVersions*(pkg: Package) =
       let versions = getTagsListRemote(pkg.url, downMethod).getVersionList()
       if versions.len > 0:
         let sortedVersions = toSeq(values(versions))
-        echo("  versions:    " & join(sortedVersions, ", "))
+        displayInfoLine("  versions:    ", join(sortedVersions, ", "))
       else:
-        echo("  versions:    (No versions tagged in the remote repository)")
+        displayInfoLine("  versions:    ", "(No versions tagged in the remote repository)")
     except CatchableError:
-      echo(getCurrentExceptionMsg())
+      displayFormatted(Error, "  Error: ")
+      displayFormatted(Error, getCurrentExceptionMsg())
+      displayFormatted(Hint, "\n")
   of DownloadMethod.hg:
-    echo("  versions:    (Remote tag retrieval not supported by " &
-        $pkg.downloadMethod & ")")
+    displayInfoLine("  versions:    ", "(Remote tag retrieval not supported by " &
+                                        $pkg.downloadMethod & ")")
 
 proc removeTrailingSlash(s: string): string =
   s.strip(chars = {'/'}, leading = false)
diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index 10c6ca6e9..ee5833edd 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -204,6 +204,7 @@ Commands:
   list                            Lists all packages.
                [-i, --installed]  Lists all installed packages.
                [--ver, --version] Also display versions for packages.
+               [-n, --nimbinaries]  Lists all installed packages.
   tasks                           Lists the tasks specified in the Nimble
                                   package's Nimble file.
   path         pkgname ...        Shows absolute path to the installed packages

From 40c54b41afacf338253728341a8032110e1fda34 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:41:30 -0700
Subject: [PATCH 17/21] prettify list

---
 src/nimblepkg/download.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim
index c3ed9285a..06ed2b46d 100644
--- a/src/nimblepkg/download.nim
+++ b/src/nimblepkg/download.nim
@@ -528,7 +528,7 @@ proc downloadPkg*(url: string, verRange: VersionRange,
 proc echoPackageVersions*(pkg: Package) =
   proc displayInfoLine(field, msg: string) =
       displayFormatted(Success, field)
-      displayFormatted(Details, msg)
+      displayFormatted(Warning, msg)
       displayFormatted(Hint, "\n")
 
   let downMethod = pkg.downloadMethod

From 570c875a69fe634abc33dc563df011572266a401 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:58:43 -0700
Subject: [PATCH 18/21] prettify list

---
 src/nimble.nim                | 20 +++++++++++++++++++-
 src/nimblepkg/cli.nim         |  6 ++++++
 src/nimblepkg/download.nim    |  5 -----
 src/nimblepkg/packageinfo.nim | 15 ++++-----------
 4 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 8be174b8c..522ed5560 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1053,6 +1053,21 @@ proc list(options: Options) =
       echoPackageVersions(pkg)
     echo(" ")
 
+proc listNimBinaries(options: Options) =
+  let nimBininstalledPkgs = getInstalledPkgsMin(options.nimBinariesDir, options)
+  displayFormatted(Message, "nim")
+  displayFormatted(Hint, "\n")
+  for idx, pkg in nimBininstalledPkgs:
+    assert pkg.basicInfo.name == "nim"
+    if idx == nimBininstalledPkgs.len() - 1:
+      displayFormatted(Hint, "└── ")
+    else:
+      displayFormatted(Hint, "├── ")
+    displayFormatted(Success, "version ")
+    displayFormatted(Details, $pkg.basicInfo.version)
+    displayFormatted(Hint, "\n")
+      # echoPackageVersions(pkg)
+
 proc listInstalled(options: Options) =
   type
     VersionChecksumTuple = tuple[version: Version, checksum: Sha1Hash]
@@ -1072,7 +1087,8 @@ proc listInstalled(options: Options) =
     cmpIgnoreCase(a[0], b[0]))
 
   displayInfo("Package list format: {PackageName} ")
-  displayInfo("                           {Version} ({CheckSum})")
+  displayInfo("  {PackageName} ")
+  displayInfo("     {Version} ({CheckSum})")
   for k in keys(vers):
     displayFormatted(Message, k)
     displayFormatted(Hint, "\n")
@@ -2343,6 +2359,8 @@ proc doAction(options: var Options) =
   of actionList:
     if options.action.onlyInstalled:
       listInstalled(options)
+    elif options.action.onlyNimBinaries:
+      listNimBinaries(options)
     else:
       list(options)
   of actionPath:
diff --git a/src/nimblepkg/cli.nim b/src/nimblepkg/cli.nim
index 759a6ed76..a09b8e0ed 100644
--- a/src/nimblepkg/cli.nim
+++ b/src/nimblepkg/cli.nim
@@ -62,12 +62,18 @@ proc isSuppressed(displayType: DisplayType): bool =
     return true
 
 proc displayFormatted*(displayType: DisplayType, msgs: varargs[string]) =
+  ## for styling outputs lines using the DisplayTypes
   for msg in msgs:
     if globalCLI.showColor:
       stdout.styledWrite(foregrounds[displayType], msg)
     else:
       stdout.write(msg)
 
+proc displayInfoLine*(field, msg: string) =
+  displayFormatted(Success, field)
+  displayFormatted(Details, msg)
+  displayFormatted(Hint, "\n")
+
 proc displayCategory(category: string, displayType: DisplayType,
                      priority: Priority) =
   if isSuppressed(displayType):
diff --git a/src/nimblepkg/download.nim b/src/nimblepkg/download.nim
index 06ed2b46d..d2323fef1 100644
--- a/src/nimblepkg/download.nim
+++ b/src/nimblepkg/download.nim
@@ -526,11 +526,6 @@ proc downloadPkg*(url: string, verRange: VersionRange,
         [$verRange, $pkginfo.basicInfo.version])
 
 proc echoPackageVersions*(pkg: Package) =
-  proc displayInfoLine(field, msg: string) =
-      displayFormatted(Success, field)
-      displayFormatted(Warning, msg)
-      displayFormatted(Hint, "\n")
-
   let downMethod = pkg.downloadMethod
   case downMethod
   of DownloadMethod.git:
diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim
index 08e10c837..b27e44722 100644
--- a/src/nimblepkg/packageinfo.nim
+++ b/src/nimblepkg/packageinfo.nim
@@ -392,11 +392,6 @@ proc echoPackage*(pkg: Package) =
   displayFormatted(Message, pkg.name & ":")
   displayFormatted(Hint, "\n")
 
-  proc displayInfoLine(field, msg: string) =
-      displayFormatted(Success, field)
-      displayFormatted(Details, msg)
-      displayFormatted(Hint, "\n")
-
   if pkg.alias.len > 0:
     displayFormatted(Warning, "  Alias for ", pkg.alias)
     displayFormatted(Hint, "\n")
@@ -408,12 +403,10 @@ proc echoPackage*(pkg: Package) =
     if pkg.web.len > 0:
       displayInfoLine("  website:     ", pkg.web)
 
-proc getDownloadDirName*(pkg: Package, verRange: VersionRange): string =
-  result = pkg.name
-  let verSimple = getSimpleString(verRange)
-  if verSimple != "":
-    result.add "_"
-    result.add verSimple
+proc echoPackage*(pkg: PackageInfo) =
+  displayFormatted(Message, pkg.basicInfo.name & ":")
+  displayFormatted(Hint, "\n")
+
 
 proc checkInstallFile(pkgInfo: PackageInfo,
                       origDir, file: string): bool =

From 69888df6de45ba8fd423ce5de1b2e7de4113a8cb Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Wed, 8 Jan 2025 23:59:24 -0700
Subject: [PATCH 19/21] prettify list

---
 src/nimblepkg/options.nim | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim
index ee5833edd..ead8d180b 100644
--- a/src/nimblepkg/options.nim
+++ b/src/nimblepkg/options.nim
@@ -675,6 +675,8 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
     case f
     of "installed", "i":
       result.action.onlyInstalled = true
+    of "nimbinaries", "n":
+      result.action.onlyNimBinaries = true
     of "versions", "ver":
       result.action.showListVersions = true
     else:

From ea533d71ad8a7a6ed69878e8f6d53504d6a6a71f Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Thu, 9 Jan 2025 00:06:23 -0700
Subject: [PATCH 20/21] prettify list

---
 src/nimble.nim | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index 522ed5560..f4228bbb9 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1063,10 +1063,11 @@ proc listNimBinaries(options: Options) =
       displayFormatted(Hint, "└── ")
     else:
       displayFormatted(Hint, "├── ")
-    displayFormatted(Success, "version ")
-    displayFormatted(Details, $pkg.basicInfo.version)
+    displayFormatted(Success, "@" & $pkg.basicInfo.version)
+    displayFormatted(Hint, " ")
+    displayFormatted(Details, fmt"({pkg.myPath})")
     displayFormatted(Hint, "\n")
-      # echoPackageVersions(pkg)
+  displayFormatted(Hint, "\n")
 
 proc listInstalled(options: Options) =
   type

From 2bea01f1e0515f1acef4b27d9853f3fa19e48613 Mon Sep 17 00:00:00 2001
From: Jaremy Creechley <creechley@gmail.com>
Date: Thu, 9 Jan 2025 00:08:16 -0700
Subject: [PATCH 21/21] prettify list

---
 src/nimble.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/nimble.nim b/src/nimble.nim
index f4228bbb9..dcdba58dc 100644
--- a/src/nimble.nim
+++ b/src/nimble.nim
@@ -1065,7 +1065,7 @@ proc listNimBinaries(options: Options) =
       displayFormatted(Hint, "├── ")
     displayFormatted(Success, "@" & $pkg.basicInfo.version)
     displayFormatted(Hint, " ")
-    displayFormatted(Details, fmt"({pkg.myPath})")
+    displayFormatted(Details, fmt"({pkg.myPath.splitPath().head})")
     displayFormatted(Hint, "\n")
   displayFormatted(Hint, "\n")