# AGENTS.md

This file provides guidance to LLMs when working with code in this repository.

## ⚠️ Mandatory Documentation Updates

**ALL AGENTS MUST FOLLOW THESE RULES:**

1. **After making any code changes**, verify that AGENTS.md and README.md accurately reflect your changes. Update them if they don't.

2. **When adding new features**, add documentation for them in both files:
   - AGENTS.md: Add to the appropriate section with implementation details - it will be used by future agents for improving this repository
   - README.md: Add user-facing documentation with usage examples (more like a guide) - this is for humans, so it must be clear, concise, and follow README best practices
   - LLM_README.md: Add LLM-facing documentation with exhaustive detailed usage and examples - it will be used by user agents that will leverage the API, so it should not contain implementation details

3. **When modifying existing features**, update their documentation if the behavior changes (or anything else).

4. **When adding new libraries or modules**, document them in the Architecture Overview section of AGENTS.md.

5. **When changing project structure** (new folders), update the Project Structure section in README.md. It must only surface the folders, the files is too detailed.

6. **Before completing a task**, run a quick check:
   - Do AGENTS.md, README.md and FEATURES.md reflect the current state of the code?
   - Are all features documented?
   - Is the project structure accurate?

Failure to keep documentation up-to-date creates confusion for future agents and developers. This is a **mandatory** part of every code change.

---

## Architecture Overview

### Project Structure

```
/src
├── index.ts          # Public API exports
├── challenge.ts      # Core ChallengeBase class and createChallenge()
├── actions.ts        # Actions library (give, teleport, announce, etc.)
├── types.ts          # TypeScript type definitions
├── constants.ts      # Shared constants (objectives, selectors, built-in vars)
├── commands.ts       # Watcher integration commands
├── api_utils.ts      # Utility functions (forEveryPlayer)
├── testmode.ts       # Test/debug mode support
└── utils.ts          # Internal utilities
```

### Key Dependencies

- **Sandstone (0.14.0-alpha.13)**: Minecraft datapack generator. Provides MCFunction, Objective, Selector, execute commands, etc. All generated code compiles to Minecraft datapacks via Sandstone's `savePack()`.

### Core Components

#### 1. ChallengeBase Class (`challenge.ts`)

The main engine that:
- Manages variable creation and objective assignment
- Generates MCFunctions for lifecycle events (start, init, tick, end)
- Processes custom events (score-based and advancement-based)
- Handles win condition evaluation and game state management

**Key Methods:**
- `constructor(config)`: Sets up objectives, variables, and built-in score events
- `events()`: Registers lifecycle event callbacks
- `custom_events()`: Registers triggered events
- `end_condition()`: Sets game termination condition
- `win_conditions()`: Sets per-role win conditions and triggers `build()`
- `build()`: Generates all MCFunctions and calls `savePack()`

**Generated MCFunctions:**
1. `start_challenge` - Initial setup, tags players, sets game state (runs globally)
2. `init_participants` - Player setup (runs globally, delayed 1 second via schedule)
3. `on_tick` - Main loop: updates variables, processes events, checks end condition (runs globally every tick)
4. `end_challenge` - Announces winners, saves game state (runs globally)

#### 2. Actions Library (`actions.ts`)

Pre-built game operations. Each action is a function that generates Sandstone commands.

**Categories:**
- Communication: `announce`, `tellraw`
- Items: `give`, `giveLoot`, `clear`, `countItems`, `summonItem`
- Entities: `summonMultiple`, `kill`, `teleport`
- World: `setBlock`, `fill`, `setTime`, `gamerule`
- Scores: `set`, `increment`, `decrement`
- Attributes: `setAttribute`
- Custom: `custom` (arbitrary Sandstone code)
- Logging: `log_variable`

**Implementation Pattern:**
```typescript
export const Actions = {
  give: ({ item, target, count = 1 }: { item: ITEMS; target: TargetNames; count?: number }) => {
    give(mapTarget(target), item, count);
  },
  // ...
};
```

**Target Mapping:**
The `mapTarget()` function converts `TargetNames` to selectors:
- `"all"` → `@a[tag=kradle_participant]`
- `"self"` → `"@s"`
- `SelectorClass` → passed through
- `"minecraft:entity_type"` → `@e[type=minecraft:entity_type]`
- Other strings → `@a[tag=<string>]` (treated as team names)

#### 3. Variable System (`types.ts`, `challenge.ts`)

**Variable Types:**
- `individual` + specific `objective_type`: Uses Minecraft's built-in stat tracking
- `individual` + `"dummy"`: Manual/computed values per player
- `global`: Shared values stored on a constant entity

**Objective Naming:**
- Uses counter-based suffixes to ensure unique names under 16 chars
- Format: `kradle.${hash}` where hash is derived from challenge name + counter

**Updater Execution:**
- All updaters run every tick in `on_tick` MCFunction
- Updaters execute as/at each participant for individual variables
- Global updaters run once per tick

#### 4. Event System (`challenge.ts`)

**Score Events:**
- Track previous tick values to detect changes
- Fire when score reaches target (fire_once) or while condition holds (repeatable)
- For individual variables: events fire per-player
- For global variables: events fire globally

**Advancement Events:**
- Always fire per-player (advancement triggers are inherently per-player)
- Converted to score events internally
- Advancement grants → score increment → event fires
- Advancement revoked after triggering

#### 5. Watcher Integration (`commands.ts`)

Communicates with external "Kradle Watcher" system via JSON tellraw commands:
```json
{
  "type": "kradle_command",
  "command": "game_over",
  "arguments": { "winners": [...], "end_state": "..." }
}
```

### Constants (`constants.ts`)

**Objectives:**
- `VISIBLE_OBJECTIVE` = "kradle.board" (sidebar display)
- `HIDDEN_OBJECTIVE` = hidden scoreboard for internal data

**Entity Selectors:**
- `ALL` = `@a[tag=kradle_participant]`
- `KRADLE_PARTICIPANT_TAG` = "kradle_participant"
- `WINNER_TAG` = "kradle_winner"

**Built-in Variables (8):**
1. `death_count` - Minecraft deathCount objective
2. `has_never_died` - 1 if alive, 0 if died
3. `alive_players` - Global count
4. `main_score` - Primary display score
5. `game_timer` - Tick counter
6. `game_state` - State enum (CREATED=0, OFF=1, ON=2)
7. `player_count` - Total players
8. `player_number` - Unique ID (1-N)

### Build Process

1. User calls `createChallenge(config)` → returns builder
2. Chain: `.events()` → `.custom_events()` → `.end_condition()` → `.win_conditions()`
3. `.win_conditions()` triggers `build()`:
   - Creates all MCFunctions
   - Generates advancements for advancement-based events
   - Creates loot tables (hashed for deduplication)
   - Calls `savePack()` to write datapack files

---

## Development Guidelines

### Adding a New Action

1. Add to `Actions` object in `src/actions.ts`
2. Define parameter interface with required/optional fields
3. Implement using Sandstone commands
4. Update LLM_README.md with full signature and examples
5. Update README.md with brief usage example

### Adding a New Built-in Variable

1. Add to `BUILTIN_VARIABLES` in `src/constants.ts`
2. Define type, objective_type, and updater
3. Update LLM_README.md built-in variables table
4. Update README.md built-in variables table

### Adding a New Event Type

1. Update `_InputCustomEventType` in `src/types.ts`
2. Handle in `processCustomEvents()` in `src/challenge.ts`
3. Document in LLM_README.md Events section
4. Add example to README.md

### Testing Changes

```bash
npm run build    # Compile TypeScript
npm run lint     # Run Biome linter
```

Test by creating a challenge using the library and verifying generated datapack.

---

## Common Implementation Patterns

### Objective Name Generation

```typescript
private getNewObjectiveName(): string {
  const hash = createHash("sha256")
    .update(this.config.name + this.objectiveCounter++)
    .digest("hex")
    .slice(0, 8);
  return `kradle.${hash}`;
}
```

### Variable Registration

```typescript
// Individual with specific objective
const obj = Objective.create(name, objectiveType);
variables[varName] = obj("@s");

// Global dummy
const obj = Objective.create(name, "dummy");
variables[varName] = obj(GLOBAL_ENTITY);
```

### Event Processing (on_tick)

```typescript
// 1. Update all variable values
for (const updater of updaters) {
  updater(value, allVariables);
}

// 2. Process score events
for (const event of scoreEvents) {
  _.if(score.greaterOrEqualThan(target), () => {
    if (mode === "fire_once") {
      _.if(prevScore.lowerThan(target), () => {
        actions();
      });
    } else {
      actions();
    }
  });
}

// 3. Update previous values
for (const [current, prev] of previousValuePairs) {
  prev.set(current);
}

// 4. Check end condition
_.if(endCondition, () => {
  endChallenge();
});
```

### Loot Table Deduplication

Loot tables are hashed by content to avoid duplicates:
```typescript
const hash = createHash("sha256")
  .update(JSON.stringify(lootTable))
  .digest("hex")
  .slice(0, 16);
```
