diff --git a/.rubocop.yml b/.rubocop.yml
index 46cf0275f..4eef9d63e 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -43,7 +43,7 @@ Layout/LineLength:
     - !ruby/regexp / \# .*$/
     - !ruby/regexp /^\# .*$/
   Exclude:
-    - lib/**/*cli*.rb
+    - 'lib/mastodon/cli/*.rb'
     - db/*migrate/**/*
     - db/seeds/**/*
 
@@ -57,7 +57,7 @@ Lint/UselessAccessModifier:
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize
 Metrics/AbcSize:
   Exclude:
-    - 'lib/**/*cli*.rb'
+    - 'lib/mastodon/cli/*.rb'
     - db/*migrate/**/*
 
 # Reason: Some functions cannot be broken up, but others may be refactor candidates
@@ -66,7 +66,7 @@ Metrics/BlockLength:
   CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
   Exclude:
     - 'config/routes.rb'
-    - 'lib/mastodon/*_cli.rb'
+    - 'lib/mastodon/cli/*.rb'
     - 'lib/tasks/*.rake'
     - 'app/models/concerns/account_associations.rb'
     - 'app/models/concerns/account_interactions.rb'
@@ -95,14 +95,14 @@ Metrics/BlockLength:
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting
 Metrics/BlockNesting:
   Exclude:
-    - 'lib/mastodon/*_cli.rb'
+    - 'lib/mastodon/cli/*.rb'
 
 # Reason: Some Excluded files would be candidates for refactoring but not currently addressed
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
 Metrics/ClassLength:
   CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
   Exclude:
-    - 'lib/mastodon/*_cli.rb'
+    - 'lib/mastodon/cli/*.rb'
     - 'app/controllers/admin/accounts_controller.rb'
     - 'app/controllers/api/base_controller.rb'
     - 'app/controllers/api/v1/admin/accounts_controller.rb'
@@ -146,7 +146,7 @@ Metrics/ClassLength:
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity
 Metrics/CyclomaticComplexity:
   Exclude:
-    - lib/mastodon/*cli*.rb
+    - lib/mastodon/cli/*.rb
     - db/*migrate/**/*
 
 # Reason: Currently disabled in .rubocop_todo.yml
@@ -154,7 +154,7 @@ Metrics/CyclomaticComplexity:
 Metrics/MethodLength:
   CountAsOne: [array, heredoc]
   Exclude:
-    - 'lib/mastodon/*_cli.rb'
+    - 'lib/mastodon/cli/*.rb'
 
 # Reason:
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
@@ -176,9 +176,7 @@ Rails/HttpStatus:
 Rails/Exit:
   Exclude:
     - 'config/boot.rb'
-    - 'lib/mastodon/*_cli.rb'
-    - 'lib/mastodon/cli_helper.rb'
-    - 'lib/cli.rb'
+    - 'lib/mastodon/cli/*.rb'
 
 # Reason: Some single letter camel case files shouldn't be split
 # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 22412a053..94439ce47 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -846,9 +846,9 @@ Rails/SkipsModelValidations:
     - 'db/post_migrate/20220617202502_migrate_roles.rb'
     - 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
     - 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
-    - 'lib/cli.rb'
-    - 'lib/mastodon/accounts_cli.rb'
-    - 'lib/mastodon/maintenance_cli.rb'
+    - 'lib/mastodon/cli/accounts.rb'
+    - 'lib/mastodon/cli/main.rb'
+    - 'lib/mastodon/cli/maintenance.rb'
     - 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
     - 'spec/lib/activitypub/activity/follow_spec.rb'
     - 'spec/services/follow_service_spec.rb'
@@ -929,7 +929,7 @@ Rails/WhereExists:
     - 'app/validators/vote_validator.rb'
     - 'app/workers/move_worker.rb'
     - 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
-    - 'lib/mastodon/email_domain_blocks_cli.rb'
+    - 'lib/mastodon/cli/email_domain_blocks.rb'
     - 'lib/tasks/tests.rake'
     - 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
     - 'spec/controllers/api/v1/tags_controller_spec.rb'
@@ -991,7 +991,7 @@ Style/FormatStringToken:
   Exclude:
     - 'app/models/privacy_policy.rb'
     - 'config/initializers/devise.rb'
-    - 'lib/mastodon/maintenance_cli.rb'
+    - 'lib/mastodon/cli/maintenance.rb'
     - 'lib/paperclip/color_extractor.rb'
 
 # This cop supports unsafe autocorrection (--autocorrect-all).
@@ -1436,9 +1436,9 @@ Style/GuardClause:
     - 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb'
     - 'lib/devise/two_factor_ldap_authenticatable.rb'
     - 'lib/devise/two_factor_pam_authenticatable.rb'
-    - 'lib/mastodon/accounts_cli.rb'
-    - 'lib/mastodon/maintenance_cli.rb'
-    - 'lib/mastodon/media_cli.rb'
+    - 'lib/mastodon/cli/accounts.rb'
+    - 'lib/mastodon/cli/maintenance.rb'
+    - 'lib/mastodon/cli/media.rb'
     - 'lib/paperclip/attachment_extensions.rb'
     - 'lib/tasks/repo.rake'
 
diff --git a/bin/tootctl b/bin/tootctl
index 9c7ae8b87..b3311c6b2 100755
--- a/bin/tootctl
+++ b/bin/tootctl
@@ -2,11 +2,11 @@
 APP_PATH = File.expand_path('../config/application', __dir__)
 
 require_relative '../config/boot'
-require_relative '../lib/cli'
+require_relative '../lib/mastodon/cli/main'
 
 begin
   Chewy.strategy(:mastodon) do
-    Mastodon::CLI.start(ARGV)
+    Mastodon::CLI::Main.start(ARGV)
   end
 rescue Interrupt
   exit(130)
diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/cli/accounts.rb
similarity index 99%
rename from lib/mastodon/accounts_cli.rb
rename to lib/mastodon/cli/accounts.rb
index c09577c9e..a44f63db0 100644
--- a/lib/mastodon/accounts_cli.rb
+++ b/lib/mastodon/cli/accounts.rb
@@ -1,13 +1,13 @@
 # frozen_string_literal: true
 
 require 'set'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class AccountsCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Accounts < Thor
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/lib/mastodon/cache_cli.rb b/lib/mastodon/cli/cache.rb
similarity index 91%
rename from lib/mastodon/cache_cli.rb
rename to lib/mastodon/cli/cache.rb
index 803404c34..b0bf6aabd 100644
--- a/lib/mastodon/cache_cli.rb
+++ b/lib/mastodon/cli/cache.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class CacheCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Cache < Thor
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/lib/mastodon/canonical_email_blocks_cli.rb b/lib/mastodon/cli/canonical_email_blocks.rb
similarity index 86%
rename from lib/mastodon/canonical_email_blocks_cli.rb
rename to lib/mastodon/cli/canonical_email_blocks.rb
index ec228d466..f3c427a2c 100644
--- a/lib/mastodon/canonical_email_blocks_cli.rb
+++ b/lib/mastodon/cli/canonical_email_blocks.rb
@@ -1,13 +1,13 @@
 # frozen_string_literal: true
 
 require 'concurrent'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class CanonicalEmailBlocksCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class CanonicalEmailBlocks < Thor
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/lib/mastodon/domains_cli.rb b/lib/mastodon/cli/domains.rb
similarity index 98%
rename from lib/mastodon/domains_cli.rb
rename to lib/mastodon/cli/domains.rb
index 05f08f462..8b5f03ce0 100644
--- a/lib/mastodon/domains_cli.rb
+++ b/lib/mastodon/cli/domains.rb
@@ -1,13 +1,13 @@
 # frozen_string_literal: true
 
 require 'concurrent'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class DomainsCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Domains < Thor
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/lib/mastodon/email_domain_blocks_cli.rb b/lib/mastodon/cli/email_domain_blocks.rb
similarity index 94%
rename from lib/mastodon/email_domain_blocks_cli.rb
rename to lib/mastodon/cli/email_domain_blocks.rb
index f39f47069..6ef35cc8c 100644
--- a/lib/mastodon/email_domain_blocks_cli.rb
+++ b/lib/mastodon/cli/email_domain_blocks.rb
@@ -1,13 +1,13 @@
 # frozen_string_literal: true
 
 require 'concurrent'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class EmailDomainBlocksCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class EmailDomainBlocks < Thor
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/lib/mastodon/emoji_cli.rb b/lib/mastodon/cli/emoji.rb
similarity index 96%
rename from lib/mastodon/emoji_cli.rb
rename to lib/mastodon/cli/emoji.rb
index 8f2432a3e..9b41a59c2 100644
--- a/lib/mastodon/emoji_cli.rb
+++ b/lib/mastodon/cli/emoji.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
 require 'rubygems/package'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class EmojiCLI < Thor
+module Mastodon::CLI
+  class Emoji < Thor
     def self.exit_on_failure?
       true
     end
diff --git a/lib/mastodon/feeds_cli.rb b/lib/mastodon/cli/feeds.rb
similarity index 90%
rename from lib/mastodon/feeds_cli.rb
rename to lib/mastodon/cli/feeds.rb
index fcfb48740..4307b880f 100644
--- a/lib/mastodon/feeds_cli.rb
+++ b/lib/mastodon/cli/feeds.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class FeedsCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Feeds < Thor
+    include Helper
     include Redisable
 
     def self.exit_on_failure?
diff --git a/lib/mastodon/cli_helper.rb b/lib/mastodon/cli/helper.rb
similarity index 98%
rename from lib/mastodon/cli_helper.rb
rename to lib/mastodon/cli/helper.rb
index ab1351ae8..b277e16eb 100644
--- a/lib/mastodon/cli_helper.rb
+++ b/lib/mastodon/cli/helper.rb
@@ -9,8 +9,8 @@ HttpLog.configuration.logger = dev_null
 Paperclip.options[:log]      = false
 Chewy.logger                 = dev_null
 
-module Mastodon
-  module CLIHelper
+module Mastodon::CLI
+  module Helper
     def dry_run?
       options[:dry_run]
     end
diff --git a/lib/mastodon/ip_blocks_cli.rb b/lib/mastodon/cli/ip_blocks.rb
similarity index 96%
rename from lib/mastodon/ip_blocks_cli.rb
rename to lib/mastodon/cli/ip_blocks.rb
index 82a08753b..561d64d18 100644
--- a/lib/mastodon/ip_blocks_cli.rb
+++ b/lib/mastodon/cli/ip_blocks.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
 require 'rubygems/package'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class IpBlocksCLI < Thor
+module Mastodon::CLI
+  class IpBlocks < Thor
     def self.exit_on_failure?
       true
     end
diff --git a/lib/cli.rb b/lib/mastodon/cli/main.rb
similarity index 75%
rename from lib/cli.rb
rename to lib/mastodon/cli/main.rb
index ac235cf03..0f2236f78 100644
--- a/lib/cli.rb
+++ b/lib/mastodon/cli/main.rb
@@ -1,73 +1,73 @@
 # frozen_string_literal: true
 
 require 'thor'
-require_relative 'mastodon/media_cli'
-require_relative 'mastodon/emoji_cli'
-require_relative 'mastodon/accounts_cli'
-require_relative 'mastodon/feeds_cli'
-require_relative 'mastodon/search_cli'
-require_relative 'mastodon/settings_cli'
-require_relative 'mastodon/statuses_cli'
-require_relative 'mastodon/domains_cli'
-require_relative 'mastodon/preview_cards_cli'
-require_relative 'mastodon/cache_cli'
-require_relative 'mastodon/upgrade_cli'
-require_relative 'mastodon/email_domain_blocks_cli'
-require_relative 'mastodon/canonical_email_blocks_cli'
-require_relative 'mastodon/ip_blocks_cli'
-require_relative 'mastodon/maintenance_cli'
-require_relative 'mastodon/version'
+require_relative 'media'
+require_relative 'emoji'
+require_relative 'accounts'
+require_relative 'feeds'
+require_relative 'search'
+require_relative 'settings'
+require_relative 'statuses'
+require_relative 'domains'
+require_relative 'preview_cards'
+require_relative 'cache'
+require_relative 'upgrade'
+require_relative 'email_domain_blocks'
+require_relative 'canonical_email_blocks'
+require_relative 'ip_blocks'
+require_relative 'maintenance'
+require_relative '../version'
 
-module Mastodon
-  class CLI < Thor
+module Mastodon::CLI
+  class Main < Thor
     def self.exit_on_failure?
       true
     end
 
     desc 'media SUBCOMMAND ...ARGS', 'Manage media files'
-    subcommand 'media', Mastodon::MediaCLI
+    subcommand 'media', Media
 
     desc 'emoji SUBCOMMAND ...ARGS', 'Manage custom emoji'
-    subcommand 'emoji', Mastodon::EmojiCLI
+    subcommand 'emoji', Emoji
 
     desc 'accounts SUBCOMMAND ...ARGS', 'Manage accounts'
-    subcommand 'accounts', Mastodon::AccountsCLI
+    subcommand 'accounts', Accounts
 
     desc 'feeds SUBCOMMAND ...ARGS', 'Manage feeds'
-    subcommand 'feeds', Mastodon::FeedsCLI
+    subcommand 'feeds', Feeds
 
     desc 'search SUBCOMMAND ...ARGS', 'Manage the search engine'
-    subcommand 'search', Mastodon::SearchCLI
+    subcommand 'search', Search
 
     desc 'settings SUBCOMMAND ...ARGS', 'Manage dynamic settings'
-    subcommand 'settings', Mastodon::SettingsCLI
+    subcommand 'settings', Settings
 
     desc 'statuses SUBCOMMAND ...ARGS', 'Manage statuses'
-    subcommand 'statuses', Mastodon::StatusesCLI
+    subcommand 'statuses', Statuses
 
     desc 'domains SUBCOMMAND ...ARGS', 'Manage account domains'
-    subcommand 'domains', Mastodon::DomainsCLI
+    subcommand 'domains', Domains
 
     desc 'preview_cards SUBCOMMAND ...ARGS', 'Manage preview cards'
-    subcommand 'preview_cards', Mastodon::PreviewCardsCLI
+    subcommand 'preview_cards', PreviewCards
 
     desc 'cache SUBCOMMAND ...ARGS', 'Manage cache'
-    subcommand 'cache', Mastodon::CacheCLI
+    subcommand 'cache', Cache
 
     desc 'upgrade SUBCOMMAND ...ARGS', 'Various version upgrade utilities'
-    subcommand 'upgrade', Mastodon::UpgradeCLI
+    subcommand 'upgrade', Upgrade
 
     desc 'email_domain_blocks SUBCOMMAND ...ARGS', 'Manage e-mail domain blocks'
-    subcommand 'email_domain_blocks', Mastodon::EmailDomainBlocksCLI
+    subcommand 'email_domain_blocks', EmailDomainBlocks
 
     desc 'ip_blocks SUBCOMMAND ...ARGS', 'Manage IP blocks'
-    subcommand 'ip_blocks', Mastodon::IpBlocksCLI
+    subcommand 'ip_blocks', IpBlocks
 
     desc 'canonical_email_blocks SUBCOMMAND ...ARGS', 'Manage canonical e-mail blocks'
-    subcommand 'canonical_email_blocks', Mastodon::CanonicalEmailBlocksCLI
+    subcommand 'canonical_email_blocks', CanonicalEmailBlocks
 
     desc 'maintenance SUBCOMMAND ...ARGS', 'Various maintenance utilities'
-    subcommand 'maintenance', Mastodon::MaintenanceCLI
+    subcommand 'maintenance', Maintenance
 
     option :dry_run, type: :boolean
     desc 'self-destruct', 'Erase the server from the federation'
diff --git a/lib/mastodon/maintenance_cli.rb b/lib/mastodon/cli/maintenance.rb
similarity index 99%
rename from lib/mastodon/maintenance_cli.rb
rename to lib/mastodon/cli/maintenance.rb
index 88bd191ea..d9f1b0257 100644
--- a/lib/mastodon/maintenance_cli.rb
+++ b/lib/mastodon/cli/maintenance.rb
@@ -1,13 +1,13 @@
 # frozen_string_literal: true
 
 require 'tty-prompt'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class MaintenanceCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Maintenance < Thor
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/cli/media.rb
similarity index 98%
rename from lib/mastodon/media_cli.rb
rename to lib/mastodon/cli/media.rb
index 1bedcd9be..509c4ca26 100644
--- a/lib/mastodon/media_cli.rb
+++ b/lib/mastodon/cli/media.rb
@@ -1,13 +1,13 @@
 # frozen_string_literal: true
 
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class MediaCLI < Thor
+module Mastodon::CLI
+  class Media < Thor
     include ActionView::Helpers::NumberHelper
-    include CLIHelper
+    include Helper
 
     VALID_PATH_SEGMENTS_SIZE = [7, 10].freeze
 
diff --git a/lib/mastodon/preview_cards_cli.rb b/lib/mastodon/cli/preview_cards.rb
similarity index 90%
rename from lib/mastodon/preview_cards_cli.rb
rename to lib/mastodon/cli/preview_cards.rb
index cf4407250..794b25693 100644
--- a/lib/mastodon/preview_cards_cli.rb
+++ b/lib/mastodon/cli/preview_cards.rb
@@ -1,14 +1,14 @@
 # frozen_string_literal: true
 
 require 'tty-prompt'
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class PreviewCardsCLI < Thor
+module Mastodon::CLI
+  class PreviewCards < Thor
     include ActionView::Helpers::NumberHelper
-    include CLIHelper
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/lib/mastodon/search_cli.rb b/lib/mastodon/cli/search.rb
similarity index 95%
rename from lib/mastodon/search_cli.rb
rename to lib/mastodon/cli/search.rb
index 31e9a3d5a..ccece5b5f 100644
--- a/lib/mastodon/search_cli.rb
+++ b/lib/mastodon/cli/search.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class SearchCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Search < Thor
+    include Helper
 
     # Indices are sorted by amount of data to be expected in each, so that
     # smaller indices can go online sooner
diff --git a/lib/mastodon/settings_cli.rb b/lib/mastodon/cli/settings.rb
similarity index 80%
rename from lib/mastodon/settings_cli.rb
rename to lib/mastodon/cli/settings.rb
index 488c655de..5869efc46 100644
--- a/lib/mastodon/settings_cli.rb
+++ b/lib/mastodon/cli/settings.rb
@@ -1,11 +1,11 @@
 # frozen_string_literal: true
 
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class RegistrationsCLI < Thor
+module Mastodon::CLI
+  class Registrations < Thor
     def self.exit_on_failure?
       true
     end
@@ -37,8 +37,8 @@ module Mastodon
     end
   end
 
-  class SettingsCLI < Thor
+  class Settings < Thor
     desc 'registrations SUBCOMMAND ...ARGS', 'Manage state of registrations'
-    subcommand 'registrations', RegistrationsCLI
+    subcommand 'registrations', Registrations
   end
 end
diff --git a/lib/mastodon/statuses_cli.rb b/lib/mastodon/cli/statuses.rb
similarity index 98%
rename from lib/mastodon/statuses_cli.rb
rename to lib/mastodon/cli/statuses.rb
index baab83e29..e5eb75960 100644
--- a/lib/mastodon/statuses_cli.rb
+++ b/lib/mastodon/cli/statuses.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class StatusesCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Statuses < Thor
+    include Helper
     include ActionView::Helpers::NumberHelper
 
     def self.exit_on_failure?
diff --git a/lib/mastodon/upgrade_cli.rb b/lib/mastodon/cli/upgrade.rb
similarity index 96%
rename from lib/mastodon/upgrade_cli.rb
rename to lib/mastodon/cli/upgrade.rb
index 2b60f9eee..f20fa4cdf 100644
--- a/lib/mastodon/upgrade_cli.rb
+++ b/lib/mastodon/cli/upgrade.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
-require_relative '../../config/boot'
-require_relative '../../config/environment'
-require_relative 'cli_helper'
+require_relative '../../../config/boot'
+require_relative '../../../config/environment'
+require_relative 'helper'
 
-module Mastodon
-  class UpgradeCLI < Thor
-    include CLIHelper
+module Mastodon::CLI
+  class Upgrade < Thor
+    include Helper
 
     def self.exit_on_failure?
       true
diff --git a/spec/lib/mastodon/ip_blocks_cli_spec.rb b/spec/lib/mastodon/cli/ip_blocks_spec.rb
similarity index 99%
rename from spec/lib/mastodon/ip_blocks_cli_spec.rb
rename to spec/lib/mastodon/cli/ip_blocks_spec.rb
index 27c005772..e7639d9aa 100644
--- a/spec/lib/mastodon/ip_blocks_cli_spec.rb
+++ b/spec/lib/mastodon/cli/ip_blocks_spec.rb
@@ -1,9 +1,9 @@
 # frozen_string_literal: true
 
 require 'rails_helper'
-require 'mastodon/ip_blocks_cli'
+require 'mastodon/cli/ip_blocks'
 
-RSpec.describe Mastodon::IpBlocksCLI do
+RSpec.describe Mastodon::CLI::IpBlocks do
   let(:cli) { described_class.new }
 
   describe '#add' do
diff --git a/spec/lib/mastodon/cli_spec.rb b/spec/lib/mastodon/cli/main_spec.rb
similarity index 81%
rename from spec/lib/mastodon/cli_spec.rb
rename to spec/lib/mastodon/cli/main_spec.rb
index 419f8b864..105c4ec37 100644
--- a/spec/lib/mastodon/cli_spec.rb
+++ b/spec/lib/mastodon/cli/main_spec.rb
@@ -1,9 +1,9 @@
 # frozen_string_literal: true
 
 require 'rails_helper'
-require 'cli'
+require 'mastodon/cli/main'
 
-describe Mastodon::CLI do
+describe Mastodon::CLI::Main do
   describe 'version' do
     it 'returns the Mastodon version' do
       expect { described_class.new.invoke(:version) }.to output(
diff --git a/spec/lib/mastodon/settings_cli_spec.rb b/spec/lib/mastodon/cli/settings_spec.rb
similarity index 92%
rename from spec/lib/mastodon/settings_cli_spec.rb
rename to spec/lib/mastodon/cli/settings_spec.rb
index 713cb7e43..01f077562 100644
--- a/spec/lib/mastodon/settings_cli_spec.rb
+++ b/spec/lib/mastodon/cli/settings_spec.rb
@@ -1,11 +1,11 @@
 # frozen_string_literal: true
 
 require 'rails_helper'
-require 'mastodon/settings_cli'
+require 'mastodon/cli/settings'
 
-RSpec.describe Mastodon::SettingsCLI do
+RSpec.describe Mastodon::CLI::Settings do
   describe 'subcommand "registrations"' do
-    let(:cli) { Mastodon::RegistrationsCLI.new }
+    let(:cli) { Mastodon::CLI::Registrations.new }
 
     before do
       Setting.registrations_mode = nil