Best use case

isomorphic-git is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Teams using isomorphic-git should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/isomorphic-git/SKILL.md --create-dirs "https://raw.githubusercontent.com/heldernoid/agentic-build-templates/main/projects/education-learning/git-tutorial-sandbox/skills/isomorphic-git/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/isomorphic-git/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How isomorphic-git Compares

Feature / Agentisomorphic-gitStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

This skill provides specific capabilities for your AI agent. See the About section for full details.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Skill: isomorphic-git

## What this skill knows

This skill covers isomorphic-git v1: a pure JavaScript Git implementation that runs in both Node.js and the browser. It covers the most commonly used APIs, the filesystem abstraction, LightningFS, branch/HEAD manipulation, and common gotchas.

## Installation

```bash
pnpm add isomorphic-git @isomorphic-git/lightning-fs
```

## Core concept: filesystem abstraction

isomorphic-git never touches the real filesystem. Every call takes an `fs` parameter - an object conforming to the `fs.promises` interface from Node.js. In the browser, LightningFS provides this:

```ts
import LightningFS from '@isomorphic-git/lightning-fs';

const fs = new LightningFS('my-app');
// Now fs.promises has: readFile, writeFile, mkdir, rmdir, stat, readdir, unlink, ...
```

In Node.js you pass the real `fs` module:

```ts
import fs from 'node:fs';
import git from 'isomorphic-git';

await git.init({ fs, dir: '/path/to/repo' });
```

## LightningFS

LightningFS stores files in IndexedDB. The constructor name (first argument) is the IndexedDB database name.

```ts
const fs = new LightningFS('git-sandbox');

// Check if repo exists
try {
  await fs.promises.stat('/repo/.git');
  // repo exists
} catch {
  // needs init
}

// Wipe everything
await fs.promises.rm('/repo', { recursive: true }).catch(() => {});
await fs.promises.mkdir('/repo');
```

LightningFS is synchronous-by-default in older versions. Use `fs.promises.*` (async) API to avoid blocking the main thread.

## Core operations

### init

```ts
await git.init({ fs, dir: '/repo' });
// Creates .git directory structure
```

### Writing files (not a git function)

Use the filesystem directly:

```ts
await fs.promises.writeFile('/repo/README.md', '# Hello\n', { encoding: 'utf8' });
```

### add

```ts
// Stage a specific file
await git.add({ fs, dir: '/repo', filepath: 'README.md' });

// Stage all changed files (walk the status matrix)
const matrix = await git.statusMatrix({ fs, dir: '/repo' });
await Promise.all(
  matrix
    .filter(([, head, workdir, stage]) => workdir !== stage)
    .map(([filepath]) => git.add({ fs, dir: '/repo', filepath }))
);
```

### commit

```ts
const oid = await git.commit({
  fs,
  dir: '/repo',
  message: 'Initial commit',
  author: {
    name: 'Learner',
    email: 'learner@sandbox.local',
    // timestamp and timezoneOffset are optional (defaults to now)
  },
});
// Returns the new commit OID (SHA-1 string)
```

### log

```ts
const commits = await git.log({ fs, dir: '/repo' });
// Returns ReadCommitResult[]
// commits[0] is the newest commit (HEAD)

for (const { commit, oid } of commits) {
  console.log(oid, commit.message, commit.parent);
}
```

The `commit` object has: `message`, `tree` (tree OID), `parent` (array of parent OIDs), `author`, `committer`.

### statusMatrix

```ts
const matrix = await git.statusMatrix({ fs, dir: '/repo' });
// Returns [filepath: string, HEAD: 0|1, WORKDIR: 0|1|2, STAGE: 0|1|2|3][]

// Decode status:
// HEAD 0 = file is new (not in last commit)
// HEAD 1 = file was committed
// WORKDIR 0 = file absent on disk
// WORKDIR 1 = file unchanged
// WORKDIR 2 = file added or modified
// STAGE 0 = not staged
// STAGE 1 = same as HEAD (no staged changes)
// STAGE 2 = staged, differs from HEAD
// STAGE 3 = staged with conflict
```

Practical helper:

```ts
function decodeStatus(row: [string, 0|1, 0|1|2, 0|1|2|3]) {
  const [file, head, workdir, stage] = row;
  if (head === 0 && workdir === 2 && stage === 0) return { file, status: 'untracked' };
  if (head === 0 && workdir === 2 && stage === 2) return { file, status: 'new-staged' };
  if (head === 1 && workdir === 2 && stage === 1) return { file, status: 'modified-unstaged' };
  if (head === 1 && workdir === 2 && stage === 2) return { file, status: 'modified-staged' };
  if (head === 1 && workdir === 1 && stage === 0) return { file, status: 'deleted-unstaged' };
  if (head === 1 && workdir === 1 && stage === 1) return { file, status: 'unchanged' };
  return { file, status: 'unknown' };
}
```

### branch and checkout

```ts
// List branches
const branches = await git.listBranches({ fs, dir: '/repo' });

// Get current branch
const current = await git.currentBranch({ fs, dir: '/repo' });

// Create a branch at current HEAD
await git.branch({ fs, dir: '/repo', ref: 'feature' });

// Switch to a branch (moves HEAD, updates working tree)
await git.checkout({ fs, dir: '/repo', ref: 'feature' });

// Create and switch (equivalent to git checkout -b)
await git.branch({ fs, dir: '/repo', ref: 'feature' });
await git.checkout({ fs, dir: '/repo', ref: 'feature' });

// Delete a branch
await git.deleteBranch({ fs, dir: '/repo', ref: 'feature' });
```

### merge

```ts
const result = await git.merge({
  fs,
  dir: '/repo',
  theirs: 'feature',      // branch or commit OID to merge in
  author: { name: 'Learner', email: 'learner@sandbox.local' },
});
// result.oid is the new merge commit OID
// result.alreadyMerged is true if fast-forward was used
// result.fastForward is true if no merge commit was needed
```

isomorphic-git does not handle merge conflicts automatically. If there is a conflict, `merge` throws a `MergeConflictError`. The sandbox avoids conflicts by constructing lessons to prevent them.

### diff (reading diff)

```ts
// Get diff of a file between two commits
const result = await git.walk({
  fs,
  dir: '/repo',
  trees: [git.TREE({ ref: 'HEAD' }), git.WORKDIR()],
  map: async (filepath, [headEntry, workdirEntry]) => {
    if (!headEntry || !workdirEntry) return null;
    const [headContent, workdirContent] = await Promise.all([
      headEntry.content().then(b => new TextDecoder().decode(b)),
      workdirEntry.content().then(b => new TextDecoder().decode(b)),
    ]);
    if (headContent === workdirContent) return null;
    return { filepath, headContent, workdirContent };
  },
});
```

For a simpler line-by-line diff, pass the contents to a diffing library like `diff` (npm package `diff`).

### resolveRef and readCommit

```ts
// Get the OID of HEAD
const headOid = await git.resolveRef({ fs, dir: '/repo', ref: 'HEAD' });

// Get the OID of a branch tip
const mainOid = await git.resolveRef({ fs, dir: '/repo', ref: 'main' });

// Read a specific commit
const { commit } = await git.readCommit({ fs, dir: '/repo', oid: headOid });
```

### writeRef (for implementing git reset)

isomorphic-git has no `git reset` equivalent. Implement it:

```ts
async function gitReset(fs: any, dir: string, ref: string, mode: 'soft' | 'mixed' | 'hard') {
  // Resolve target OID
  const targetOid = await git.resolveRef({ fs, dir, ref });

  // Move branch pointer
  const branch = await git.currentBranch({ fs, dir });
  if (branch) {
    await git.writeRef({ fs, dir, ref: `refs/heads/${branch}`, value: targetOid, force: true });
  }

  if (mode === 'soft') return; // Done: staging preserved

  // Clear staging area (mixed)
  const { commit } = await git.readCommit({ fs, dir, oid: targetOid });
  await git.checkout({ fs, dir, ref: targetOid, noCheckout: mode === 'mixed' });

  if (mode === 'hard') {
    // Update working tree to match target commit
    await git.checkout({ fs, dir, ref: targetOid });
  }
}
```

## Error types

```ts
try {
  await git.merge({ ... });
} catch (err) {
  if (err.name === 'MergeConflictError') {
    // conflicting files: err.data.filepaths
  } else if (err.name === 'NotFoundError') {
    // branch or ref does not exist
  } else if (err.name === 'CheckoutConflictError') {
    // uncommitted changes would be overwritten
  }
}
```

## HEAD states

- `currentBranch` returns the branch name (string) when HEAD points to a branch.
- `currentBranch` returns `undefined` when HEAD is detached (points directly to a commit OID).
- `resolveRef({ ref: 'HEAD' })` always returns the commit OID regardless of detached state.

## Performance in the browser

- Each LightningFS operation is an IndexedDB transaction. Batching writes reduces latency.
- `git.log` fetches all commits - on repos with many commits, pass `depth` to limit: `git.log({ depth: 50, ... })`.
- `git.statusMatrix` walks the entire working tree. On repos with many files this is slow. Call it only when you need to update the UI.
- For the sandbox (max ~20 files), performance is not a concern.

## Testing isomorphic-git code

Use a real LightningFS in tests (jsdom environment includes IndexedDB via fake-indexeddb or idb-keyval):

```ts
import LightningFS from '@isomorphic-git/lightning-fs';
import git from 'isomorphic-git';

const fs = new LightningFS('test');
const dir = '/test-repo';

beforeEach(async () => {
  await fs.promises.rm(dir, { recursive: true }).catch(() => {});
  await fs.promises.mkdir(dir);
  await git.init({ fs, dir });
});

test('commit stores message', async () => {
  await fs.promises.writeFile(`${dir}/a.txt`, 'hello');
  await git.add({ fs, dir, filepath: 'a.txt' });
  await git.commit({ fs, dir, message: 'add a', author: { name: 'T', email: 't@t.com' } });

  const [{ commit }] = await git.log({ fs, dir });
  expect(commit.message).toBe('add a\n');
});
```

Note: isomorphic-git appends a newline to commit messages. Strip it when displaying.

## Packages required

```json
{
  "dependencies": {
    "isomorphic-git": "^1.27.0",
    "@isomorphic-git/lightning-fs": "^4.6.0"
  }
}
```

No additional polyfills are needed in modern browsers or Node.js 18+.

Related Skills

Skill: Uptime Monitoring

7
from heldernoid/agentic-build-templates

## Overview

Skill: Status Page

7
from heldernoid/agentic-build-templates

## Overview

Skill: unit-conversion

7
from heldernoid/agentic-build-templates

## Overview

Skill: recipe-scaler

7
from heldernoid/agentic-build-templates

## Overview

reading-list

7
from heldernoid/agentic-build-templates

Operate the reading-list API to save, manage, tag, search, and export articles.

email-digest

7
from heldernoid/agentic-build-templates

Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.

websocket-realtime

7
from heldernoid/agentic-build-templates

Use the WebSocket connection in poll-builder to receive live vote updates. Use when you need to stream real-time poll results, monitor a poll for new votes, or build a live dashboard. Triggers include "live results", "real-time updates", "stream votes", "watch poll", or "WebSocket".

poll-builder

7
from heldernoid/agentic-build-templates

Self-hosted poll creation tool with real-time results. Use when you need to create a poll, check vote counts, close a poll, export results, or get the shareable link for a poll. Triggers include "create poll", "vote", "poll results", "survey", "collect votes", "share poll", or any task involving polling or voting.

Skill: personal-finance

7
from heldernoid/agentic-build-templates

## Overview

Skill: csv-import

7
from heldernoid/agentic-build-templates

## Overview

Skill: Syntax Highlighting

7
from heldernoid/agentic-build-templates

## Purpose

Skill: Pastebin Core

7
from heldernoid/agentic-build-templates

## Purpose