just-cli 0.4.0__tar.gz → 0.5.0__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 (149) hide show
  1. just_cli-0.5.0/.claude/settings.local.json +8 -0
  2. just_cli-0.5.0/PKG-INFO +208 -0
  3. just_cli-0.5.0/README.md +190 -0
  4. just_cli-0.5.0/docs/images/editor_demo.png +0 -0
  5. just_cli-0.5.0/docs/images/note_demo.png +0 -0
  6. just_cli-0.5.0/docs/images/viewer_demo.png +0 -0
  7. {just_cli-0.4.0 → just_cli-0.5.0}/pyproject.toml +2 -2
  8. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/cli.py +33 -12
  9. just_cli-0.5.0/src/just/commands/archive.py +57 -0
  10. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/ext/add.py +32 -10
  11. just_cli-0.5.0/src/just/commands/update.py +52 -0
  12. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/extension/generator.py +63 -5
  13. just_cli-0.5.0/src/just/utils/archive/__init__.py +33 -0
  14. just_cli-0.5.0/src/just/utils/archive/archiver.py +97 -0
  15. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/archive/compression_handler.py +90 -9
  16. just_cli-0.5.0/src/just/utils/archive/sevenzip_handler.py +88 -0
  17. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/typer_utils.py +35 -7
  18. just_cli-0.5.0/tests/test_archive_creation.py +372 -0
  19. {just_cli-0.4.0 → just_cli-0.5.0}/uv.lock +15 -18
  20. just_cli-0.4.0/.claude/settings.local.json +0 -7
  21. just_cli-0.4.0/PKG-INFO +0 -210
  22. just_cli-0.4.0/README.md +0 -192
  23. just_cli-0.4.0/docs/images/editor_demo.png +0 -0
  24. just_cli-0.4.0/docs/images/viewer_demo.png +0 -0
  25. just_cli-0.4.0/src/just/utils/archive/__init__.py +0 -22
  26. just_cli-0.4.0/src/just/utils/archive/sevenzip_handler.py +0 -44
  27. {just_cli-0.4.0 → just_cli-0.5.0}/.claude/skills/textual/README.md +0 -0
  28. {just_cli-0.4.0 → just_cli-0.5.0}/.claude/skills/textual/SKILL.md +0 -0
  29. {just_cli-0.4.0 → just_cli-0.5.0}/.claude/skills/textual/guide.md +0 -0
  30. {just_cli-0.4.0 → just_cli-0.5.0}/.claude/skills/textual/quick-reference.md +0 -0
  31. {just_cli-0.4.0 → just_cli-0.5.0}/.github/workflows/publish.yml +0 -0
  32. {just_cli-0.4.0 → just_cli-0.5.0}/.gitignore +0 -0
  33. {just_cli-0.4.0 → just_cli-0.5.0}/AGENTS.md +0 -0
  34. {just_cli-0.4.0 → just_cli-0.5.0}/Dockerfile +0 -0
  35. {just_cli-0.4.0 → just_cli-0.5.0}/docs/extension_guide.md +0 -0
  36. {just_cli-0.4.0 → just_cli-0.5.0}/scripts/ai_coding/agents2claude.py +0 -0
  37. {just_cli-0.4.0 → just_cli-0.5.0}/scripts/ai_coding/claude2agents.py +0 -0
  38. {just_cli-0.4.0 → just_cli-0.5.0}/scripts/system/linux/proxy/proxy.sh +0 -0
  39. {just_cli-0.4.0 → just_cli-0.5.0}/scripts/system/mac/proxy/proxy.sh +0 -0
  40. {just_cli-0.4.0 → just_cli-0.5.0}/scripts/system/windows/proxy/proxy.bat +0 -0
  41. {just_cli-0.4.0 → just_cli-0.5.0}/scripts/system/windows/proxy/proxy.ps1 +0 -0
  42. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/__init__.py +0 -0
  43. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/__init__.py +0 -0
  44. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/download.py +0 -0
  45. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/edit.py +0 -0
  46. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/ext/__init__.py +0 -0
  47. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/ext/edit.py +0 -0
  48. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/ext/list.py +0 -0
  49. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/ext/remove.py +0 -0
  50. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/extract.py +0 -0
  51. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/install.py +0 -0
  52. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/linux.py +0 -0
  53. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/note.py +0 -0
  54. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/tunnel.py +0 -0
  55. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/view.py +0 -0
  56. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/commands/workspace.py +0 -0
  57. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/__init__.py +0 -0
  58. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/config/__init__.py +0 -0
  59. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/config/config.py +0 -0
  60. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/config/utils.py +0 -0
  61. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/extension/__init__.py +0 -0
  62. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/extension/parser.py +0 -0
  63. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/extension/utils.py +0 -0
  64. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/extension/validator.py +0 -0
  65. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/AGENTS.md +0 -0
  66. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/__init__.py +0 -0
  67. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/decorator.py +0 -0
  68. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/install_package.py +0 -0
  69. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/installers/__init__.py +0 -0
  70. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/installers/binary_release.py +0 -0
  71. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/installers/remote_script.py +0 -0
  72. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/package_info.py +0 -0
  73. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/source/__init__.py +0 -0
  74. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/source/base.py +0 -0
  75. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/source/http.py +0 -0
  76. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/source/local.py +0 -0
  77. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/installer/utils.py +0 -0
  78. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/system_probe/__init__.py +0 -0
  79. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/system_probe/system_info.py +0 -0
  80. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/core/system_probe/system_probe.py +0 -0
  81. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/AGENTS.md +0 -0
  82. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/__init__.py +0 -0
  83. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/brew/installer.py +0 -0
  84. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/claude-code/__init__.py +0 -0
  85. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/claude-code/installer.py +0 -0
  86. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/cloudflare/__init__.py +0 -0
  87. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/cloudflare/installer.py +0 -0
  88. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/docker/__init__.py +0 -0
  89. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/docker/installer.py +0 -0
  90. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/edit/__init__.py +0 -0
  91. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/edit/installer.py +0 -0
  92. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/gh/__init__.py +0 -0
  93. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/gh/installer.py +0 -0
  94. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/glab/__init__.py +0 -0
  95. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/glab/installer.py +0 -0
  96. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/kimi/installer.py +0 -0
  97. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/miniconda3/installer.py +0 -0
  98. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/mssh/__init__.py +0 -0
  99. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/mssh/installer.py +0 -0
  100. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/nvm/__init__.py +0 -0
  101. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/nvm/installer.py +0 -0
  102. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/openclaw/installer.py +0 -0
  103. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/opencode/installer.py +0 -0
  104. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/qodercli/__init__.py +0 -0
  105. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/qodercli/installer.py +0 -0
  106. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/tailscale/__init__.py +0 -0
  107. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/tailscale/installer.py +0 -0
  108. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/uv/__init__.py +0 -0
  109. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/installers/uv/installer.py +0 -0
  110. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/tui/__init__.py +0 -0
  111. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/tui/editor.py +0 -0
  112. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/tui/extension.py +0 -0
  113. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/tui/markdown.py +0 -0
  114. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/tui/note.py +0 -0
  115. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/tui/workspace.py +0 -0
  116. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/AGENTS.md +0 -0
  117. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/__init__.py +0 -0
  118. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/archive/extractor.py +0 -0
  119. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/archive/format_detect.py +0 -0
  120. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/archive/tar_handler.py +0 -0
  121. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/archive/zip_handler.py +0 -0
  122. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/docker_utils.py +0 -0
  123. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/download_utils.py +0 -0
  124. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/echo_utils.py +0 -0
  125. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/env_utils.py +0 -0
  126. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/file_utils.py +0 -0
  127. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/format_utils.py +0 -0
  128. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/note_utils.py +0 -0
  129. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/progress.py +0 -0
  130. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/shell_utils.py +0 -0
  131. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/system_probe.py +0 -0
  132. {just_cli-0.4.0 → just_cli-0.5.0}/src/just/utils/user_interaction.py +0 -0
  133. {just_cli-0.4.0 → just_cli-0.5.0}/tests/AGENTS.md +0 -0
  134. {just_cli-0.4.0 → just_cli-0.5.0}/tests/requirements.txt +0 -0
  135. {just_cli-0.4.0 → just_cli-0.5.0}/tests/run_tests.py +0 -0
  136. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_archive_extraction.py +0 -0
  137. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_bash_script_installer.py +0 -0
  138. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_download.py +0 -0
  139. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_extension/README.md +0 -0
  140. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_extension/test_ext.py +0 -0
  141. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_extension/test_extension_add.py +0 -0
  142. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_extension.py +0 -0
  143. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_file_utils.py +0 -0
  144. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_init.py +0 -0
  145. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_installer_e2e.py +0 -0
  146. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_linux_commands.py +0 -0
  147. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_progress_utils.py +0 -0
  148. {just_cli-0.4.0 → just_cli-0.5.0}/tests/test_system_probe.py +0 -0
  149. {just_cli-0.4.0 → just_cli-0.5.0}/tests/testing.py +0 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(just install:*)",
5
+ "mcp__plugin_context7_context7__resolve-library-id"
6
+ ]
7
+ }
8
+ }
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: just-cli
3
+ Version: 0.5.0
4
+ Summary: Your all-in-one CLI Toolkit
5
+ Author-email: zzzcb <zjxs.zzzcb@gmail.com>
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: httpx
8
+ Requires-Dist: prompt-toolkit>=3.0.0
9
+ Requires-Dist: py7zr
10
+ Requires-Dist: python-dotenv
11
+ Requires-Dist: rich>=14.2.0
12
+ Requires-Dist: textual>=6.4.0
13
+ Requires-Dist: typer>=0.26.0
14
+ Requires-Dist: zstandard
15
+ Provides-Extra: dev
16
+ Requires-Dist: loguru; extra == 'dev'
17
+ Description-Content-Type: text/markdown
18
+
19
+ <h1 align="center">🛠️ JUST CLI</h1>
20
+
21
+ <p align="center"><strong>The simple stuff should be simple.</strong></p>
22
+
23
+ <p align="center">
24
+ <a href="https://pypi.org/project/just-cli/"> <img src="https://img.shields.io/pypi/v/just-cli?color=blue" alt="PyPI"></a>
25
+ <a href="https://pypi.org/project/just-cli/"> <img src="https://img.shields.io/pypi/pyversions/just-cli" alt="Python"></a>
26
+ <img src="https://img.shields.io/badge/license-MIT-purple.svg" alt="License: MIT">
27
+ </p>
28
+
29
+ <p align="center">
30
+ Tired of Googling "how to install X" for the 47th time, just to end up copy-pasting from official docs?
31
+ </p>
32
+ <p align="center">
33
+ Tired of copy-pasting giant commands every time, just to change one little parameter?
34
+ </p>
35
+ <p align="center">
36
+ Start using <b>Just</b> — an all-in-one CLI toolkit that makes everyday developer tasks <i>actually simple</i>.
37
+ </p>
38
+
39
+ ---
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ uv tool install just-cli
45
+ ```
46
+
47
+ Or with pip:
48
+
49
+ ```bash
50
+ pip install just-cli
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Features
56
+
57
+ ### 📦 Archive & Extract
58
+
59
+ Create and extract archives with automatic format detection.
60
+
61
+ ```bash
62
+ # Create archives — format is determined by output extension
63
+ just archive mydir -o backup.zip
64
+ just archive mydir -o backup.tar.gz
65
+ just archive mydir -o backup.7z
66
+ just archive myfile.txt -o myfile.txt.gz # single-file compression
67
+
68
+ # Extract archives — magic bytes detection, works even with wrong extensions
69
+ just extract backup.zip
70
+ just extract data.tar.gz -o ./output_dir
71
+ ```
72
+
73
+ <details>
74
+ <summary><strong>Supported formats</strong></summary>
75
+
76
+ | Format | Archive | Extract |
77
+ |--------|:-------:|:-------:|
78
+ | `.zip` | ✅ | ✅ |
79
+ | `.tar` | ✅ | ✅ |
80
+ | `.tar.gz` / `.tgz` | ✅ | ✅ |
81
+ | `.tar.bz2` / `.tbz2` | ✅ | ✅ |
82
+ | `.tar.xz` / `.txz` | ✅ | ✅ |
83
+ | `.tar.zst` / `.tzst` | ✅ | ✅ |
84
+ | `.gz` | ✅ | ✅ |
85
+ | `.bz2` | ✅ | ✅ |
86
+ | `.xz` | ✅ | ✅ |
87
+ | `.zst` | ✅ | ✅ |
88
+ | `.7z` | ✅ | ✅ |
89
+
90
+ </details>
91
+
92
+ ### 📥 Download
93
+
94
+ Like `wget` or `curl`, but with auto-resume **by default** and a progress bar.
95
+
96
+ ```bash
97
+ # Filename is automatically extracted from URL, resume is on by default
98
+ just download https://example.com/big-file.zip
99
+
100
+ # Custom headers and output name
101
+ just download https://api.example.com/data -H "Authorization: Bearer token" -o data.json
102
+ ```
103
+
104
+ ### 📝 Edit
105
+
106
+ A lightweight TUI text editor built right in.
107
+
108
+ ```bash
109
+ just edit README.md
110
+ ```
111
+
112
+ ![Editor Screenshot](docs/images/editor_demo.png)
113
+
114
+ ### 📖 View
115
+
116
+ Smart file viewer with syntax highlighting, TOC, and structured rendering.
117
+
118
+ ```bash
119
+ just view README.md # Markdown
120
+ just view data.json # JSON
121
+ just view config.xml # XML
122
+ ```
123
+
124
+ ![Viewer Screenshot](docs/images/viewer_demo.png)
125
+
126
+ ### 🌐 Tunnel
127
+
128
+ Expose your local server to the internet via Cloudflare Tunnel.
129
+
130
+ ```bash
131
+ just tunnel http://localhost:8000
132
+ ```
133
+
134
+ ### 🐧 File Operations
135
+
136
+ Common file commands, cross-platform, no syntax surprises.
137
+
138
+ ```bash
139
+ just cat file.txt
140
+ just ls
141
+ just cp src dst
142
+ just mv old new
143
+ just rm file
144
+ just mkdir dir
145
+ ```
146
+
147
+ ### 📒 Notes
148
+
149
+ Quick notes stored in `~/.just/notes`.
150
+
151
+ ```bash
152
+ just note
153
+ ```
154
+
155
+ ![Note Screenshot](docs/images/note_demo.png)
156
+
157
+ ### 🧩 Extension System
158
+
159
+ Turn any long command into a reusable, type-safe CLI in 2 steps.
160
+
161
+ **1. Register the base command:**
162
+ ```bash
163
+ just ext add docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' f523e75ca4ef
164
+ ```
165
+
166
+ **2. Define your CLI by marking dynamic parts:**
167
+ ```text
168
+ Enter extension commands: just docker ip f523e75ca4ef[container_id:str#The Container ID]
169
+ ```
170
+
171
+ **Done.** Now use it:
172
+ ```bash
173
+ just docker ip <container_id>
174
+ ```
175
+
176
+ Under the hood, `just` generates a native Python script using typer — with auto-completion, type validation, and help messages. All from simple **string replacement**.
177
+
178
+ ### 💿 Installer Framework
179
+
180
+ Automate installation scripts with system probing and binary/archive helpers.
181
+
182
+ ```python
183
+ @just.installer(check="cloudflared --version")
184
+ def install_cloudflared():
185
+ if just.system.pms.brew.is_available():
186
+ just.execute_commands("brew install cloudflared")
187
+ elif just.system.platform == 'linux':
188
+ just.BinaryInstaller(
189
+ url='https://github.com/cloudflare/cloudflared/releases/.../cloudflared-linux-amd64',
190
+ alias='cloudflared'
191
+ ).run()
192
+ ```
193
+
194
+ Built-in system info:
195
+ - `just.system.platform` → `linux`, `windows`, `darwin`
196
+ - `just.system.arch` → `x86_64`, `aarch64`
197
+ - `just.system.pms` → detects `winget`, `brew`, `apt`, etc.
198
+
199
+ ---
200
+
201
+ ## Contributing
202
+
203
+ Found a bug? Want a new feature?
204
+ Fork it, fix it, ship it. Keep it cool, keep it simple, don't break the *"just works"* vibe.
205
+
206
+ ## License
207
+
208
+ [MIT](LICENSE)
@@ -0,0 +1,190 @@
1
+ <h1 align="center">🛠️ JUST CLI</h1>
2
+
3
+ <p align="center"><strong>The simple stuff should be simple.</strong></p>
4
+
5
+ <p align="center">
6
+ <a href="https://pypi.org/project/just-cli/"> <img src="https://img.shields.io/pypi/v/just-cli?color=blue" alt="PyPI"></a>
7
+ <a href="https://pypi.org/project/just-cli/"> <img src="https://img.shields.io/pypi/pyversions/just-cli" alt="Python"></a>
8
+ <img src="https://img.shields.io/badge/license-MIT-purple.svg" alt="License: MIT">
9
+ </p>
10
+
11
+ <p align="center">
12
+ Tired of Googling "how to install X" for the 47th time, just to end up copy-pasting from official docs?
13
+ </p>
14
+ <p align="center">
15
+ Tired of copy-pasting giant commands every time, just to change one little parameter?
16
+ </p>
17
+ <p align="center">
18
+ Start using <b>Just</b> — an all-in-one CLI toolkit that makes everyday developer tasks <i>actually simple</i>.
19
+ </p>
20
+
21
+ ---
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ uv tool install just-cli
27
+ ```
28
+
29
+ Or with pip:
30
+
31
+ ```bash
32
+ pip install just-cli
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Features
38
+
39
+ ### 📦 Archive & Extract
40
+
41
+ Create and extract archives with automatic format detection.
42
+
43
+ ```bash
44
+ # Create archives — format is determined by output extension
45
+ just archive mydir -o backup.zip
46
+ just archive mydir -o backup.tar.gz
47
+ just archive mydir -o backup.7z
48
+ just archive myfile.txt -o myfile.txt.gz # single-file compression
49
+
50
+ # Extract archives — magic bytes detection, works even with wrong extensions
51
+ just extract backup.zip
52
+ just extract data.tar.gz -o ./output_dir
53
+ ```
54
+
55
+ <details>
56
+ <summary><strong>Supported formats</strong></summary>
57
+
58
+ | Format | Archive | Extract |
59
+ |--------|:-------:|:-------:|
60
+ | `.zip` | ✅ | ✅ |
61
+ | `.tar` | ✅ | ✅ |
62
+ | `.tar.gz` / `.tgz` | ✅ | ✅ |
63
+ | `.tar.bz2` / `.tbz2` | ✅ | ✅ |
64
+ | `.tar.xz` / `.txz` | ✅ | ✅ |
65
+ | `.tar.zst` / `.tzst` | ✅ | ✅ |
66
+ | `.gz` | ✅ | ✅ |
67
+ | `.bz2` | ✅ | ✅ |
68
+ | `.xz` | ✅ | ✅ |
69
+ | `.zst` | ✅ | ✅ |
70
+ | `.7z` | ✅ | ✅ |
71
+
72
+ </details>
73
+
74
+ ### 📥 Download
75
+
76
+ Like `wget` or `curl`, but with auto-resume **by default** and a progress bar.
77
+
78
+ ```bash
79
+ # Filename is automatically extracted from URL, resume is on by default
80
+ just download https://example.com/big-file.zip
81
+
82
+ # Custom headers and output name
83
+ just download https://api.example.com/data -H "Authorization: Bearer token" -o data.json
84
+ ```
85
+
86
+ ### 📝 Edit
87
+
88
+ A lightweight TUI text editor built right in.
89
+
90
+ ```bash
91
+ just edit README.md
92
+ ```
93
+
94
+ ![Editor Screenshot](docs/images/editor_demo.png)
95
+
96
+ ### 📖 View
97
+
98
+ Smart file viewer with syntax highlighting, TOC, and structured rendering.
99
+
100
+ ```bash
101
+ just view README.md # Markdown
102
+ just view data.json # JSON
103
+ just view config.xml # XML
104
+ ```
105
+
106
+ ![Viewer Screenshot](docs/images/viewer_demo.png)
107
+
108
+ ### 🌐 Tunnel
109
+
110
+ Expose your local server to the internet via Cloudflare Tunnel.
111
+
112
+ ```bash
113
+ just tunnel http://localhost:8000
114
+ ```
115
+
116
+ ### 🐧 File Operations
117
+
118
+ Common file commands, cross-platform, no syntax surprises.
119
+
120
+ ```bash
121
+ just cat file.txt
122
+ just ls
123
+ just cp src dst
124
+ just mv old new
125
+ just rm file
126
+ just mkdir dir
127
+ ```
128
+
129
+ ### 📒 Notes
130
+
131
+ Quick notes stored in `~/.just/notes`.
132
+
133
+ ```bash
134
+ just note
135
+ ```
136
+
137
+ ![Note Screenshot](docs/images/note_demo.png)
138
+
139
+ ### 🧩 Extension System
140
+
141
+ Turn any long command into a reusable, type-safe CLI in 2 steps.
142
+
143
+ **1. Register the base command:**
144
+ ```bash
145
+ just ext add docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' f523e75ca4ef
146
+ ```
147
+
148
+ **2. Define your CLI by marking dynamic parts:**
149
+ ```text
150
+ Enter extension commands: just docker ip f523e75ca4ef[container_id:str#The Container ID]
151
+ ```
152
+
153
+ **Done.** Now use it:
154
+ ```bash
155
+ just docker ip <container_id>
156
+ ```
157
+
158
+ Under the hood, `just` generates a native Python script using typer — with auto-completion, type validation, and help messages. All from simple **string replacement**.
159
+
160
+ ### 💿 Installer Framework
161
+
162
+ Automate installation scripts with system probing and binary/archive helpers.
163
+
164
+ ```python
165
+ @just.installer(check="cloudflared --version")
166
+ def install_cloudflared():
167
+ if just.system.pms.brew.is_available():
168
+ just.execute_commands("brew install cloudflared")
169
+ elif just.system.platform == 'linux':
170
+ just.BinaryInstaller(
171
+ url='https://github.com/cloudflare/cloudflared/releases/.../cloudflared-linux-amd64',
172
+ alias='cloudflared'
173
+ ).run()
174
+ ```
175
+
176
+ Built-in system info:
177
+ - `just.system.platform` → `linux`, `windows`, `darwin`
178
+ - `just.system.arch` → `x86_64`, `aarch64`
179
+ - `just.system.pms` → detects `winget`, `brew`, `apt`, etc.
180
+
181
+ ---
182
+
183
+ ## Contributing
184
+
185
+ Found a bug? Want a new feature?
186
+ Fork it, fix it, ship it. Keep it cool, keep it simple, don't break the *"just works"* vibe.
187
+
188
+ ## License
189
+
190
+ [MIT](LICENSE)
Binary file
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "just-cli"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Your all-in-one CLI Toolkit"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -14,7 +14,7 @@ dependencies = [
14
14
  "py7zr",
15
15
  "rich>=14.2.0",
16
16
  "textual>=6.4.0",
17
- "typer>=0.20.0",
17
+ "typer>=0.26.0",
18
18
  "zstandard",
19
19
  ]
20
20
 
@@ -9,7 +9,10 @@ from typing import Any, Callable, List, TypeVar, Optional
9
9
 
10
10
  from just.core.config import load_env_config, get_command_dir, get_extension_dir, ensure_extensions_dir_exists
11
11
  from just.utils import echo
12
- from just.utils.typer_utils import create_typer_app
12
+ from just.utils.typer_utils import create_typer_app, get_just_version
13
+
14
+ import typer
15
+ from typing import Annotated
13
16
 
14
17
 
15
18
  just_cli = create_typer_app()
@@ -17,6 +20,27 @@ just_cli = create_typer_app()
17
20
  T = TypeVar('T')
18
21
 
19
22
 
23
+ def _version_callback(value: Optional[bool]):
24
+ if value:
25
+ echo.echo(f"just {get_just_version()}")
26
+ raise SystemExit(0)
27
+
28
+
29
+ @just_cli.callback()
30
+ def _main_callback(
31
+ version: Annotated[
32
+ Optional[bool],
33
+ typer.Option(
34
+ "-v", "--version",
35
+ help="Show version and exit",
36
+ is_eager=True,
37
+ callback=_version_callback,
38
+ ),
39
+ ] = None,
40
+ ):
41
+ pass
42
+
43
+
20
44
  def capture_exception(func: Callable[..., T]) -> Callable[..., Optional[T]]:
21
45
  @functools.wraps(func)
22
46
  def wrapper(*args: Any, **kwargs: Any) -> Optional[T]:
@@ -32,8 +56,8 @@ def capture_exception(func: Callable[..., T]) -> Callable[..., Optional[T]]:
32
56
  class SortedGroup(TyperGroup):
33
57
  def list_commands(self, ctx):
34
58
  return sorted(super().list_commands(ctx))
35
-
36
-
59
+
60
+
37
61
  def run_just_cli(*args, **kwargs):
38
62
  load_env_config()
39
63
  just_cli(*args, **kwargs)
@@ -56,11 +80,11 @@ def load_extensions_dynamically():
56
80
  extensions_dir = get_extension_dir()
57
81
  if not extensions_dir.exists():
58
82
  return
59
-
83
+
60
84
  import sys
61
85
  if str(extensions_dir.parent) not in sys.path:
62
86
  sys.path.insert(0, str(extensions_dir.parent))
63
-
87
+
64
88
  for root, dirs, files in os.walk(extensions_dir):
65
89
  for file in files:
66
90
  if not file.startswith('_') and file.endswith(".py"):
@@ -72,11 +96,10 @@ def load_extensions_dynamically():
72
96
  echo.warning(f"Failed to load extension {file}: {e}")
73
97
 
74
98
 
75
-
76
99
  def main():
77
100
  # Ensure extensions directory exists
78
101
  ensure_extensions_dir_exists()
79
-
102
+
80
103
  # Dynamically import all script modules to register their commands
81
104
  script_modules = []
82
105
  commands_dir = get_command_dir()
@@ -85,22 +108,20 @@ def main():
85
108
  missing_packages = []
86
109
  for module_name in script_modules:
87
110
  try:
88
- # echo.debug("Importing", module_name)
89
111
  importlib.import_module(module_name)
90
112
  except ImportError as e:
91
113
  traceback.print_exc()
92
114
  package_name = e.name
93
115
  if package_name not in missing_packages:
94
116
  missing_packages.append(package_name)
95
- # Handle the case where the module cannot be found
96
117
  echo.warning(
97
118
  f"`{package_name}` is not installed, some sub commands are disabled, "
98
119
  f"refer to README.md for instructions."
99
120
  )
100
121
  continue
101
-
122
+
102
123
  # Load extensions from ~/.just/extensions
103
124
  load_extensions_dynamically()
104
-
125
+
105
126
  # Run the CLI application
106
- run_just_cli()
127
+ run_just_cli()
@@ -0,0 +1,57 @@
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 archive as archive_create, detect_format_by_extension, ArchiveFormat
8
+
9
+
10
+ @just_cli.command(name="archive")
11
+ @capture_exception
12
+ def archive_command(
13
+ source: Annotated[str, typer.Argument(
14
+ help="Path to the file or directory to archive"
15
+ )],
16
+ output: Annotated[str, typer.Option(
17
+ "-o", "--output",
18
+ help="Output archive file path (format detected from extension)"
19
+ )],
20
+ ) -> None:
21
+ """
22
+ Create archives and compressed files.
23
+
24
+ Supports: ZIP, TAR (with gz/bz2/xz/zst compression), GZIP, BZIP2, XZ, ZSTD, 7Z
25
+
26
+ Format is automatically determined by the output file extension.
27
+ Single-file formats (gz, bz2, xz, zst) only accept a single file source.
28
+
29
+ Optional dependencies:
30
+ - 7Z support: pip install py7zr
31
+ - ZSTD support: pip install zstandard
32
+
33
+ Examples:
34
+ just archive mydir -o backup.zip # Create ZIP archive
35
+ just archive mydir -o backup.tar.gz # Create tar.gz archive
36
+ just archive myfile.txt -o myfile.txt.gz # Compress single file with gzip
37
+ just archive mydir -o backup.7z # Create 7z archive
38
+ """
39
+ source_path = Path(source)
40
+
41
+ if not source_path.exists():
42
+ echo.error(f"Source not found: {source}")
43
+ raise typer.Exit(1)
44
+
45
+ fmt = detect_format_by_extension(output)
46
+ if fmt is None or fmt == ArchiveFormat.UNKNOWN:
47
+ echo.error(f"Unknown archive format for output: {output}")
48
+ echo.info("Supported: .zip, .tar, .tar.gz, .tgz, .tar.bz2, .tbz, "
49
+ ".tar.xz, .txz, .tar.zst, .tzst, .gz, .bz2, .xz, .zst, .7z")
50
+ raise typer.Exit(1)
51
+
52
+ if fmt == ArchiveFormat.RAR:
53
+ echo.error("RAR format is not supported for archiving")
54
+ raise typer.Exit(1)
55
+
56
+ if not archive_create([str(source_path)], output, base_dir=str(source_path.parent)):
57
+ raise Exception("Failed to create archive")
@@ -1,6 +1,6 @@
1
- import shlex
2
1
  import subprocess
3
2
  import typer
3
+ from typing import List
4
4
 
5
5
  from just import echo
6
6
  from just.core.extension.generator import generate_extension_script
@@ -14,11 +14,12 @@ def show_success(script_path, commands):
14
14
  echo.green(f"\n✅ Extension created successfully!")
15
15
  echo.cyan(f" File: {script_path}")
16
16
  echo.echo("")
17
-
17
+
18
18
  # Run -h to show help
19
19
  echo.echo("Help for the new extension:")
20
20
  echo.echo("-" * 40)
21
- subprocess.run(['just'] + commands + ['-h'], shell=True)
21
+ cmd_str = ' '.join(['just'] + commands + ['-h'])
22
+ subprocess.run(cmd_str, shell=True)
22
23
 
23
24
 
24
25
  def add_extension(
@@ -32,6 +33,13 @@ def add_extension(
32
33
  False,
33
34
  "--tui",
34
35
  help="Launch TUI to configure the command"
36
+ ),
37
+ command: List[str] = typer.Option(
38
+ None,
39
+ "--command", "-c",
40
+ help="Shell command to register. May be repeated for sequential "
41
+ "commands (run in order, stop on first failure). "
42
+ "Mutually exclusive with positional command args."
35
43
  )
36
44
  ):
37
45
  """
@@ -40,6 +48,7 @@ def add_extension(
40
48
  \b
41
49
  USAGE:
42
50
  just ext add <command> [args...]
51
+ just ext add -c "<command>" [-c "<command>" ...]
43
52
  Enter: just <name> <syntax...>
44
53
 
45
54
  \b
@@ -60,14 +69,27 @@ def add_extension(
60
69
  -s/--source[target:type]
61
70
  → User runs --target, appends --source to command
62
71
  """
63
- # Get all arguments after 'just ext add'
64
- commands = ctx.args
65
-
66
- # If no command provided or TUI mode requested, launch TUI
67
- if not commands or tui:
72
+ # Get all arguments after 'just ext add' (positional command tokens)
73
+ positional_commands = ctx.args
74
+
75
+ # -c/--command and positional args are mutually exclusive
76
+ if command and positional_commands:
77
+ echo.red("Error: -c/--command cannot be combined with positional command args.")
78
+ raise typer.Exit(code=1)
79
+
80
+ # If no command provided either way, or TUI mode requested, launch TUI
81
+ if not command and (not positional_commands or tui):
68
82
  launch_tui()
69
83
  return
70
84
 
85
+ # Normalize into a list of command strings.
86
+ # Positional tokens form a single command (joined as-is, no escaping);
87
+ # each -c is one complete command string as the user typed it.
88
+ if command:
89
+ commands = command
90
+ else:
91
+ commands = [' '.join(positional_commands)]
92
+
71
93
  # Prompt for extension syntax
72
94
  just_extension_commands = get_input("Enter extension commands: ")
73
95
  # Split the command line to handle annotations with spaces properly
@@ -76,7 +98,7 @@ def add_extension(
76
98
  # Generate the extension script, with overwrite confirmation if exists
77
99
  try:
78
100
  script_path, ext_commands = generate_extension_script(
79
- shlex.join(commands),
101
+ commands,
80
102
  just_extension_commands,
81
103
  )
82
104
  show_success(script_path, ext_commands)
@@ -84,7 +106,7 @@ def add_extension(
84
106
  echo.yellow(f"Warning: {e}")
85
107
  if yes or confirm_action("Do you want to overwrite the existing extension?"):
86
108
  script_path, ext_commands = generate_extension_script(
87
- shlex.join(commands),
109
+ commands,
88
110
  just_extension_commands,
89
111
  overwrite=True
90
112
  )