Packages
fnord
0.9.29
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/cmd/config/external_configs.ex
defmodule Cmd.Config.ExternalConfigs do
@moduledoc """
CLI surface for toggling per-project support for Cursor rules, Cursor
skills, Claude Code skills, and Claude Code subagents. The underlying
persistence lives in `Settings.ExternalConfigs`; this module is the
user-facing entry point.
Four sources are supported: `cursor:rules`, `cursor:skills`,
`claude:skills`, `claude:agents`. All default to disabled; enabling any
source opts the selected project into discovery of the matching files
from both the user's home directory and the project source root.
"""
@doc """
Dispatches `fnord config external-configs` subcommands.
"""
@spec run(map(), list(), list()) :: :ok
def run(opts, command, args)
def run(opts, [:external_configs, :list], _args) do
with {:ok, project_name} <- resolve_project(opts),
:ok <- ensure_project_exists(project_name) do
project_name
|> Settings.ExternalConfigs.flags()
|> format_flags()
|> SafeJson.encode!(pretty: true)
|> UI.puts()
else
{:error, :project_not_set} ->
UI.error("Project not specified or not found")
{:error, :project_not_found} ->
UI.error("Project not found")
end
end
def run(opts, [:external_configs, :enable], args), do: toggle(opts, args, true)
def run(opts, [:external_configs, :disable], args), do: toggle(opts, args, false)
# Matches the precondition that Settings.ExternalConfigs.set/3 enforces
# for enable/disable. Without this, `list --project does-not-exist`
# silently returns the all-false defaults and the user has no way to
# tell they typed the wrong name.
defp ensure_project_exists(project_name) do
case Settings.get_project_data(Settings.new(), project_name) do
nil -> {:error, :project_not_found}
_ -> :ok
end
end
defp toggle(opts, args, value) do
with {:ok, project_name} <- resolve_project(opts),
{:ok, raw_source} <- Cmd.Config.Utils.require_key(opts, args, :source, "Source"),
{:ok, source} <- Settings.ExternalConfigs.source_from_string(raw_source),
{:ok, flags} <- Settings.ExternalConfigs.set(project_name, source, value) do
flags
|> format_flags()
|> SafeJson.encode!(pretty: true)
|> UI.puts()
else
{:error, :project_not_set} ->
UI.error("Project not specified or not found")
{:error, :project_not_found} ->
UI.error("Project not found")
{:error, {:invalid_source, raw}} ->
UI.error(
"Invalid source: #{inspect(raw)}. Valid sources: #{Enum.join(Settings.ExternalConfigs.source_strings(), ", ")}"
)
{:error, message} when is_binary(message) ->
UI.error(message)
end
end
@spec resolve_project(map()) :: {:ok, String.t()} | {:error, :project_not_set}
defp resolve_project(%{project: project_name}) when is_binary(project_name) do
Settings.set_project(project_name)
{:ok, project_name}
end
defp resolve_project(opts) when is_map(opts) do
Settings.get_selected_project()
end
@spec format_flags(Settings.ExternalConfigs.flags()) :: map()
defp format_flags(flags) do
Map.new(flags, fn {k, v} -> {Settings.ExternalConfigs.source_to_string(k), v} end)
end
end