{"id":47750885,"url":"https://github.com/ehud-am/gitlocal","last_synced_at":"2026-06-09T03:01:36.459Z","repository":{"id":348411909,"uuid":"1194930373","full_name":"ehud-am/gitlocal","owner":"ehud-am","description":"Local git repository viewer. GitHub-like browsing experience without leaving your machine","archived":false,"fork":false,"pushed_at":"2026-06-02T06:23:23.000Z","size":1044,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-02T07:25:03.552Z","etag":null,"topics":["developer-tools","git","github","gitlab"],"latest_commit_sha":null,"homepage":"https://github.com/ehud-am/gitlocal","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ehud-am.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-29T01:57:47.000Z","updated_at":"2026-06-02T06:23:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ehud-am/gitlocal","commit_stats":null,"previous_names":["ehud-am/gitlocal"],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/ehud-am/gitlocal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehud-am%2Fgitlocal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehud-am%2Fgitlocal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehud-am%2Fgitlocal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehud-am%2Fgitlocal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ehud-am","download_url":"https://codeload.github.com/ehud-am/gitlocal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehud-am%2Fgitlocal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34089329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["developer-tools","git","github","gitlab"],"created_at":"2026-04-03T03:05:27.711Z","updated_at":"2026-06-09T03:01:36.453Z","avatar_url":"https://github.com/ehud-am.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitLocal\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/ehud-am/gitlocal/main/ui/public/gitlocal-logo.svg\" alt=\"GitLocal icon\" width=\"96\" height=\"96\"\u003e\n\u003c/p\u003e\n\n[![CI](https://github.com/ehud-am/gitlocal/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ehud-am/gitlocal/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/npm/v/gitlocal)](https://www.npmjs.com/package/gitlocal)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org)\n\nGitLocal is a local folder and git repository viewer for less-technical builders working in today's AI-driven development lifecycle.\n\nWhen 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.\n\nEverything 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.\n\n---\n\n## Requirements\n\n| Dependency | npm package | macOS app |\n|-----------|-------------|-----------|\n| Node.js | 22+ | Bundled in the app |\n| git | 2.22+ | 2.22+ |\n\n---\n\n## Choose an Install\n\nGitLocal 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.\n\n| Option | Best for | What you get | Tradeoff |\n|--------|----------|--------------|----------|\n| 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 |\n| 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 |\n\nBoth 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.\n\n### Option 1: npm Package\n\n```bash\nnpm install -g gitlocal\n```\n\nOpen a folder or repository:\n\n```bash\ngitlocal .\n```\n\nGitLocal starts a local server, opens your default browser, and prints the local URL:\n\n```text\ngitlocal listening on http://localhost:54321\n```\n\nKeep that terminal window open while you use GitLocal. Press **Ctrl+C** when you want to stop it.\n\nYou can also run GitLocal without installing it globally:\n\n```bash\nnpx gitlocal\n```\n\n### Option 2: macOS App Beta\n\nInstall with Homebrew:\n\n```bash\nbrew tap ehud-am/gitlocal\nbrew install --cask gitlocal\n```\n\nThen open `GitLocal.app` from your Applications folder.\n\nThe 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.\n\n**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:\n\n```bash\nxattr -dr com.apple.quarantine /Applications/GitLocal.app\n```\n\nThen open `GitLocal.app` normally. Future signed releases should remove this extra step.\n\nUpgrade the app:\n\n```bash\nbrew update\nbrew upgrade --cask gitlocal\n```\n\nUninstall the app:\n\n```bash\nbrew uninstall --cask gitlocal\n```\n\n### Which One Should I Use?\n\nUse the npm package if you want the most mature and portable install path, especially on Windows or Linux.\n\nUse 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.\n\n---\n\n## What GitLocal Helps With\n\n- **Browse local projects** with a lazy-loading file tree for regular folders and git repositories.\n- **Read Markdown clearly** with GitHub-like rendering for READMEs, specs, plans, tables, task lists, and code blocks.\n- **Review code with context** using line numbers, branch information, local path, remote details, and file sync state.\n- **Search intentionally** with repository search from the header and file-level find inside the current file.\n- **Make small edits** by creating, updating, and deleting files directly from the viewer.\n- **Manage folders** by creating child folders or deleting subfolders with typed confirmation and impact counts.\n- **Switch branches safely** with commit or discard confirmation when the working tree is dirty.\n- **Manage local git identity** by saving repo-local `user.name`, `user.email`, and optional SSH key settings.\n- **Share rendered Markdown** through print, Save as PDF, local email/share flows, copy, and download fallbacks.\n- **Stay local-first** because browsing, editing, and git actions run against your local filesystem and installed `git`; there are no accounts or telemetry.\n\nGitLocal 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.\n\n---\n\n## Homebrew Troubleshooting\n\n- If `brew` is not found, install Homebrew first from the official Homebrew site.\n- If installation reports a checksum mismatch, run `brew update` and retry. Do not bypass checksum verification.\n- If macOS blocks the beta app, run `xattr -dr com.apple.quarantine /Applications/GitLocal.app`, then launch it again.\n- If your macOS version or processor architecture is unsupported, use the npm package until a compatible native artifact is available.\n- If app launch fails, the npm browser workflow remains available as a fallback.\n\n---\n\n## From Source\n\n```bash\ngit clone https://github.com/ehud-am/gitlocal.git\ncd gitlocal\nnpm ci\nnpm --prefix ui ci\nnpm run build\n```\n\nRun the built CLI from the repository root:\n\n```bash\n# Open the current repository\nnode dist/cli.js .\n\n# Open a specific repository\nnode dist/cli.js ~/projects/my-app\n\n# Open the folder picker\nnode dist/cli.js\n```\n\n---\n\n## Development\n\n### Prerequisites\n\n- Node.js 22+\n\n### Run in dev mode\n\n```bash\n# Terminal 1: backend with auto-restart on changes\nnpm run dev:server\n\n# Terminal 2: Vite dev server for the frontend\nnpm run dev:ui\n```\n\nUse both commands together during development:\n\n- `npm run dev:server` starts the backend in watch mode\n- `npm run dev:ui` starts the Vite frontend with hot reload\n\n### Run tests\n\n```bash\nnpm test\n```\n\nRuns backend tests (Vitest + coverage) and frontend tests. All source files must maintain **≥90% branch coverage** per file.\nFrontend component tests also include automated accessibility checks for key browsing surfaces.\n\n### Backend tests only\n\n```bash\nnpm run test:server\n```\n\n### Build\n\n```bash\nnpm run build\n```\n\nBuilds the React frontend and compiles the Node.js CLI into `dist/cli.js`.\n\n### Build and run the macOS native app from source\n\nOn macOS with Xcode installed, build the shared GitLocal app and the native wrapper:\n\n```bash\npackaging/macos/release/package-app.sh\n```\n\nThis 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/`.\n\nList the generated artifacts:\n\n```bash\nls packaging/macos/release/artifacts/\n```\n\nOr open the artifact folder in Finder:\n\n```bash\nopen packaging/macos/release/artifacts/\n```\n\nLaunch the locally built app:\n\n```bash\nopen native/macos/build/Build/Products/Release/GitLocal.app\n```\n\nValidate the local app bundle:\n\n```bash\npackaging/macos/release/test-package.sh\n```\n\nValidate the local Homebrew cask against the generated artifact:\n\n```bash\npackaging/macos/cask/test-install-cask.sh \\\n  packaging/macos/cask/gitlocal.rb \\\n  packaging/macos/release/artifacts/GitLocal-$(node -p \"require('./package.json').version\")-macos.zip\n```\n\nThe local app is unsigned unless you run the signing/notarization release flow. It is intended for development validation, not public distribution.\n\n### Full verification\n\n```bash\nnpm run verify\n```\n\nRuns tests, builds, and dependency audits for both the root package and the UI package.\n\n---\n\n## Architecture\n\nGitLocal is a Node.js CLI that serves a React SPA:\n\n```\ngitlocal/\n├── src/\n│   ├── cli.ts           — entry point: arg parsing, server start, browser/app-mode launch\n│   ├── server.ts        — Hono app, route registration, static serving + SPA fallback\n│   ├── git/\n│   │   ├── repo.ts      — repo metadata, branch helpers, deferred git context, README lookup, repository-local identity, and file sync state\n│   │   ├── identity-settings.ts — SSH private key path expansion, discovery, and validation\n│   │   └── tree.ts      — listDir (git ls-tree wrapper, sorted dirs-first)\n│   ├── services/\n│   │   └── repo-watch.ts — working-tree revision and sync status snapshots for the UI\n│   └── handlers/\n│       ├── repo.ts      — /api/info, /api/readme, /api/branches, /api/commits, /api/repo/open, and git-specific repo actions\n│       ├── file.ts      — /api/tree and /api/file\n│       ├── folder.ts    — /api/folder browsing, setup, create, and delete actions\n│       ├── sync.ts      — /api/sync\n│       └── search.ts    — /api/search\n└── ui/src/\n    ├── App.tsx                      — layout, repo sync polling, deferred git context, dialogs, README auto-load, picker mode\n    ├── components/\n    │   ├── FileTree/                — lazy expand/collapse tree\n    │   ├── Breadcrumb/              — path navigation\n    │   ├── ContentPanel/            — Markdown, code, image, binary rendering, and local file editing\n    │   ├── RepoContext/             — branch switcher, sync summary, repo metadata, and identity editing\n    │   └── Picker/                  — PickerPage table browser with setup actions\n    └── services/api.ts              — typed fetch wrappers for all endpoints\n```\n\nOptional macOS native app distribution:\n\n```\nnative/macos/           — Swift/AppKit/WebKit wrapper for GitLocal.app\npackaging/macos/        — Homebrew cask, package, checksum, and release helpers\n```\n\n**Key design decisions:**\n\n- **No external runtime dependencies beyond Node.js for the product core** — all git operations shell out to the local `git` binary via `child_process.spawnSync`\n- **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\n- **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\n- **Hash-based routing** — `HashRouter` means the server only needs to serve `index.html` for all non-asset routes\n- **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 and repository metadata\n\n---\n\n## API\n\nAll endpoints are served under `/api/`:\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /api/info` | Active root metadata (name, branch, isGitRepo, pickerMode) |\n| `GET /api/git/context` | Deferred git identity and selected remote context for the active repository |\n| `GET /api/readme` | Detects and returns the README filename for the current repo |\n| `GET /api/branches` | List of branches with `isCurrent` flag |\n| `POST /api/branches/switch` | Switch branches with dirty-tree confirmation flows |\n| `POST /api/git/commit` | Compatibility route for creating a local commit; not exposed in the current repository UI |\n| `POST /api/git/sync` | Compatibility route for remote sync; not exposed in the current repository UI |\n| `PUT /api/git/identity` | Save repository-local `user.name`, `user.email`, and optional SSH command behavior in local git config |\n| `GET /api/git/identity/ssh-keys` | List valid SSH private keys from the user's conventional SSH folder |\n| `POST /api/git/identity/ssh-key/validate` | Validate an arbitrary SSH private key path before saving it |\n| `GET /api/tree?path=\u0026branch=` | Directory listing (dirs first, alphabetical) |\n| `GET /api/file?path=\u0026branch=` | File content with type and language detection |\n| `GET /api/search?query=\u0026branch=\u0026mode=\u0026caseSensitive=` | Repository search across file names, file contents, or both |\n| `POST /api/file` | Create a new file in the current folder root or repository working tree |\n| `PUT /api/file` | Update an existing file using its revision token |\n| `DELETE /api/file` | Delete an existing file using its revision token |\n| `POST /api/folder` | Create a direct child folder in the current folder root or repository working tree |\n| `GET /api/folder/delete-preview?path=` | Preview recursive folder deletion impact before confirmation |\n| `DELETE /api/folder` | Delete a subfolder after exact typed-name confirmation and preview-impact validation |\n| `GET /api/commits?branch=\u0026limit=` | Recent commits (default 10, max 100) |\n| `GET /api/sync?path=\u0026branch=` | Working-tree and upstream sync summary for the current path and branch |\n| `GET /api/folder/browse?path=` | Folder-picker directory listing with files, folders, and git-repo detection |\n| `POST /api/repo/open` | Open a local folder or repository root from the picker UI (body: `{\"path\":\"...\"}`) |\n| `POST /api/folder/create-child` | Create a child folder from the picker setup flow |\n| `POST /api/folder/init-repository` | Initialize git in the selected picker folder |\n| `POST /api/folder/clone-repository` | Clone a repository into a child folder from the picker |\n| `POST /api/repo/parent-folder` | Leave the current repository view and browse its parent folder |\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n---\n\n## Contributing\n\nIssues and pull requests are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md) and follow the [Code of Conduct](CODE_OF_CONDUCT.md). This project follows the [GitLocal constitution](.specify/memory/constitution.md) — all changes must maintain ≥90% branch coverage per file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehud-am%2Fgitlocal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fehud-am%2Fgitlocal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehud-am%2Fgitlocal/lists"}