Packages
fnord
0.9.38
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/resolve_project.ex
# This module determines the current project based on the current working directory
# or falls back to git worktree and repository roots to ensure the most specific project
# context is identified.
defmodule ResolveProject do
@moduledoc """
Project resolution from the current working directory.
Behavior:
- With no explicit cwd, the effective directory is the session's project
root override (`Settings.get_project_root_override/0`) when set, else
the real cwd. In prod the override is always nil at resolution time;
see the comment in `resolve/1`.
- If inside a git worktree, `resolve/1` will map the cwd to the repository root.
- Select the configured project whose root contains that directory, choosing the one with the deepest (longest) root path.
Returns `{:ok, project_name}` or `{:error, :not_in_project}`.
"""
@type project_name :: binary
# Find the configured project whose root most specifically contains the provided directory.
@spec resolve(cwd :: binary | nil) :: {:ok, project_name} | {:error, :not_in_project}
def resolve(cwd \\ nil) do
projects =
Settings.new()
|> Settings.get_projects()
|> Enum.flat_map(fn
{name, %{"root" => nil}} ->
UI.warn("Project '#{name}' missing 'root' configuration, skipping.")
[]
{name, %{"root" => root}} ->
[{Path.absname(root), name}]
{name, _project_config} ->
UI.warn("Project '#{name}' missing 'root' configuration, skipping.")
[]
end)
# Resolution base: an explicit cwd argument wins; otherwise honor the
# session's project-root override before falling back to the real cwd.
# In prod the override is always nil here - resolution runs once at CLI
# parse time, and every override writer (ask's worktree machinery) runs
# later - so consulting it changes nothing today. It exists for
# consistency (if resolution ever re-runs with an override set, the
# worktree mapping below converges on the same project) and as the
# async-safe alternative to File.cd! in tests.
base_dir =
case cwd do
nil -> Settings.get_project_root_override() || File.cwd!()
dir -> dir
end
# If we are inside a git worktree, prefer resolving at the primary repo
# root so that project selection reflects the clone from which the
# worktree was created. Skipped when an explicit cwd was passed,
# preserving resolve/1's literal-directory contract for that form.
repo_root_in_ctx =
case cwd do
nil -> GitCli.primary_root_at(base_dir)
_ -> nil
end
cwd_base = repo_root_in_ctx || base_dir
cwd_abs = Path.absname(cwd_base)
# Filter to projects whose root contains the current working directory.
matching_projects =
projects
|> Enum.filter(fn {root_abs, _name} -> Util.path_within_root?(cwd_abs, root_abs) end)
# Decide outcome based on whether any matching projects were found.
case matching_projects do
# No matching project roots found, return error.
[] ->
{:error, :not_in_project}
# Select the project with the deepest root path as the most specific match.
_ ->
{_, project_name} =
Enum.max_by(matching_projects, fn {root_abs, _} -> String.length(root_abs) end)
{:ok, project_name}
end
end
end