Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 41 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
# one repository); without it the job leaves a notice.
# summary one table of results; fails the run when anything failed.
#
# workflow_dispatch builds and signs the desktop apps (version 0.0.0) as
# workflow artifacts without releasing anything: a way to check signing.
# workflow_dispatch, run by hand:
# tag empty builds and signs the desktop apps (version 0.0.0) as
# workflow artifacts and releases nothing: a signing check.
# tag v2.4.0 finishes that existing DRAFT release with this workflow:
# builds at the tag, uploads, publishes, updates the tap.
# For a release whose run failed after the draft was made
# (a re-run would replay the old workflow file).
#
# The web app deploys separately (pages.yml), so a failed release never
# takes the site down.
Expand All @@ -29,6 +34,11 @@ on:
push:
branches: [main]
workflow_dispatch:
inputs:
tag:
description: "Existing draft release to finish (e.g. v2.4.0). Empty: build and sign only."
required: false
default: ""

permissions:
contents: read
Expand All @@ -40,7 +50,6 @@ concurrency:
jobs:
release:
name: Version and draft release
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -52,17 +61,21 @@ jobs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
if: github.event_name == 'push'
with:
fetch-depth: 0

- uses: actions/setup-node@v4
if: github.event_name == 'push'
with:
node-version: 20
cache: npm

- run: npm ci
- if: github.event_name == 'push'
run: npm ci

- name: semantic-release
if: github.event_name == 'push'
run: npx semantic-release
env:
GITHUB_TOKEN: ${{ github.token }}
Expand All @@ -72,7 +85,28 @@ jobs:
# tag there means this push did not warrant a release.
- name: The new tag
id: tag
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG: ${{ inputs.tag }}
run: |
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
if [ -z "$INPUT_TAG" ]; then
echo "Build and sign only; nothing is released."
echo "published=false" >> "$GITHUB_OUTPUT"
exit 0
fi
[[ "$INPUT_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "::error::tag must look like v1.2.3"; exit 1; }
draft="$(gh release view "$INPUT_TAG" --repo "$GITHUB_REPOSITORY" --json isDraft --jq .isDraft)" \
|| { echo "::error::No release $INPUT_TAG"; exit 1; }
[ "$draft" = "true" ] || { echo "::error::$INPUT_TAG is already published; only a draft can be finished"; exit 1; }
{
echo "published=true"
echo "tag=$INPUT_TAG"
echo "version=${INPUT_TAG#v}"
} >> "$GITHUB_OUTPUT"
echo "Finishing the draft release $INPUT_TAG"
exit 0
fi
tag="$(git tag --points-at HEAD --list 'v*' | head -n1)"
if [ -z "$tag" ]; then
echo "No release for this push."
Expand All @@ -89,7 +123,7 @@ jobs:
desktop:
name: Desktop (${{ matrix.name }})
needs: release
if: ${{ !cancelled() && (github.event_name == 'workflow_dispatch' || needs.release.outputs.published == 'true') }}
if: github.event_name == 'workflow_dispatch' || needs.release.outputs.published == 'true'
runs-on: ${{ matrix.os }}
environment: release
permissions:
Expand Down Expand Up @@ -232,7 +266,8 @@ jobs:
archs="$(lipo -archs "$app/Contents/MacOS/gitcontext-desktop")"
case "$archs" in *x86_64*arm64*|*arm64*x86_64*) echo "Architectures: $archs" ;; *) echo "::error::Not universal: $archs"; exit 1 ;; esac
# Only system libraries: a Homebrew dylib would not exist on users' Macs.
if otool -L "$app/Contents/MacOS/gitcontext-desktop" | tail -n +2 | grep -v -E '^\s*(/System/|/usr/lib/)'; then
# Library lines are indented; unindented lines are file/architecture headers.
if otool -L "$app/Contents/MacOS/gitcontext-desktop" | grep -E '^[[:space:]]' | grep -v -E '^[[:space:]]*(/System/|/usr/lib/)'; then
echo "::error::The binary links a library outside the OS"; exit 1
fi

Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@
</p>

<p align="center">
<a href="https://kccarlos.github.io/gitcontext/"><strong>Try it in your browser</strong></a> ·
<a href="#install">Install</a> ·
<a href="#install"><strong>Install the desktop app</strong></a> ·
<a href="https://kccarlos.github.io/gitcontext/">Try it in your browser</a> ·
<a href="#how-it-works">How it works</a>
</p>

<p align="center">
<img src="docs/screenshots/01-diff-overview.png" alt="GitContext comparing two branches: file tree with added, modified and removed files, prompt template, model picker and token usage" width="900">
<img src="docs/screenshots/01-desktop-overview.png" alt="GitContext desktop app comparing two branches: file tree with added, modified and removed files, selected files with per-file token counts, and the total against the model's context window" width="900">
</p>

---

**GitContext** turns the part of a repository you care about into one clean prompt for ChatGPT, Claude or any other LLM. Pick two branches, tick the files, and copy a single block with the file tree, the diffs and your instructions, with a live token count against the model's context window.

Everything runs on your machine. There is no server and no upload: the desktop app reads Git through native Rust (`libgit2`), and the web app runs Git in your browser with `isomorphic-git`.
Everything runs on your machine. There is no server and no upload: the desktop app reads Git through native Rust (`libgit2`). A web version runs Git in your browser with `isomorphic-git` if you want to try it before installing.

It is similar to Repomix or GitIngest, with a few differences:

Expand All @@ -41,6 +41,8 @@ It is similar to Repomix or GitIngest, with a few differences:

## Install

The **desktop app is the recommended way** to use GitContext: native Git through `libgit2`, direct access to your folders, saved workspaces, and no browser limits on large repositories.

### macOS (Homebrew)

```sh
Expand All @@ -59,23 +61,21 @@ The cask installs the same signed and notarized universal DMG (Apple silicon and

All from the [latest release](https://github.com/kccarlos/gitcontext/releases/latest).

### Web app
### Try it in the browser

Open **[kccarlos.github.io/gitcontext](https://kccarlos.github.io/gitcontext/)** in Chrome or Edge (it needs the File System Access API). Nothing to install, and your code still never leaves the browser.
The web version at **[kccarlos.github.io/gitcontext](https://kccarlos.github.io/gitcontext/)** is a quick way to try GitContext without installing anything. Your code still never leaves the browser. It needs Chrome or Edge (File System Access API), and it is slower and less robust on large repositories than the desktop app, which is the one to use day to day.

---

## Screenshots

These show the web app. The desktop app has the same core workflow with a native folder picker, saved workspaces and a tabbed side panel.

<table>
<tr>
<td width="50%"><img src="docs/screenshots/02-file-preview.png" alt="Side-by-side diff preview of a modified file"><br><sub><b>Diff preview</b>: unified or side-by-side, before you include a file.</sub></td>
<td width="50%"><img src="docs/screenshots/04-dark-mode.png" alt="Main view in dark mode"><br><sub><b>Dark mode</b>, following the system or set by hand.</sub></td>
<td width="50%"><img src="docs/screenshots/02-desktop-diff-preview.png" alt="Side-by-side diff preview of a modified file in the desktop app"><br><sub><b>Diff preview</b>: unified or side-by-side, with word-level highlights, before you include a file.</sub></td>
<td width="50%"><img src="docs/screenshots/03-desktop-dark.png" alt="Desktop app in dark mode"><br><sub><b>Dark mode</b>, following the system or set by hand.</sub></td>
</tr>
<tr>
<td colspan="2"><img src="docs/screenshots/03-output.png" alt="Selected files with per-file token counts and the Copy All Selected button"><br><sub><b>Selected files</b> with per-file token counts, sorted by size, then one click to copy the whole prompt.</sub></td>
<td colspan="2"><img src="docs/screenshots/04-desktop-output.png" alt="Settings tab with target model, prompt template, output options and token totals"><br><sub><b>Settings</b>: target model and its context window, prompt templates, file tree and binary options, then one click to copy the whole prompt.</sub></td>
</tr>
</table>

Expand Down
Binary file added docs/screenshots/01-desktop-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/screenshots/01-diff-overview.png
Binary file not shown.
Binary file added docs/screenshots/02-desktop-diff-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/screenshots/02-file-preview.png
Binary file not shown.
Binary file added docs/screenshots/03-desktop-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/screenshots/03-output.png
Binary file not shown.
Binary file removed docs/screenshots/04-dark-mode.png
Binary file not shown.
Binary file added docs/screenshots/04-desktop-output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/ui/src/components/PreviewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ export default function PreviewModal({ open, onClose, path, status, baseLabel, c
light: { codeFoldGutterBackground: 'transparent' },
dark: { codeFoldGutterBackground: 'transparent' },
} as any,
// The library's inline-flex + padding turns each Prism token inside a
// changed word into a flex item, splitting words ("c on st").
wordDiff: { display: 'inline', padding: 0 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add coverage for the word-diff style override

Add a regression test that renders or mocks ReactDiffViewer with syntax-highlighted changed words and verifies this override is supplied. This is a user-visible behavior fix for the case where Prism splits a changed word across multiple token spans, but the commit adds no test to prevent a later style or dependency change from reintroducing the broken rendering, contrary to the repository's testing requirement for every behavior change.

AGENTS.md reference: AGENTS.md:L11-L12

Useful? React with 👍 / 👎.

}}
/>
)}
Expand Down
Loading