Metadata-Version: 2.4
Name: py-fs-shell
Version: 0.0.2
Summary: Python virtual filesystem with structured state backend
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: hatchling>=1.25; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: semversioner>=2.0; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: s3
Requires-Dist: boto3>=1.34; extra == 's3'
Description-Content-Type: text/markdown

# py_fs_shell

Python virtual filesystem primitives for agent workflows, tests, and sandboxed file operations.

It provides:

- `InMemoryFs` for ephemeral virtual filesystems
- `LocalFileSystem` for sandboxed real-disk access
- `Workspace` for metadata + blob backed storage
- `FileSystemStateBackend` for JSON, search/replace, diffs, archives, hashing, and edit planning

## Installation

With `uv`:

```bash
uv add py-fs-shell
```

With `pip`:

```bash
pip install py-fs-shell
```

For S3-backed workspaces:

```bash
uv add 'py-fs-shell[s3]'
pip install 'py-fs-shell[s3]'
```

For local development from this repository:

```bash
uv sync --extra dev --extra s3
```

## Example

```python
import asyncio
from py_fs_shell import StateWriteEditInstruction, workspace

async def main():
    ws = await workspace.memory()
    state = ws.state()

    await state.write_file("/src/main.py", "print('hello world')\n")
    await state.write_json("/config.json", {"debug": True})

    matches = await state.search_files("**/*.py", "hello")
    print(matches[0].path)

    plan = await state.plan_edits([
        StateWriteEditInstruction(
            path="/README.md",
            content="# Demo\n",
        ),
    ])
    await state.apply_edit_plan(plan)

    await state.write_file_bytes("/video.mp4", b"video bytes")

asyncio.run(main())
```

S3-backed workspace:

```python
from py_fs_shell import workspace

ws = await workspace.s3(bucket="my-bucket", prefix="runs/123")
state = ws.state()
```

## Releases

Releases are tracked with `semversioner`. See [RELEASE.md](RELEASE.md) and [CHANGELOG.md](CHANGELOG.md).

## License

MIT. See [LICENSE](LICENSE).

Inspired by Cloudflare's `@cloudflare/shell` package.
