Beyond Local File
Project your local dev files across projects as physical copies — without committing them to Git.
Table of Contents
- What is this?
- Why not GNU Stow or chezmoi?
- Architecture: Tool and Data Separation
- Installation
- Quick Start
- Configuration
- Available Commands
- Documentation
- Important Notes
- Platform Support
- Contributing
- How the author uses it
- License
What is this?
In real-world development, local files accumulate that are genuinely useful but shouldn't be committed to Git: HTTP client files with private environment variables, AI agent hooks and steering documents, task runner configs referencing local paths, debug logs, scratch specs. You want them in your project directory — your editor, your AI tools, your task runner all expect them there — but not in the repository.
beyond-local-file manages these files centrally and projects them into your target projects
as physical copies (files and directories). It also automatically adds those projections to
each project's Git exclude list, so Git never sees them.
A few concrete things it handles that are hard to do with a shell script:
- Projecting an entire directory subtree (e.g.,
.kiro/hooks/) into multiple projects at once - Keeping each target project's copies live while a daemon observes the hub and fans updates out
- Isolating a replica that loses an update (out-of-sync) instead of overwriting it
- Checking status across all managed projects at a glance (
blf link check)
🎬 Quick Demo
Watch beyond-local-file in action: install from GitHub, project files into a target, and manage Git excludes automatically.
Why not GNU Stow or chezmoi?
GNU Stow and chezmoi are excellent tools for dotfiles management — organizing your personal configuration files (.bashrc, .vimrc, .gitconfig) across machines.
- Stow uses a package-based approach with CLI parameters to create symlinks from a stow directory to
$HOME. - chezmoi is a comprehensive dotfiles manager with templating, encryption, password manager integration, and Git-based sync across machines.
beyond-local-file is designed for a different use case: per-project development files that shouldn't be committed to Git. Instead of managing $HOME dotfiles, it projects local dev files (HTTP client configs, AI hooks, task runner configs) across multiple projects using a centralized config.yml. It handles Git excludes automatically. Every projection is a physical copy, so tools that refuse workspace-escape (Kiro) can read the files inside the target project.
Use Stow/chezmoi for: Personal dotfiles in $HOME
Use beyond-local-file for: Local dev files across multiple projects with different layouts
For a detailed comparison with use case examples, see docs/alternatives-comparison.md.
Architecture: Tool and Data Separation
beyond-local-file follows a clean separation between the tool (code) and managed projects (data):
- The tool is the CLI application itself — installed once via
uvxoruv tool install, lives in Python's site-packages, contains no user data. - Managed projects are your directories containing the local development files you want to share — live wherever you choose, can be version-controlled separately, independent of the tool.
# Tool (installed via uvx)
~/.local/share/uv/tools/beyond-local-file/ # managed by uv
# Managed Projects (your data, separate repository)
~/my-dev-files/
├── config.yml
├── project-a/
│ └── test.http
└── project-b/
└── dev-config.yml
# Target Projects (where physical copies are projected)
~/workspace/project-a/
└── test.http # regular file, copied from ~/my-dev-files/project-a/test.http
link is the metaphor: a managed item is visible in a target project. The runtime is a daemon that copies, observes, and applies mapping changes. Each target project holds its own tree; there is no live inode sharing.
Installation
Recommended: uv tool install from PyPI
uv tool install beyond-local-file
# Update to latest version
uv tool install --upgrade beyond-local-file
# Or use the built-in upgrade command (auto-detects install method)
blf upgrade
Alternative: pipx from PyPI
pipx install beyond-local-file
# Update
pipx upgrade beyond-local-file
# Or use the built-in upgrade command (auto-detects install method)
blf upgrade
Install from GitHub (development version)
# Using uv
uv tool install git+https://github.com/xingyuli/beyond-local-file.git
# Using pipx
pipx install git+https://github.com/xingyuli/beyond-local-file.git
For development setup, see docs/development.md.
Recommended: Create an Alias
The command name beyond-local-file is long. For convenience, create an alias:
# Add to your ~/.bashrc, ~/.zshrc, or equivalent
alias blf='beyond-local-file'
This documentation uses blf in all examples.
Quick Start
- Create a
config.ymlin your managed projects directory:
project-a:
- /Users/username/workspace/project-a
- /Users/username/workspace/project-a-fork
project-b: /Users/username/workspace/project-b
- Start the daemon (the runtime that projects copies and keeps them live):
cd ~/my-dev-files
blf daemon start
daemon start stays in the foreground until phase ready. On a TTY it rewrites one status line (Catching up i/n … item).
- Check status:
blf link check
link check, revlink create / restore, and remove talk to the daemon. If it is down they fail with:
Error: daemon is not running. Start it with: blf daemon start
If the process is in phase catch-up, those shells wait until ready rather than failing.
Configuration
The mapping file (config.yml) maps project names to target paths. Three formats are supported:
1. Simple string — single target
project-a: /Users/username/workspace/project-a
2. Simple list — multiple targets
project-b:
- /Users/username/workspace/project-b
- /Users/username/workspace/project-b-fork
3. Selective subpaths — project specific items only
project-c:
target: /Users/username/workspace/project-c
subpath:
- .kiro/hooks
- .vscode/settings.json
Only the listed subpaths are projected. Intermediate directories are created automatically. Files and directories are both physical copies.
Multiple targets: The target key accepts a string or list in all formats.
copy: true is not a valid option. Every projection is already a copy; leftover copy: true in a mapping or subpath entry is rejected at config load:
Unsupported option 'copy: true' (project: my-project, mapping: 1, key: copy)
For detailed examples, see docs/configuration-reference.md.
Configuration set
One daemon process loads one configuration set — the mapping yaml files that worker uses.
Resolution order:
-c/--config PATH— a singleton set identified by that file's resolved path~/.blf/config— the global set, a pointer list of mapping yaml pathsconfig.ymlin the current directory — a singleton set
# ~/.blf/config — pointer list (not itself a mapping document)
config_file: ~/my-dev-files/config.yml
# Or combine personal and company mapping files in one daemon process
config_file:
- ~/personal/config.yml
- ~/company/config.yml
A mapping file already loaded by a running set is served by that process — -c does not start a second watcher. Starting a set that shares a mapping file with another running set is an error.
See Configuration set in the CLI reference for full details.
Available Commands
| Command | Description |
|---|---|
blf daemon start |
Start the background runtime that copies, observes, and applies mappings |
blf daemon stop |
Stop the running daemon |
blf daemon status |
Show whether the daemon is running, plus out-of-sync paths and held copies |
blf daemon logs |
Follow the daemon log (Ctrl-C stops following, not the daemon) |
blf daemon reload |
Apply external mapping edits from the set's mapping files |
blf link check [PROJECT] |
Check copy projections and Git excludes |
blf revlink create PATH |
Adopt an existing file or directory as a copy projection |
blf revlink restore PATH |
Stop managing PATH and leave the target file in place |
blf remove PATH |
Permanently remove a managed item and its validated projections |
blf upgrade |
Upgrade to the latest version (auto-detects install method) |
There is no link sync. The daemon is the runtime.
Live updates, out-of-sync, and held copies
The managed project is the hub. After a successful hub apply, the daemon fans the generation out to other in-sync replicas of that managed project except the source (the tree that already has the bytes).
If two target projects edit the same path, the first apply wins. The loser is out-of-sync for that path: later fan-out skips it, and further edits from it are discarded. The live path on the hub and on in-sync replicas keeps moving.
A delete past generation gap 3 still removes the live path and keeps the previous hub bytes under ~/.blf/held/<sha256 of the managed project path>/ (a held copy). blf daemon status lists out-of-sync paths and held copies. start and reload warn and ask you to continue; 0.5.0 does not interview you to pick winners.
Mapping edits
Edit a mapping file by hand, then run blf daemon start (if the daemon is down) or blf daemon reload (if it is already up). Adds apply automatically. Removals print one plan and require confirmation; decline commits nothing. The daemon does not watch mapping files.
For full option details and usage examples, see docs/cli-reference.md.
Documentation
Comprehensive documentation is available in the docs/ directory:
- Documentation Hub - Complete documentation index
- Configuration Reference - Complete configuration documentation
- CLI Reference - Complete command-line interface documentation
- Shell Completion - Tab completion setup for bash, zsh, and fish
- Config Format Guide - Understanding configuration
- Architecture Design - Internal architecture
- Platform Support - Cross-platform compatibility
- Windows Support - Windows-specific guide
- Development Guide - Contributing and development
Important Notes
- Every projection is a regular file or directory inside the target project
- Leftover blf symlinks from older versions become copies on the first daemon catch-up
- Only use in local development environments; do not commit projected copies to Git
- If you move the managed project, restart the daemon so catch-up can rewrite projections
- Shells (
link check,revlink,remove) require a running daemon
Platform Support
Tested on macOS, Linux, and Windows 10. See docs/platform-support.md for details.
Projections are copies, so Windows Developer Mode is not required for ordinary files and directories. Enable it when a directory item contains nested symlinks (venv interpreters). See docs/windows-support.md for path and install notes.
Contributing
Contributions are welcome! See docs/development.md for development setup and guidelines.
How the author uses it
I maintain two managed-project repos with beyond-local-file — one for personal GitHub
projects (viclau-local-files, a private repo), one for company work. They're completely
independent, each with its own config.yml, and the tool doesn't need to know about either.
The company-scoped repo's most involved config entry projects an entire AI-assisted development
environment into a backend project: Kiro hooks for code review, requirement breakdown, and
weekly report generation; .qoder agent definitions, rules, and skills; .vscode settings;
a Taskfile.yml with build and deploy tasks; and a structured local-file/ directory that
AI agents read and write into during development. Those trees are physical copies, including
directory items such as .kiro/hooks, so tools that refuse workspace-escape can read them
inside the target workspace.
The personal repo has a single entry: beyond-local-file itself. The tool manages its own
development environment — a local task tracker, per-release archived changelogs, and an
agentic workspace for drafts and analysis — none of it committed to the main repo.
License
MIT License — see the LICENSE file for details.
Release files for beyond-local-file 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| beyond_local_file-0.5.0.tar.gz | 1.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| beyond_local_file-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.0 MB
Release files / beyond_local_file-0.5.0.tar.gz
| Download URL | beyond_local_file-0.5.0.tar.gz |
|---|---|
| Size | 1.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
28f11c3181cab4e4d622988f4ec164d114970325f14b76d5f993d73103045d95
|
|
BLAKE2b-256 checksum How to use checksums |
188af050dda86acb7e2f519d1d56fdcbf877e86df959a0b3a20fb251be159c94
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency logRelease files / beyond_local_file-0.5.0-py3-none-any.whl
| Download URL | beyond_local_file-0.5.0-py3-none-any.whl |
|---|---|
| Size | 101.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8178c6a555994b5a849d73afa2cb3289d4469b1e11a9fbd064cd475345ecf49b
|
|
BLAKE2b-256 checksum How to use checksums |
67799614942fa3bd5c04e9e2442c0dcfa87a007b74fc19f711e912e428fe2e67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency log