A unified virtual filesystem for AI agents. Mount S3, Google Drive, Slack, Gmail, GitHub, Linear, Notion, Postgres, MongoDB, SSH, and more behind one filesystem so agents read, write, and pipe across services with familiar shell commands. Ships Python (mirage-ai) and TypeScript (@struktoai/mirage-*) SDKs, a CLI, FUSE mounts, and adapters for OpenAI Agents SDK, Vercel AI SDK, LangChain deepagents, Pydantic AI, CAMEL, OpenHands, Mastra, Pi Coding Agent, plus FUSE-based integration with Claude Code and Codex. Apache 2.0 licensed.
4.5 KiB
CLAUDE.md
MIRAGE is a package that allows you to mount anything as a filesystem and make it usable by AI Agents.
Repo Layout
This monorepo hosts two sibling implementations:
python/— the Python package (mirage/,tests/,pyproject.toml,uv.lock).typescript/— the TypeScript monorepo (packages/core,packages/node, etc.).docs/,examples/,.github/— shared across both.
Run Python commands from python/, TypeScript commands from typescript/.
Development Setup
This project uses uv for Python dependency management. Install dependencies with:
cd python && uv sync --all-extras --no-extra camel
camel is declared as conflicting with openai (and other extras) in pyproject.toml, so uv sync --all-extras fails. Exclude camel to keep the openai stack.
Running examples
Examples under examples/python/ load .env.development from the repo root (cwd-relative). To keep cwd at the root while using the python/ venv, invoke the venv interpreter directly:
./python/.venv/bin/python examples/python/s3/s3.py
Avoid uv --directory python run ... for examples — it changes cwd to python/ and breaks load_dotenv(".env.development").
Backward Compatibility
- No need to consider backward compatibility for the code.
Create a PR
When asked to create a PR, please follow the following steps:
- Run
pre-commit run --all-filesfrom the repo root to lint and format the code. - Run
cd python && uv run pytestto run the Python tests. - Run
git add -Ato add all changes. - Run
git checkout -b <branch-name>to create a new branch. - Run
git commit -m "<commit-message>"to commit the changes. - Run
git push origin <branch-name>to push the changes to the remote repository. - Run
gh pr create --title "<pr-title>" --body "<pr-body>"to create a PR.
Commands
Linting and Formatting
After making major changes, run pre-commit from the repo root to ensure code quality:
./python/.venv/bin/pre-commit run --all-files
Invoke the venv's pre-commit binary directly (not via uv --directory python run) so cwd stays at the repo root — otherwise git ls-files only lists files under python/ and examples/ gets silently skipped.
Type Conventions
- Paths must always be represented as
PathSpec, never raw strings. All functions that accept or return paths uselist[str | PathSpec]wherestris for text arguments andPathSpecis for paths. Never pass a path as a plainstr— wrap it inPathSpec.
Rules
- Avoid add any comments or docstrings on the top of the file.
- Do not create nested functions.
- Add type to Args for docstring.
- Do not add comment after each line of code in the format of "# 10MB - trigger segmentation for files larger than this". The most you can add is "# 10MB".
- For all imports you need to put to the top of the file. Don't have imports within each function.
- No circular imports. If putting an import at the top would cause a cycle, that's a sign the dependency direction is wrong — fix the design (dependency injection, splitting modules, moving the shared piece to a leaf), don't paper over it with function-local lazy imports. Verify by checking that running
cd python && uv run python -c "import <every changed module>"succeeds without ImportError. - Never silently swallow exceptions.
try: ... except: pass(orexcept SomeError: pass) hides real bugs. If you genuinely need to ignore an error, log it (logger.debug(...)) or document loudly why it's safe. Default behavior should be: let the exception propagate. Especially never silently swallowRuntimeError— it usually signals something deeper (event loop in wrong state, recursion limit, etc.) that you need to actually fix. - Never call
asyncio.run()inside a sync function that might be invoked under an outer event loop. It will raiseRuntimeError: asyncio.run() cannot be called from a running event loop. If you need async behavior from a sync API, either: (a) make the calling functionasync, (b) operate on the underlying sync state directly (e.g. write to a dict instead of calling an async setter), or (c) use a sync alternative of the same library (e.g.redis.Redisinstead ofredis.asyncio.Redis). Do NOT wrap withtry/except RuntimeError: pass— that masks the bug AND leaks the unawaited coroutine. - Please don't change any file name unless I ask you to do so.
- Don't add too many printings or comments in the code.
- Don't add README.md unless I ask you to do so.
- Use uv add to install new dependencies.