Class: Rails::Generators::Hyperdrive::InstallGenerator

Inherits:
Base
  • Object
show all
Includes:
ContentSyncSupport, GitignoreSupport
Defined in:
lib/generators/hyperdrive/install/install_generator.rb

Constant Summary collapse

ENGINE_MOUNT_TOKEN =
"Rails::Hyperdrive::Engine"
DEFAULT_MOUNT_AT =
"/_hyperdrive".freeze
MCP_JSON_PATH =
".mcp.json".freeze
MCP_SERVER_KEY =
"rails-hyperdrive".freeze
GEMFILE =
"Gemfile".freeze
BUNDLER_PLUGIN =
"bundler-hyperdrive".freeze

Constants included from GitignoreSupport

GitignoreSupport::GITIGNORE

Instance Method Summary collapse

Methods included from GitignoreSupport

#ensure_gitignored

Methods included from ContentSyncSupport

#options

Instance Method Details

#discover_artifactsObject



38
39
40
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 38

def discover_artifacts
  runner.discover_artifacts(skip: options[:skip_content])
end

#ignore_discover_cacheObject



61
62
63
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 61

def ignore_discover_cache
  ensure_gitignored(::Rails::Hyperdrive::CompanionDiscovery::CACHE_RELATIVE_PATH)
end

#mount_engineObject



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 85

def mount_engine
  return if options[:skip_mcp]

  routes_file = "config/routes.rb"
  unless File.exist?(::Rails.root.join(routes_file))
    say_status :skip, "no #{routes_file} found; skipping engine mount", :yellow
    return
  end

  contents = File.read(::Rails.root.join(routes_file))
  if contents.include?(ENGINE_MOUNT_TOKEN)
    say_status :identical, "#{routes_file} (engine already mounted)", :blue
    return
  end

  snippet = "  mount Rails::Hyperdrive::Engine => \"#{mount_path}\" if Rails.env.development?\n"
  inject_into_file routes_file, snippet, after: /Rails\.application\.routes\.draw do\s*\n/
end


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 112

def print_summary
  say ""
  say_status :done, "hyperdrive initialized", :green
  if options[:skip_mcp]
    say "  MCP: skipped (--skip-mcp)"
  else
    say "  Mount: #{mount_path} (in config/routes.rb)"
    say "  Server: #{::Rails::Hyperdrive::McpServer::TOOLS.size} MCP tools at http://localhost:3000#{mount_path}/mcp"
  end
  runner.summary_lines.each { |line| say line } unless options[:skip_content]
  # Every next step is about reaching the MCP endpoint.
  return if options[:skip_mcp]

  say ""
  say "  Next steps:"
  say "    1. bin/rails server"
  say "    2. Open Claude Code in this directory; it will read .mcp.json"
  say "    3. Verify the connection: #{connection_check_command}"
end

#register_bundler_pluginObject

Any existing directive counts as registered — a path- or version-qualified line is a deliberate choice this must not duplicate or rewrite.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 68

def register_bundler_plugin
  gemfile = ::Rails.root.join(GEMFILE)
  unless File.exist?(gemfile)
    say_status :skip, "no #{GEMFILE} found; add plugin #{BUNDLER_PLUGIN.inspect} manually", :yellow
    return
  end

  body = File.read(gemfile)
  if body.match?(/^\s*plugin\s+["']#{BUNDLER_PLUGIN}["']/)
    say_status :identical, "#{GEMFILE} (#{BUNDLER_PLUGIN} plugin already registered)", :blue
    return
  end

  prefix = body.end_with?("\n") || body.empty? ? "" : "\n"
  append_to_file GEMFILE, "#{prefix}plugin #{BUNDLER_PLUGIN.inspect}\n"
end

#sync_contentObject

--skip-content writes no lockfile either: the lock is a manifest of installed content, and an empty one would assert "zero files is the managed set". A later init or sync reconstructs the full state.



107
108
109
110
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 107

def sync_content
  return if options[:skip_content]
  runner.install(mode: :preserve)
end

#verify_environmentObject



34
35
36
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 34

def verify_environment
  runner.verify_environment!
end

#write_mcp_jsonObject

The write is forced: Thor's conflict prompt would otherwise block the run waiting on stdin.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 44

def write_mcp_json
  return if options[:skip_mcp]

  existing = mcp_json_on_disk
  document = existing ? parse_mcp_json(existing) : {}
  return if document.nil?

  (document["mcpServers"] ||= {})[MCP_SERVER_KEY] = mcp_server_entry
  content = JSON.pretty_generate(document) + "\n"

  if existing == content
    say_status :unchanged, MCP_JSON_PATH, :blue
  else
    create_file MCP_JSON_PATH, content, force: true
  end
end