diff --git a/Library/Homebrew/cli/args.rb b/Library/Homebrew/cli/args.rb
index 4032bb9dab7047019696263784d11d067de9e254..a32c50b01c0cbaa44f35f73545907f7ccdf489ba 100644
--- a/Library/Homebrew/cli/args.rb
+++ b/Library/Homebrew/cli/args.rb
@@ -5,16 +5,23 @@ require "ostruct"
 module Homebrew
   module CLI
     class Args < OpenStruct
-      attr_accessor :processed_options
+      attr_reader :processed_options, :args_parsed
       # undefine tap to allow --tap argument
       undef tap
 
       def initialize(argv:)
         super
         @argv = argv
+        @args_parsed = false
         @processed_options = []
       end
 
+      def freeze_processed_options!(processed_options)
+        @processed_options += processed_options
+        @processed_options.freeze
+        @args_parsed = true
+      end
+
       def option_to_name(option)
         option.sub(/\A--?/, "")
               .tr("-", "_")
@@ -51,22 +58,36 @@ module Homebrew
         options_only - CLI::Parser.global_options.values.map(&:first).flatten
       end
 
-      def downcased_unique_named
-        # Only lowercase names, not paths, bottle filenames or URLs
-        @downcased_unique_named ||= remaining.map do |arg|
-          if arg.include?("/") || arg.end_with?(".tar.gz") || File.exist?(arg)
-            arg
+      def named
+        remaining
+      end
+
+      def formulae
+        require "formula"
+        @formulae ||= (downcased_unique_named - casks).map do |name|
+          if name.include?("/") || File.exist?(name)
+            Formulary.factory(name, spec)
           else
-            arg.downcase
+            Formulary.find_with_priority(name, spec)
           end
-        end.uniq
+        end.uniq(&:name)
+      end
+
+      def resolved_formulae
+        require "formula"
+        @resolved_formulae ||= (downcased_unique_named - casks).map do |name|
+          Formulary.resolve(name, spec: spec(nil))
+        end.uniq(&:name)
+      end
+
+      def casks
+        @casks ||= downcased_unique_named.grep HOMEBREW_CASK_TAP_CASK_REGEX
       end
 
       def kegs
         require "keg"
         require "formula"
         require "missing_formula"
-
         @kegs ||= downcased_unique_named.map do |name|
           raise UsageError if name.empty?
 
@@ -113,6 +134,42 @@ module Homebrew
           end
         end
       end
+
+      private
+
+      def downcased_unique_named
+        # Only lowercase names, not paths, bottle filenames or URLs
+        arguments = if args_parsed
+          remaining
+        else
+          cmdline_args.reject { |arg| arg.start_with?("-") }
+        end
+        arguments.map do |arg|
+          if arg.include?("/") || arg.end_with?(".tar.gz") || File.exist?(arg)
+            arg
+          else
+            arg.downcase
+          end
+        end.uniq
+      end
+
+      def head
+        (args_parsed && HEAD?) || cmdline_args.include?("--HEAD")
+      end
+
+      def devel
+        (args_parsed && devel?) || cmdline_args.include?("--devel")
+      end
+
+      def spec(default = :stable)
+        if head
+          :head
+        elsif devel
+          :devel
+        else
+          default
+        end
+      end
     end
   end
 end
diff --git a/Library/Homebrew/cli/parser.rb b/Library/Homebrew/cli/parser.rb
index 1e767091705582e02543e2ebadb9837e815e4c63..bda488b0c3f248d45ef55de7c7afd29047e87116 100644
--- a/Library/Homebrew/cli/parser.rb
+++ b/Library/Homebrew/cli/parser.rb
@@ -13,7 +13,7 @@ module Homebrew
       attr_reader :processed_options, :hide_from_man_page
 
       def self.parse(args = ARGV, &block)
-        new(&block).parse(args)
+        new(args, &block).parse(args)
       end
 
       def self.global_options
@@ -25,9 +25,11 @@ module Homebrew
         }
       end
 
-      def initialize(&block)
+      def initialize(args = ARGV, &block)
         @parser = OptionParser.new
         @args = Homebrew::CLI::Args.new(argv: ARGV_WITHOUT_MONKEY_PATCHING)
+        @args[:remaining] = []
+        @args[:cmdline_args] = args.dup
         @constraints = []
         @conflicts = []
         @switch_sources = {}
@@ -138,10 +140,10 @@ module Homebrew
         end
         check_constraint_violations
         @args[:remaining] = remaining_args
-        @args_parsed = true
-        @args.processed_options = @processed_options
+        @args.freeze_processed_options!(@processed_options)
         Homebrew.args = @args
         cmdline_args.freeze
+        @args_parsed = true
         @parser
       end
 
@@ -159,7 +161,7 @@ module Homebrew
       end
 
       def formula_options
-        ARGV.formulae.each do |f|
+        @args.formulae.each do |f|
           next if f.options.empty?
 
           f.options.each do |o|
diff --git a/Library/Homebrew/cmd/--cache.rb b/Library/Homebrew/cmd/--cache.rb
index dc253a37ed6996ebd7aa85cbb0b26ed47d564ee9..5e72c776ccfab0e385376a5c6366cfa0a50189ae 100644
--- a/Library/Homebrew/cmd/--cache.rb
+++ b/Library/Homebrew/cmd/--cache.rb
@@ -29,7 +29,7 @@ module Homebrew
     if ARGV.named.empty?
       puts HOMEBREW_CACHE
     else
-      ARGV.formulae.each do |f|
+      Homebrew.args.formulae.each do |f|
         if Fetch.fetch_bottle?(f)
           puts f.bottle.cached_download
         else
diff --git a/Library/Homebrew/cmd/--env.rb b/Library/Homebrew/cmd/--env.rb
index f1c3e3e94fcfd0a4cc65006d6ecda1f5b37c8621..8da098ea266dae4037f996a43f903abb50f4eb74 100644
--- a/Library/Homebrew/cmd/--env.rb
+++ b/Library/Homebrew/cmd/--env.rb
@@ -30,7 +30,7 @@ module Homebrew
     __env_args.parse
 
     ENV.activate_extensions!
-    ENV.deps = ARGV.formulae if superenv?
+    ENV.deps = Homebrew.args.formulae if superenv?
     ENV.setup_build_environment
     ENV.universal_binary if ARGV.build_universal?
 
diff --git a/Library/Homebrew/cmd/cat.rb b/Library/Homebrew/cmd/cat.rb
index c7f2f4c790303a2a7eb5bcc583da7cf5167f9fe6..1f2386da673423dce83382b605f4193394b4e64c 100644
--- a/Library/Homebrew/cmd/cat.rb
+++ b/Library/Homebrew/cmd/cat.rb
@@ -20,7 +20,7 @@ module Homebrew
     # do not "fix" this to support multiple arguments, the output would be
     # unparsable, if the user wants to cat multiple formula they can call
     # brew cat multiple times.
-    formulae = ARGV.formulae
+    formulae = Homebrew.args.formulae
     raise FormulaUnspecifiedError if formulae.empty?
     raise "`brew cat` doesn't support multiple arguments" if args.remaining.size > 1
 
diff --git a/Library/Homebrew/cmd/deps.rb b/Library/Homebrew/cmd/deps.rb
index fe6484a025eaa8cfe37a8759f021bccf13c53ee7..3031f9d2aa75f0e31766e9b93493177ae08d56cc 100644
--- a/Library/Homebrew/cmd/deps.rb
+++ b/Library/Homebrew/cmd/deps.rb
@@ -67,16 +67,16 @@ module Homebrew
       if args.installed?
         puts_deps_tree Formula.installed.sort, recursive
       else
-        raise FormulaUnspecifiedError if args.remaining.empty?
+        raise FormulaUnspecifiedError if Homebrew.args.remaining.empty?
 
-        puts_deps_tree ARGV.formulae, recursive
+        puts_deps_tree Homebrew.args.formulae, recursive
       end
       return
     elsif args.all?
       puts_deps Formula.sort, recursive
       return
-    elsif !args.remaining.empty? && args.for_each?
-      puts_deps ARGV.formulae, recursive
+    elsif !Homebrew.args.remaining.empty? && args.for_each?
+      puts_deps Homebrew.args.formulae, recursive
       return
     end
 
@@ -88,14 +88,14 @@ module Homebrew
                                 !args.include_optional? &&
                                 !args.skip_recommended?
 
-    if args.remaining.empty?
+    if Homebrew.args.remaining.empty?
       raise FormulaUnspecifiedError unless args.installed?
 
       puts_deps Formula.installed.sort, recursive
       return
     end
 
-    all_deps = deps_for_formulae(ARGV.formulae, recursive, &(args.union? ? :| : :&))
+    all_deps = deps_for_formulae(Homebrew.args.formulae, recursive, &(args.union? ? :| : :&))
     all_deps = condense_requirements(all_deps)
     all_deps.select!(&:installed?) if args.installed?
     all_deps.map!(&method(:dep_display_name))
diff --git a/Library/Homebrew/cmd/desc.rb b/Library/Homebrew/cmd/desc.rb
index 3512712047af60707420b8da6e829c1f2bd1cabb..46f50430a60a6988334385497722fc54be2afdfb 100644
--- a/Library/Homebrew/cmd/desc.rb
+++ b/Library/Homebrew/cmd/desc.rb
@@ -50,7 +50,7 @@ module Homebrew
       raise FormulaUnspecifiedError if ARGV.named.empty?
 
       desc = {}
-      ARGV.formulae.each { |f| desc[f.full_name] = f.desc }
+      Homebrew.args.formulae.each { |f| desc[f.full_name] = f.desc }
       Descriptions.new(desc)
     else
       arg = ARGV.named.join(" ")
diff --git a/Library/Homebrew/cmd/fetch.rb b/Library/Homebrew/cmd/fetch.rb
index e9159f504d86d98afafe07eb834ab2a8559328a0..ee91163281fe80a1d63c7adc2c26c2242d8420be 100644
--- a/Library/Homebrew/cmd/fetch.rb
+++ b/Library/Homebrew/cmd/fetch.rb
@@ -49,13 +49,13 @@ module Homebrew
 
     if args.deps?
       bucket = []
-      ARGV.formulae.each do |f|
+      Homebrew.args.formulae.each do |f|
         bucket << f
         bucket.concat f.recursive_dependencies.map(&:to_formula)
       end
       bucket.uniq!
     else
-      bucket = ARGV.formulae
+      bucket = Homebrew.args.formulae
     end
 
     puts "Fetching: #{bucket * ", "}" if bucket.size > 1
diff --git a/Library/Homebrew/cmd/home.rb b/Library/Homebrew/cmd/home.rb
index 8746c0feed07a7c5c5934ee99c448207501033e3..98099c090a8e0ee8ee9eda7b9628f952c2bc88a1 100644
--- a/Library/Homebrew/cmd/home.rb
+++ b/Library/Homebrew/cmd/home.rb
@@ -23,7 +23,7 @@ module Homebrew
     if args.remaining.empty?
       exec_browser HOMEBREW_WWW
     else
-      exec_browser(*ARGV.formulae.map(&:homepage))
+      exec_browser(*Homebrew.args.formulae.map(&:homepage))
     end
   end
 end
diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb
index ad04e74dac8c6c4b8ea6382d41ce09e47d91ebbc..94a82c68b0f4da33436727e91d55803d37a20e87 100644
--- a/Library/Homebrew/cmd/info.rb
+++ b/Library/Homebrew/cmd/info.rb
@@ -80,7 +80,7 @@ module Homebrew
 
       print_json
     elsif args.github?
-      exec_browser(*ARGV.formulae.map { |f| github_info(f) })
+      exec_browser(*Homebrew.args.formulae.map { |f| github_info(f) })
     else
       print_info
     end
@@ -129,7 +129,7 @@ module Homebrew
     elsif args.installed?
       Formula.installed.sort
     else
-      ARGV.formulae
+      Homebrew.args.formulae
     end
     json = ff.map(&:to_hash)
     puts JSON.generate(json)
diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb
index 8c61e87d647ab8ad0d4f11f5272cf2c3f12686e3..a3c428c7543a659ae52b5c023ff19b3cab83b70e 100644
--- a/Library/Homebrew/cmd/install.rb
+++ b/Library/Homebrew/cmd/install.rb
@@ -130,7 +130,7 @@ module Homebrew
     # developer tools are available, we need to stop them early on
     FormulaInstaller.prevent_build_flags unless DevelopmentTools.installed?
 
-    ARGV.formulae.each do |f|
+    Homebrew.args.formulae.each do |f|
       # head-only without --HEAD is an error
       if !Homebrew.args.HEAD? && f.stable.nil? && f.devel.nil?
         raise <<~EOS
diff --git a/Library/Homebrew/cmd/options.rb b/Library/Homebrew/cmd/options.rb
index 99948ef6f75bfd771d82890e984491de1bf11aa5..c42f9f06618e7825dd89d182bbadaeb0e33a16b2 100644
--- a/Library/Homebrew/cmd/options.rb
+++ b/Library/Homebrew/cmd/options.rb
@@ -35,7 +35,7 @@ module Homebrew
     else
       raise FormulaUnspecifiedError if args.remaining.empty?
 
-      puts_options ARGV.formulae
+      puts_options Homebrew.args.formulae
     end
   end
 
diff --git a/Library/Homebrew/cmd/style.rb b/Library/Homebrew/cmd/style.rb
index 77c9a0d67f11d920dc702f12916f71d3fe4d4fdb..560329f679e6ae41b0ee9d5720ef845088f64c49 100644
--- a/Library/Homebrew/cmd/style.rb
+++ b/Library/Homebrew/cmd/style.rb
@@ -45,7 +45,7 @@ module Homebrew
     elsif ARGV.named.any? { |tap| tap.count("/") == 1 }
       ARGV.named.map { |tap| Tap.fetch(tap).path }
     else
-      ARGV.formulae.map(&:path)
+      Homebrew.args.formulae.map(&:path)
     end
 
     only_cops = args.only_cops
diff --git a/Library/Homebrew/cmd/unpack.rb b/Library/Homebrew/cmd/unpack.rb
index 8b49a963bb818dd1814d168c319ef052366e216d..522fc5a891ad579e0bc42620bbe42dded817e376 100644
--- a/Library/Homebrew/cmd/unpack.rb
+++ b/Library/Homebrew/cmd/unpack.rb
@@ -32,7 +32,7 @@ module Homebrew
   def unpack
     unpack_args.parse
 
-    formulae = ARGV.formulae
+    formulae = Homebrew.args.formulae
     raise FormulaUnspecifiedError if formulae.empty?
 
     if dir = args.destdir
diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb
index ef083c50552d1bff60825a4d1b58dcffe323b398..9f6e235bf27aa0399e7545e06242711c733d12ce 100644
--- a/Library/Homebrew/cmd/uses.rb
+++ b/Library/Homebrew/cmd/uses.rb
@@ -50,7 +50,7 @@ module Homebrew
 
     used_formulae_missing = false
     used_formulae = begin
-      ARGV.formulae
+      Homebrew.args.formulae
     rescue FormulaUnavailableError => e
       opoo e
       used_formulae_missing = true
diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb
index dfd59f3a2385c8fc46d5dcd170807148aa0c5c2c..e984152e78d12e4e455c4edd7cd8a41b5e2fb8da 100644
--- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb
+++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb
@@ -114,7 +114,7 @@ module Homebrew
     # Use the user's browser, too.
     ENV["BROWSER"] = ENV["HOMEBREW_BROWSER"]
 
-    formula = ARGV.formulae.first
+    formula = Homebrew.args.formulae.first
 
     if formula
       tap_full_name, origin_branch, previous_branch = use_correct_linux_tap(formula)
diff --git a/Library/Homebrew/dev-cmd/bump-revision.rb b/Library/Homebrew/dev-cmd/bump-revision.rb
index af34df763add9dd9e737aac208c9fe4d50d3ceef..ef5f4aee7bab7aec04152a265355b34603489bc6 100644
--- a/Library/Homebrew/dev-cmd/bump-revision.rb
+++ b/Library/Homebrew/dev-cmd/bump-revision.rb
@@ -32,10 +32,11 @@ module Homebrew
     # user path, too.
     ENV["PATH"] = ENV["HOMEBREW_PATH"]
 
-    raise FormulaUnspecifiedError if ARGV.formulae.empty?
-    raise "Multiple formulae given, only one is allowed." if ARGV.formulae.length > 1
+    formulae = Homebrew.args.formulae
+    raise FormulaUnspecifiedError if formulae.empty?
+    raise "Multiple formulae given, only one is allowed." if formulae.length > 1
 
-    formula = ARGV.formulae.first
+    formula = formulae.first
     current_revision = formula.revision
 
     if current_revision.zero?
diff --git a/Library/Homebrew/dev-cmd/mirror.rb b/Library/Homebrew/dev-cmd/mirror.rb
index c90c9f98cb9d2a84678530c7fe0f2271f49ed5b1..464d96b5b1cd8213e47c51dda42e21b0e5fc84e7 100644
--- a/Library/Homebrew/dev-cmd/mirror.rb
+++ b/Library/Homebrew/dev-cmd/mirror.rb
@@ -27,7 +27,7 @@ module Homebrew
     bintray_key = ENV["HOMEBREW_BINTRAY_KEY"]
     raise "Missing HOMEBREW_BINTRAY_USER or HOMEBREW_BINTRAY_KEY variables!" if !bintray_user || !bintray_key
 
-    ARGV.formulae.each do |f|
+    Homebrew.args.formulae.each do |f|
       bintray_package = Utils::Bottles::Bintray.package f.name
       bintray_repo_url = "https://api.bintray.com/packages/homebrew/mirror"
       package_url = "#{bintray_repo_url}/#{bintray_package}"
diff --git a/Library/Homebrew/test/cli/parser_spec.rb b/Library/Homebrew/test/cli/parser_spec.rb
index 8a326d52a772642ffccd9026af78db5b7c75b6d0..3061e484e88dbcfffe7c9667b3f9835958666009 100644
--- a/Library/Homebrew/test/cli/parser_spec.rb
+++ b/Library/Homebrew/test/cli/parser_spec.rb
@@ -236,6 +236,21 @@ describe Homebrew::CLI::Parser do
       expect(Homebrew.args.passthrough).to eq %w[--foo --bar=value -s]
     end
 
+    it "#formulae raises an error when a Formula is unavailable" do
+      parser.parse(["mxcl"])
+      expect { Homebrew.args.formulae }.to raise_error FormulaUnavailableError
+    end
+
+    it "#formulae returns an empty array when there are no Formulae" do
+      parser.parse([])
+      expect(Homebrew.args.formulae).to be_empty
+    end
+
+    it "#casks returns an empty array when there are no matching casks" do
+      parser.parse([])
+      expect(Homebrew.args.casks).to eq []
+    end
+
     context "kegs" do
       before do
         keg = HOMEBREW_CELLAR + "mxcl/10.0"
@@ -252,5 +267,15 @@ describe Homebrew::CLI::Parser do
         expect(Homebrew.args.kegs).to be_empty
       end
     end
+
+    it "#named returns an array of non-option arguments" do
+      parser.parse(["foo", "-v", "-s"])
+      expect(Homebrew.args.named).to eq ["foo"]
+    end
+
+    it "#named returns an empty array when there are no named arguments" do
+      parser.parse([])
+      expect(Homebrew.args.named).to be_empty
+    end
   end
 end