usecli 0.1.70__tar.gz → 0.1.71__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.
Files changed (70) hide show
  1. {usecli-0.1.70 → usecli-0.1.71}/PKG-INFO +1 -1
  2. {usecli-0.1.70 → usecli-0.1.71}/pyproject.toml +1 -1
  3. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/make/make_command.py +12 -3
  4. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/base_command.py +4 -1
  5. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/ui/list.py +44 -9
  6. {usecli-0.1.70 → usecli-0.1.71}/LICENSE +0 -0
  7. {usecli-0.1.70 → usecli-0.1.71}/README.md +0 -0
  8. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/__init__.py +0 -0
  9. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/__init__.py +0 -0
  10. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/README.md +0 -0
  11. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/__init__.py +0 -0
  12. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/custom/README.md +0 -0
  13. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/custom/__init__.py +0 -0
  14. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/__init__.py +0 -0
  15. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/base/__init__.py +0 -0
  16. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/base/about_command.py +0 -0
  17. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/base/help_command.py +0 -0
  18. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/base/inspire_command.py +0 -0
  19. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/base/internal/__init__.py +0 -0
  20. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/base/internal/fzf_command.py +0 -0
  21. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/core/__init__.py +0 -0
  22. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/core/utils.py +0 -0
  23. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/make/__init__.py +0 -0
  24. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/defaults/make/make_theme_command.py +0 -0
  25. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/commands/init_command.py +0 -0
  26. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/config/__init__.py +0 -0
  27. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/config/colors.py +0 -0
  28. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/__init__.py +0 -0
  29. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/error/__init__.py +0 -0
  30. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/error/handler.py +0 -0
  31. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/error/utils.py +0 -0
  32. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/exceptions/__init__.py +0 -0
  33. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/exceptions/base.py +0 -0
  34. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/exceptions/config.py +0 -0
  35. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/exceptions/usage.py +0 -0
  36. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/exceptions/validation.py +0 -0
  37. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/ui/__init__.py +0 -0
  38. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/ui/title.py +0 -0
  39. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/ui/title.txt +0 -0
  40. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/validators/__init__.py +0 -0
  41. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/validators/network.py +0 -0
  42. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/validators/numeric.py +0 -0
  43. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/validators/path.py +0 -0
  44. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/core/validators/string.py +0 -0
  45. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/services/__init__.py +0 -0
  46. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/services/command_service.py +0 -0
  47. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/templates/command.py.j2 +0 -0
  48. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/templates/theme.toml.j2 +0 -0
  49. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/templates/usecli.config.toml.j2 +0 -0
  50. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/ayu_dark.toml +0 -0
  51. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/catppuccin_frappe.toml +0 -0
  52. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/catppuccin_latte.toml +0 -0
  53. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/catppuccin_macchiato.toml +0 -0
  54. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/catppuccin_mocha.toml +0 -0
  55. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/default.toml +0 -0
  56. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/dracula.toml +0 -0
  57. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/gruvbox_dark.toml +0 -0
  58. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/nord.toml +0 -0
  59. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/themes/tokyo_night.toml +0 -0
  60. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/utils/__init__.py +0 -0
  61. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/utils/interactive/__init__.py +0 -0
  62. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/cli/utils/interactive/terminal_menu.py +0 -0
  63. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/menu.py +0 -0
  64. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/params.py +0 -0
  65. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/shared/__init__.py +0 -0
  66. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/shared/config/__init__.py +0 -0
  67. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/shared/config/globals.py +0 -0
  68. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/shared/config/manager.py +0 -0
  69. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/ui.py +0 -0
  70. {usecli-0.1.70 → usecli-0.1.71}/src/usecli/usecli.config.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: usecli
3
- Version: 0.1.70
3
+ Version: 0.1.71
4
4
  Summary: A powerful Python CLI framework for building beautiful, developer-friendly command-line tools.
5
5
  Author: Edward Boswell
6
6
  Author-email: Edward Boswell <thememium@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "usecli"
3
- version = "0.1.70"
3
+ version = "0.1.71"
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" }]
@@ -43,9 +43,18 @@ class MakeCommand(BaseCommand):
43
43
  name: The name of the command to create.
44
44
  """
45
45
  clean_name = name.replace("Command", "").replace("command", "")
46
- class_name = pascalcase(clean_name.replace(":", "_")) + "Command"
47
- command_name = snakecase(clean_name.replace(":", "_"))
48
- file_name = f"{snakecase(clean_name.replace(':', '_'))}_command.py"
46
+ class_name = (
47
+ pascalcase(clean_name.replace(":", "_").replace(" ", "_")) + "Command"
48
+ )
49
+ if ":" in clean_name:
50
+ command_name = clean_name
51
+ elif " " in clean_name:
52
+ command_name = clean_name
53
+ else:
54
+ command_name = snakecase(clean_name)
55
+ file_name = (
56
+ f"{snakecase(clean_name.replace(':', '_').replace(' ', '_'))}_command.py"
57
+ )
49
58
 
50
59
  config = get_config()
51
60
  current_root = find_project_root(Path.cwd()) or Path.cwd().resolve()
@@ -42,6 +42,7 @@ class NestedCommandRegistry:
42
42
  _instance: ClassVar[NestedCommandRegistry | None] = None
43
43
  _groups: dict[str, typer.Typer]
44
44
  _group_commands: dict[str, list[dict[str, Any]]]
45
+ _main_app: typer.Typer | None
45
46
 
46
47
  def __new__(cls) -> NestedCommandRegistry:
47
48
  """Ensure singleton pattern for the registry."""
@@ -49,6 +50,7 @@ class NestedCommandRegistry:
49
50
  cls._instance = super().__new__(cls)
50
51
  cls._instance._groups = {}
51
52
  cls._instance._group_commands = {}
53
+ cls._instance._main_app = None
52
54
  return cls._instance
53
55
 
54
56
  def get_or_create_group(
@@ -73,6 +75,7 @@ class NestedCommandRegistry:
73
75
  )
74
76
  self._groups[group_name] = group_app
75
77
  self._group_commands[group_name] = []
78
+ self._main_app = main_app
76
79
 
77
80
  # Add the group app to main app
78
81
  main_app.add_typer(group_app, name=group_name)
@@ -142,7 +145,7 @@ class NestedCommandRegistry:
142
145
 
143
146
  group_app = self._groups.get(group_name)
144
147
  if group_app:
145
- list_group_commands(group_app, group_name)
148
+ list_group_commands(group_app, group_name, self._main_app)
146
149
 
147
150
  def register_command(
148
151
  self,
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, TypedDict
7
7
  import click
8
8
  import typer
9
9
  from rich.console import Console
10
+ from rich.markup import escape
10
11
 
11
12
  from usecli.cli.config.colors import COLOR
12
13
  from usecli.cli.core.ui.title import (
@@ -178,14 +179,14 @@ def list_commands(app: typer.Typer, prefix_filter: str | None = None) -> None:
178
179
  for cmd in commands
179
180
  )
180
181
  if has_colon_commands:
181
- # Colon-separated: use make:COMMAND [OPTIONS]
182
+ # Colon-separated: use make:[COMMAND] [OPTIONS]
182
183
  console.print(
183
- f" [not bold {COLOR.PRIMARY}]{command_name} {prefix_filter}:COMMAND [OPTIONS][/]"
184
+ f" [not bold {COLOR.PRIMARY}]{command_name} {prefix_filter}:{escape('[COMMAND]')} {escape('[OPTIONS]')}[/]"
184
185
  )
185
186
  else:
186
187
  # Space-separated nested groups: use make [COMMAND] [OPTIONS]
187
188
  console.print(
188
- f" [not bold {COLOR.PRIMARY}]{command_name} {prefix_filter} [COMMAND] [OPTIONS][/]"
189
+ f" [not bold {COLOR.PRIMARY}]{command_name} {prefix_filter} {escape('[COMMAND]')} {escape('[OPTIONS]')}[/]"
189
190
  )
190
191
  console.print()
191
192
  console.print(f"[bold {COLOR.SECONDARY}]Options:")
@@ -203,7 +204,7 @@ def list_commands(app: typer.Typer, prefix_filter: str | None = None) -> None:
203
204
  else:
204
205
  console.print(f"[bold {COLOR.SECONDARY}]Usage:[/bold {COLOR.SECONDARY}]")
205
206
  console.print(
206
- f" [not bold {COLOR.PRIMARY}]{command_name} [OPTIONS] [ARGUMENTS][/]"
207
+ f" [not bold {COLOR.PRIMARY}]{command_name} {escape('[COMMAND]')} {escape('[OPTIONS]')} {escape('[ARGUMENTS]')}[/]"
207
208
  )
208
209
  console.print()
209
210
 
@@ -266,7 +267,9 @@ def list_commands(app: typer.Typer, prefix_filter: str | None = None) -> None:
266
267
  console.print()
267
268
 
268
269
 
269
- def list_group_commands(group_app: typer.Typer, group_name: str) -> None:
270
+ def list_group_commands(
271
+ group_app: typer.Typer, group_name: str, main_app: typer.Typer | None = None
272
+ ) -> None:
270
273
  """List all commands within a specific command group.
271
274
 
272
275
  Displays commands in a formatted list for a nested command group,
@@ -275,16 +278,31 @@ def list_group_commands(group_app: typer.Typer, group_name: str) -> None:
275
278
  Args:
276
279
  group_app: The Typer sub-app for the command group.
277
280
  group_name: The name of the command group.
281
+ main_app: Optional main app to include colon-separated commands.
278
282
  """
279
283
  alias_registry = _get_alias_registry(group_app)
280
284
  alias_to_primary = _build_alias_to_primary(alias_registry)
281
285
 
282
286
  command_name = get_script_command_name(default="usecli")
283
287
 
284
- console.print(f"[bold {COLOR.SECONDARY}]Usage:[/bold {COLOR.SECONDARY}]")
285
- console.print(
286
- f" [not bold {COLOR.PRIMARY}]{command_name} {group_name} [COMMAND] [OPTIONS][/]"
288
+ # Check if group has both colon-separated and space-separated commands
289
+ has_colon_commands = main_app is not None and any(
290
+ cmd.name and cmd.name.startswith(f"{group_name}:")
291
+ for cmd in (main_app.registered_commands or [])
287
292
  )
293
+ has_nested_commands = len(group_app.registered_commands) > 0
294
+
295
+ console.print(f"[bold {COLOR.SECONDARY}]Usage:[/bold {COLOR.SECONDARY}]")
296
+ if has_colon_commands and has_nested_commands:
297
+ # Both formats exist: usecli [COMMAND] [OPTIONS] [ARGUMENTS]
298
+ console.print(
299
+ f" [not bold {COLOR.PRIMARY}]{command_name} {escape('[COMMAND]')} {escape('[OPTIONS]')} {escape('[ARGUMENTS]')}[/]"
300
+ )
301
+ else:
302
+ # Only space-separated: usecli make [COMMAND] [OPTIONS]
303
+ console.print(
304
+ f" [not bold {COLOR.PRIMARY}]{command_name} {group_name} {escape('[COMMAND]')} {escape('[OPTIONS]')}[/]"
305
+ )
288
306
  console.print()
289
307
  commands_by_name: dict[str, CommandMeta] = {}
290
308
  for command in group_app.registered_commands:
@@ -303,6 +321,23 @@ def list_group_commands(group_app: typer.Typer, group_name: str) -> None:
303
321
  "aliases": alias_registry.get(name, list[str]()),
304
322
  }
305
323
 
324
+ # Include colon-separated commands from main app with matching prefix
325
+ if main_app is not None:
326
+ prefix = f"{group_name}:"
327
+ main_alias_registry = _get_alias_registry(main_app)
328
+ for command in main_app.registered_commands:
329
+ cmd_name = command.name or ""
330
+ if not cmd_name.startswith(prefix):
331
+ continue
332
+ if cmd_name in commands_by_name:
333
+ continue
334
+ help_text = command.help or ""
335
+ aliases = main_alias_registry.get(cmd_name, [])
336
+ commands_by_name[cmd_name] = {
337
+ "help": help_text,
338
+ "aliases": aliases,
339
+ }
340
+
306
341
  commands: list[CommandEntry] = []
307
342
  for name in commands_by_name:
308
343
  aliases = commands_by_name[name]["aliases"]
@@ -355,7 +390,7 @@ def list_group_commands(group_app: typer.Typer, group_name: str) -> None:
355
390
  display_name = cmd["display_name"]
356
391
  padding = " " * (longest_label_length - len(display_name) + SPACER_LENGTH)
357
392
  console.print(
358
- f" [{COLOR.COMMAND}]{display_name}[/{COLOR.COMMAND}]{padding}{cmd['help']}"
393
+ f" [{COLOR.COMMAND} not bold]{display_name}[/{COLOR.COMMAND} not bold]{padding}{cmd['help']}"
359
394
  )
360
395
 
361
396
  console.print()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes