There is! Instead of using inheritance, you can solve this problem with composition - aka “mixins”.
We want to extract the common options into a module, and include that module into each command.
If you want to see how this would work, just skip to the section Solution. If you’d like a brief journey on how you might have figured this out yourself, read on 
Attempt #1: Just Put The Code In a Module
If we try this naively, it won’t work:
module CommonOptions
  option :host, type: :string, default: 'localhost', desc: 'set the host'
end
class A < Dry::CLI::Command
  ...
  include CommonOptions
end
# .my-cli a --host "8.8.8.8"
# 🔥 undefined method `option' for CommonOptions:Module (NoMethodError)
option is actually a method call, and the module gets evaluated outside a Dry::CLI::Command.  so it has no idea what that method should be! We want the module code to be evaluated only when inside the Command object.
Attempt #2: eval? 
Our instincts tell us that this might require some kind of eval, and our instincts would be right. So we try this:
module CommonOptions
  self.instance_eval do
    option :host, type: :string, default: 'localhost', desc: 'set the host'
  end
end
# ...
# 🔥 undefined method `option' for CommonOptions:Module (NoMethodError)
but this doesn’t help. (In fact, conceptually, this is almost exactly the same as Attempt 1! Why is that? 
 ) Calling instance_eval on ourself doesn’t work - it’s too early! We’re still operating within the context of the module, and the module still doesn’t know what option means. We want this to be executed later, in the context of the including Command. But how would we know which Command that is?
Solution: Module.included
Modules in Ruby can define callback methods, which are called whenever certain events happen. One of those is included, which is called whenever (you guessed it) its module gets included by something - exactly what we want!
If we define this method for our Options module, we can evaluate that call to option inside the Command, where it’s defined:
module CommonOptions
  # Called when CommonOptions is included somewhere.
  # mod is the object (a Class, or Module) doing the 
  # including
  def self.included(mod)
    mod.instance_eval do
      option :host, type: :string, default: 'localhost', desc: 'set the host'
    end
  end
end
And this works!
class A < Dry::CLI::Command
  include CommonOptions
  def call(host:)
    puts "A: #{host}"
  end
end
class B < Dry::CLI::Command
  include CommonOptions
  def call(host:)
    puts "B: #{host}"
  end
end
# my-cli b --host "8.8.8.8"
#  ✅ => "B: 8.8.8.8"
# my-cli a
#  ✅ => "A: localhost"
PS: Side notes
Some things to ponder: (perhaps! I don’t know what you already know - this is not meant to be at all condescending!)
- What’s the difference between just putting the code in a module (Attempt 1) and wrapping the code in 
self.instance_eval (Attempt 2)? What does this tell you about what instance_eval means? 
- We noticed that each 
option line is actually a method call. But on what object? And if it’s a method, how did it get defined?