This document explains how the levels and stages system works in Git Mastery and how to add new levels.
The level system is organized into:
- Stages: Collection of related levels (e.g., "Intro", "Branches")
- Levels: Individual challenges within each stage
- Requirements: Specific tasks to complete a level
Each level is defined using the following structure:
export interface LevelType {
id: number;
name: string;
description: string;
objectives: string[];
hints: string[];
requirements: LevelRequirement[];
requirementLogic?: "any" | "all";
completedRequirements?: string[];
story?: StoryContext;
resetGitRepo?: boolean;
initialState?: LevelInitialState;
}The LevelRequirement defines what the user needs to do to complete the level:
export type LevelRequirement = {
id?: string;
command: string;
requiresArgs?: string[];
description: string;
successMessage?: string;
};Levels are created using the helper functions in src/level/LevelCreator.ts:
- Create a new level in the appropriate stage file:
// In src/level/stages/my-stage.ts
import {
createLevel,
createRequirement,
createStory,
createInitialState,
createFileStructure,
createGitState,
} from "../LevelCreator";
const myStageLevel1 = createLevel({
id: 1,
name: "mystage.level1.name", // Translation key
description: "mystage.level1.description", // Translation key
objectives: ["mystage.level1.objective1"], // Translation keys
hints: ["mystage.level1.hint1", "mystage.level1.hint2"], // Translation keys
requirements: [
createRequirement({
command: "git command", // The command that completes this level
requiresArgs: ["optional", "required", "args"],
description: "mystage.level1.requirement1.description",
successMessage: "mystage.level1.requirement1.success",
}),
],
story: createStory({
title: "mystage.level1.story.title",
narrative: "mystage.level1.story.narrative",
realWorldContext: "mystage.level1.story.realWorldContext",
taskIntroduction: "mystage.level1.story.taskIntroduction",
}),
initialState: createInitialState({
files: [createFileStructure("/path/to/file.ext", "file content")],
git: createGitState({
initialized: true, // Start with git initialized
currentBranch: "main",
branches: ["main", "feature"],
commits: [
{
message: "Initial commit",
files: ["/path/to/file.ext"],
},
],
}),
}),
});
export const myStageLevels = {
1: myStageLevel1,
// Add more levels here
};- Add the stage to the allStages object in
src/level/index.ts:
import { myStageLevels } from "./stages/my-stage";
export const allStages = {
// ...existing stages,
MyStage: createStage({
id: "mystage",
name: "mystage.name",
description: "mystage.description",
icon: "🔍",
levels: myStageLevels,
}),
};Requirements define what a user must do to complete a level. The main ways to define requirements are:
-
Command Match: User must execute a specific command
createRequirement({ command: "git init", description: "Initialize a git repository", });
-
Command with Arguments: User must execute a command with specific arguments
createRequirement({ command: "git switch", requiresArgs: ["-c"], // Required argument description: "Create a new branch", });
-
Special Case - Any Argument: User must provide any argument
createRequirement({ command: "git add", requiresArgs: ["any"], // Any argument will satisfy this description: "Add a file to staging", });
The initial state controls how the environment is set up when the level starts:
-
Files: Create initial files in the file system
files: [ createFileStructure("/README.md", "# Project\n\nThis is a README file"), createFileStructure("/src/index.js", 'console.log("Hello world");'), ];
-
Git State: Set up Git repository state
git: createGitState({ initialized: true, currentBranch: "main", branches: ["main", "feature"], commits: [ { message: "Initial commit", files: ["/README.md"], }, { message: "Add feature", files: ["/src/feature.js"], branch: "feature", // Switch to this branch for this commit }, ], fileChanges: [ { path: "/README.md", content: "Updated content", status: "modified", }, ], });
All user-facing strings should use translation keys rather than hardcoded strings. Add the corresponding translations to:
src/translations/en/levels.tsfor Englishsrc/translations/de/levels.tsfor German- Add more language files as needed
Follow this naming convention for keys:
stageid.level#.elementtype.elementname
Example:
intro.level1.hint1: "Use the git init command"
To test your level:
- Add it to the code as described above
- Add all necessary translations
- Run the application and navigate to your level
- Try to complete it by following the objectives
- Check all edge cases (wrong commands, incomplete steps, etc.)