cd-browser 0.1.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Saky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: cd-browser
3
+ Version: 0.1.1
4
+ Summary: A fast keyboard-driven directory navigator for the terminal.
5
+ Author: Saky
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Utilities
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=8.0; extra == "dev"
22
+ Requires-Dist: black>=24.0; extra == "dev"
23
+ Requires-Dist: ruff>=0.3.0; extra == "dev"
24
+ Requires-Dist: mypy>=1.8.0; extra == "dev"
25
+ Requires-Dist: pre-commit>=3.7.0; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # cd-browser
29
+
30
+ **Stop typing paths. Browse them.**
31
+
32
+ `cd-browser` is a fast keyboard-driven directory navigator for the terminal.
33
+ It lets you explore directory trees visually and jump to any folder instantly.
34
+
35
+ ![cd-browser demo](docs/demo.gif)
36
+
37
+ ## Why cd-browser?
38
+
39
+ Working in the terminal often means:
40
+
41
+ - typing long directory paths
42
+ - navigating deep folder trees
43
+ - repeating `cd ..` multiple times
44
+
45
+ `cd-browser` provides an **interactive terminal UI** that allows you to browse directories with the keyboard and return the selected path directly to your shell.
46
+
47
+ ## Features
48
+
49
+ - 🚀 Fast visual navigation of directory trees
50
+ - ⌨️ Fully keyboard-driven workflow
51
+ - 🌲 Expand and collapse directories
52
+ - 📜 Navigation history inside the session
53
+ - 🖥 Native terminal interface
54
+ - 🔁 Works with Bash, Zsh and other shells
55
+ - ⚡ Returns the selected path to the shell
56
+ - 🔎 Press `.` to toggle hidden directories
57
+
58
+ ## Quick Demo
59
+
60
+ Run:
61
+
62
+ ```bash
63
+ cd_
64
+ ```
65
+
66
+ Browse directories using the arrow keys and press **Enter** to jump directly to the selected folder.
67
+
68
+ ## Documentation
69
+
70
+ See the documentation index:
71
+
72
+ ```
73
+ docs/index.md
74
+ ```
75
+
76
+ ## Installation
77
+
78
+ Install the project in user mode:
79
+
80
+ ```bash
81
+ pip install .
82
+ ```
83
+
84
+ For editable local development:
85
+
86
+ ```bash
87
+ pip install -e '.[dev]'
88
+ ```
89
+
90
+ After installation, the application command is:
91
+
92
+ ```bash
93
+ cd_browser
94
+ ```
95
+
96
+ When the interactive session exits, the program prints the final selected directory path.
97
+
98
+ ## Shell Integration For `cd_`
99
+
100
+ Because a Python subprocess cannot directly change the parent shell directory, use a shell wrapper that captures the final path printed by `cd_browser`.
101
+
102
+ Bash or Zsh example:
103
+
104
+ ```bash
105
+ cd_() {
106
+ local target
107
+ target="$(cd_browser)" || return
108
+
109
+ if [ -n "$target" ] && [ -d "$target" ]; then
110
+ cd "$target"
111
+ fi
112
+ }
113
+ ```
114
+
115
+ After adding the function to your shell profile, reload it:
116
+
117
+ ```bash
118
+ source ~/.bashrc
119
+ ```
120
+
121
+ Or:
122
+
123
+ ```bash
124
+ source ~/.zshrc
125
+ ```
126
+
127
+ Then use:
128
+
129
+ ```bash
130
+ cd_
131
+ ```
132
+
133
+ - Dentro de `cd_browser`, presiona `.` para alternar la visualización de carpetas ocultas.
134
+
135
+ ## Uninstall
136
+
137
+ If the project was installed with `pip`, remove it with:
138
+
139
+ ```bash
140
+ pip uninstall cd-browser
141
+ ```
142
+
143
+ If you also added the `cd_` shell wrapper, remove that function from your shell profile and reload the shell configuration.
144
+
145
+ ## Developer Setup
146
+
147
+ Recommended setup:
148
+
149
+ ```bash
150
+ make dev
151
+ ```
152
+
153
+ Run the application in development mode:
154
+
155
+ ```bash
156
+ python -m app.main
157
+ ```
158
+
159
+ Useful development commands:
160
+
161
+ ```bash
162
+ make fix
163
+ make quality
164
+ make run
165
+ ```
166
+
167
+ ## Template Origin
168
+
169
+ This project was created from the Python AI Dev Template.
170
+
171
+ The original template documentation has been preserved in `README_TEMPLATE.md` so the project-specific README can focus on `cd-browser` usage and development.
172
+
173
+ For the original template setup, conventions, and generic workflow notes, see `README_TEMPLATE.md`.
@@ -0,0 +1,146 @@
1
+ # cd-browser
2
+
3
+ **Stop typing paths. Browse them.**
4
+
5
+ `cd-browser` is a fast keyboard-driven directory navigator for the terminal.
6
+ It lets you explore directory trees visually and jump to any folder instantly.
7
+
8
+ ![cd-browser demo](docs/demo.gif)
9
+
10
+ ## Why cd-browser?
11
+
12
+ Working in the terminal often means:
13
+
14
+ - typing long directory paths
15
+ - navigating deep folder trees
16
+ - repeating `cd ..` multiple times
17
+
18
+ `cd-browser` provides an **interactive terminal UI** that allows you to browse directories with the keyboard and return the selected path directly to your shell.
19
+
20
+ ## Features
21
+
22
+ - 🚀 Fast visual navigation of directory trees
23
+ - ⌨️ Fully keyboard-driven workflow
24
+ - 🌲 Expand and collapse directories
25
+ - 📜 Navigation history inside the session
26
+ - 🖥 Native terminal interface
27
+ - 🔁 Works with Bash, Zsh and other shells
28
+ - ⚡ Returns the selected path to the shell
29
+ - 🔎 Press `.` to toggle hidden directories
30
+
31
+ ## Quick Demo
32
+
33
+ Run:
34
+
35
+ ```bash
36
+ cd_
37
+ ```
38
+
39
+ Browse directories using the arrow keys and press **Enter** to jump directly to the selected folder.
40
+
41
+ ## Documentation
42
+
43
+ See the documentation index:
44
+
45
+ ```
46
+ docs/index.md
47
+ ```
48
+
49
+ ## Installation
50
+
51
+ Install the project in user mode:
52
+
53
+ ```bash
54
+ pip install .
55
+ ```
56
+
57
+ For editable local development:
58
+
59
+ ```bash
60
+ pip install -e '.[dev]'
61
+ ```
62
+
63
+ After installation, the application command is:
64
+
65
+ ```bash
66
+ cd_browser
67
+ ```
68
+
69
+ When the interactive session exits, the program prints the final selected directory path.
70
+
71
+ ## Shell Integration For `cd_`
72
+
73
+ Because a Python subprocess cannot directly change the parent shell directory, use a shell wrapper that captures the final path printed by `cd_browser`.
74
+
75
+ Bash or Zsh example:
76
+
77
+ ```bash
78
+ cd_() {
79
+ local target
80
+ target="$(cd_browser)" || return
81
+
82
+ if [ -n "$target" ] && [ -d "$target" ]; then
83
+ cd "$target"
84
+ fi
85
+ }
86
+ ```
87
+
88
+ After adding the function to your shell profile, reload it:
89
+
90
+ ```bash
91
+ source ~/.bashrc
92
+ ```
93
+
94
+ Or:
95
+
96
+ ```bash
97
+ source ~/.zshrc
98
+ ```
99
+
100
+ Then use:
101
+
102
+ ```bash
103
+ cd_
104
+ ```
105
+
106
+ - Dentro de `cd_browser`, presiona `.` para alternar la visualización de carpetas ocultas.
107
+
108
+ ## Uninstall
109
+
110
+ If the project was installed with `pip`, remove it with:
111
+
112
+ ```bash
113
+ pip uninstall cd-browser
114
+ ```
115
+
116
+ If you also added the `cd_` shell wrapper, remove that function from your shell profile and reload the shell configuration.
117
+
118
+ ## Developer Setup
119
+
120
+ Recommended setup:
121
+
122
+ ```bash
123
+ make dev
124
+ ```
125
+
126
+ Run the application in development mode:
127
+
128
+ ```bash
129
+ python -m app.main
130
+ ```
131
+
132
+ Useful development commands:
133
+
134
+ ```bash
135
+ make fix
136
+ make quality
137
+ make run
138
+ ```
139
+
140
+ ## Template Origin
141
+
142
+ This project was created from the Python AI Dev Template.
143
+
144
+ The original template documentation has been preserved in `README_TEMPLATE.md` so the project-specific README can focus on `cd-browser` usage and development.
145
+
146
+ For the original template setup, conventions, and generic workflow notes, see `README_TEMPLATE.md`.
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cd-browser"
7
+ version = "0.1.1"
8
+ description = "A fast keyboard-driven directory navigator for the terminal."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ authors = [
12
+ { name = "Saky" }
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Utilities",
26
+ ]
27
+ dependencies = []
28
+
29
+ [project.scripts]
30
+ cd_browser = "app.main:main"
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=8.0",
35
+ "black>=24.0",
36
+ "ruff>=0.3.0",
37
+ "mypy>=1.8.0",
38
+ "pre-commit>=3.7.0"
39
+ ]
40
+
41
+ [tool.setuptools]
42
+ package-dir = {"" = "src"}
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
46
+
47
+ [tool.black]
48
+ line-length = 88
49
+ target-version = ["py311"]
50
+
51
+ [tool.ruff]
52
+ line-length = 88
53
+ target-version = "py311"
54
+
55
+ [tool.ruff.lint]
56
+ select = ["E", "F", "I", "B", "UP"]
57
+ ignore = []
58
+
59
+ [tool.mypy]
60
+ python_version = "3.11"
61
+ strict = true
62
+ warn_unused_configs = true
63
+ disallow_untyped_defs = true
64
+ ignore_missing_imports = true
65
+
66
+ [tool.pytest.ini_options]
67
+ testpaths = ["tests"]
68
+ pythonpath = ["src"]
69
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from app.navigator import Navigator
6
+ from app.ui import TerminalUI
7
+
8
+
9
+ def run_cli(start_path: Path | None = None, ui: TerminalUI | None = None) -> Path:
10
+ """Run the interactive directory browser and return the selected path."""
11
+
12
+ initial_path = (start_path or Path.cwd()).expanduser().resolve()
13
+ navigator = Navigator(initial_path)
14
+ terminal_ui = ui or TerminalUI()
15
+ return terminal_ui.run(navigator)
@@ -0,0 +1,9 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass(slots=True)
5
+ class AppConfig:
6
+ """Application configuration."""
7
+
8
+ app_name: str = "Python AI Dev Template"
9
+ debug: bool = False
File without changes
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+
7
+ @dataclass(frozen=True, slots=True)
8
+ class DirectoryEntry:
9
+ """Directory metadata used by the navigator layer."""
10
+
11
+ name: str
12
+ path: Path
13
+ has_children: bool
14
+
15
+
16
+ def list_directories(path: Path, show_hidden: bool = False) -> list[DirectoryEntry]:
17
+ """Return direct child directories sorted by name."""
18
+
19
+ directory_path = path.expanduser().resolve()
20
+ entries: list[DirectoryEntry] = []
21
+
22
+ for child in directory_path.iterdir():
23
+ if not child.is_dir():
24
+ continue
25
+ if not show_hidden and child.name.startswith("."):
26
+ continue
27
+
28
+ entries.append(
29
+ DirectoryEntry(
30
+ name=child.name,
31
+ path=child,
32
+ has_children=has_subdirectories(child, show_hidden=show_hidden),
33
+ )
34
+ )
35
+
36
+ return sorted(entries, key=lambda entry: entry.name.casefold())
37
+
38
+
39
+ def has_subdirectories(path: Path, show_hidden: bool = False) -> bool:
40
+ """Return whether the directory contains at least one subdirectory."""
41
+
42
+ directory_path = path.expanduser().resolve()
43
+
44
+ for child in directory_path.iterdir():
45
+ if not child.is_dir():
46
+ continue
47
+
48
+ if not show_hidden and child.name.startswith("."):
49
+ continue
50
+
51
+ return True
52
+
53
+ return False
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from pathlib import Path
5
+
6
+
7
+ @dataclass(slots=True)
8
+ class SessionHistory:
9
+ """In-memory session history for visited directories."""
10
+
11
+ _entries: list[Path] = field(default_factory=list)
12
+ _index: int = -1
13
+
14
+ def visit(self, path: Path) -> Path:
15
+ """Record a directory visit and discard forward history when needed."""
16
+
17
+ resolved_path = path.expanduser().resolve()
18
+
19
+ if self.current == resolved_path:
20
+ return resolved_path
21
+
22
+ if self._index < len(self._entries) - 1:
23
+ self._entries = self._entries[: self._index + 1]
24
+
25
+ self._entries.append(resolved_path)
26
+ self._index = len(self._entries) - 1
27
+ return resolved_path
28
+
29
+ @property
30
+ def current(self) -> Path | None:
31
+ """Return the current history entry."""
32
+
33
+ if self._index == -1:
34
+ return None
35
+
36
+ return self._entries[self._index]
37
+
38
+ def back(self) -> Path | None:
39
+ """Move backward in history when possible."""
40
+
41
+ if self._index <= 0:
42
+ return None
43
+
44
+ self._index -= 1
45
+ return self._entries[self._index]
46
+
47
+ def forward(self) -> Path | None:
48
+ """Move forward in history when possible."""
49
+
50
+ if self._index == -1 or self._index >= len(self._entries) - 1:
51
+ return None
52
+
53
+ self._index += 1
54
+ return self._entries[self._index]
55
+
56
+ def select(self, index: int) -> Path:
57
+ """Jump to a previously visited directory by history index."""
58
+
59
+ if index < 0 or index >= len(self._entries):
60
+ raise IndexError("History index out of range")
61
+
62
+ self._index = index
63
+ return self._entries[self._index]
64
+
65
+ def entries(self) -> tuple[Path, ...]:
66
+ """Return the full history as an immutable sequence."""
67
+
68
+ return tuple(self._entries)
File without changes
@@ -0,0 +1,11 @@
1
+ import logging
2
+ import sys
3
+
4
+
5
+ def setup_logging() -> None:
6
+ """Configure application logging."""
7
+ logging.basicConfig(
8
+ level=logging.INFO,
9
+ format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
10
+ stream=sys.stderr,
11
+ )
@@ -0,0 +1,16 @@
1
+ import sys
2
+
3
+ from app.cli import run_cli
4
+ from app.logging_config import setup_logging
5
+
6
+
7
+ def main() -> None:
8
+ """Run the application entry point."""
9
+ setup_logging()
10
+
11
+ final_path = run_cli()
12
+ sys.stdout.write(f"{final_path}\n")
13
+
14
+
15
+ if __name__ == "__main__":
16
+ main()