Best use case
git-tutorial-sandbox is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Teams using git-tutorial-sandbox 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
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/git-tutorial-sandbox/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How git-tutorial-sandbox Compares
| Feature / Agent | git-tutorial-sandbox | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/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: git-tutorial-sandbox
## What this skill knows
This skill covers the git-tutorial-sandbox project: an in-browser interactive Git learning environment powered by isomorphic-git and LightningFS. The agent runs real Git commands in a fake terminal UI against an in-memory filesystem, renders a live commit DAG with D3, and validates lesson steps.
## Tech stack summary
- isomorphic-git v1 for all Git operations
- @isomorphic-git/lightning-fs for the in-browser filesystem (IndexedDB backend)
- React + Zustand for UI and state
- D3-force / D3-DAG for commit graph layout
- React Router v6 for navigation
- Vitest + Testing Library for tests
## isomorphic-git essentials
Every isomorphic-git call needs `fs` (the LightningFS instance) and `dir` (the repo path, always `/repo`).
```ts
import git from 'isomorphic-git';
import { fs } from '@/git/lightningfs';
const DIR = '/repo';
// Init
await git.init({ fs, dir: DIR });
// Stage all
await git.add({ fs, dir: DIR, filepath: '.' });
// Commit
await git.commit({
fs,
dir: DIR,
message: 'Initial commit',
author: { name: 'Learner', email: 'learner@sandbox.local' },
});
// Log (returns ReadCommitResult[])
const log = await git.log({ fs, dir: DIR });
// Branches
const branches = await git.listBranches({ fs, dir: DIR });
await git.branch({ fs, dir: DIR, ref: 'feature' });
await git.checkout({ fs, dir: DIR, ref: 'feature' });
// Merge
await git.merge({ fs, dir: DIR, theirs: 'feature',
author: { name: 'Learner', email: 'learner@sandbox.local' } });
// Status matrix
const matrix = await git.statusMatrix({ fs, dir: DIR });
// Each row: [filepath, HEAD, WORKDIR, STAGE]
// 0 = absent, 1 = identical, 2 = modified, 3 = ...
```
## LightningFS singleton
```ts
// src/git/lightningfs.ts
import LightningFS from '@isomorphic-git/lightning-fs';
export const fs = new LightningFS('git-sandbox');
export async function resetFs(): Promise<void> {
await fs.promises.rm('/repo', { recursive: true }).catch(() => {});
await fs.promises.mkdir('/repo');
}
```
Import `{ fs }` everywhere. Never create a second LightningFS instance.
## Command parser usage
```ts
import { parseCommand } from '@/git/parser';
const cmd = parseCommand('git branch -d feature');
// => { type: 'branch', name: 'feature', delete: true }
const bad = parseCommand('git blame README.md');
// => { type: 'unknown', raw: 'git blame README.md' }
```
The parser strips leading `git ` before switching on the subcommand.
## useGitCommand hook
```ts
const { runCommand } = useGitCommand();
await runCommand('git commit -m "Add feature"');
// Side effects:
// - Appends prompt + output lines to terminal store
// - Refreshes repo store (commits, branches, HEAD, files)
// - Advances lesson step if command matches expected
```
## Lesson step validation
The expected command in lesson YAML is normalized before comparison:
```ts
function normalize(cmd: string): string {
return cmd
.toLowerCase()
.trim()
.replace(/\s+/g, ' ')
.replace(/^git /, '');
}
```
A step advances if `normalize(actual) === normalize(expected)`.
Steps with multiple valid forms use `expectedCommands: string[]` array in the YAML.
## Lesson YAML frontmatter
```yaml
---
id: "03-branching"
title: "Branching Basics"
order: 3
estimatedMinutes: 8
prerequisites: ["01-first-commit", "02-staging"]
steps:
- id: "create-branch"
instruction: "Create a new branch called feature."
expectedCommand: "git branch feature"
hint: "Use git branch <name> to create a branch without switching."
---
```
## Commit graph derivation
```ts
// src/hooks/useCommitGraph.ts
function deriveGraph(commits: Commit[], branches: Branch[]) {
const nodes: GraphNode[] = commits.map(c => ({
id: c.oid,
shortId: c.shortOid,
isHead: c.oid === head,
isMerge: c.parents.length > 1,
branches: branches.filter(b => b.oid === c.oid).map(b => b.name),
}));
const edges: GraphEdge[] = commits.flatMap(c =>
c.parents.map(parentOid => ({ source: c.oid, target: parentOid }))
);
return { nodes, edges };
}
```
## Supported commands
| Command | Parsed as |
|---------|-----------|
| `git init` | `{ type: 'init' }` |
| `git add .` | `{ type: 'add', paths: ['.'] }` |
| `git add README.md auth.js` | `{ type: 'add', paths: ['README.md', 'auth.js'] }` |
| `git commit -m "msg"` | `{ type: 'commit', message: 'msg' }` |
| `git branch feature` | `{ type: 'branch', name: 'feature' }` |
| `git branch -d feature` | `{ type: 'branch', name: 'feature', delete: true }` |
| `git checkout main` | `{ type: 'checkout', ref: 'main' }` |
| `git checkout -b hotfix` | `{ type: 'checkout', ref: 'hotfix', newBranch: true }` |
| `git merge feature` | `{ type: 'merge', branch: 'feature' }` |
| `git log` | `{ type: 'log' }` |
| `git log --oneline` | `{ type: 'log', oneline: true }` |
| `git status` | `{ type: 'status' }` |
| `git diff` | `{ type: 'diff' }` |
| `git diff --staged` | `{ type: 'diff', staged: true }` |
| `git reset --soft HEAD~1` | `{ type: 'reset', mode: 'soft', ref: 'HEAD~1' }` |
| `git reset --hard HEAD` | `{ type: 'reset', mode: 'hard', ref: 'HEAD' }` |
| `git stash` | `{ type: 'stash' }` |
| `git stash pop` | `{ type: 'stash', subcommand: 'pop' }` |
| `git stash list` | `{ type: 'stash', subcommand: 'list' }` |
| `clear` | `{ type: 'clear' }` |
| anything else | `{ type: 'unknown', raw: '...' }` |
## Terminal line kinds
| Kind | Color | When to use |
|------|-------|-------------|
| `prompt` | green | The command the user typed |
| `stdout` | light grey `#d6d3d1` | Normal git output |
| `stderr` | red `#f87171` | Errors, unsupported commands |
| `success` | green `#4ade80` | Step completed confirmation |
| `info` | blue `#93c5fd` | Hints, tips, context |
## localStorage schema
| Key | Type | Contents |
|-----|------|----------|
| `gts_progress` | JSON | `{ [lessonId]: { completedSteps: string[], completedAt?: number } }` |
| `gts_settings` | JSON | terminal font size, hints auto-show, graph visibility |
Progress is saved immediately when each step completes.
## Routes
| Path | Component |
|------|-----------|
| `/` | `HomePage` - lesson card grid |
| `/lesson/:id` | `LessonPage` - 3-column layout |
| `/sandbox` | `SandboxPage` - terminal + graph |
| `/progress` | `ProgressPage` - completion tracker |
## Common mistakes
- Always pass `author` to `git.commit` and `git.merge`. isomorphic-git will throw without it.
- `git.log` returns commits newest first. The DAG renders newest at top.
- `git.statusMatrix` rows: `[filepath, HEAD, WORKDIR, STAGE]`. Values: 0=absent, 1=unchanged, 2=added/modified. Row `[file, 0, 2, 0]` means new untracked file.
- After `git.merge`, call `git.log` to refresh commits - the merge commit OID is not returned by `merge`.
- `git reset` is not a built-in isomorphic-git function. Implement it by calling `git.writeRef` to move HEAD and `git.checkout` or `git.resetIndex` to restore files.Related Skills
sql-tutorial-engine
No description provided.
Skill: Uptime Monitoring
## Overview
Skill: Status Page
## Overview
Skill: unit-conversion
## Overview
Skill: recipe-scaler
## Overview
reading-list
Operate the reading-list API to save, manage, tag, search, and export articles.
email-digest
Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.
websocket-realtime
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
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
## Overview
Skill: csv-import
## Overview
Skill: Syntax Highlighting
## Purpose