just-cli 0.3.0__tar.gz → 0.4.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 (139) hide show
  1. {just_cli-0.3.0 → just_cli-0.4.0}/.claude/settings.local.json +1 -1
  2. {just_cli-0.3.0 → just_cli-0.4.0}/.gitignore +4 -1
  3. {just_cli-0.3.0 → just_cli-0.4.0}/PKG-INFO +1 -1
  4. {just_cli-0.3.0 → just_cli-0.4.0}/pyproject.toml +1 -1
  5. just_cli-0.4.0/scripts/ai_coding/agents2claude.py +41 -0
  6. just_cli-0.4.0/scripts/ai_coding/claude2agents.py +41 -0
  7. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/workspace.py +5 -2
  8. just_cli-0.4.0/src/just/installers/edit/installer.py +72 -0
  9. just_cli-0.4.0/src/just/installers/glab/installer.py +65 -0
  10. just_cli-0.4.0/src/just/installers/kimi/installer.py +24 -0
  11. just_cli-0.4.0/src/just/installers/mssh/installer.py +9 -0
  12. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/tui/workspace.py +101 -8
  13. just_cli-0.4.0/tests/test_extension/test_extension_add.py +0 -0
  14. {just_cli-0.3.0 → just_cli-0.4.0}/uv.lock +30 -1
  15. just_cli-0.3.0/src/just/installers/edit/installer.py +0 -31
  16. just_cli-0.3.0/src/just/installers/mssh/installer.py +0 -14
  17. {just_cli-0.3.0 → just_cli-0.4.0}/.claude/skills/textual/README.md +0 -0
  18. {just_cli-0.3.0 → just_cli-0.4.0}/.claude/skills/textual/SKILL.md +0 -0
  19. {just_cli-0.3.0 → just_cli-0.4.0}/.claude/skills/textual/guide.md +0 -0
  20. {just_cli-0.3.0 → just_cli-0.4.0}/.claude/skills/textual/quick-reference.md +0 -0
  21. {just_cli-0.3.0 → just_cli-0.4.0}/.github/workflows/publish.yml +0 -0
  22. {just_cli-0.3.0 → just_cli-0.4.0}/AGENTS.md +0 -0
  23. {just_cli-0.3.0 → just_cli-0.4.0}/Dockerfile +0 -0
  24. {just_cli-0.3.0 → just_cli-0.4.0}/README.md +0 -0
  25. {just_cli-0.3.0 → just_cli-0.4.0}/docs/extension_guide.md +0 -0
  26. {just_cli-0.3.0 → just_cli-0.4.0}/docs/images/editor_demo.png +0 -0
  27. {just_cli-0.3.0 → just_cli-0.4.0}/docs/images/viewer_demo.png +0 -0
  28. {just_cli-0.3.0 → just_cli-0.4.0}/scripts/system/linux/proxy/proxy.sh +0 -0
  29. {just_cli-0.3.0 → just_cli-0.4.0}/scripts/system/mac/proxy/proxy.sh +0 -0
  30. {just_cli-0.3.0 → just_cli-0.4.0}/scripts/system/windows/proxy/proxy.bat +0 -0
  31. {just_cli-0.3.0 → just_cli-0.4.0}/scripts/system/windows/proxy/proxy.ps1 +0 -0
  32. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/__init__.py +0 -0
  33. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/cli.py +0 -0
  34. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/__init__.py +0 -0
  35. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/download.py +0 -0
  36. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/edit.py +0 -0
  37. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/ext/__init__.py +0 -0
  38. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/ext/add.py +0 -0
  39. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/ext/edit.py +0 -0
  40. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/ext/list.py +0 -0
  41. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/ext/remove.py +0 -0
  42. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/extract.py +0 -0
  43. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/install.py +0 -0
  44. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/linux.py +0 -0
  45. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/note.py +0 -0
  46. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/tunnel.py +0 -0
  47. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/commands/view.py +0 -0
  48. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/__init__.py +0 -0
  49. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/config/__init__.py +0 -0
  50. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/config/config.py +0 -0
  51. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/config/utils.py +0 -0
  52. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/extension/__init__.py +0 -0
  53. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/extension/generator.py +0 -0
  54. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/extension/parser.py +0 -0
  55. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/extension/utils.py +0 -0
  56. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/extension/validator.py +0 -0
  57. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/AGENTS.md +0 -0
  58. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/__init__.py +0 -0
  59. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/decorator.py +0 -0
  60. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/install_package.py +0 -0
  61. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/installers/__init__.py +0 -0
  62. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/installers/binary_release.py +0 -0
  63. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/installers/remote_script.py +0 -0
  64. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/package_info.py +0 -0
  65. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/source/__init__.py +0 -0
  66. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/source/base.py +0 -0
  67. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/source/http.py +0 -0
  68. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/source/local.py +0 -0
  69. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/installer/utils.py +0 -0
  70. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/system_probe/__init__.py +0 -0
  71. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/system_probe/system_info.py +0 -0
  72. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/core/system_probe/system_probe.py +0 -0
  73. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/AGENTS.md +0 -0
  74. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/__init__.py +0 -0
  75. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/brew/installer.py +0 -0
  76. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/claude-code/__init__.py +0 -0
  77. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/claude-code/installer.py +0 -0
  78. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/cloudflare/__init__.py +0 -0
  79. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/cloudflare/installer.py +0 -0
  80. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/docker/__init__.py +0 -0
  81. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/docker/installer.py +0 -0
  82. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/edit/__init__.py +0 -0
  83. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/gh/__init__.py +0 -0
  84. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/gh/installer.py +0 -0
  85. {just_cli-0.3.0/src/just/installers/mssh → just_cli-0.4.0/src/just/installers/glab}/__init__.py +0 -0
  86. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/miniconda3/installer.py +0 -0
  87. {just_cli-0.3.0/src/just/installers/nvm → just_cli-0.4.0/src/just/installers/mssh}/__init__.py +0 -0
  88. {just_cli-0.3.0/src/just/installers/qodercli → just_cli-0.4.0/src/just/installers/nvm}/__init__.py +0 -0
  89. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/nvm/installer.py +0 -0
  90. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/openclaw/installer.py +0 -0
  91. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/opencode/installer.py +0 -0
  92. {just_cli-0.3.0/src/just/installers/tailscale → just_cli-0.4.0/src/just/installers/qodercli}/__init__.py +0 -0
  93. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/qodercli/installer.py +0 -0
  94. /just_cli-0.3.0/tests/test_extension/test_extension_add.py → /just_cli-0.4.0/src/just/installers/tailscale/__init__.py +0 -0
  95. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/tailscale/installer.py +0 -0
  96. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/uv/__init__.py +0 -0
  97. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/installers/uv/installer.py +0 -0
  98. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/tui/__init__.py +0 -0
  99. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/tui/editor.py +0 -0
  100. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/tui/extension.py +0 -0
  101. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/tui/markdown.py +0 -0
  102. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/tui/note.py +0 -0
  103. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/AGENTS.md +0 -0
  104. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/__init__.py +0 -0
  105. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/archive/__init__.py +0 -0
  106. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/archive/compression_handler.py +0 -0
  107. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/archive/extractor.py +0 -0
  108. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/archive/format_detect.py +0 -0
  109. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/archive/sevenzip_handler.py +0 -0
  110. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/archive/tar_handler.py +0 -0
  111. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/archive/zip_handler.py +0 -0
  112. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/docker_utils.py +0 -0
  113. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/download_utils.py +0 -0
  114. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/echo_utils.py +0 -0
  115. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/env_utils.py +0 -0
  116. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/file_utils.py +0 -0
  117. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/format_utils.py +0 -0
  118. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/note_utils.py +0 -0
  119. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/progress.py +0 -0
  120. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/shell_utils.py +0 -0
  121. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/system_probe.py +0 -0
  122. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/typer_utils.py +0 -0
  123. {just_cli-0.3.0 → just_cli-0.4.0}/src/just/utils/user_interaction.py +0 -0
  124. {just_cli-0.3.0 → just_cli-0.4.0}/tests/AGENTS.md +0 -0
  125. {just_cli-0.3.0 → just_cli-0.4.0}/tests/requirements.txt +0 -0
  126. {just_cli-0.3.0 → just_cli-0.4.0}/tests/run_tests.py +0 -0
  127. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_archive_extraction.py +0 -0
  128. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_bash_script_installer.py +0 -0
  129. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_download.py +0 -0
  130. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_extension/README.md +0 -0
  131. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_extension/test_ext.py +0 -0
  132. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_extension.py +0 -0
  133. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_file_utils.py +0 -0
  134. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_init.py +0 -0
  135. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_installer_e2e.py +0 -0
  136. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_linux_commands.py +0 -0
  137. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_progress_utils.py +0 -0
  138. {just_cli-0.3.0 → just_cli-0.4.0}/tests/test_system_probe.py +0 -0
  139. {just_cli-0.3.0 → just_cli-0.4.0}/tests/testing.py +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "mcp__web-search-prime__web_search_prime"
4
+ "Bash(just install:*)"
5
5
  ]
6
6
  }
7
7
  }
@@ -21,4 +21,7 @@ workspace/
21
21
  extensions/
22
22
  CLAUDE.md
23
23
 
24
- .DS_Store
24
+ .DS_Store
25
+
26
+ .claude/worktrees
27
+ .claude/settings.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: just-cli
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Your all-in-one CLI Toolkit
5
5
  Author-email: zzzcb <zjxs.zzzcb@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "just-cli"
3
- version = "0.3.0"
3
+ version = "0.4.0"
4
4
  description = "Your all-in-one CLI Toolkit"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -0,0 +1,41 @@
1
+ """Rename all AGENTS.md files to CLAUDE.md under a given workspace."""
2
+
3
+ import argparse
4
+ import os
5
+ from pathlib import Path
6
+
7
+
8
+ def agents_to_claude(workspace: str) -> list[Path]:
9
+ renamed = []
10
+ for dirpath, _, filenames in os.walk(workspace):
11
+ if "AGENTS.md" in filenames:
12
+ old_path = Path(dirpath) / "AGENTS.md"
13
+ new_path = Path(dirpath) / "CLAUDE.md"
14
+ old_path.rename(new_path)
15
+ renamed.append(new_path)
16
+ return renamed
17
+
18
+
19
+ def main() -> None:
20
+ parser = argparse.ArgumentParser(
21
+ description="Rename all AGENTS.md files to CLAUDE.md under a workspace."
22
+ )
23
+ parser.add_argument(
24
+ "workspace",
25
+ nargs="?",
26
+ default="./",
27
+ help="Root directory to search (default: current directory)",
28
+ )
29
+ args = parser.parse_args()
30
+
31
+ renamed = agents_to_claude(args.workspace)
32
+ if renamed:
33
+ for path in renamed:
34
+ print(f"Renamed: {path}")
35
+ print(f"\nTotal: {len(renamed)} file(s) renamed.")
36
+ else:
37
+ print("No AGENTS.md files found.")
38
+
39
+
40
+ if __name__ == "__main__":
41
+ main()
@@ -0,0 +1,41 @@
1
+ """Rename all CLAUDE.md files to AGENTS.md under a given workspace."""
2
+
3
+ import argparse
4
+ import os
5
+ from pathlib import Path
6
+
7
+
8
+ def claude_to_agents(workspace: str) -> list[Path]:
9
+ renamed = []
10
+ for dirpath, _, filenames in os.walk(workspace):
11
+ if "CLAUDE.md" in filenames:
12
+ old_path = Path(dirpath) / "CLAUDE.md"
13
+ new_path = Path(dirpath) / "AGENTS.md"
14
+ old_path.rename(new_path)
15
+ renamed.append(new_path)
16
+ return renamed
17
+
18
+
19
+ def main() -> None:
20
+ parser = argparse.ArgumentParser(
21
+ description="Rename all CLAUDE.md files to AGENTS.md under a workspace."
22
+ )
23
+ parser.add_argument(
24
+ "workspace",
25
+ nargs="?",
26
+ default="./",
27
+ help="Root directory to search (default: current directory)",
28
+ )
29
+ args = parser.parse_args()
30
+
31
+ renamed = claude_to_agents(args.workspace)
32
+ if renamed:
33
+ for path in renamed:
34
+ print(f"Renamed: {path}")
35
+ print(f"\nTotal: {len(renamed)} file(s) renamed.")
36
+ else:
37
+ print("No CLAUDE.md files found.")
38
+
39
+
40
+ if __name__ == "__main__":
41
+ main()
@@ -1,4 +1,7 @@
1
1
  from pathlib import Path
2
+ from typing_extensions import Annotated
3
+
4
+ import typer
2
5
 
3
6
  from just import just_cli, capture_exception
4
7
  from just.tui.workspace import WorkspaceApp
@@ -7,12 +10,12 @@ from just.tui.workspace import WorkspaceApp
7
10
  @just_cli.command(name="code")
8
11
  @capture_exception
9
12
  def workspace_command(
10
- path: str = ".",
13
+ workdir: Annotated[str, typer.Argument(help="Working directory")] = ".",
11
14
  ):
12
15
  """
13
16
  Open a VSCode-style workspace editor.
14
17
  """
15
- workspace_path = Path(path).resolve()
18
+ workspace_path = Path(workdir).resolve()
16
19
  if not workspace_path.exists():
17
20
  print(f"Error: Path does not exist: {workspace_path}")
18
21
  return
@@ -0,0 +1,72 @@
1
+ import just
2
+
3
+ from just.core.installer.utils import create_symlink_or_wrapper
4
+ from just.core.config.utils import get_bin_dir
5
+
6
+
7
+ @just.installer(check="edit -v")
8
+ def install_edit(
9
+ version: just.Annotated[
10
+ str, just.Option("-v", "--version", help="The version of Edit to install.")
11
+ ] = None,
12
+ ):
13
+ """
14
+ Install a terminal text editor.
15
+
16
+ - Windows/Linux: Microsoft Edit
17
+ - macOS: micro (with 'edit' symlink)
18
+ """
19
+ # Resolve default version based on platform
20
+ if version is None:
21
+ if just.system.platform == "darwin":
22
+ version = "2.0.15" # micro latest stable
23
+ else:
24
+ version = "1.2.0" # Microsoft Edit
25
+
26
+ if just.system.platform == "windows":
27
+ just.echo.info(f"Installing Microsoft Edit v{version} as the default editor...")
28
+ url = f"https://github.com/microsoft/edit/releases/download/v{version}/edit-{version}-{just.system.arch}-windows.zip"
29
+ just.ArchiveInstaller(
30
+ url=url,
31
+ executables=["edit.exe"],
32
+ name="edit",
33
+ ).run()
34
+ elif just.system.platform == "linux":
35
+ just.echo.info(f"Installing Microsoft Edit v{version} as the default editor...")
36
+ url = f"https://github.com/microsoft/edit/releases/download/v{version}/edit-{version}-{just.system.arch}-linux-gnu.tar.zst"
37
+ just.ArchiveInstaller(
38
+ url,
39
+ executables=["edit"],
40
+ name="edit",
41
+ ).run()
42
+ elif just.system.platform == "darwin":
43
+ # Install micro text editor for macOS
44
+ editor_name = "micro"
45
+ # Map architecture to micro's platform naming
46
+ micro_platform = "macos-arm64" if just.system.arch == "aarch64" else "osx"
47
+ url = f"https://github.com/micro-editor/micro/releases/download/v{version}/micro-{version}-{micro_platform}.tar.gz"
48
+
49
+ just.echo.info(f"Installing {editor_name} v{version} as the default editor...")
50
+
51
+ just.ArchiveInstaller(
52
+ url=url,
53
+ executables=["micro"],
54
+ name="micro",
55
+ ).run()
56
+
57
+ # Create edit -> micro symlink for consistency
58
+ micro_path = get_bin_dir() / "micro"
59
+
60
+ # Verify micro was installed successfully before creating symlink
61
+ if not micro_path.exists():
62
+ raise RuntimeError(f"micro installation failed - executable not found at {micro_path}")
63
+
64
+ create_symlink_or_wrapper(
65
+ target=micro_path,
66
+ link_dir=get_bin_dir(),
67
+ link_name="edit"
68
+ )
69
+ else:
70
+ raise NotImplementedError(
71
+ f"No editor is available for {just.system.platform}."
72
+ )
@@ -0,0 +1,65 @@
1
+ import just
2
+
3
+
4
+ @just.installer(check="glab version")
5
+ def install_glab():
6
+ """Install GitLab CLI (glab)."""
7
+
8
+ if just.system.platform == "darwin":
9
+ if just.system.pms.brew.is_available():
10
+ just.execute_commands("brew install glab")
11
+ else:
12
+ raise NotImplementedError(
13
+ "Homebrew is required to install glab on macOS.\n"
14
+ "Visit: https://gitlab.com/gitlab-org/cli/-/releases"
15
+ )
16
+
17
+ elif just.system.platform == "windows":
18
+ if just.system.pms.winget.is_available():
19
+ just.execute_commands("winget install glab.glab")
20
+ else:
21
+ raise NotImplementedError(
22
+ "winget is required to install glab on Windows.\n"
23
+ "Visit: https://gitlab.com/gitlab-org/cli/-/releases"
24
+ )
25
+
26
+ elif just.system.platform == "linux":
27
+ _install_linux()
28
+
29
+ else:
30
+ raise NotImplementedError(
31
+ f"glab is not supported on {just.system.platform}.\n"
32
+ "Visit: https://gitlab.com/gitlab-org/cli/-/releases"
33
+ )
34
+
35
+ just.echo.success("GitLab CLI (glab) installed successfully.")
36
+
37
+
38
+ def _install_linux():
39
+ arch_map = {"x86_64": "amd64", "aarch64": "arm64"}
40
+ arch = arch_map.get(just.system.arch)
41
+ if not arch:
42
+ raise NotImplementedError(f"Unsupported arch: {just.system.arch}")
43
+
44
+ if just.system.pms.brew.is_available():
45
+ just.execute_commands("brew install glab")
46
+ return
47
+
48
+ if just.system.pms.apt.is_available():
49
+ try:
50
+ just.execute_commands(
51
+ "sudo apt update && sudo apt install -y glab"
52
+ )
53
+ return
54
+ except Exception:
55
+ pass
56
+
57
+ url = (
58
+ "https://gitlab.com/gitlab-org/cli/-/releases/permalink/latest/"
59
+ f"downloads/glab_linux_{arch}.tar.gz"
60
+ )
61
+ just.ArchiveInstaller(
62
+ url=url,
63
+ executables=["glab"],
64
+ name="glab",
65
+ ).run()
@@ -0,0 +1,24 @@
1
+ import just
2
+ from just.core.installer import BashScriptInstaller, PowerShellInstaller
3
+
4
+
5
+ @just.installer(check="kimi --version")
6
+ def install_kimi():
7
+ """Install Kimi Code CLI by Moonshot AI."""
8
+
9
+ if just.system.platform in ("linux", "darwin"):
10
+ BashScriptInstaller(
11
+ script_url="https://code.kimi.com/install.sh",
12
+ name="kimi",
13
+ ).run()
14
+
15
+ elif just.system.platform == "windows":
16
+ PowerShellInstaller(
17
+ script_url="https://code.kimi.com/install.ps1",
18
+ name="kimi",
19
+ ).run()
20
+
21
+ else:
22
+ raise NotImplementedError(
23
+ f"Kimi Code CLI is not supported on {just.system.platform}."
24
+ )
@@ -0,0 +1,9 @@
1
+ import just
2
+
3
+
4
+ @just.installer
5
+ def install_mssh():
6
+ """
7
+ Install TUI SSH Manager
8
+ """
9
+ just.execute_commands("pip install git+https://github.com/zjxszzzcb/ssh-manager.git")
@@ -1,12 +1,106 @@
1
1
  """Minimal workspace editor with file tree and editor"""
2
2
 
3
3
  from pathlib import Path
4
+ from typing import Iterable
4
5
 
5
6
  from textual.app import App, ComposeResult
6
7
  from textual.binding import Binding
7
8
  from textual.containers import Horizontal, Vertical
8
9
  from textual.screen import ModalScreen
9
- from textual.widgets import DirectoryTree, TextArea, Input, Button, Label
10
+ from textual.widgets import DirectoryTree, TextArea, Input, Button, Label, Footer
11
+ from rich.text import Text
12
+
13
+
14
+ class IconDirectoryTree(DirectoryTree):
15
+ """DirectoryTree with custom file type icons."""
16
+
17
+ # File icon mapping by extension
18
+ FILE_ICONS: dict[str, str] = {
19
+ # Python
20
+ ".py": "🐍",
21
+ # JavaScript/TypeScript
22
+ ".js": "📜",
23
+ ".ts": "📜",
24
+ ".jsx": "📜",
25
+ ".tsx": "📜",
26
+ # Config files
27
+ ".json": "⚙️",
28
+ ".yaml": "⚙️",
29
+ ".yml": "⚙️",
30
+ ".toml": "⚙️",
31
+ ".xml": "⚙️",
32
+ ".ini": "⚙️",
33
+ ".conf": "⚙️",
34
+ ".cfg": "⚙️",
35
+ # Markdown
36
+ ".md": "📝",
37
+ ".markdown": "📝",
38
+ # Text
39
+ ".txt": "📄",
40
+ # Styles
41
+ ".css": "🎨",
42
+ ".scss": "🎨",
43
+ ".sass": "🎨",
44
+ ".less": "🎨",
45
+ # HTML
46
+ ".html": "🌐",
47
+ ".htm": "🌐",
48
+ # Shell
49
+ ".sh": "🖥️",
50
+ ".bash": "🖥️",
51
+ ".zsh": "🖥️",
52
+ ".fish": "🖥️",
53
+ # Rust
54
+ ".rs": "🦀",
55
+ # Go
56
+ ".go": "🐹",
57
+ # Java
58
+ ".java": "☕",
59
+ # C/C++
60
+ ".c": "🏗️",
61
+ ".cpp": "🏗️",
62
+ ".h": "🏗️",
63
+ ".hpp": "🏗️",
64
+ ".cc": "🏗️",
65
+ ".cxx": "🏗️",
66
+ # Other common files
67
+ ".lock": "🔒",
68
+ ".gitignore": "🙈",
69
+ ".env": "🔐",
70
+ "dockerfile": "🐳",
71
+ "dockerignore": "🐳",
72
+ "makefile": "🛠️",
73
+ # Fallback
74
+ "": "📄",
75
+ }
76
+
77
+ def render_label(
78
+ self, node, base_style: str, style: str
79
+ ) -> Text:
80
+ """Render label with custom icon based on file extension."""
81
+ if node.data is None:
82
+ return Text("")
83
+
84
+ # node.data is DirEntry, which has a path attribute
85
+ entry = node.data
86
+ path = entry.path
87
+
88
+ if path.is_dir():
89
+ # Folder icon
90
+ return Text(f"📁 {path.name}")
91
+
92
+ # Get file extension
93
+ ext = path.suffix.lower()
94
+ name_lower = path.name.lower()
95
+
96
+ # Check for special files by name
97
+ if name_lower in self.FILE_ICONS:
98
+ icon = self.FILE_ICONS[name_lower]
99
+ else:
100
+ # Check by extension
101
+ icon = self.FILE_ICONS.get(ext, self.FILE_ICONS[""])
102
+
103
+ return Text(f"{icon} {path.name}")
10
104
 
11
105
 
12
106
  class InputDialog(ModalScreen[str]):
@@ -160,8 +254,8 @@ class WorkspaceApp(App):
160
254
  BINDINGS = [
161
255
  Binding("ctrl+s", "save", "Save"),
162
256
  Binding("ctrl+n", "new", "New"),
163
- Binding("d", "delete", "Delete"),
164
- Binding("q", "quit", "Quit"),
257
+ Binding("ctrl+d", "delete", "Delete"),
258
+ Binding("escape", "quit", "ESC"),
165
259
  ]
166
260
 
167
261
  def __init__(self, path: str = "."):
@@ -172,8 +266,9 @@ class WorkspaceApp(App):
172
266
  def compose(self) -> ComposeResult:
173
267
  with Horizontal():
174
268
  with Vertical(id="sidebar"):
175
- yield DirectoryTree(self.workspace_path)
269
+ yield IconDirectoryTree(self.workspace_path)
176
270
  yield CodeEditor(Path("/dev/null"), id="editor")
271
+ yield Footer()
177
272
 
178
273
  def on_mount(self) -> None:
179
274
  self.title = str(self.workspace_path)
@@ -201,13 +296,11 @@ class WorkspaceApp(App):
201
296
  def _create_note(self, name: str) -> None:
202
297
  if not name:
203
298
  return
204
- if not name.endswith(".md"):
205
- name += ".md"
206
299
  file_path = self.workspace_path / name
207
300
  if file_path.exists():
208
301
  self.notify("File already exists", severity="error")
209
302
  return
210
- file_path.write_text(f"# {name[:-3]}\n\n", encoding="utf-8")
303
+ file_path.write_text("", encoding="utf-8")
211
304
  self._refresh_tree()
212
305
  self._open_file(file_path)
213
306
 
@@ -233,7 +326,7 @@ class WorkspaceApp(App):
233
326
  self.notify("Deleted")
234
327
 
235
328
  def _refresh_tree(self) -> None:
236
- tree = self.query_one(DirectoryTree)
329
+ tree = self.query_one(IconDirectoryTree)
237
330
  tree.reload()
238
331
 
239
332
  def _open_file(self, file_path: Path) -> None:
@@ -316,7 +316,7 @@ wheels = [
316
316
 
317
317
  [[package]]
318
318
  name = "just-cli"
319
- version = "0.2.1"
319
+ version = "0.4.0"
320
320
  source = { editable = "." }
321
321
  dependencies = [
322
322
  { name = "httpx" },
@@ -329,9 +329,15 @@ dependencies = [
329
329
  { name = "zstandard" },
330
330
  ]
331
331
 
332
+ [package.optional-dependencies]
333
+ dev = [
334
+ { name = "loguru" },
335
+ ]
336
+
332
337
  [package.metadata]
333
338
  requires-dist = [
334
339
  { name = "httpx" },
340
+ { name = "loguru", marker = "extra == 'dev'" },
335
341
  { name = "prompt-toolkit", specifier = ">=3.0.0" },
336
342
  { name = "py7zr" },
337
343
  { name = "python-dotenv" },
@@ -340,6 +346,7 @@ requires-dist = [
340
346
  { name = "typer", specifier = ">=0.20.0" },
341
347
  { name = "zstandard" },
342
348
  ]
349
+ provides-extras = ["dev"]
343
350
 
344
351
  [[package]]
345
352
  name = "linkify-it-py"
@@ -353,6 +360,19 @@ wheels = [
353
360
  { url = "https://files.pythonhosted.org/packages/04/1e/b832de447dee8b582cac175871d2f6c3d5077cc56d5575cadba1fd1cccfa/linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79", size = 19820, upload-time = "2024-02-04T14:48:02.496Z" },
354
361
  ]
355
362
 
363
+ [[package]]
364
+ name = "loguru"
365
+ version = "0.7.3"
366
+ source = { registry = "https://pypi.org/simple" }
367
+ dependencies = [
368
+ { name = "colorama", marker = "sys_platform == 'win32'" },
369
+ { name = "win32-setctime", marker = "sys_platform == 'win32'" },
370
+ ]
371
+ sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" }
372
+ wheels = [
373
+ { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" },
374
+ ]
375
+
356
376
  [[package]]
357
377
  name = "markdown-it-py"
358
378
  version = "4.0.0"
@@ -802,6 +822,15 @@ wheels = [
802
822
  { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" },
803
823
  ]
804
824
 
825
+ [[package]]
826
+ name = "win32-setctime"
827
+ version = "1.2.0"
828
+ source = { registry = "https://pypi.org/simple" }
829
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" }
830
+ wheels = [
831
+ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" },
832
+ ]
833
+
805
834
  [[package]]
806
835
  name = "zstandard"
807
836
  version = "0.25.0"
@@ -1,31 +0,0 @@
1
- import just
2
-
3
-
4
- @just.installer(check="edit -v")
5
- def install_edit(
6
- version: just.Annotated[
7
- str, just.Option("-v", "--version", help="The version of Edit to install.")
8
- ] = "1.2.0",
9
- ):
10
- """
11
- Install Microsoft Edit.
12
- """
13
-
14
- if just.system.platform == "windows":
15
- url = f"https://github.com/microsoft/edit/releases/download/v{version}/edit-{version}-{just.system.arch}-windows.zip"
16
- just.ArchiveInstaller(
17
- url=url,
18
- executables=["edit.exe"],
19
- name="edit",
20
- ).run()
21
- elif just.system.platform == "linux":
22
- url = f"https://github.com/microsoft/edit/releases/download/v{version}/edit-{version}-{just.system.arch}-linux-gnu.tar.zst"
23
- just.ArchiveInstaller(
24
- url,
25
- executables=["edit"],
26
- name="edit",
27
- ).run()
28
- else:
29
- raise NotImplementedError(
30
- f"Microsoft Edit is not supported on {just.system.platform}."
31
- )
@@ -1,14 +0,0 @@
1
- import just
2
-
3
-
4
- @just.installer
5
- def install_mssh(
6
- version: just.Annotated[str, just.Option(
7
- '-v', '--version',
8
- help="The version of nvm to install."
9
- )] = ""
10
- ):
11
- """
12
- Install TUI SSH Manager
13
- """
14
- just.execute_commands("pip install https://github.com/zjxszzzcb/ssh-manager/releases/download/v0.1.0/ssh_manager-0.1.0-py3-none-any.whl")
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