inventory

import "github.com/jbcom/radioactive-ralph/internal/inventory"

Package inventory discovers the operator’s installed Claude Code helper integrations, MCP servers, and subagents by walking the filesystem and parsing Claude Code’s settings files.

Discovery is pure shell / filesystem work. No Claude is involved — we don’t prompt any session to self-describe. This package runs during `radioactive_ralph init` so the operator can pick preferences for ambiguous helper categories (multiple review helpers, for instance) and is re-run at `radioactive_ralph run` start so the supervisor can filter variant biases against what’s actually installed at runtime.

Discovery sources, in order:

  1. User-level helpers at ~/.claude/skills/<name>/SKILL.md

  2. Plugin-bundled helpers at ~/.claude/plugins/cache/<marketplace>/ <plugin>/<version>/skills/<name>/SKILL.md

  3. User-level subagents at ~/.claude/agents/

  4. MCP servers declared in ~/.claude/settings.json “mcpServers”

  5. Project-local MCP servers from ./.claude/settings.json if the caller provides a repo root

  6. Plugin-declared MCP servers from each plugin’s plugin.json

Output is a stable JSON document written to the workspace inventory path (see xdg.Paths.Inventory) and re-readable by the supervisor.

Index

type Agent

Agent represents a Claude Code subagent declared under ~/.claude/agents/.

type Agent struct {
    Name   string `json:"name"`
    Source string `json:"source"`
}

type Environment

Environment captures free-floating signals the supervisor might want when deciding how to spawn sessions. Kept small and boolean-heavy so the inventory JSON stays readable.

type Environment struct {
    GhAuthenticated bool `json:"gh_authenticated"`
    InClaudeCode    bool `json:"in_claude_code"`
}

type Inventory

Inventory is the JSON-serialisable discovery snapshot. It’s the single source of truth for “what helper capabilities does this operator have?” and is consumed by the init wizard, the supervisor’s prompt renderer, and `radioactive_ralph doctor`.

type Inventory struct {
    GeneratedAt   time.Time   `json:"generated_at"`
    ClaudeVersion string      `json:"claude_version,omitempty"`
    Skills        []Skill     `json:"skills"`
    MCPServers    []MCPServer `json:"mcp_servers"`
    Agents        []Agent     `json:"agents"`
    Environment   Environment `json:"environment"`
}

func Discover

func Discover(opts Options) (Inventory, []error)

Discover walks the filesystem and returns an Inventory. Errors are collected per-source rather than returned as a single terminal error: a missing ~/.claude/skills/ directory should not prevent plugin discovery, and a malformed plugin.json should not prevent MCP discovery.

func Load

func Load(path string) (Inventory, error)

Load reads an inventory JSON file previously written by Save.

func (Inventory) HasMCP

func (inv Inventory) HasMCP(name string) bool

HasMCP reports whether the inventory contains an MCP server with the given name. Reachability is not considered — use the MCPServer field Reachable directly if that matters.

func (Inventory) HasSkill

func (inv Inventory) HasSkill(fullName string) bool

HasSkill reports whether the inventory contains a skill with the given full name (either plain “name” or “plugin:name”).

func (Inventory) Save

func (inv Inventory) Save(path string) error

Save writes the inventory JSON to path with 0o600 mode.

type MCPServer

MCPServer represents one entry from settings.json mcpServers. We do not attempt to reach the server during inventory; Reachable is populated later by the runtime connectivity check.

type MCPServer struct {
    Name      string `json:"name"`
    Reachable bool   `json:"reachable"`
    Source    string `json:"source"` // "user" | "project" | "plugin"
}

type Options

Options controls discovery. Every field is optional; zero value does “normal” discovery against the current user’s home dir.

type Options struct {
    // Home overrides the home directory probe. Useful for tests.
    Home string

    // RepoRoot, if non-empty, triggers project-local discovery (reads
    // .claude/settings.json inside the repo).
    RepoRoot string

    // ClaudeVersion is a caller-provided version string. We don't shell
    // out to `claude --version` from this package to avoid a subprocess
    // dependency; the CLI supplies it.
    ClaudeVersion string

    // GhAuthenticated is a caller-provided signal. Same reasoning.
    GhAuthenticated bool

    // InClaudeCode is a caller-provided signal for the CLAUDECODE env.
    InClaudeCode bool
}

type Skill

Skill represents one discovered helper entry. The Name field matches the `name:` value in SKILL.md frontmatter; for plugin-bundled helpers the Plugin field holds the plugin ID that provides it (e.g. “superpowers”) so operators can disambiguate same-named helpers from different plugins.

type Skill struct {
    Name        string `json:"name"`
    Description string `json:"description,omitempty"`
    Plugin      string `json:"plugin,omitempty"` // empty for user-level helpers
    Source      string `json:"source"`           // absolute path to SKILL.md
}

func (Skill) FullName

func (s Skill) FullName() string

FullName returns “plugin:name” when the skill is plugin-bundled, or just “name” when it’s a user-level helper. This matches how variant profiles reference helpers in their BiasSnippet tables.

Generated by gomarkdoc