Skip to content

feat(python): add direct VFS convenience methods on Bash/BashTool #1257

@chaliy

Description

@chaliy

Context

Node bindings expose VFS operations directly on the Bash class (bash.readFile(), bash.writeFile(), bash.ls(), bash.glob(), etc.) for ergonomic access. Python requires an extra indirection: bash.fs().read_file().

Both patterns should be available in Python: direct methods for convenience, fs() accessor for advanced use.

This is part of the Python ↔ Node binding parity effort (Phase 1 — API Parity).

What to implement

Add 13 delegate methods to PyBash and PyBashTool in crates/bashkit-python/src/lib.rs that forward to the internal FileSystem:

bash.read_file(path: str) -> str
bash.write_file(path: str, content: str) -> None
bash.append_file(path: str, content: str) -> None
bash.mkdir(path: str, recursive: bool = False) -> None
bash.exists(path: str) -> bool
bash.remove(path: str, recursive: bool = False) -> None
bash.stat(path: str) -> dict
bash.chmod(path: str, mode: int) -> None
bash.symlink(target: str, link: str) -> None
bash.read_link(path: str) -> str
bash.read_dir(path: str) -> list[dict]
bash.ls(path: str = ".") -> list[str]       # simple name list
bash.glob(pattern: str) -> list[str]         # safe glob matching

Important design notes

  • These should return str (not bytes like FileSystem.read_file()), matching Node's behavior and the common case
  • ls() returns a simple list[str] of names (like Node's ls()), not the full metadata that read_dir() returns
  • glob() must use safe glob only (no arbitrary shell expansion)
  • The fs() accessor remains available for users who need bytes-level access or the FileSystem static constructors
  • Methods should be added to both PyBash AND PyBashTool

Node reference

In crates/bashkit-js/src/lib.rs and wrapper.ts, Node implements these as methods on the Bash class that delegate to the internal interpreter's VFS.

Acceptance criteria

  • All 13 methods added to PyBash class
  • All 13 methods added to PyBashTool class
  • read_file() returns str (UTF-8 decoded), raises on binary/non-UTF-8
  • write_file() and append_file() accept str
  • ls() returns list[str] of entry names in the given directory
  • glob() returns list[str] of matching paths (safe glob only)
  • stat() returns dict with keys: file_type, size, mode, modified, created
  • read_dir() returns list[dict] with name and metadata per entry
  • All methods raise appropriate errors for invalid paths / missing files
  • Methods work correctly after reset() (VFS is cleared)
  • Methods reflect changes made by bash command execution (e.g., bash.execute_sync("echo hi > /tmp/f") then bash.read_file("/tmp/f"))
  • Type stubs updated in _bashkit.pyi with docstrings
  • Tests for each method (positive + negative cases)
  • cargo clippy and ruff check pass
  • README updated showing direct method usage

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions