usecli 0.1.34__tar.gz → 0.1.36__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {usecli-0.1.34 → usecli-0.1.36}/PKG-INFO +1 -1
- {usecli-0.1.34 → usecli-0.1.36}/pyproject.toml +1 -1
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/init_command.py +38 -10
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/config/colors.py +82 -3
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/shared/config/manager.py +79 -3
- {usecli-0.1.34 → usecli-0.1.36}/LICENSE +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/README.md +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/README.md +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/custom/README.md +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/custom/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/base/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/base/about_command.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/base/help_command.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/base/inspire_command.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/base/internal/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/base/internal/fzf_command.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/core/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/core/utils.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/make/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/make/make_command.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/make/make_theme_command.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/config/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/base_command.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/error/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/error/handler.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/error/utils.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/exceptions/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/exceptions/base.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/exceptions/config.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/exceptions/usage.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/exceptions/validation.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/skill_generator.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/ui/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/ui/list.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/ui/title.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/validators/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/validators/network.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/validators/numeric.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/validators/path.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/core/validators/string.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/services/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/services/command_service.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/templates/command.py.j2 +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/templates/theme.toml.j2 +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/templates/usecli.config.toml.j2 +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/ayu_dark.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/catppuccin_frappe.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/catppuccin_latte.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/catppuccin_macchiato.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/catppuccin_mocha.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/default.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/dracula.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/gruvbox_dark.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/nord.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/themes/tokyo_night.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/usecli.config.toml +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/utils/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/utils/interactive/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/utils/interactive/terminal_menu.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/menu.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/params.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/shared/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/shared/config/__init__.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/shared/config/globals.py +0 -0
- {usecli-0.1.34 → usecli-0.1.36}/src/usecli/ui.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "usecli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.36"
|
|
4
4
|
description = "A powerful Python CLI framework for building beautiful, developer-friendly command-line tools."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Edward Boswell", email = "thememium@gmail.com" }]
|
|
@@ -88,18 +88,10 @@ class InitCommand(BaseCommand):
|
|
|
88
88
|
|
|
89
89
|
def _write_usecli_config(
|
|
90
90
|
self,
|
|
91
|
-
|
|
91
|
+
config_path: Path,
|
|
92
92
|
config_content: str,
|
|
93
93
|
force: bool,
|
|
94
|
-
commands_path: Path,
|
|
95
94
|
) -> str:
|
|
96
|
-
config_root = project_root
|
|
97
|
-
if commands_path.parent != project_root:
|
|
98
|
-
config_root = commands_path.parent
|
|
99
|
-
config_path = config_root / USECLI_CONFIG_TOML
|
|
100
|
-
discovered_config = ConfigManager(start_dir=config_root).usecli_config_path
|
|
101
|
-
if discovered_config.exists():
|
|
102
|
-
config_path = discovered_config
|
|
103
95
|
existed = config_path.exists()
|
|
104
96
|
if existed and not force:
|
|
105
97
|
should_overwrite = Confirm.ask(
|
|
@@ -113,6 +105,14 @@ class InitCommand(BaseCommand):
|
|
|
113
105
|
config_path.write_text(config_content.rstrip() + "\n")
|
|
114
106
|
return "updated" if existed else "created"
|
|
115
107
|
|
|
108
|
+
def _resolve_config_path(self, value: str, project_root: Path) -> Path:
|
|
109
|
+
path = Path(value).expanduser()
|
|
110
|
+
if not path.is_absolute():
|
|
111
|
+
path = project_root / path
|
|
112
|
+
if path.exists() and path.is_dir():
|
|
113
|
+
return (path / USECLI_CONFIG_TOML).resolve()
|
|
114
|
+
return path.resolve()
|
|
115
|
+
|
|
116
116
|
def _ensure_project_scripts(
|
|
117
117
|
self, pyproject_path: Path, command_name: str, force: bool
|
|
118
118
|
) -> str:
|
|
@@ -716,8 +716,36 @@ include = ["{root_package}*"]
|
|
|
716
716
|
|
|
717
717
|
self._sync_environment(project_root, command_name)
|
|
718
718
|
|
|
719
|
+
config_root = project_root
|
|
720
|
+
if commands_path.parent != project_root:
|
|
721
|
+
config_root = commands_path.parent
|
|
722
|
+
existing_config = ConfigManager(start_dir=config_root).usecli_config_path
|
|
723
|
+
default_config_path = (
|
|
724
|
+
existing_config
|
|
725
|
+
if existing_config.exists()
|
|
726
|
+
else config_root / USECLI_CONFIG_TOML
|
|
727
|
+
)
|
|
728
|
+
config_location = Prompt.ask(
|
|
729
|
+
f"[bold {COLOR.SECONDARY}]Config file location[/bold {COLOR.SECONDARY}]"
|
|
730
|
+
" (path or directory)",
|
|
731
|
+
default=str(default_config_path),
|
|
732
|
+
)
|
|
733
|
+
config_path = self._resolve_config_path(config_location, project_root)
|
|
734
|
+
if (
|
|
735
|
+
existing_config.exists()
|
|
736
|
+
and config_path.resolve() != existing_config.resolve()
|
|
737
|
+
and not force
|
|
738
|
+
):
|
|
739
|
+
replace_existing = Confirm.ask(
|
|
740
|
+
f"[{COLOR.WARNING}]Existing {USECLI_CONFIG_TOML} found at {existing_config}.[/{COLOR.WARNING}]\n"
|
|
741
|
+
"Replace it instead of writing to the new location?",
|
|
742
|
+
default=False,
|
|
743
|
+
)
|
|
744
|
+
if replace_existing:
|
|
745
|
+
config_path = existing_config
|
|
746
|
+
|
|
719
747
|
usecli_config_status = self._write_usecli_config(
|
|
720
|
-
|
|
748
|
+
config_path, config_content, force
|
|
721
749
|
)
|
|
722
750
|
if usecli_config_status == "created":
|
|
723
751
|
console.print(
|
|
@@ -11,6 +11,7 @@ Usage:
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
+
import importlib.util
|
|
14
15
|
import sys
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
from typing import Any, Callable, Final, final
|
|
@@ -25,6 +26,7 @@ PYPROJECT_TOML = "pyproject.toml"
|
|
|
25
26
|
USECLI_CONFIG_TOML = "usecli.config.toml"
|
|
26
27
|
DEFAULT_THEME_NAME = "default"
|
|
27
28
|
THEMES_DIR = Path(__file__).resolve().parent.parent / "themes"
|
|
29
|
+
_PACKAGE_PREFER_DIRS = {".venv", "venv", "site-packages"}
|
|
28
30
|
DEFAULT_THEME_COLORS: dict[str, str] = {
|
|
29
31
|
"primary": "#60D7FF",
|
|
30
32
|
"secondary": "#5EFF87",
|
|
@@ -48,11 +50,19 @@ DEFAULT_THEME_COLORS: dict[str, str] = {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
|
|
51
|
-
def _find_usecli_config_path(
|
|
53
|
+
def _find_usecli_config_path(
|
|
54
|
+
root_dir: Path, start_dir: Path, *, skip_venv: bool
|
|
55
|
+
) -> Path | None:
|
|
52
56
|
if not root_dir.exists() or not root_dir.is_dir():
|
|
53
57
|
return None
|
|
54
58
|
|
|
55
59
|
candidates = [path for path in root_dir.rglob(USECLI_CONFIG_TOML)]
|
|
60
|
+
if skip_venv:
|
|
61
|
+
candidates = [
|
|
62
|
+
path
|
|
63
|
+
for path in candidates
|
|
64
|
+
if not any(part in {".venv", "venv"} for part in path.parts)
|
|
65
|
+
]
|
|
56
66
|
if not candidates:
|
|
57
67
|
return None
|
|
58
68
|
|
|
@@ -79,6 +89,42 @@ def _find_usecli_config_path(root_dir: Path, start_dir: Path) -> Path | None:
|
|
|
79
89
|
return selection[0]
|
|
80
90
|
|
|
81
91
|
|
|
92
|
+
def _find_usecli_config_in_package() -> Path | None:
|
|
93
|
+
spec = importlib.util.find_spec(_get_package_name())
|
|
94
|
+
if spec is None or not spec.submodule_search_locations:
|
|
95
|
+
return None
|
|
96
|
+
for location in spec.submodule_search_locations:
|
|
97
|
+
package_root = Path(location)
|
|
98
|
+
if not package_root.exists() or not package_root.is_dir():
|
|
99
|
+
continue
|
|
100
|
+
candidates = [
|
|
101
|
+
path for path in package_root.rglob(USECLI_CONFIG_TOML) if path.exists()
|
|
102
|
+
]
|
|
103
|
+
if candidates:
|
|
104
|
+
candidates.sort(key=lambda path: (len(path.parts), str(path)))
|
|
105
|
+
return candidates[0]
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _is_preferred_package_path(path: Path) -> bool:
|
|
110
|
+
return any(part in _PACKAGE_PREFER_DIRS for part in path.parts)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _is_within_usecli_package(start_dir: Path) -> bool:
|
|
114
|
+
spec = importlib.util.find_spec(_get_package_name())
|
|
115
|
+
if spec is None or not spec.submodule_search_locations:
|
|
116
|
+
return False
|
|
117
|
+
start_dir = start_dir.resolve()
|
|
118
|
+
for location in spec.submodule_search_locations:
|
|
119
|
+
package_root = Path(location)
|
|
120
|
+
try:
|
|
121
|
+
start_dir.relative_to(package_root)
|
|
122
|
+
return True
|
|
123
|
+
except ValueError:
|
|
124
|
+
continue
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
82
128
|
def _find_project_root(start_dir: Path | None = None) -> Path | None:
|
|
83
129
|
if start_dir is None:
|
|
84
130
|
start_dir = Path.cwd()
|
|
@@ -86,6 +132,10 @@ def _find_project_root(start_dir: Path | None = None) -> Path | None:
|
|
|
86
132
|
current = start_dir.resolve()
|
|
87
133
|
git_root: Path | None = None
|
|
88
134
|
|
|
135
|
+
package_match = _find_usecli_config_in_package()
|
|
136
|
+
if package_match and _is_preferred_package_path(package_match):
|
|
137
|
+
return package_match.parent
|
|
138
|
+
|
|
89
139
|
while True:
|
|
90
140
|
pyproject_path = current / PYPROJECT_TOML
|
|
91
141
|
if pyproject_path.exists():
|
|
@@ -106,10 +156,16 @@ def _find_project_root(start_dir: Path | None = None) -> Path | None:
|
|
|
106
156
|
current = parent
|
|
107
157
|
|
|
108
158
|
search_root = git_root or start_dir.resolve()
|
|
109
|
-
config_match = _find_usecli_config_path(
|
|
159
|
+
config_match = _find_usecli_config_path(
|
|
160
|
+
search_root, start_dir, skip_venv=_is_within_usecli_package(start_dir)
|
|
161
|
+
)
|
|
110
162
|
if config_match:
|
|
111
163
|
return config_match.parent
|
|
112
164
|
|
|
165
|
+
package_match = _find_usecli_config_in_package()
|
|
166
|
+
if package_match:
|
|
167
|
+
return package_match.parent
|
|
168
|
+
|
|
113
169
|
return git_root
|
|
114
170
|
|
|
115
171
|
|
|
@@ -117,12 +173,28 @@ def _load_usecli_config(project_root: Path | None) -> dict[str, Any]:
|
|
|
117
173
|
if project_root is None:
|
|
118
174
|
return {}
|
|
119
175
|
|
|
176
|
+
package_match = _find_usecli_config_in_package()
|
|
177
|
+
if package_match and _is_preferred_package_path(package_match):
|
|
178
|
+
return _load_usecli_config_file(package_match)
|
|
179
|
+
|
|
120
180
|
config_path = project_root / USECLI_CONFIG_TOML
|
|
121
181
|
if not config_path.exists():
|
|
122
|
-
config_path = _find_usecli_config_path(
|
|
182
|
+
config_path = _find_usecli_config_path(
|
|
183
|
+
project_root,
|
|
184
|
+
project_root,
|
|
185
|
+
skip_venv=_is_within_usecli_package(project_root),
|
|
186
|
+
)
|
|
187
|
+
if not config_path or not config_path.exists():
|
|
188
|
+
package_match = _find_usecli_config_in_package()
|
|
189
|
+
if package_match:
|
|
190
|
+
config_path = package_match
|
|
123
191
|
if not config_path or not config_path.exists():
|
|
124
192
|
return {}
|
|
125
193
|
|
|
194
|
+
return _load_usecli_config_file(config_path)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _load_usecli_config_file(config_path: Path) -> dict[str, Any]:
|
|
126
198
|
try:
|
|
127
199
|
data = tomllib.loads(config_path.read_text())
|
|
128
200
|
except (tomllib.TOMLDecodeError, OSError):
|
|
@@ -141,6 +213,13 @@ def _load_usecli_config(project_root: Path | None) -> dict[str, Any]:
|
|
|
141
213
|
return {}
|
|
142
214
|
|
|
143
215
|
|
|
216
|
+
def _get_package_name() -> str:
|
|
217
|
+
package = __package__ or __name__
|
|
218
|
+
if not package:
|
|
219
|
+
return "usecli"
|
|
220
|
+
return package.split(".")[0]
|
|
221
|
+
|
|
222
|
+
|
|
144
223
|
def _normalize_color(value: Any) -> str | None:
|
|
145
224
|
if not isinstance(value, str):
|
|
146
225
|
return None
|
|
@@ -5,6 +5,7 @@ Handles loading and accessing configuration from project-level files.
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
import importlib.util
|
|
8
9
|
import sys
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from typing import Any
|
|
@@ -58,6 +59,9 @@ def _dedupe_items(items: list[str]) -> list[str]:
|
|
|
58
59
|
class ConfigManager:
|
|
59
60
|
"""Manages useCli configuration from project-level files."""
|
|
60
61
|
|
|
62
|
+
_SKIP_DIRS = {".venv", "venv"}
|
|
63
|
+
_PACKAGE_PREFER_DIRS = {".venv", "venv", "site-packages"}
|
|
64
|
+
|
|
61
65
|
DEFAULT_CONFIG: dict[str, Any] = {
|
|
62
66
|
"title": "usecli",
|
|
63
67
|
"title_file": None,
|
|
@@ -166,6 +170,10 @@ class ConfigManager:
|
|
|
166
170
|
def _find_usecli_config(cls, start_dir: Path) -> Path | None:
|
|
167
171
|
current = start_dir.resolve()
|
|
168
172
|
|
|
173
|
+
package_match = cls._find_usecli_config_in_package()
|
|
174
|
+
if package_match and cls._is_preferred_package_path(package_match):
|
|
175
|
+
return package_match
|
|
176
|
+
|
|
169
177
|
while True:
|
|
170
178
|
config_path = current / USECLI_CONFIG_TOML
|
|
171
179
|
if config_path.exists():
|
|
@@ -176,19 +184,33 @@ class ConfigManager:
|
|
|
176
184
|
break
|
|
177
185
|
current = parent
|
|
178
186
|
|
|
187
|
+
in_usecli_package = cls._is_within_usecli_package(start_dir)
|
|
188
|
+
if in_usecli_package and package_match:
|
|
189
|
+
return package_match
|
|
190
|
+
|
|
179
191
|
search_root = find_project_root(start_dir) or start_dir.resolve()
|
|
180
|
-
recursive_match = cls._find_usecli_config_in_tree(
|
|
192
|
+
recursive_match = cls._find_usecli_config_in_tree(
|
|
193
|
+
search_root, start_dir, skip_venv=in_usecli_package
|
|
194
|
+
)
|
|
181
195
|
if recursive_match:
|
|
182
196
|
return recursive_match
|
|
183
197
|
|
|
184
198
|
return cls._find_usecli_config_on_sys_path()
|
|
185
199
|
|
|
186
200
|
@staticmethod
|
|
187
|
-
def _find_usecli_config_in_tree(
|
|
201
|
+
def _find_usecli_config_in_tree(
|
|
202
|
+
root_dir: Path, start_dir: Path, *, skip_venv: bool
|
|
203
|
+
) -> Path | None:
|
|
188
204
|
if not root_dir.exists() or not root_dir.is_dir():
|
|
189
205
|
return None
|
|
190
206
|
|
|
191
207
|
candidates = [path for path in root_dir.rglob(USECLI_CONFIG_TOML)]
|
|
208
|
+
if skip_venv:
|
|
209
|
+
candidates = [
|
|
210
|
+
path
|
|
211
|
+
for path in candidates
|
|
212
|
+
if not any(part in ConfigManager._SKIP_DIRS for part in path.parts)
|
|
213
|
+
]
|
|
192
214
|
if not candidates:
|
|
193
215
|
return None
|
|
194
216
|
|
|
@@ -214,6 +236,42 @@ class ConfigManager:
|
|
|
214
236
|
selection.sort(key=_depth_key)
|
|
215
237
|
return selection[0]
|
|
216
238
|
|
|
239
|
+
@staticmethod
|
|
240
|
+
def _find_usecli_config_in_package() -> Path | None:
|
|
241
|
+
spec = importlib.util.find_spec(_get_package_name())
|
|
242
|
+
if spec is None or not spec.submodule_search_locations:
|
|
243
|
+
return None
|
|
244
|
+
for location in spec.submodule_search_locations:
|
|
245
|
+
package_root = Path(location)
|
|
246
|
+
if not package_root.exists() or not package_root.is_dir():
|
|
247
|
+
continue
|
|
248
|
+
candidates = [
|
|
249
|
+
path for path in package_root.rglob(USECLI_CONFIG_TOML) if path.exists()
|
|
250
|
+
]
|
|
251
|
+
if candidates:
|
|
252
|
+
candidates.sort(key=lambda path: (len(path.parts), str(path)))
|
|
253
|
+
return candidates[0]
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def _is_preferred_package_path(path: Path) -> bool:
|
|
258
|
+
return any(part in ConfigManager._PACKAGE_PREFER_DIRS for part in path.parts)
|
|
259
|
+
|
|
260
|
+
@staticmethod
|
|
261
|
+
def _is_within_usecli_package(start_dir: Path) -> bool:
|
|
262
|
+
spec = importlib.util.find_spec(_get_package_name())
|
|
263
|
+
if spec is None or not spec.submodule_search_locations:
|
|
264
|
+
return False
|
|
265
|
+
start_dir = start_dir.resolve()
|
|
266
|
+
for location in spec.submodule_search_locations:
|
|
267
|
+
package_root = Path(location)
|
|
268
|
+
try:
|
|
269
|
+
start_dir.relative_to(package_root)
|
|
270
|
+
return True
|
|
271
|
+
except ValueError:
|
|
272
|
+
continue
|
|
273
|
+
return False
|
|
274
|
+
|
|
217
275
|
@staticmethod
|
|
218
276
|
def _find_usecli_config_on_sys_path() -> Path | None:
|
|
219
277
|
for entry in sys.path:
|
|
@@ -322,6 +380,9 @@ class ConfigManager:
|
|
|
322
380
|
|
|
323
381
|
def reload(self) -> None:
|
|
324
382
|
"""Reload configuration from disk."""
|
|
383
|
+
self.usecli_config_path = self._find_usecli_config(self.start_dir) or (
|
|
384
|
+
self.start_dir / USECLI_CONFIG_TOML
|
|
385
|
+
)
|
|
325
386
|
self._load_config()
|
|
326
387
|
|
|
327
388
|
@property
|
|
@@ -372,6 +433,10 @@ def find_project_root(start_dir: Path | None = None) -> Path | None:
|
|
|
372
433
|
|
|
373
434
|
current = start_dir.resolve()
|
|
374
435
|
|
|
436
|
+
package_match = ConfigManager._find_usecli_config_in_package()
|
|
437
|
+
if package_match and ConfigManager._is_preferred_package_path(package_match):
|
|
438
|
+
return package_match.parent
|
|
439
|
+
|
|
375
440
|
git_root: Path | None = None
|
|
376
441
|
while True:
|
|
377
442
|
pyproject_path = current / PYPROJECT_TOML
|
|
@@ -393,8 +458,19 @@ def find_project_root(start_dir: Path | None = None) -> Path | None:
|
|
|
393
458
|
current = parent
|
|
394
459
|
|
|
395
460
|
search_root = git_root or start_dir.resolve()
|
|
396
|
-
config_match = ConfigManager._find_usecli_config_in_tree(
|
|
461
|
+
config_match = ConfigManager._find_usecli_config_in_tree(
|
|
462
|
+
search_root,
|
|
463
|
+
start_dir,
|
|
464
|
+
skip_venv=ConfigManager._is_within_usecli_package(start_dir),
|
|
465
|
+
)
|
|
397
466
|
if config_match:
|
|
398
467
|
return config_match.parent
|
|
399
468
|
|
|
400
469
|
return git_root
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _get_package_name() -> str:
|
|
473
|
+
package = __package__ or __name__
|
|
474
|
+
if not package:
|
|
475
|
+
return "usecli"
|
|
476
|
+
return package.split(".")[0]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{usecli-0.1.34 → usecli-0.1.36}/src/usecli/cli/commands/defaults/base/internal/fzf_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|