ExternalConfigs.Loader (fnord v0.9.31)

View Source

Discovers and loads Cursor rules, Cursor skills, and Claude Code skills from the user's home directory and the current project's source root.

Project-scoped entries override user/global entries of the same name (CSS-style layering).

Summary

Functions

Clears all external-configs loader caches. Call from tests that mutate the on-disk rules/skills mid-test; production code should never need this since the escript VM is short-lived.

Remove cursor skills that duplicate a claude skill, preferring claude.

Cheap on-disk presence check, used by the coordinator's startup nudge to detect "feature is off, but the files are sitting right there." Skips parsing; just asks whether any candidate file exists under the global or project search paths for the given source.

Load all enabled external configs for the given project, honoring the per-project external_configs settings toggles.

Load all Claude Code subagents (global + project), project overriding global by name. Cached per session and per project.

Load all Claude Code skills (global + project), project overriding global. Cached per session and per project.

Load only cursor rules (global + project) regardless of settings. Cached per session and per project. The hot path is the Injector, which reruns this on every file read/write while the rule set is stable for the life of the invocation.

Load all cursor skills (global + project), project overriding global. Cached per session and per project.

Types

loaded()

@type loaded() :: %{
  cursor_rules: [ExternalConfigs.CursorRule.t()],
  cursor_skills: [ExternalConfigs.Skill.t()],
  claude_skills: [ExternalConfigs.Skill.t()],
  claude_agents: [ExternalConfigs.Agent.t()]
}

Functions

clear_cache()

@spec clear_cache() :: :ok

Clears all external-configs loader caches. Call from tests that mutate the on-disk rules/skills mid-test; production code should never need this since the escript VM is short-lived.

dedup_cross_flavor(cursor_skills, claude_skills)

@spec dedup_cross_flavor([ExternalConfigs.Skill.t()], [ExternalConfigs.Skill.t()]) ::
  [
    ExternalConfigs.Skill.t()
  ]

Remove cursor skills that duplicate a claude skill, preferring claude.

Two dedup passes run in order:

  1. Inode identity — cursor skills whose on-disk directory resolves to the same inode as a claude skill are dropped. Handles individual directory symlinks and whole-tree symlinks at any depth. File.stat/1 follows symlinks; cursor entries that cannot be stat'd are kept (conservative: don't silently drop on filesystem errors).

  2. Name identity — cursor skills whose name matches a claude skill are dropped even when stored at a distinct path. A same-name clash across flavors almost always means one is a copy or re-export of the other.

has_any_on_disk?(project, atom)

@spec has_any_on_disk?(Store.Project.t(), Settings.ExternalConfigs.source()) ::
  boolean()

Cheap on-disk presence check, used by the coordinator's startup nudge to detect "feature is off, but the files are sitting right there." Skips parsing; just asks whether any candidate file exists under the global or project search paths for the given source.

load(project)

@spec load(Store.Project.t()) :: loaded()

Load all enabled external configs for the given project, honoring the per-project external_configs settings toggles.

load_claude_agents(project)

@spec load_claude_agents(Store.Project.t()) :: [ExternalConfigs.Agent.t()]

Load all Claude Code subagents (global + project), project overriding global by name. Cached per session and per project.

load_claude_skills(project)

@spec load_claude_skills(Store.Project.t()) :: [ExternalConfigs.Skill.t()]

Load all Claude Code skills (global + project), project overriding global. Cached per session and per project.

load_cursor_rules(project)

@spec load_cursor_rules(Store.Project.t()) :: [ExternalConfigs.CursorRule.t()]

Load only cursor rules (global + project) regardless of settings. Cached per session and per project. The hot path is the Injector, which reruns this on every file read/write while the rule set is stable for the life of the invocation.

load_cursor_skills(project)

@spec load_cursor_skills(Store.Project.t()) :: [ExternalConfigs.Skill.t()]

Load all cursor skills (global + project), project overriding global. Cached per session and per project.