just-cli 0.1.0__py3-none-any.whl

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 (64) hide show
  1. just/__init__.py +40 -0
  2. just/cli.py +79 -0
  3. just/commands/__init__.py +0 -0
  4. just/commands/download.py +62 -0
  5. just/commands/edit.py +37 -0
  6. just/commands/ext/__init__.py +7 -0
  7. just/commands/ext/add.py +58 -0
  8. just/commands/extract.py +76 -0
  9. just/commands/install.py +36 -0
  10. just/commands/linux.py +201 -0
  11. just/commands/tunnel.py +27 -0
  12. just/commands/view.py +33 -0
  13. just/core/__init__.py +0 -0
  14. just/core/config/__init__.py +34 -0
  15. just/core/config/config.py +7 -0
  16. just/core/config/utils.py +94 -0
  17. just/core/extension/__init__.py +0 -0
  18. just/core/extension/generator.py +405 -0
  19. just/core/extension/parser.py +145 -0
  20. just/core/extension/utils.py +145 -0
  21. just/core/installer/__init__.py +10 -0
  22. just/core/installer/binary.py +148 -0
  23. just/core/installer/decorator.py +15 -0
  24. just/core/installer/install_package.py +48 -0
  25. just/core/installer/package_info.py +12 -0
  26. just/core/installer/simple_release.py +296 -0
  27. just/core/installer/source/__init__.py +17 -0
  28. just/core/installer/source/base.py +22 -0
  29. just/core/installer/source/http.py +103 -0
  30. just/core/installer/source/local.py +54 -0
  31. just/core/system_probe/__init__.py +15 -0
  32. just/core/system_probe/system_info.py +101 -0
  33. just/core/system_probe/system_probe.py +198 -0
  34. just/installers/__init__.py +0 -0
  35. just/installers/cloudflare/__init__.py +1 -0
  36. just/installers/cloudflare/installer.py +14 -0
  37. just/installers/edit/__init__.py +0 -0
  38. just/installers/edit/installer.py +23 -0
  39. just/tui/__init__.py +4 -0
  40. just/tui/editor.py +111 -0
  41. just/tui/extension.py +118 -0
  42. just/tui/markdown.py +70 -0
  43. just/utils/__init__.py +38 -0
  44. just/utils/archive/__init__.py +22 -0
  45. just/utils/archive/compression_handler.py +178 -0
  46. just/utils/archive/extractor.py +87 -0
  47. just/utils/archive/format_detect.py +199 -0
  48. just/utils/archive/sevenzip_handler.py +44 -0
  49. just/utils/archive/tar_handler.py +117 -0
  50. just/utils/archive/zip_handler.py +81 -0
  51. just/utils/download_utils.py +574 -0
  52. just/utils/echo_utils.py +74 -0
  53. just/utils/env_utils.py +164 -0
  54. just/utils/file_utils.py +184 -0
  55. just/utils/format_utils.py +9 -0
  56. just/utils/progress.py +457 -0
  57. just/utils/shell_utils.py +72 -0
  58. just/utils/system_probe.py +214 -0
  59. just/utils/typer_utils.py +35 -0
  60. just/utils/user_interaction.py +4 -0
  61. just_cli-0.1.0.dist-info/METADATA +107 -0
  62. just_cli-0.1.0.dist-info/RECORD +64 -0
  63. just_cli-0.1.0.dist-info/WHEEL +4 -0
  64. just_cli-0.1.0.dist-info/entry_points.txt +2 -0
just/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ from typing_extensions import Annotated
2
+ from typer import Option, Argument
3
+
4
+ from just.cli import just_cli,capture_exception
5
+ from just.core.config import JustConfig, load_env_config, update_env_config, get_cache_dir
6
+ from just.core.installer import installer, SimpleReleaseInstaller, BinaryInstaller
7
+ from just.utils import (
8
+ SystemProbe,
9
+ create_typer_app,
10
+ confirm_action,
11
+ docstring,
12
+ download_with_resume,
13
+ echo,
14
+ extract,
15
+ execute_commands
16
+ )
17
+
18
+
19
+ config = JustConfig()
20
+ system = SystemProbe()
21
+
22
+ __all__ = [
23
+ "Annotated",
24
+ "Argument",
25
+ "Option",
26
+ "capture_exception",
27
+ "create_typer_app",
28
+ "docstring",
29
+ "echo",
30
+ "extract",
31
+ "execute_commands",
32
+ "config",
33
+ "just_cli",
34
+ "installer",
35
+ "SimpleReleaseInstaller",
36
+ "BinaryInstaller",
37
+ "load_env_config",
38
+ "update_env_config",
39
+ "system"
40
+ ]
just/cli.py ADDED
@@ -0,0 +1,79 @@
1
+ import functools
2
+ import importlib
3
+ import os
4
+ import traceback
5
+
6
+ from pathlib import Path
7
+ from typer.core import TyperGroup
8
+ from typing import Any, Callable, List, TypeVar, Optional
9
+
10
+ from just.core.config import load_env_config, get_command_dir, get_extension_dir
11
+ from just.utils import echo
12
+ from just.utils.typer_utils import create_typer_app
13
+
14
+
15
+ just_cli = create_typer_app()
16
+
17
+ T = TypeVar('T')
18
+
19
+
20
+ def capture_exception(func: Callable[..., T]) -> Callable[..., Optional[T]]:
21
+ @functools.wraps(func)
22
+ def wrapper(*args: Any, **kwargs: Any) -> Optional[T]:
23
+ try:
24
+ return func(*args, **kwargs)
25
+ except Exception as e:
26
+ echo.error(str(e))
27
+ exit(1)
28
+
29
+ return wrapper
30
+
31
+
32
+ class SortedGroup(TyperGroup):
33
+ def list_commands(self, ctx):
34
+ return sorted(super().list_commands(ctx))
35
+
36
+
37
+ def run_just_cli(*args, **kwargs):
38
+ load_env_config()
39
+ just_cli(*args, **kwargs)
40
+
41
+
42
+ def traverse_script_dir(directory: str) -> List[str]:
43
+ script_modules = []
44
+ for root, dirs, files in os.walk(directory):
45
+ for file in files:
46
+ if not file.startswith('_') and file.endswith(".py"):
47
+ script_modules.append(
48
+ "just." +
49
+ '.'.join(Path(root).relative_to(Path(__file__).parent).parts) +
50
+ f".{str(Path(file).stem)}"
51
+ )
52
+ return script_modules
53
+
54
+
55
+
56
+ def main():
57
+ # Dynamically import all script modules to register their commands
58
+ script_modules = []
59
+ script_modules.extend(traverse_script_dir(get_command_dir().as_posix()))
60
+ script_modules.extend(traverse_script_dir(get_extension_dir().as_posix()))
61
+ script_modules.sort()
62
+ missing_packages = []
63
+ for module_name in script_modules:
64
+ try:
65
+ # echo.debug("Importing", module_name)
66
+ importlib.import_module(module_name)
67
+ except ImportError as e:
68
+ traceback.print_exc()
69
+ package_name = e.name
70
+ if package_name not in missing_packages:
71
+ missing_packages.append(package_name)
72
+ # Handle the case where the module cannot be found
73
+ echo.warning(
74
+ f"`{package_name}` is not installed, some sub commands are disabled, "
75
+ f"refer to README.md for instructions."
76
+ )
77
+ continue
78
+ # Run the CLI application
79
+ run_just_cli()
File without changes
@@ -0,0 +1,62 @@
1
+ import typer
2
+ from typing import List, Optional, Dict
3
+ from typing_extensions import Annotated
4
+
5
+ from just import just_cli, capture_exception, echo
6
+ from just.utils import download_with_resume
7
+ from just.utils.download_utils import DownloadError, NetworkError, FileSystemError, DownloadCancelledError
8
+
9
+
10
+ def parse_headers(header_list: Optional[List[str]]) -> Optional[Dict[str, str]]:
11
+ """Parse header list to dictionary."""
12
+ if not header_list:
13
+ return None
14
+ headers: Dict[str, str] = {}
15
+ for header in header_list:
16
+ if ':' in header:
17
+ key, value = header.split(':', 1)
18
+ headers[key.strip()] = value.strip()
19
+ return headers
20
+
21
+
22
+ @just_cli.command(name="download")
23
+ @capture_exception
24
+ def download_command(
25
+ url: Annotated[str, typer.Argument(
26
+ help="URL to download",
27
+ show_default=False
28
+ )],
29
+ headers: Annotated[Optional[List[str]], typer.Option(
30
+ "-H", "--header",
31
+ help="Custom headers (can be used multiple times)"
32
+ )] = None,
33
+ output: Annotated[Optional[str], typer.Option(
34
+ "-o", "--output",
35
+ help="Output filename"
36
+ )] = None,
37
+ verbose: Annotated[bool, typer.Option(
38
+ "--verbose", "-v",
39
+ help="Enable verbose logging"
40
+ )] = False
41
+ ) -> None:
42
+ """
43
+ Download a file from URL with resume support and custom headers.
44
+
45
+ Examples:
46
+ just download https://example.com/file.zip
47
+ just download https://example.com/file.zip -H "Authorization: Bearer token" -H "User-Agent: MyApp/1.0"
48
+ just download https://example.com/file.zip -o myfile.zip
49
+ """
50
+ try:
51
+ download_with_resume(
52
+ url=url,
53
+ headers=parse_headers(headers),
54
+ output_file=output,
55
+ verbose=verbose
56
+ )
57
+ except DownloadCancelledError as e:
58
+ echo.info(str(e))
59
+ raise typer.Exit(1)
60
+ except (NetworkError, FileSystemError, DownloadError) as e:
61
+ echo.error(f"Download failed: {e}")
62
+ raise typer.Exit(1)
just/commands/edit.py ADDED
@@ -0,0 +1,37 @@
1
+ import os
2
+ import typer
3
+
4
+ from pathlib import Path
5
+ from typing_extensions import Annotated
6
+
7
+ from just import just_cli, capture_exception
8
+ from just.core.config import get_env_config_file
9
+ from just.tui import FileEditor
10
+
11
+
12
+ INTERNAL_FILES = {
13
+ "config": str(get_env_config_file())
14
+ }
15
+
16
+
17
+ def edit_file_by_textual(file_path):
18
+ """Launch the TUI file editor for the specified file"""
19
+ editor = FileEditor(file_path)
20
+ editor.run()
21
+
22
+
23
+ @just_cli.command(name="edit")
24
+ @capture_exception
25
+ def edit_file(
26
+ file_path: Annotated[str, typer.Argument(
27
+ help="The file to edit",
28
+ show_default=False
29
+ )]
30
+ ):
31
+ """
32
+ Edit file.
33
+ """
34
+ if file_path.lower() in INTERNAL_FILES:
35
+ file_path = INTERNAL_FILES[file_path.lower()]
36
+
37
+ edit_file_by_textual(file_path)
@@ -0,0 +1,7 @@
1
+ from just import create_typer_app, just_cli
2
+
3
+
4
+ ext_cli = create_typer_app(name="ext", help="Manage just extensions.")
5
+ just_cli.add_typer(ext_cli)
6
+
7
+ __all__ = ["ext_cli"]
@@ -0,0 +1,58 @@
1
+ import shlex
2
+ import typer
3
+
4
+ from typing import List, Optional
5
+
6
+
7
+ from just import echo
8
+ from just.core.extension.generator import generate_extension_script
9
+ from just.core.extension.utils import split_command_line
10
+ from just.tui.extension import ExtensionTUI
11
+ from . import ext_cli
12
+
13
+
14
+ @ext_cli.command(name="add", context_settings={"ignore_unknown_options": True})
15
+ def add_extension(
16
+ commands: Optional[List[str]] = typer.Argument(
17
+ None,
18
+ help="The command to register as a just extension"
19
+ ),
20
+ tui: bool = typer.Option(
21
+ False,
22
+ "--tui",
23
+ help="Launch TUI to configure the command"
24
+ )
25
+ ):
26
+ """
27
+ Parse and register a command as a just extension.
28
+
29
+ Args:
30
+ commands: The commands to register, e.g., "docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' f523e75ca4ef"
31
+ tui: Whether to launch TUI mode
32
+ """
33
+ # If no command provided or TUI mode requested, launch TUI
34
+ if not commands or tui:
35
+ launch_tui()
36
+ return
37
+
38
+ # Print parsed command for debugging
39
+ echo.echo(str(commands))
40
+
41
+ just_extension_commands = input("Enter extension commands: ")
42
+ # Split the command line to handle annotations with spaces properly
43
+ just_extension_commands = split_command_line(just_extension_commands)
44
+ echo.echo(str(just_extension_commands))
45
+
46
+ # Generate the extension script
47
+ generate_extension_script(
48
+ shlex.join(commands),
49
+ just_extension_commands,
50
+ )
51
+
52
+
53
+ def launch_tui():
54
+ """Launch TUI for configuring extension commands"""
55
+
56
+ # Launch the TUI app
57
+ app = ExtensionTUI()
58
+ app.run()
@@ -0,0 +1,76 @@
1
+ import typer
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ from just import Annotated, just_cli, capture_exception
6
+ from just.utils import echo
7
+ from just.utils.archive import extract as archive_extract, detect_archive_format, ArchiveFormat
8
+
9
+
10
+ def get_default_output_dir(archive_path: str) -> str:
11
+ """
12
+ Get default output directory from archive filename.
13
+
14
+ Simply removes all extensions by taking everything before the first dot.
15
+
16
+ Args:
17
+ archive_path: Path to the archive file
18
+
19
+ Returns:
20
+ Directory name without extensions
21
+ """
22
+ return archive_path.split(".")[0]
23
+
24
+
25
+ @just_cli.command(name="extract")
26
+ @capture_exception
27
+ def extract_command(
28
+ archive: Annotated[str, typer.Argument(
29
+ help="Path to the archive file to extract"
30
+ )],
31
+ output: Annotated[Optional[str], typer.Option(
32
+ "-o", "--output",
33
+ help="Output directory for extracted files (default: archive name without extension)"
34
+ )] = None
35
+ ) -> None:
36
+ """
37
+ Extract archive and compressed files with automatic format detection.
38
+
39
+ Supports: ZIP, TAR (with gz/bz2/xz/zst compression), GZIP, BZIP2, XZ, ZSTD, 7Z
40
+
41
+ Automatically detects format using magic bytes (file signatures) for reliable
42
+ identification, even with incorrect file extensions. Extracts to a directory
43
+ named after the archive by default.
44
+
45
+ Optional dependencies:
46
+ - 7Z support: pip install py7zr
47
+ - ZSTD support: pip install zstandard
48
+
49
+ Examples:
50
+ just extract archive.zip # Extracts to ./archive/
51
+ just extract archive.tar.gz # Extracts to ./archive/
52
+ just extract file.gz # Extracts to ./file
53
+ just extract data.7z -o out/ # Extracts to ./out/
54
+ """
55
+ archive_path = Path(archive)
56
+
57
+ if not archive_path.exists():
58
+ echo.error(f"Archive not found: {archive}")
59
+ raise typer.Exit(1)
60
+
61
+ fmt = detect_archive_format(str(archive_path))
62
+
63
+ if fmt == ArchiveFormat.UNKNOWN:
64
+ echo.error(f"Unknown or unsupported archive format: {archive}")
65
+ raise typer.Exit(1)
66
+
67
+ if fmt == ArchiveFormat.RAR:
68
+ echo.error("RAR format detected but not supported")
69
+ echo.info("RAR is a proprietary format. Install rarfile: pip install rarfile")
70
+ raise typer.Exit(1)
71
+
72
+ if output is None:
73
+ output = get_default_output_dir(str(archive_path))
74
+
75
+ if not archive_extract(str(archive_path), output):
76
+ raise Exception("Failed to extract archive")
@@ -0,0 +1,36 @@
1
+ import typer
2
+ from typing import List, Optional
3
+ from typing_extensions import Annotated
4
+
5
+ from just import just_cli, echo
6
+ from just.core.installer.install_package import install_package
7
+ from just.utils.typer_utils import show_help
8
+
9
+
10
+ @just_cli.command(name="install", context_settings={"ignore_unknown_options": True})
11
+ def install(
12
+ args: Annotated[Optional[List[str]], typer.Argument(
13
+ help="The arguments to install the package"
14
+ )] = None,
15
+ help_flag: Annotated[bool, typer.Option(
16
+ "--help", "-h",
17
+ help="Show this help message and exit"
18
+ )] = False
19
+ ):
20
+ if not args:
21
+ show_help(just_cli, "install")
22
+ return
23
+
24
+ package_name = args.pop(0)
25
+ if help_flag:
26
+ args.append("--help")
27
+
28
+ try:
29
+ install_package(package_name, args)
30
+ except Exception as e:
31
+ echo.fail(f"Error installing {package_name}: {e}")
32
+ raise typer.Exit(1)
33
+
34
+
35
+ if __name__ == "__main__":
36
+ just_cli()
just/commands/linux.py ADDED
@@ -0,0 +1,201 @@
1
+ import os
2
+ import typer
3
+ import shutil
4
+ from pathlib import Path
5
+ from typing_extensions import Annotated
6
+
7
+ from just import just_cli, capture_exception, echo
8
+ from just.utils.file_utils import read_file_text
9
+ from just.utils import confirm_action
10
+
11
+
12
+ @just_cli.command(name="cat")
13
+ @capture_exception
14
+ def cat_file(
15
+ file_paths: Annotated[list[str], typer.Argument(
16
+ help="Files to display",
17
+ show_default=False
18
+ )],
19
+ with_line_numbers: Annotated[bool, typer.Option(
20
+ "--number", "-n",
21
+ help="Number all output lines"
22
+ )] = False
23
+ ):
24
+ """
25
+ Concatenate and print files.
26
+ """
27
+ for file_path in file_paths:
28
+ if os.path.isdir(file_path):
29
+ echo.red(f"cat: {file_path} is a directory")
30
+ exit(1)
31
+ try:
32
+ echo.echo(read_file_text(file_path, with_line_numbers=with_line_numbers))
33
+ except FileNotFoundError:
34
+ echo.red(f"cat: The file {file_path} does not exist")
35
+ exit(1)
36
+
37
+
38
+ @just_cli.command(name="ls")
39
+ @capture_exception
40
+ def list_files(
41
+ path: Annotated[str, typer.Argument(
42
+ help="Directory to list",
43
+ show_default=False
44
+ )] = ".",
45
+ long_format: Annotated[bool, typer.Option(
46
+ "-l",
47
+ help="Use a long listing format"
48
+ )] = False,
49
+ all_format: Annotated[bool, typer.Option(
50
+ "--all", "-a",
51
+ help="Do not ignore entries starting with ."
52
+ )] = False
53
+ ):
54
+ """
55
+ List directory contents.
56
+ """
57
+ p = Path(path)
58
+ if not p.exists():
59
+ echo.red(f"ls: cannot access '{path}': No such file or directory")
60
+ exit(1)
61
+
62
+ if not p.is_dir():
63
+ if long_format:
64
+ stat = p.stat()
65
+ echo.echo(f"-rw-r--r-- 1 user group {stat.st_size:>8} {p.name}")
66
+ else:
67
+ echo.echo(p.name)
68
+ return
69
+
70
+ entries = list(p.iterdir())
71
+ if not all_format:
72
+ entries = [entry for entry in entries if not entry.name.startswith('.')]
73
+
74
+ if long_format:
75
+ for entry in entries:
76
+ stat = entry.stat()
77
+ permissions = "drwxr-xr-x" if entry.is_dir() else "-rw-r--r--"
78
+ size = stat.st_size
79
+ echo.echo(f"{permissions} 1 user group {size:>8} {entry.name}")
80
+ else:
81
+ for entry in entries:
82
+ echo.echo(entry.name)
83
+
84
+
85
+ @just_cli.command(name="mkdir")
86
+ @capture_exception
87
+ def make_directory(
88
+ dir_names: Annotated[list[str], typer.Argument(
89
+ help="Directories to create",
90
+ show_default=False
91
+ )],
92
+ make_parents: Annotated[bool, typer.Option(
93
+ "--parents", "-p",
94
+ help="No error if existing, make parent directories as needed"
95
+ )] = False
96
+ ):
97
+ """
98
+ Create directories.
99
+ """
100
+ for dir_name in dir_names:
101
+ if make_parents:
102
+ os.makedirs(dir_name, exist_ok=True)
103
+ else:
104
+ os.mkdir(dir_name)
105
+
106
+
107
+ @just_cli.command(name="rm")
108
+ @capture_exception
109
+ def remove_files(
110
+ targets: Annotated[list[str], typer.Argument(
111
+ help="Files or directories to remove",
112
+ show_default=False
113
+ )],
114
+ recursive: Annotated[bool, typer.Option(
115
+ "--recursive", "-r",
116
+ help="Remove directories and their contents recursively"
117
+ )] = False
118
+ ):
119
+ """
120
+ Remove files or directories.
121
+ """
122
+ for target in targets:
123
+ target_path = Path(target)
124
+ if not target_path.exists():
125
+ echo.red(f"rm: cannot remove '{target}': No such file or directory")
126
+ exit(1)
127
+ if target_path.is_dir():
128
+ # Prompt for confirmation when removing directory without -r
129
+ if not recursive and not confirm_action(f"rm: descend into directory '{target}'?"):
130
+ continue
131
+ shutil.rmtree(target_path)
132
+ else:
133
+ os.remove(target_path)
134
+
135
+
136
+ @just_cli.command(name="cp")
137
+ @capture_exception
138
+ def copy_files(
139
+ source: Annotated[str, typer.Argument(
140
+ help="Source file or directory",
141
+ show_default=False
142
+ )],
143
+ destination: Annotated[str, typer.Argument(
144
+ help="Destination file or directory",
145
+ show_default=False
146
+ )],
147
+ recursive: Annotated[bool, typer.Option(
148
+ "--recursive", "-r",
149
+ help="Copy directories recursively"
150
+ )] = False
151
+ ):
152
+ """
153
+ Copy files or directories.
154
+ """
155
+ source_path = Path(source)
156
+ dest_path = Path(destination)
157
+
158
+ if not source_path.exists():
159
+ echo.red(f"cp: cannot stat '{source}': No such file or directory")
160
+ exit(1)
161
+
162
+
163
+ if source_path.is_dir():
164
+ if not recursive:
165
+ # Prompt for confirmation when copying directory without -r
166
+ if not confirm_action(f"cp: -r not specified; omitting directory '{source}'"):
167
+ exit(1)
168
+ if dest_path.exists() and dest_path.is_dir():
169
+ # Copy directory into existing directory
170
+ shutil.copytree(source_path, dest_path / source_path.name)
171
+ else:
172
+ shutil.copytree(source_path, dest_path)
173
+ else:
174
+ shutil.copy2(source_path, dest_path)
175
+
176
+
177
+ @just_cli.command(name="mv")
178
+ @capture_exception
179
+ def move_files(
180
+ source: Annotated[str, typer.Argument(
181
+ help="Source file or directory",
182
+ show_default=False
183
+ )],
184
+ destination: Annotated[str, typer.Argument(
185
+ help="Destination file or directory",
186
+ show_default=False
187
+ )]
188
+ ):
189
+ """
190
+ Move or rename files or directories.
191
+ """
192
+ source_path = Path(source)
193
+ dest_path = Path(destination)
194
+
195
+ if not source_path.exists():
196
+ echo.red(f"mv: cannot stat '{source}': No such file or directory")
197
+ exit(1)
198
+ if dest_path.exists() and not confirm_action(f"mv: overwrite '{destination}'?"):
199
+ exit(1)
200
+
201
+ shutil.move(str(source_path), str(dest_path))
@@ -0,0 +1,27 @@
1
+ import typer
2
+
3
+ from typing_extensions import Annotated
4
+
5
+ from just import create_typer_app, capture_exception, just_cli
6
+ from just.utils import execute_command
7
+
8
+
9
+ tunnel_cli = create_typer_app()
10
+
11
+
12
+ @just_cli.command(name="tunnel")
13
+ @capture_exception
14
+ def create_cloudflare_tunnel(
15
+ url: Annotated[str, typer.Argument(
16
+ help='The URL to tunnel',
17
+ show_default=False
18
+ )]
19
+ ):
20
+ """
21
+ Create Cloudflare tunnel.
22
+ """
23
+ command = r"""
24
+ cloudflared tunnel --url <URL>
25
+ """
26
+ command = command.replace('<URL>', url)
27
+ execute_command(command)
just/commands/view.py ADDED
@@ -0,0 +1,33 @@
1
+ import typer
2
+
3
+ from pathlib import Path
4
+ from typing_extensions import Annotated
5
+
6
+ from just import just_cli
7
+ from just.tui.markdown import MarkdownApp
8
+ from just.commands.edit import edit_file
9
+
10
+
11
+ def view_markdown_by_textual(file_path: str):
12
+ """View markdown file."""
13
+ app = MarkdownApp()
14
+ app.path = Path(file_path)
15
+ app.run()
16
+
17
+
18
+ @just_cli.command(name="view")
19
+ def view_file(
20
+ file_path: Annotated[str, typer.Argument(
21
+ help="The file to view",
22
+ show_default=False
23
+ )]
24
+ ):
25
+ """
26
+ Preview the structured text files (e.g., Markdown, JSON, XML)
27
+ """
28
+ ext = "".join(Path(file_path).suffixes)
29
+ if ext == '.md':
30
+ view_markdown_by_textual(file_path)
31
+ # TODO: support other file types
32
+ else:
33
+ edit_file(file_path)
just/core/__init__.py ADDED
File without changes