GitLocal is a local folder and git repository viewer for less-technical builders working in today's AI-driven development lifecycle.
When AI agents do most of the code generation, direct hand-editing source files becomes the exception rather than the default. A full IDE can be overkill. GitLocal focuses on the work humans still need to do constantly: browse the codebase, understand structure, read Markdown documents clearly, inspect changes, and make small edits when needed. Editing remains possible, but the product is optimized first for navigation, reading, review, and lightweight intervention.
Everything runs locally, there are no accounts or telemetry, and any clone, fetch, pull, or push action goes through your installed git only when you choose it.
| Dependency | npm package | macOS app |
|---|---|---|
| Node.js | 22+ | Bundled in the app |
| git | 2.22+ | 2.22+ |
GitLocal has two ways to install the same product. The npm package is the mature cross-platform option. The macOS app is newer, easier to launch on a Mac, and currently unsigned.
| Option | Best for | What you get | Tradeoff |
|---|---|---|---|
| npm package | macOS, Windows, and Linux users who are comfortable with a terminal | One-command install, opens in your browser, mature distribution | The terminal process must stay open while GitLocal is running |
| macOS app beta | Mac users who want GitLocal to behave like an app | Homebrew install, opens as GitLocal.app, no terminal needed after install |
Unsigned beta app, so macOS shows security warnings on first launch |
Both options use the same GitLocal server and React viewer for a given release. The Mac app is a native wrapper around the same local app code, not a separate fork.
npm install -g gitlocalOpen a folder or repository:
gitlocal .GitLocal starts a local server, opens your default browser, and prints the local URL:
gitlocal listening on http://localhost:54321
Keep that terminal window open while you use GitLocal. Press Ctrl+C when you want to stop it.
You can also run GitLocal without installing it globally:
npx gitlocalInstall with Homebrew:
brew tap ehud-am/gitlocal
brew install --cask gitlocalThen open GitLocal.app from your Applications folder.
The macOS app starts the same local GitLocal service in the background for the app session, shows the viewer in an embedded WebKit window, and stops the service when the app quits. You do not need to keep a terminal open.
Unsigned beta notice: the current Mac app is not signed or notarized with an Apple Developer ID. macOS will show security warnings the first time you open it. After installing, approve the app with:
xattr -dr com.apple.quarantine /Applications/GitLocal.appThen open GitLocal.app normally. Future signed releases should remove this extra step.
Upgrade the app:
brew update
brew upgrade --cask gitlocalUninstall the app:
brew uninstall --cask gitlocalUse the npm package if you want the most mature and portable install path, especially on Windows or Linux.
Use the macOS app beta if you are on a Mac and want a normal app experience without a running terminal. It is aligned with the npm version, but the first-launch security warnings are expected until the app is signed and notarized.
- Browse local projects with a lazy-loading file tree for regular folders and git repositories.
- Read Markdown clearly in the normal repository viewer with GitHub-like rendering, relative links, heading anchors, rendered find highlights, and share/copy actions.
- Start from useful repository context with a root dashboard for status, key documents, recent files, recently changed files, and raw directory browsing.
- Review background changes with active-file refresh/deletion notices and a changed-files panel for modified, deleted, untracked, generated, and local-only paths.
- Search intentionally with a separate repository search surface, current-folder and Markdown-focused scopes, generated/local controls, result counts, and file-level find inside the current file.
- Understand git state quickly through a plain-language repository summary with branch, remote, sync, and local-change context backed by technical badges.
- Make small edits safely by creating, updating, and deleting files directly from the viewer with dirty-navigation warnings and revision-token conflict protection.
- Manage folders by creating child folders or deleting subfolders with typed confirmation and impact counts.
- Switch branches safely with commit or discard confirmation when the working tree is dirty.
- Manage local git identity by saving repo-local
user.name,user.email, and optional SSH key settings. - Share rendered Markdown through print, Save as PDF, local email/share flows, copy, and download fallbacks.
- Stay local-first because browsing, editing, and git actions run against your local filesystem and installed
git; there are no accounts or telemetry.
GitLocal supports light and dark themes. The macOS app also supports native menu and keyboard shortcuts for standard editing commands, preview-scoped Find, panel-scoped Select All, and Refresh.
- If
brewis not found, install Homebrew first from the official Homebrew site. - If installation reports a checksum mismatch, run
brew updateand retry. Do not bypass checksum verification. - If macOS blocks the beta app, run
xattr -dr com.apple.quarantine /Applications/GitLocal.app, then launch it again. - If your macOS version or processor architecture is unsupported, use the npm package until a compatible native artifact is available.
- If app launch fails, the npm browser workflow remains available as a fallback.
git clone https://github.com/ehud-am/gitlocal.git
cd gitlocal
npm ci
npm --prefix ui ci
npm run buildRun the built CLI from the repository root:
# Open the current repository
node dist/cli.js .
# Open a specific repository
node dist/cli.js ~/projects/my-app
# Open the folder picker
node dist/cli.js- Node.js 22+
# Terminal 1: backend with auto-restart on changes
npm run dev:server
# Terminal 2: Vite dev server for the frontend
npm run dev:uiUse both commands together during development:
npm run dev:serverstarts the backend in watch modenpm run dev:uistarts the Vite frontend with hot reload
npm testRuns backend tests (Vitest + coverage) and frontend tests. All source files must maintain ≥90% branch coverage per file. Frontend component tests also include automated accessibility checks for key browsing surfaces.
npm run test:servernpm run buildBuilds the React frontend and compiles the Node.js CLI into dist/cli.js.
On macOS with Xcode installed, build the shared GitLocal app and the native wrapper:
packaging/macos/release/package-app.shThis runs the normal npm build, builds GitLocal.app, bundles the server/UI assets, copies the local Node runtime into the app bundle, and creates a local unsigned artifact in packaging/macos/release/artifacts/.
List the generated artifacts:
ls packaging/macos/release/artifacts/Or open the artifact folder in Finder:
open packaging/macos/release/artifacts/Launch the locally built app:
open native/macos/build/Build/Products/Release/GitLocal.appValidate the local app bundle:
packaging/macos/release/test-package.shValidate the local Homebrew cask against the generated artifact:
packaging/macos/cask/test-install-cask.sh \
packaging/macos/cask/gitlocal.rb \
packaging/macos/release/artifacts/GitLocal-$(node -p "require('./package.json').version")-macos.zipThe local app is unsigned unless you run the signing/notarization release flow. It is intended for development validation, not public distribution.
npm run verifyRuns tests, builds, and dependency audits for both the root package and the UI package.
GitLocal is a Node.js CLI that serves a React SPA:
gitlocal/
├── src/
│ ├── cli.ts — entry point: arg parsing, server start, browser/app-mode launch
│ ├── server.ts — Hono app, route registration, static serving + SPA fallback
│ ├── git/
│ │ ├── repo.ts — repo metadata, branch helpers, deferred git context, README lookup, repository-local identity, and file sync state
│ │ ├── identity-settings.ts — SSH private key path expansion, discovery, and validation
│ │ └── tree.ts — listDir (git ls-tree wrapper, sorted dirs-first)
│ ├── services/
│ │ └── repo-watch.ts — working-tree revision and sync status snapshots for the UI
│ └── handlers/
│ ├── repo.ts — /api/info, /api/readme, /api/branches, /api/commits, /api/repo/open, and git-specific repo actions
│ ├── file.ts — /api/tree and /api/file
│ ├── folder.ts — /api/folder browsing, setup, create, and delete actions
│ ├── sync.ts — /api/sync
│ └── search.ts — /api/search
└── ui/src/
├── App.tsx — layout, repo sync polling, deferred git context, dialogs, README auto-load, picker mode
├── components/
│ ├── FileTree/ — lazy expand/collapse tree
│ ├── Breadcrumb/ — path navigation
│ ├── ContentPanel/ — Markdown, code, image, binary rendering, and local file editing
│ ├── RepoContext/ — branch switcher, sync summary, repo metadata, and identity editing
│ └── Picker/ — PickerPage table browser with setup actions
└── services/api.ts — typed fetch wrappers for all endpoints
Optional macOS native app distribution:
native/macos/ — Swift/AppKit/WebKit wrapper for GitLocal.app
packaging/macos/ — Homebrew cask, package, checksum, and release helpers
Key design decisions:
- No external runtime dependencies beyond Node.js for the product core — all git operations shell out to the local
gitbinary viachild_process.spawnSync - Primary npm toolchain — build, test, and install the cross-platform package via
npm; macOS app packaging adds scoped Swift and shell/Ruby release helpers outside the npm package - Shared app code across distributions — npm and Homebrew use the same TypeScript server and React UI; the Mac wrapper only owns native windowing, service lifecycle, and packaging
- Hash-based routing —
HashRoutermeans the server only needs to serveindex.htmlfor all non-asset routes - Local-first editing and repo awareness — GitLocal can create, update, and delete files in folder roots and repository working trees while using git commands for branch, repository metadata, changed files, and conflict-safe revision checks
All endpoints are served under /api/:
| Endpoint | Description |
|---|---|
GET /api/info |
Active root metadata (name, branch, isGitRepo, pickerMode) |
GET /api/git/context |
Deferred git identity and selected remote context for the active repository |
GET /api/readme |
Detects and returns the README filename for the current repo |
GET /api/branches |
List of branches with isCurrent flag |
POST /api/branches/switch |
Switch branches with dirty-tree confirmation flows |
POST /api/git/commit |
Compatibility route for creating a local commit; not exposed in the current repository UI |
POST /api/git/sync |
Compatibility route for remote sync; not exposed in the current repository UI |
PUT /api/git/identity |
Save repository-local user.name, user.email, and optional SSH command behavior in local git config |
GET /api/git/identity/ssh-keys |
List valid SSH private keys from the user's conventional SSH folder |
POST /api/git/identity/ssh-key/validate |
Validate an arbitrary SSH private key path before saving it |
GET /api/tree?path=&branch= |
Directory listing (dirs first, alphabetical) |
GET /api/file?path=&branch= |
File content with type and language detection |
GET /api/repo/summary?branch= |
Plain-language repository status, key documents, and local/generated visibility context |
GET /api/repo/changes?branch=&includeGeneratedLocal= |
Changed-file review items with status, availability, and local/generated labels |
GET /api/repo/navigation-hints?branch=&includeRecent=&includeGeneratedLocal= |
Key documents and navigation shortcuts for the root dashboard |
GET /api/search?query=&branch=&mode=&caseSensitive=&rootPath=&contentKinds=&trackedMode=&limit=&cursor= |
Scoped repository search across file names, contents, Markdown, current folder, and generated/local modes |
POST /api/file |
Create a new file in the current folder root or repository working tree |
PUT /api/file |
Update an existing file using its revision token, returning a conflict if the file changed on disk |
DELETE /api/file |
Delete an existing file using its revision token |
POST /api/folder |
Create a direct child folder in the current folder root or repository working tree |
GET /api/folder/delete-preview?path= |
Preview recursive folder deletion impact before confirmation |
DELETE /api/folder |
Delete a subfolder after exact typed-name confirmation and preview-impact validation |
GET /api/commits?branch=&limit= |
Recent commits (default 10, max 100) |
GET /api/sync?path=&branch= |
Working-tree and upstream sync summary for the current path and branch |
GET /api/folder/browse?path= |
Folder-picker directory listing with files, folders, and git-repo detection |
POST /api/repo/open |
Open a local folder or repository root from the picker UI (body: {"path":"..."}) |
POST /api/folder/create-child |
Create a child folder from the picker setup flow |
POST /api/folder/init-repository |
Initialize git in the selected picker folder |
POST /api/folder/clone-repository |
Clone a repository into a child folder from the picker |
POST /api/repo/parent-folder |
Leave the current repository view and browse its parent folder |
MIT — see LICENSE.
Issues and pull requests are welcome. Start with CONTRIBUTING.md and follow the Code of Conduct. This project follows the GitLocal constitution — all changes must maintain ≥90% branch coverage per file.