Enable AI to browse Git repos like local folders.
GitFs is a small read-only, git-backed filesystem adapter designed for AI tooling and just-bash.
It preloads a repo manifest for a specific commit, exposes the tree as normal filesystem paths, loads file blobs lazily, and caches both manifests and blobs by commit SHA.
- provider-agnostic
GitRepoFilesystem - in-memory
GitFsCache - persistent disk-backed
PersistentGitFsCache - subtree mounting (
docs/content/encan become/) - lazy blob reads
- symlink support
- GitHub provider using commit/tree/blob APIs
- GitLab provider using commit/tree/blob APIs
- Gitea provider using commit/tree/blob APIs
- TTL-aware in-memory and persistent cache pruning
- a read-only API shaped like the
just-bashfilesystem interface - a tiny local browsing CLI
just-bash works best when content looks like a normal folder tree.
Instead of teaching an agent how to clone a repo, call a host-specific API, and guess file paths, GitFs lets the agent use predictable paths with filesystem operations like:
lsfindrgcat- glob traversal
Install the published package with:
npm install @taowang1993/gitfsNote: the unscoped
gitfspackage on npm is a different project. Use@taowang1993/gitfsfor this library.
GitFs itself uses only Node.js built-ins at runtime.
This repo now uses TypeScript source files and runs them directly on Node.js 22+ via --experimental-strip-types.
If you are working in this repo directly:
npm install
npm run typecheck
npm run typecheck:tests
npm test
npm run buildThis project now ships compiled output in dist/ for non-Node-22 consumers.
The build emits:
- compiled JavaScript
.d.tstype declarations- declaration maps
- source maps
That means:
- repo development can run
.tssource directly on Node 22+ - package consumers can use compiled
dist/*.js+dist/*.d.ts
The repo also includes a prepare script so installs from a git checkout can build dist/ automatically after dependencies are installed.
You can run a full local verification with:
npm run checkAnd a release-oriented verification with:
npm run release:checkIf you want to mount GitFs inside just-bash, install just-bash in the application or example package that will run the integration.
The core GitFs package does not need just-bash as a hard dependency.
Typical app-level install:
npm install just-bashIf GitFs is consumed as a local workspace package, install just-bash in the consuming app and import GitFs from your workspace/local package.
import {
GitHubProvider,
GitRepoFilesystem,
PersistentGitFsCache,
} from "@taowang1993/gitfs";
const provider = new GitHubProvider({
owner: "vercel",
repo: "ai",
token: process.env.GITHUB_TOKEN,
});
const fs = await GitRepoFilesystem.create({
provider,
ref: "main",
root: "content/docs",
cache: new PersistentGitFsCache({ dir: ".gitfs-cache" }),
});
console.log(fs.info());
console.log(await fs.readdir("/"));
console.log(await fs.readFile("/index.mdx"));import {
GitHubProvider,
GitRepoFilesystem,
PersistentGitFsCache,
} from "./src/index.ts";
const provider = new GitHubProvider({
owner: "vercel",
repo: "ai",
token: process.env.GITHUB_TOKEN,
});
const fs = await GitRepoFilesystem.create({
provider,
ref: "main",
root: "content/docs",
cache: new PersistentGitFsCache({ dir: ".gitfs-cache" }),
});GitRepoFilesystem is deliberately duck-typed to match the just-bash filesystem contract.
just-bash is optional. You only install it if you want the mounted bash workflow.
If your app wants the GitFs + just-bash flow, install just-bash in that app:
npm install just-bashThis repo includes an agent skill that helps coding agents wire GitFs + just-bash into an AI agent:
skills/gitfs-just-bash-integration/SKILL.md
Use that skill when you want an agent to scaffold or document:
- provider setup
- cache setup
/repo+/workspacemount layout- just-bash integration code
- tests and docs for the integration
You do not need to install just-bash just to use:
GitRepoFilesystem- provider adapters
- cache layers
- the GitFs CLI
- Create a Git provider.
- Create a
GitRepoFilesystemsnapshot. - Mount it at a read-only location such as
/repo. - Mount a separate writable workspace such as
/workspace. - Start
just-bashwithcwd: "/workspace". - Let the agent search
/repoand write artifacts into/workspace.
import { Bash, InMemoryFs, MountableFs } from "just-bash";
import { ReadWriteFs } from "just-bash/fs/read-write-fs";
import {
GitHubProvider,
GitRepoFilesystem,
PersistentGitFsCache,
} from "@taowang1993/gitfs";
const provider = new GitHubProvider({
owner: "my-org",
repo: "knowledge-base",
token: process.env.GITHUB_TOKEN,
});
const repoFs = await GitRepoFilesystem.create({
provider,
ref: "main",
root: "docs",
cache: new PersistentGitFsCache({ dir: ".gitfs-cache" }),
});
const fs = new MountableFs({ base: new InMemoryFs() });
fs.mount("/repo", repoFs);
fs.mount("/workspace", new ReadWriteFs({ root: "/tmp/agent-workspace" }));
const bash = new Bash({ fs, cwd: "/workspace" });
await bash.exec("find /repo -name '*.md' | head -20");
await bash.exec("cp /repo/getting-started.md /workspace/getting-started.md || true");
await bash.exec("rg 'TODO|FIXME' /repo || true");GitFs is read-only by design.
That means a good default layout is:
/repo→ GitFs snapshot of the remote repo/workspace→ writable area for notes, patches, generated files, and copied artifacts
This matches the guidance from just-bash: keep trusted/runtime code separate from guest-writable workspace paths.
A provider returns git tree entries for a resolved commit SHA. GitFs builds an in-memory manifest with normalized POSIX paths.
File and symlink contents are fetched on first read via provider.getBlob().
- manifests:
provider + commit SHA + subtree - blobs:
provider + commit SHA + blob SHA
Branch names are only used to resolve a commit. Caching is keyed by the resolved commit SHA.
GitHubProvider uses:
GET /repos/:owner/:repo/commits/:refGET /repos/:owner/:repo/git/trees/:treeSha?recursive=1GET /repos/:owner/:repo/git/blobs/:sha
If GitHub returns a truncated recursive tree, the provider throws instead of building an incomplete manifest.
GitLabProvider uses:
GET /projects/:project/repository/commits/:refGET /projects/:project/repository/tree?ref=:commitSha&recursive=trueGET /projects/:project/repository/blobs/:sha/raw
The GitLab tree loader follows X-Next-Page headers so paginated trees are merged into a single manifest.
GiteaProvider uses:
GET /repos/:owner/:repo/commits/:refGET /repos/:owner/:repo/git/trees/:treeSha?recursive=1GET /repos/:owner/:repo/git/blobs/:sha
PersistentGitFsCache stores manifests and blobs on disk so later processes can reuse commit snapshots without re-fetching the tree or blob data.
import { PersistentGitFsCache } from "./src/index.ts";
const cache = new PersistentGitFsCache({ dir: ".gitfs-cache" });Both cache implementations also support TTL-based expiry and pruning:
import { GitFsCache, PersistentGitFsCache } from "./src/index.ts";
const memoryCache = new GitFsCache({ manifestTtlMs: 60_000, blobTtlMs: 300_000 });
await memoryCache.pruneExpired();
const diskCache = new PersistentGitFsCache({
dir: ".gitfs-cache",
manifestTtlMs: 60_000,
blobTtlMs: 300_000,
});
await diskCache.pruneExpired();A tiny CLI is included for local snapshot browsing.
npm run cli -- \
--provider github \
--owner vercel \
--repo ai \
--ref main \
--root content/docs \
ls /npm run build
npm run cli:dist -- \
--provider github \
--owner vercel \
--repo ai \
--ref main \
--root content/docs \
ls /Other commands:
infols [path]cat <path>stat <path>paths [needle]find [needle]readlink <path>
This repo includes GitHub Actions workflows for:
- CI verification on pushes and pull requests
- release checks on tags / manual dispatch
- tarball creation
- npm publish via trusted publishing from GitHub Actions
- GitHub release creation from version tags
Based on npm's documentation, this project should publish with trusted publishing, not a long-lived npm token.
Why:
- it bypasses interactive 2FA correctly
- it is npm's recommended path for CI/CD publishing
- it uses GitHub OIDC instead of a long-lived write token
- npm automatically generates provenance for public packages from public GitHub repos
Configure npm trusted publishing for @taowang1993/gitfs with these values:
- Organization or user:
taowang1993 - Repository:
gitfs - Workflow filename:
release.yml - Environment name: leave blank unless you add a GitHub environment later
The workflow is already configured for the npm docs requirements:
- GitHub-hosted runner
permissions.id-token: write- tag-triggered publish flow
npm publish --access public
After trusted publishing is configured on npm, no NPM_TOKEN secret is needed for publishing this package.
npm's trusted publishing docs describe configuration from the package's npm settings page. If npm does not let you configure trusted publishing for an unpublished package yet, we may need a one-time manual/bootstrap publish first.
If that happens, the fallback is:
- create a granular npm access token
- enable bypass 2FA
- use it only for the initial publish
- then immediately switch the package to trusted publishing and revoke the token
Use npm versioning so git tags and package versions stay aligned:
npm run version:patch
# or
npm run version:minor
# or
npm run version:majorThen push the commit and tag:
git push origin main --follow-tagsThe release workflow is triggered by v* tags and can publish to npm plus create a GitHub release.
After trusted publishing is configured on npm, publishing should happen from GitHub Actions without any npm token secret.
If a one-time bootstrap publish is required before trusted publishing can be configured, publish locally only after authenticating successfully:
npm login
npm whoami
npm publish --access publicA real just-bash demo app is included at:
examples/just-bash-demo/app.tsexamples/just-bash-demo/run-with-just-bash.mts
Run it after installing the example's dependencies and setting the required env vars:
cd examples/just-bash-demo
npm install
GITFS_GITHUB_OWNER=vercel \
GITFS_GITHUB_REPO=ai \
GITHUB_TOKEN=... \
npm startThe core GitFs library still does not require just-bash; only the demo package/app that wants the integration does.
- read-only only
- no GitBucket adapter yet
- submodules are skipped and reported as manifest warnings
- no provider-specific auth helpers beyond token/header support
If the repo already exists on disk, the simplest path is still just-bash + OverlayFs.
GitFs is mainly for the remote-only case where you still want filesystem-shaped browsing.