Module: RailsInformant::ClaudeIntegrationContent

Defined in:
lib/rails_informant/claude_integration_content.rb

Overview

Single source of truth for the generated Claude Code integration content.

Both SkillGenerator (generation) and Integration (drift detection) build their fragments here so the two can never silently diverge. Intentionally free of any rails/generators (Thor) dependency: Integration runs on the host-app boot path via the engine initializer, which must not load Thor.

Constant Summary collapse

HOOK_SCRIPT_PATH =

Paths, relative to the host app root, of the files the generator writes.

".claude/hooks/informant-alerts.sh"
SKILL_PATH =
".claude/skills/informant/SKILL.md"
SETTINGS_PATH =
".claude/settings.json"
MCP_PATH =
".mcp.json"
HOOK_COMMAND =

The command string a settings.json hook entry uses to invoke the script. Match-by-path detection keys on this value across every event key.

HOOK_SCRIPT_PATH
HOOK_EVENT =

The event key the current gem registers the hook under.

"UserPromptSubmit"

Class Method Summary collapse

Class Method Details

.expected_registrationsObject

The informant-owned hooks map the generator produces: the registration keyed by its event. Detection compares the host app's extracted informant registrations against this, so a leftover entry under a different event key (e.g. a stale SessionStart) reads as drift.



64
65
66
# File 'lib/rails_informant/claude_integration_content.rb', line 64

def expected_registrations
  { HOOK_EVENT => [ hook_registration ] }
end

.gem_dirObject

Directory of the installed gem, resolved from RubyGems rather than a generator's source_root so it works on the host-app boot path. Never RailsInformant::VERSION — that constant is env-driven and resolves to the 0.0.0.dev fallback in host apps.



30
31
32
# File 'lib/rails_informant/claude_integration_content.rb', line 30

def gem_dir
  Gem.loaded_specs["rails-informant"].gem_dir
end

.hook_registrationObject

A single UserPromptSubmit hook registration for settings.json.



52
53
54
55
56
57
58
# File 'lib/rails_informant/claude_integration_content.rb', line 52

def hook_registration
  {
    "hooks" => [
      { "type" => "command", "command" => HOOK_COMMAND, "timeout" => 10 }
    ]
  }
end

.hook_scriptObject



38
39
40
# File 'lib/rails_informant/claude_integration_content.rb', line 38

def hook_script
  File.read File.join(templates_dir, "informant-alerts.sh")
end

.informant_hook_entry?(entry) ⇒ Boolean

Whether a settings.json hook entry targets the informant script — the shared match-by-path predicate used by both generation (to sweep stale registrations) and detection (to extract the informant fragment).

Returns:

  • (Boolean)


71
72
73
74
75
# File 'lib/rails_informant/claude_integration_content.rb', line 71

def informant_hook_entry?(entry)
  entry.is_a?(Hash) && Array(entry["hooks"]).any? do |hook|
    hook.is_a?(Hash) && hook["command"] == HOOK_COMMAND
  end
end

.mcp_entryObject

The informant server entry inside .mcp.json's "mcpServers".



47
48
49
# File 'lib/rails_informant/claude_integration_content.rb', line 47

def mcp_entry
  { "command" => "informant-mcp" }
end

.skill_markdownObject



42
43
44
# File 'lib/rails_informant/claude_integration_content.rb', line 42

def skill_markdown
  File.read File.join(templates_dir, "SKILL.md")
end

.templates_dirObject



34
35
36
# File 'lib/rails_informant/claude_integration_content.rb', line 34

def templates_dir
  File.join gem_dir, "lib", "generators", "rails_informant", "skill", "templates"
end