Skip to main content
How-To Guides Last updated: 8 October 2026

Browse project files

Nathan Schram
By Founder, Little Bear Apps

Browse your project’s directory tree and preview files without leaving Telegram — check a config, review a file, or orient yourself in the repo from your phone or any device.

Start browsing

Send /browse to open the project root:

/browse

Untether replies with a directory listing rendered as inline keyboard buttons. Each button is a file or directory you can tap.

/browse needs a project root: the chat must be bound to a project (chat_id under [projects.<alias>]), or default_project must be set in untether.toml. Otherwise it refuses rather than guessing a directory (#389):

Untether

No project directory for this chat. Bind the chat to a project (chat_id under [projects.<alias>]) or set default_project in untether.toml.

/browse showing project root with directory and file buttons

Untether

📁 /
3 dirs · 4 files

📂 docs/ · 📂 src/
📂 tests/ · 📄 .gitignore (1k)
📄 CHANGELOG.md (12k) · 📄 README.md (4k)
📄 pyproject.toml (2k)

Tap a directory button to drill into it. The listing updates in place, showing the contents of the selected directory.

Preview a file

Tap a file button to see a syntax-highlighted preview. Previews show up to 25 lines and 2,000 characters of the file content, which is enough to check config files, review small modules, or confirm file structure.

Untether

📄 src/main.py

import sys
from pathlib import Path

from untether.app import create_app

def main():
    app = create_app()
    app.run()

Go back

Below the project root, each listing starts with a 📂 .. button that moves up one level. A file preview has a 📂 Back button that returns to its directory.

Browse a specific path

Pass a path argument to jump directly to a directory or file:

/browse src/
/browse package.json

If the path is a directory, Untether shows its listing. If it’s a file, you get the preview directly.

Limits and filtering

The file browser applies sensible defaults to keep listings readable:

LimitValue
Max entries per listing20
Hidden paths (any component starting with .)Denied in listings, previews and direct paths, except .github and .gitignore
[transports.telegram.files] deny_globsApplied to listings, previews and direct paths (e.g. .env, key.pem, .ssh/…)
Excluded directories (listing only)__pycache__, node_modules, .git, .venv, venv

If a directory has more than 20 entries, only the first 20 are shown. Use /browse path/to/subdir to navigate deeper.

Refused paths get a short reply instead of a preview: Path denied by rule: <glob>, Hidden paths can't be browsed., Path outside project. or Path could not be resolved (symlink loop?). A denied path gets the same reply whether or not it exists.

Path traversal protection

The browser cannot navigate outside the project root. Paths are checked after symlinks are resolved, so neither .. nor an in-root symlink pointing elsewhere (e.g. home → ~) can escape the project directory; such links are left out of listings. Button ids are scoped to the chat that created them, so a button id from another chat reads as expired (#389).

Was this helpful?

Related Articles