Module: Rails::Hyperdrive::BundlerArtifactDiscovery

Defined in:
lib/rails/hyperdrive/bundler_artifact_discovery.rb

Defined Under Namespace

Classes: Artifact

Constant Summary collapse

SKILL_FILE_NAMES =
["SKILL.md", "SKILL.md.erb"].freeze

Class Method Summary collapse

Class Method Details

.discover(specs: nil, warnings: [], enabled_gems: [], notices: []) ⇒ Object

Non-fatal problems are appended to warnings and the artifact is dropped; discovery never raises. A gem that has not opted in as a companion is never scanned — its skills.sh content is only reported through notices.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 40

def discover(specs: nil, warnings: [], enabled_gems: [], notices: [])
  specs ||= safe_bundler_specs
  enabled = Array(enabled_gems).map(&:to_s)
  resolved = specs.each_with_object({}) { |s, h| h[s.name.to_s] = s.version }

  candidates = []
  specs.each do |spec|
    unless opted_in?(spec, enabled_gems: enabled)
      notice_skills_sh_content(spec, notices: notices)
      next
    end
    manifest = GemManifest.load(spec, warnings: warnings)
    seen = { skill: [], guideline: [] }
    each_artifact_path(spec, warnings: warnings) do |path, type, support_root, key|
      seen[type] << key
      gate = type == :skill ? manifest.skill_gate(key) : manifest.guideline_gate(key)
      artifact = parse(path, source_spec: spec, type: type, resolved: resolved,
        warnings: warnings, support_root: support_root, gate: gate)
      candidates << artifact if artifact
    end
    warn_unknown_manifest_keys(manifest, spec, seen, warnings)
  end

  # Collapse same-name variants within one source gem (highest
  # spec_version, path as tiebreak); never across sources — composite
  # identity is (name, source_gem).
  candidates.group_by { |a| [a.name, a.source_gem, a.artifact_type] }.map do |_key, group|
    group.max_by { |a| [Gem::Version.new(a.spec_version), a.path] }
  end
end

.install_ready_body(artifact) ⇒ Object

Guideline frontmatter is stripped because the installed file is @-included eagerly into agent context, where it would be inert noise.



376
377
378
379
380
381
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 376

def install_ready_body(artifact)
  return artifact.body if artifact.skill?

  _frontmatter, rest = split_frontmatter(artifact.body)
  (rest || artifact.body).sub(/\A\n+/, "")
end

.split_frontmatter(body) ⇒ Object



383
384
385
386
387
388
389
390
391
392
# File 'lib/rails/hyperdrive/bundler_artifact_discovery.rb', line 383

def split_frontmatter(body)
  lines = body.lines
  return [nil, body] unless lines.first&.strip == "---"

  closing_index = lines[1..].index { |l| l.strip == "---" }
  return [nil, body] unless closing_index

  absolute_closing = closing_index + 1
  [lines[1...absolute_closing].join, lines[(absolute_closing + 1)..].join]
end