9 Commits

Author SHA1 Message Date
4052c6f71f release: 0.2.4 fix PyPI README gif rendering 2026-04-03 21:30:40 +02:00
0e94895349 merge: bring 0.2.x releases into main 2026-04-03 21:14:23 +02:00
d9cf164b2c release: 0.2.3 add demo gif 2026-04-02 22:38:42 +02:00
a64bc768b5 release: 0.2.2 fix post-install packaging and robustness 2026-04-02 21:35:45 +02:00
f7f09b895d feat: update pyproject.toml for PyPI release
- Lower Python requirement to >=3.8 for broader compatibility
- Update version to 0.1.1
- Add classifiers for better PyPI discoverability
- Fix description to match cd-browser functionality
2026-04-02 17:06:16 +02:00
10c3849fb3 cleanup: remove AI internal files for public repo focus
- Removed AGENT.md, AGENT_WORKFLOW.md, AI_TASK_TEMPLATE.md, instructions-agent.md
- These were internal AI development tools, not needed for end-users
- Repository now cleaner and more product-focused
- Files can be recovered from git history if needed for future development
2026-04-02 16:48:58 +02:00
f28fef7758 docs: update changelog with hidden toggle feature 2026-04-02 16:45:15 +02:00
ab689cfbb4 feat: toggle hidden directories in cd-browser with . key 2026-04-02 16:28:21 +02:00
9688ed0974 docs: update changelog and readme for cd-browser v0.1.1 2026-04-02 16:00:06 +02:00
17 changed files with 353 additions and 1017 deletions

260
AGENT.md
View File

@@ -1,260 +0,0 @@
# AGENT.md
AI Agent Development Guide
This file defines the operational rules that AI agents must follow when working in this repository.
The goal is to ensure:
- reproducible AI-assisted development
- traceable decisions
- consistent repository history
- safe automated contributions
This document complements:
- `instructions-agent.md`
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
---
# Core Principles
Agents must prioritize:
1. Deterministic changes
2. Minimal scope modifications
3. Explicit logging of work
4. Repository stability
Agents must never:
- fabricate repository state
- invent files that do not exist
- modify unrelated parts of the project
- skip validation steps
---
# Development Workflow
Agents operate using two primary task types:
## PLAN
Used to:
- investigate problems
- inspect repository state
- propose architecture changes
PLAN tasks must:
- analyze the current implementation
- identify the root cause
- propose minimal changes
PLAN tasks **must not modify code**.
---
## BUILD
Used to:
- implement changes
- update code
- add tests
- update documentation
BUILD tasks may:
- modify source files
- update tests
- update documentation
BUILD tasks must:
- remain minimal in scope
- preserve existing behavior unless explicitly changed
---
# Validation Requirements
All BUILD tasks must run:
```
make fix
make quality
```
These commands must pass before changes are considered valid.
---
# Logging System
All significant AI actions must be recorded.
Two log files are used:
```
docs/ai-worklog.md
docs/ai-prompts.md
```
---
# Worklog Entries
Executed work must be recorded in:
```
docs/ai-worklog.md
```
Each entry must follow the standard format defined in `instructions-agent.md`.
Mandatory fields:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task
- Objective
- Scope
- Files Modified / Files Inspected
- Key Decisions / Proposed Changes
- Validation
- Result
- Open Issues
---
# Prompt Log
Prompts that trigger significant work must be recorded in:
```
docs/ai-prompts.md
```
Each prompt entry must include:
- Date
- Task ID
- Agent
- Task
- Scope
- Prompt Summary
- Result Summary
Chain-of-thought reasoning must **never be stored**.
---
# Task IDs
All tasks must include a unique identifier.
Format:
```
BUILD-YYYYMMDD-XXX
PLAN-YYYYMMDD-XXX
```
Examples:
```
BUILD-20260315-001
PLAN-20260316-002
```
This allows prompts and worklog entries to be correlated.
---
# Git Rules
Agents must follow these repository rules.
Agents must **not commit or push** unless explicitly requested.
When committing:
Use structured commit messages.
Example:
```
feat: improve terminal navigation behavior
- refine tree navigation
- improve scrolling continuity
- update AI logs
```
Documentation-only updates should use:
```
docs:
```
---
# Code Modification Rules
Agents should prefer:
- small targeted patches
- isolated logic changes
- preserving existing abstractions
Agents should avoid:
- rewriting entire modules
- introducing unnecessary dependencies
- altering unrelated code paths
---
# UI Changes
When modifying UI behavior:
- keep navigation predictable
- preserve keyboard ergonomics
- ensure selections remain visible
- maintain compatibility with history mode
---
# Testing Rules
Whenever logic changes:
- update existing tests
- add focused tests when appropriate
Tests should validate behavior, not implementation details.
---
# Scope Discipline
Agents must strictly respect scope defined in prompts.
If additional changes appear necessary:
Agents must propose them in a PLAN task before implementing.
---
# Repository Safety
Agents must not:
- delete important files
- alter project configuration without justification
- introduce breaking changes without explicit instruction
---
# End of Document
---

View File

@@ -1,298 +0,0 @@
# AGENT_WORKFLOW.md
AI Agent Operational Workflow
This document defines the deterministic workflow that AI agents must follow when performing work in this repository.
The workflow ensures:
- predictable development cycles
- traceable decisions
- minimal risk of unintended changes
- consistent logging of AI actions
This file complements:
- `AGENT.md`
- `instructions-agent.md`
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
- `docs/ai-execution-log.md`
---
# Standard Workflow
Every AI task must follow this sequence:
PLAN → REVIEW → BUILD → VALIDATE → LOG
---
# Execution Order (Strict)
The following operational order must be respected when executing any BUILD task.
1. Implement changes (BUILD)
2. Run validation
make fix
make quality
3. Update AI logs
- docs/ai-execution-log.md
- docs/ai-prompts.md
- docs/ai-worklog.md
4. Stage all modified files including logs
5. Create commit
6. Push only if explicitly requested
Important rule:
Logs must always be written **before creating a commit**. If log files are modified after a commit, the task is considered incomplete and must be corrected.
---
This sequence must not be skipped.
---
# 1. PLAN
Goal:
Understand the problem and determine the minimal solution.
Actions:
- inspect repository state
- read relevant source files
- analyze existing architecture
- identify root cause of the problem
- propose a minimal change
Rules:
- PLAN must **not modify code**
- PLAN must **not change files**
- PLAN must only produce analysis and a proposal
Output should include:
- diagnosis summary
- proposed changes
- expected file scope
---
# 2. REVIEW
Goal:
Validate the plan before implementing it.
Actions:
- confirm the proposed scope is minimal
- ensure no unrelated components are affected
- verify the change aligns with project architecture
Rules:
- If the scope expands unexpectedly, return to PLAN.
---
# 3. BUILD
Goal:
Implement the approved change.
Actions:
- modify only the files defined in scope
- preserve existing architecture
- avoid unnecessary refactors
- maintain compatibility with existing behavior
BUILD tasks may modify:
- source files
- tests
- documentation
BUILD tasks must remain minimal and targeted.
---
# 4. VALIDATE
Goal:
Ensure the repository remains stable.
Agents must run:
```
make fix
make quality
```
Validation must pass before changes are considered complete.
---
# 5. LOG
Goal:
Record the AI activity for traceability.
Three log files must be updated.
### Work Log
```
docs/ai-worklog.md
```
Record:
- AI BUILD ENTRY
- or AI PLAN ENTRY
Including:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task
- Objective
- Scope
- Files Modified / Files Inspected
- Key Decisions / Proposed Changes
- Validation
- Result
- Open Issues
### Prompt Log
```
docs/ai-prompts.md
```
Record:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task
- prompt summary
- scope
- result summary
Do not store chain-of-thought reasoning.
### Execution Log
```
docs/ai-execution-log.md
```
Record the full visible execution trace including:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task Type
- Full prompt text
- Agent todos
- Execution report
The execution log preserves the complete operational record of the AI session.
---
# Task ID Coordination
Every PLAN or BUILD must generate a unique Task ID.
Format:
```
PLAN-YYYYMMDD-XXX
BUILD-YYYYMMDD-XXX
```
Example:
```
PLAN-20260316-001
BUILD-20260316-002
```
The same Task ID must appear in:
- ai-execution-log.md
- ai-prompts.md
- ai-worklog.md
---
# Commit Workflow
Agents must **not commit or push automatically** unless explicitly instructed.
Before creating a commit the agent must ensure:
- validation has passed
- AI logs have been written
- docs/ai-execution-log.md, docs/ai-worklog.md and docs/ai-prompts.md are staged
Commits that omit required AI log entries violate the repository workflow rules.
When commits are requested:
1. Validate repository state
2. Stage modified files
3. Use structured commit messages
Example:
```
feat: improve terminal navigation
- refine tree navigation
- improve scrolling
- update AI logs
```
Documentation-only updates should use:
```
docs:
```
---
# Failure Handling
If validation fails:
Agents must:
1. stop the BUILD process
2. report the error
3. propose a fix
Agents must **not silently bypass failing checks**.
---
# Scope Control
Agents must strictly respect the defined scope.
If additional changes appear necessary:
- return to PLAN
- propose a new change
Do not expand scope during BUILD.
---
# End of Document

View File

@@ -1,179 +0,0 @@
# AI Task Template
Reusable prompt template for launching tasks with AI agents.
This template enforces the repository workflow defined in:
- AGENT.md
- AGENT_WORKFLOW.md
- instructions-agent.md
It ensures every task follows the deterministic sequence:
PLAN → REVIEW → BUILD → VALIDATE → LOG
---
# Basic Task Structure
Use the following structure when requesting work from an AI agent.
```
Task Type:
PLAN | BUILD
Task ID:
PLAN-YYYYMMDD-XXX | BUILD-YYYYMMDD-XXX
Date:
YYYY-MM-DD HH:MM
Objective:
Short description of the goal.
Context:
Relevant background about the current repository state.
Scope:
List of files or directories the agent may modify or inspect.
Requirements:
Detailed description of the behavior that must be implemented or analyzed.
Constraints:
Things the agent must NOT change or must preserve.
Testing:
Describe expected tests or validations.
Validation:
The agent must run:
make fix
make quality
Logging requirements:
- Append an entry to docs/ai-worklog.md
- Append an entry to docs/ai-prompts.md
- Append an entry to docs/ai-execution-log.md
- Include Date, Task ID, Agent, Scope, Result
Execution Logging requirements:
- Append an entry to docs/ai-execution-log.md
- Include:
- full prompt
- agent todos
- final execution report
- Use the standard execution log format defined in docs/ai-execution-log.md
Git requirements:
- Do NOT commit or push unless explicitly requested.
Expected Report:
The agent must summarize:
- files changed
- behavior changes
- validation results
```
---
# Example BUILD Task
```
Task Type:
BUILD
Task ID:
BUILD-YYYYMMDD-XXX
Date:
YYYY-MM-DD HH:MM
Objective:
Improve vertical scrolling behavior in the terminal UI.
Scope:
- src/app/ui.py
- tests/test_ui.py
Requirements:
Ensure the selected entry remains visible when navigating through long directory lists.
Constraints:
Do not modify navigator logic.
Testing:
Add helper tests if necessary.
Validation:
make fix
make quality
Logging requirements:
- Update `docs/ai-worklog.md` and `docs/ai-prompts.md` using the standard log format.
Execution Logging requirements:
- Update docs/ai-execution-log.md with:
- full prompt
- agent todos
- final execution report
Git requirements:
- Do NOT commit or push unless explicitly requested.
```
---
# Example PLAN Task
```
Task Type:
PLAN
Task ID:
PLAN-YYYYMMDD-XXX
Date:
YYYY-MM-DD HH:MM
Objective:
Investigate inconsistent navigation behavior.
Scope:
- src/app/ui.py
- src/app/navigator.py
Requirements:
Analyze the current navigation flow and propose a minimal fix.
Constraints:
Do not modify code.
Validation:
make fix
make quality
Logging requirements:
- Update `docs/ai-worklog.md` and `docs/ai-prompts.md` using the standard log format.
Execution Logging requirements:
- Update docs/ai-execution-log.md with:
- full prompt
- agent todos
- final execution report
Git requirements:
- Do NOT commit or push unless explicitly requested.
Expected Output:
- diagnosis summary
- proposed changes
- expected file scope
```
---
# Notes
This template keeps AI interactions consistent across tasks and helps maintain a clean development history.

View File

@@ -4,16 +4,47 @@ All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project follows Semantic Versioning.
## [1.0.0] - 2026-03-14
## [0.2.4] - 2026-04-03
### Fixed
- Use absolute demo GIF URL in README so image renders correctly on PyPI
## [0.2.3] - 2026-04-02
### Added
- Initial release of the Python AI Dev Template
- Universal Python project structure using `src/`
- `pyproject.toml`-based project configuration
- Development tooling with `black`, `ruff`, `mypy`, and `pytest`
- `pre-commit` integration
- `Makefile` with development, quality, and diagnostic commands
- `instructions-agent.md` for AI coding agent guidance
- `.env.example` for environment-based configuration
- README with setup, workflow, and template reuse instructions
- MIT license
- Add project demo GIF at `docs/demo.gif`
- Show GIF demo in README
## [0.2.2] - 2026-04-02
### Fixed
- Include `cd_browser_post_install.py` in wheel/sdist so `cd_browser_post_install` works after `pip install`
## [0.2.1] - 2026-04-02
### Fixed
- Prevent crashes when scanning directories with restricted permissions
- Handle non-interactive post-install runs without failing on EOF
- Fix shell reload message interpolation in post-install output
### Improved
- Make post-install shell integration idempotent (avoid duplicate `cd_()` entries)
- Add test coverage for permission errors and post-install flows
- Update default application name to `cd-browser`
## [0.2.0] - 2026-04-02
### Added
- Interactive post-install script (`cd_browser_post_install`) for automatic shell integration
- Option to automatically add `cd_()` function to ~/.bashrc or ~/.zshrc during installation
- Improved uninstall instructions in README
### Improved
- Installation UX: clearer setup process with user prompts
- Shell integration: better guidance for enabling `cd_` command
### Added
- Initial release of **cd-browser**
- Terminal-based directory navigation
- Interactive directory tree navigation
- History navigation mode

View File

@@ -1,46 +1,66 @@
# cd-browser
`cd-browser` is a terminal-native directory navigator for fast filesystem browsing from the command line.
**Stop typing paths. Browse them.**
It provides an interactive interface for exploring directories with the keyboard and returns the selected path at the end of the session so shell wrappers can change the current shell directory.
`cd-browser` is a fast keyboard-driven directory navigator for the terminal.
It lets you explore directory trees visually and jump to any folder instantly.
![cd-browser demo](https://gitea.sakydogalo.es/saky/cd-browser/raw/branch/main/docs/demo.gif)
## Why cd-browser?
Working in the terminal often means:
- typing long directory paths
- navigating deep folder trees
- repeating `cd ..` multiple times
`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.
## Features
- 🚀 Fast visual navigation of directory trees
- ⌨️ Fully keyboard-driven workflow
- 🌲 Expand and collapse directories
- 📜 Navigation history inside the session
- 🖥 Native terminal interface
- 🔁 Works with Bash, Zsh and other shells
- ⚡ Returns the selected path to the shell
- 🔎 Press `.` to toggle hidden directories
## Quick Demo
Run:
```bash
cd_
```
Browse directories using the arrow keys and press **Enter** to jump directly to the selected folder.
## Documentation
See the documentation index:
```
docs/index.md
## Features
- Interactive terminal directory browser
- Keyboard-driven navigation
- Parent directory entry via `..`
- Expand and collapse directory trees
- Session navigation history support in the application state
- Installable CLI command: `cd_browser`
```
## Installation
Install the project in user mode:
```bash
pip install .
pip install cd-browser
```
For editable local development:
**Important**: After installation, run this to set up the `cd_` command:
```bash
pip install -e '.[dev]'
cd_browser_post_install
```
After installation, the application command is:
```bash
cd_browser
```
When the interactive session exits, the program prints the final selected directory path.
This interactive script will guide you through enabling `cd_` in your shell.
## Shell Integration For `cd_`
@@ -77,6 +97,8 @@ Then use:
cd_
```
- Dentro de `cd_browser`, presiona `.` para alternar la visualización de carpetas ocultas.
## Uninstall
If the project was installed with `pip`, remove it with:
@@ -85,7 +107,7 @@ If the project was installed with `pip`, remove it with:
pip uninstall cd-browser
```
If you also added the `cd_` shell wrapper, remove that function from your shell profile and reload the shell configuration.
**Important**: After uninstalling, remove the `cd_()` function from your shell profile (~/.bashrc or ~/.zshrc) to clean up completely.
## Developer Setup

BIN
docs/demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

View File

@@ -1,229 +0,0 @@
# Agent Instructions: Universal Python Project Template
## Project Overview
This repository is a universal and adaptable Python template intended to serve as a professional starting point for new projects.
## Core Directives
1. All code, comments, variables, string literals, commit messages, and documentation must be written in English.
2. Use Python 3.11+ with type hints whenever possible.
3. Follow clean code principles and PEP 8.
4. Keep modules small, cohesive, and maintainable.
5. Use consistent docstrings for public classes and functions.
6. Prefer explicit error handling over silent failures.
## Architecture Guidelines
1. Keep business logic separate from infrastructure concerns.
2. Avoid mixing configuration, I/O, and domain logic in the same module.
3. Prefer reusable services over duplicated logic.
4. Do not introduce unnecessary complexity or premature abstractions.
## Quality Standards
1. Every meaningful feature should include tests.
2. Run formatting, linting, and type checks before considering a task complete.
3. Keep the README updated whenever setup, commands, or structure changes.
4. Do not leave placeholder code unless clearly marked.
## Tooling
- Testing: pytest
- Formatting: black
- Linting: ruff
- Static typing: mypy
## Expected Project Structure
src/app/
tests/
scripts/
README.md
instructions-agent.md
pyproject.toml
## Agent Workflow
1. First understand the repository structure.
2. Propose a short implementation plan before major changes.
3. Reuse existing modules whenever possible.
4. Keep changes minimal, coherent, and production-oriented.
5. Validate changes with tests and quality tools when possible.
## Application Specification
This repository includes an application specification that defines the expected behavior of the project.
Primary specification file:
- `specs/cd_browser_spec.md`
Agent rules:
1. Before implementing any feature, read the application specification.
2. If implementation details are unclear, follow the specification first.
3. If the specification conflicts with a previous assumption, the specification takes precedence.
4. Keep implementation aligned with the MVP scope unless explicitly asked to extend it.
5. After code changes, run the appropriate validation workflow defined by this repository.
## AI Worklog Policy
This repository maintains an AI-assisted development log.
Files:
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
- `docs/ai-execution-log.md`
Rules:
1. After every significant `plan` or `build` task, append a concise worklog entry to `docs/ai-worklog.md`.
2. Each worklog entry must include:
- date
- task type (`plan` or `build`)
- short objective
- files inspected or modified
- key decisions
- validation commands run
- result
- unresolved issues if any
3. When a prompt meaningfully changes architecture, behavior, workflow, debugging direction, or project structure, append the prompt (or a concise cleaned version of it) to `docs/ai-prompts.md`.
4. Do not store private chain-of-thought or internal reasoning.
5. Store only concise, user-facing summaries of what was done.
6. Keep entries chronological and easy to scan.
## Mandatory AI Logging
Every significant AI interaction in this repository must be logged.
This applies to:
- `plan` tasks
- `build` tasks
- debugging tasks
- documentation tasks
- architectural discussions that affect the project
Required files:
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
- `docs/ai-execution-log.md`
Mandatory rules:
1. After every significant `plan` task, append a concise entry to `docs/ai-worklog.md`.
2. After every significant `build` task, append a concise entry to `docs/ai-worklog.md`.
3. If the task was driven by a meaningful prompt, also append an entry to `docs/ai-prompts.md`.
4. The agent must append a full execution entry to `docs/ai-execution-log.md` including:
- the full prompt provided to the agent
- the agent todo list or execution plan
- the final execution report produced by the agent
5. Logging is part of the task itself and must not be skipped.
6. The task is not complete until the logs are updated.
7. Logs must always be written **before creating a commit**.
8. The agent must ensure that the following files are updated and staged before any commit:
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
- `docs/ai-execution-log.md`
9. If a commit is created without the corresponding log entries, the task is considered incomplete and must be corrected.
10. Log entries must be part of the same commit whenever possible to maintain traceability between the code change and the AI task that produced it.
Each worklog entry must include:
- date
- task type (`plan` or `build`)
- short objective
- files inspected or modified
- key decisions
- validation commands run, if any
- result
- unresolved issues, if any
Validation logging rule:
- If validation commands such as `make fix`, `make quality`, `pytest`, or other checks are executed during the task, the worklog entry must record them explicitly.
- The value `validation: not run` may only be used if no validation commands were executed.
- The worklog entry must reflect the actual commands run during the task.
Each prompt log entry must include:
- date
- task type (`plan` or `build`)
- short prompt summary
- scope
- result summary
Rules:
- Do not store chain-of-thought or private reasoning.
- Store only concise user-facing summaries.
- Keep entries chronological.
- Even small but meaningful tasks must be logged.
Execution log entries must include:
- date
- task ID
- task type (`plan` or `build`)
- full prompt text
- agent todos
- execution report
## Standard AI Log Entry Format
To ensure consistency and readability of long AI-assisted development histories, all log entries must follow a standardized block format.
Agents must write log entries using the following structure.
Example for build tasks:
### AI BUILD ENTRY
Date: YYYY-MM-DD HH:MM
Task ID: BUILD-YYYYMMDD-XXX
Agent: OpenCode
Task: build
Objective: short description of the task
Scope:
- src/app/module.py
- tests/test_module.py
Files Modified:
- file/path/example.py
- another/file.md
Key Decisions:
- short bullet explaining important choices
Validation:
- make fix (passed)
- make quality (passed)
Result:
- short description of what changed or was achieved
Open Issues:
- optional list of unresolved problems
Example for plan tasks:
### AI PLAN ENTRY
Date: YYYY-MM-DD HH:MM
Task ID: PLAN-YYYYMMDD-XXX
Agent: OpenCode
Task: plan
Objective: short description of planning objective
Scope:
- src/app/module.py
- specs/module_spec.md
Files Inspected:
- src/app/example.py
- specs/example_spec.md
Proposed Changes:
- summary of planned changes
Notes:
- optional relevant observations
Rules:
- Always use these headers exactly (`AI BUILD ENTRY` or `AI PLAN ENTRY`).
- Keep entries concise and structured.
- Do not include chain-of-thought reasoning.
- Use bullet points when possible.
- This format must be used when updating `docs/ai-worklog.md`.
- The Date field must include both date and time using the format YYYY-MM-DD HH:MM.
- Every log entry must include a `Task ID`.
- `Task ID` format must be `BUILD-YYYYMMDD-XXX` or `PLAN-YYYYMMDD-XXX`.
- `Agent` must identify the tool or AI system performing the task (e.g., OpenCode).
- `Scope` must describe the main files or project areas affected.
- These fields make the AI worklog suitable for long-term traceability and large histories.

View File

@@ -4,17 +4,31 @@ build-backend = "setuptools.build_meta"
[project]
name = "cd-browser"
version = "0.1.0"
description = "A universal and adaptable Python project template for AI-assisted development."
version = "0.2.4"
description = "A fast keyboard-driven directory navigator for the terminal."
readme = "README.md"
requires-python = ">=3.11"
requires-python = ">=3.8"
authors = [
{ name = "Saky" }
]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Utilities",
]
dependencies = []
[project.scripts]
cd_browser = "app.main:main"
cd_browser_post_install = "cd_browser_post_install:main"
[project.optional-dependencies]
dev = [
@@ -27,6 +41,7 @@ dev = [
[tool.setuptools]
package-dir = {"" = "src"}
py-modules = ["cd_browser_post_install"]
[tool.setuptools.packages.find]
where = ["src"]

View File

@@ -5,5 +5,5 @@ from dataclasses import dataclass
class AppConfig:
"""Application configuration."""
app_name: str = "Python AI Dev Template"
app_name: str = "cd-browser"
debug: bool = False

View File

@@ -13,28 +13,61 @@ class DirectoryEntry:
has_children: bool
def list_directories(path: Path) -> list[DirectoryEntry]:
def list_directories(path: Path, show_hidden: bool = False) -> list[DirectoryEntry]:
"""Return direct child directories sorted by name."""
directory_path = path.expanduser().resolve()
entries: list[DirectoryEntry] = []
for child in directory_path.iterdir():
if child.is_dir():
entries.append(
DirectoryEntry(
name=child.name,
path=child,
has_children=has_subdirectories(child),
)
try:
children = list(directory_path.iterdir())
except PermissionError:
return []
for child in children:
try:
is_directory = child.is_dir()
except OSError:
continue
if not is_directory:
continue
if not show_hidden and child.name.startswith("."):
continue
entries.append(
DirectoryEntry(
name=child.name,
path=child,
has_children=has_subdirectories(child, show_hidden=show_hidden),
)
)
return sorted(entries, key=lambda entry: entry.name.casefold())
def has_subdirectories(path: Path) -> bool:
def has_subdirectories(path: Path, show_hidden: bool = False) -> bool:
"""Return whether the directory contains at least one subdirectory."""
directory_path = path.expanduser().resolve()
return any(child.is_dir() for child in directory_path.iterdir())
try:
children = list(directory_path.iterdir())
except PermissionError:
return False
for child in children:
try:
is_directory = child.is_dir()
except OSError:
continue
if not is_directory:
continue
if not show_hidden and child.name.startswith("."):
continue
return True
return False

View File

@@ -28,6 +28,7 @@ class Navigator:
current_path: Path = field(init=False)
selected_index: int = field(init=False, default=0)
_expanded_paths: set[Path] = field(init=False, default_factory=set)
show_hidden: bool = False
def __post_init__(self) -> None:
self.current_path = self.start_path.expanduser().resolve()
@@ -144,6 +145,13 @@ class Navigator:
self._set_current_path(path)
return path
def toggle_hidden(self) -> None:
"""Toggle showing/hiding hidden directory entries."""
self.show_hidden = not self.show_hidden
self.selected_index = 0
self._expanded_paths.clear()
def select_history_entry(self, index: int) -> Path:
"""Jump to a directory stored in session history."""
@@ -154,7 +162,7 @@ class Navigator:
def _build_entries(self, path: Path, depth: int) -> list[VisibleEntry]:
entries: list[VisibleEntry] = []
for directory in list_directories(path):
for directory in list_directories(path, show_hidden=self.show_hidden):
is_expanded = directory.path in self._expanded_paths
entries.append(
VisibleEntry(

View File

@@ -145,7 +145,8 @@ class TerminalUI:
stdscr.refresh()
return
path_text = str(navigator.current_path)
hidden_status = "on" if navigator.show_hidden else "off"
path_text = f"{navigator.current_path} [hidden: {hidden_status}]"
stdscr.addnstr(0, 0, path_text, width - 1)
lines = build_render_lines(navigator)
@@ -282,6 +283,11 @@ class TerminalUI:
self._tree_scroll_offset = 0
return None
if key == ord("."):
navigator.toggle_hidden()
self._tree_scroll_offset = 0
return None
if key in (curses.KEY_ENTER, 10, 13):
return navigator.selected_entry.path

View File

@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Post-install script for cd-browser to guide users on shell integration."""
import os
import sys
def _profile_for_shell(shell: str) -> str:
if shell == "zsh":
return "~/.zshrc"
return "~/.bashrc"
def _is_cd_function_present(profile_path: str) -> bool:
if not os.path.exists(profile_path):
return False
with open(profile_path, encoding="utf-8") as profile_file:
return "cd_() {" in profile_file.read()
def main() -> None:
"""Print shell integration instructions after installation."""
print("\n" + "=" * 60)
print("🎉 cd-browser installed successfully!")
print("=" * 60)
print()
print(
"To enable the 'cd_' command, you need to add a function to your shell profile."
)
print()
shell = os.environ.get("SHELL", "").split("/")[-1]
profile = _profile_for_shell(shell)
cd_function = """cd_() {
local target
target="$(cd_browser)" || return
if [ -n "$target" ] && [ -d "$target" ]; then
cd "$target"
fi
}"""
print(f"Copy this to the end of {profile}:")
print()
print(cd_function)
print()
profile_path = os.path.expanduser(profile)
try:
response = (
input("Do you want me to add it automatically? (y/n): ").strip().lower()
)
if response == "y":
if _is_cd_function_present(profile_path):
print(f"\n cd_() already exists in {profile}, no changes made.")
else:
with open(profile_path, "a", encoding="utf-8") as f:
f.write("\n" + cd_function + "\n")
print(f"\n✅ Added to {profile}")
print(f"Run 'source {profile}' or restart your terminal to activate cd_.")
else:
print(f"\nAdd the function to {profile} manually.")
print(f"Then run 'source {profile}' or restart terminal.")
except EOFError:
print(
f"\nNo interactive input detected. Add the function to {profile} manually."
)
print(f"Then run 'source {profile}' or restart terminal.")
except KeyboardInterrupt:
print("\n\nSetup cancelled. You can add the function manually later.")
sys.exit(0)
print("\n" + "=" * 60)
print(
f"For uninstall: after 'pip uninstall cd-browser', remove cd_() from {profile}."
)
print("=" * 60 + "\n")
if __name__ == "__main__":
main()

View File

@@ -4,9 +4,10 @@ import pytest
from app.cli import run_cli
from app.navigator import Navigator
from app.ui import TerminalUI
class StubUI:
class StubUI(TerminalUI):
def __init__(self, result: Path) -> None:
self.result = result
self.navigator: Navigator | None = None

View File

@@ -44,3 +44,33 @@ def test_list_directories_raises_for_missing_directory(tmp_path: Path) -> None:
with pytest.raises(FileNotFoundError):
list_directories(missing_path)
def test_list_directories_skips_permission_errors_for_children(tmp_path: Path) -> None:
root = tmp_path / "root"
root.mkdir()
allowed = root / "allowed"
blocked = root / "blocked"
allowed.mkdir()
blocked.mkdir()
blocked.chmod(0)
try:
entries = list_directories(root)
finally:
blocked.chmod(0o700)
assert any(entry.name == "allowed" for entry in entries)
def test_has_subdirectories_returns_false_on_permission_error(tmp_path: Path) -> None:
blocked = tmp_path / "blocked"
blocked.mkdir()
blocked.chmod(0)
try:
result = has_subdirectories(blocked)
finally:
blocked.chmod(0o700)
assert result is False

View File

@@ -25,6 +25,31 @@ def test_navigator_initial_state_includes_parent_entry(tmp_path: Path) -> None:
]
def test_toggle_hidden_directories(tmp_path: Path) -> None:
root = tmp_path / "root"
root.mkdir()
visible = root / "visible"
visible.mkdir()
hidden = root / ".hidden"
hidden.mkdir()
navigator = Navigator(root)
assert [entry.name for entry in navigator.visible_entries] == ["..", "visible"]
navigator.toggle_hidden()
assert navigator.show_hidden is True
assert [entry.name for entry in navigator.visible_entries] == [
"..",
".hidden",
"visible",
]
navigator.toggle_hidden()
assert navigator.show_hidden is False
assert [entry.name for entry in navigator.visible_entries] == ["..", "visible"]
def test_expand_selected_directory_reveals_nested_entries(tmp_path: Path) -> None:
current = tmp_path / "workspace"
current.mkdir()

View File

@@ -0,0 +1,47 @@
from pathlib import Path
import pytest
from cd_browser_post_install import main
CD_FUNCTION_SNIPPET = "cd_() {"
def test_post_install_handles_eof_without_crashing(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
monkeypatch.setenv("SHELL", "/bin/zsh")
monkeypatch.setattr(
"builtins.input", lambda _prompt: (_ for _ in ()).throw(EOFError)
)
main()
output = capsys.readouterr().out
assert "No interactive input detected" in output
assert "source ~/.zshrc" in output
def test_post_install_adds_cd_function_once(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
monkeypatch.setenv("SHELL", "/bin/zsh")
monkeypatch.setenv("HOME", str(tmp_path))
responses = iter(["y", "y"])
monkeypatch.setattr("builtins.input", lambda _prompt: next(responses))
main()
main()
profile_path = tmp_path / ".zshrc"
content = profile_path.read_text(encoding="utf-8")
assert content.count(CD_FUNCTION_SNIPPET) == 1
output = capsys.readouterr().out
assert "already exists" in output
assert "source ~/.zshrc" in output