Files
Mana-Loop/AGENTS.md
T
n8n-gitea 07b311bd7a
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 31s
chore: cleanup — remove dead weight, update .gitignore for binary test artifacts
2026-05-13 12:38:41 +02:00

10 KiB
Executable File

Mana Loop — Agent Guide

Mana Loop is a browser-based incremental/idle game. Pure client-side Next.js + Zustand — no backend, no database, no server state.


🔑 Git Credentials

HTTPS URL with credentials:

https://n8n-gitea:tkF9HFgxL2k4cmT@gitea.tailf367e3.ts.net/Anexim/Mana-Loop.git

Configure once per session:

git config --global user.name "n8n-gitea"
git config --global user.email "n8n-gitea@anexim.local"

⚠️ Mandatory Git Workflow

Every session:

# Start
cd /home/user/repos/Mana-Loop && git pull origin master

# Finish
git add -A
git commit -m "type: short description"
git push origin master

No exceptions. Always pull first, always push when done.


🗂️ Gitea Is The Source Of Truth

All task state lives in Gitea issues. No markdown files track status — the issue label and comments are the record.

Gitea URL: https://gitea.tailf367e3.ts.net/Anexim/Mana-Loop

Labels:

Label Meaning
ai:todo Not started
ai:in-progress Being worked on now
ai:review Done, needs human review
ai:blocked Stuck — comment explains why
ai:done Complete, issue closed

Session Start Protocol

1. Read docs/project-structure.txt     ← mandatory, every session
2. Read docs/dependency-graph.json     ← mandatory, every session
3. Call get_repo_summary → see what's in-progress, blocked, todo
4. If an issue is in-progress → call get_issue_context on it and resume
5. If nothing in-progress → pick the highest-priority todo
6. Call update_issue_status → move it to "in-progress"
7. Work on it
8. Log significant steps with add_comment
9. When done → update_issue_status to "done" (closes the issue)

Steps 1 and 2 are non-negotiable even if you think you know the codebase. project-structure.txt tells you exactly where files live so you don't waste tool calls searching. dependency-graph.json tells you the blast radius of any change before you make it — who imports what, so you never edit a shared file blind.

How to use dependency-graph.json:

# What files will break if I touch stores/skillStore.ts?
node -e "
const d = require('./docs/dependency-graph.json').graph;
const target = 'stores/skillStore.ts';
Object.entries(d)
  .filter(([,deps]) => deps.some(dep => dep.includes(target)))
  .forEach(([f]) => console.log(f));
"

# What does a file depend on?
node -e "
const d = require('./docs/dependency-graph.json').graph;
const key = Object.keys(d).find(k => k.includes('skillStore'));
console.log(JSON.stringify(d[key], null, 2));
"

Progress Logging

Use add_comment on the issue to log what you did. A good comment includes:

  • What was changed and why
  • Any errors encountered and how they were resolved
  • Exact files modified
  • Output of bun run typecheck or bun run test if relevant

If you stop mid-task for any reason, leave a comment with STATUS: STOPPED so the next session knows exactly where to resume.


⚙️ Terminal Tool Protocol — READ THIS

The terminal tool is async. run_command starts a process and returns immediately with status: "running". You must follow up with get_process_status in the same response turn.

Correct pattern — always do this:

run_command({ command: "cd /home/user/repos/Mana-Loop && bun run typecheck" })
→ { status: "running", id: "abc123" }

[IMMEDIATELY call — do NOT stop here]

get_process_status({ process_id: "abc123", wait: 60 })
→ { status: "done", output: [...] }

Never end your response after seeing status: "running". Always call get_process_status in the same turn. If you hit output length limits, the user will say "continue" and you pick up from get_process_status.

For long-running commands (full tsc, test suite), use wait: 120.


🤖 Sub-Agent Usage

Use run_sub_agent when a task needs 3+ sequential tool calls that don't depend on the main conversation (file reading, grep investigations, isolated fixes).

Key rule: Sub-agents have zero context from the parent conversation. Paste everything they need directly into the prompt field — file contents, task description, constraints, expected output format.

When to use sub-agents:

  • Investigating what files access a given function/type
  • Running a fix + typecheck + commit as an isolated unit
  • Parallel context gathering (use run_parallel_sub_agents)

When NOT to use sub-agents:

  • Simple single-file reads — just read the file directly
  • Tasks that need the result of a prior tool call from this session

🛠️ Tools Available

Tool When to use
get_repo_summary Session start — see all open issues by state
get_issue_context Resuming — read issue body + all comments
update_issue_status Move issue through workflow, optionally add comment
add_comment Log progress, errors, decisions on an issue
create_issue Found a new bug or task that warrants its own issue
run_sub_agent Delegate isolated investigation or fix
run_parallel_sub_agents Parallel independent investigations
ask_advisor Second opinion before a refactor or architectural decision
run_command + get_process_status Terminal commands (always pair these)
read_file Read a file
write_file Write a file (prefer over Edit for whole-file rewrites)
Edit Targeted in-file edits — always verify with read_file after

⚠️ Edit silently returns "" on both success and failure. Always read the file back after editing to confirm the change applied. If it didn't, use write_file instead.


🏗️ Project Architecture

Stack

  • Framework: Next.js 16, App Router
  • Language: TypeScript 5
  • Styling: Tailwind CSS 4 + shadcn/ui
  • State: Zustand with persist middleware — all game state lives here, no backend
  • Tests: Vitest (unit), Playwright (E2E)
  • Package manager: Bun

Key Directories

src/lib/game/stores/        ← ✅ Active Zustand store modules (USE THESE)
src/lib/game/crafting-actions/  ← Modular crafting actions
src/lib/game/constants/     ← Game data (guardians, spells, skills, elements)
src/lib/game/types/         ← TypeScript interfaces
src/lib/game/utils/         ← Pure utility functions
src/lib/game/store/         ← ⚠️ Legacy store slices (migration in progress)
src/lib/game/store-modules/ ← ⚠️ Legacy modules (being replaced)
src/components/game/        ← All UI components
e2e/                        ← Playwright E2E tests

Store Map

Store File Owns
Game stores/gameStore.ts Core state, tick loop, equip/unequip
Mana stores/manaStore.ts Raw mana, elements, conversion
Combat stores/combatStore.ts Spire, spells, floor progression
Prestige stores/prestigeStore.ts Insight, upgrades, loop end
Skill stores/skillStore.ts Skill levels, studying
UI stores/uiStore.ts Modal state, debug flags

Cross-store reads:

// In combatStore.ts reading mana state:
const manaState = useManaStore.getState();

Effect System — Critical

All stat modifications flow through getUnifiedEffects(state) in effects.ts. Never read skill levels directly to compute a stat. Always use the unified effects object.

Adding a new stat:

  1. Add to ComputedEffects interface in upgrade-effects.types.ts
  2. Add mapping in computeEquipmentEffects() in effects.ts
  3. Apply in game logic via getUnifiedEffects(state)

Crafting System (3-Step Enchantment)

Flow: Design → Prepare → Apply

Action modules:

  • crafting-actions/design-actions.ts
  • crafting-actions/preparation-actions.ts
  • crafting-actions/application-actions.ts

The crafting store is the single source of truth for enchantment selection state. Never use local component state for selected effects.

Skill System (v2)

New skills: define in constants/skills-v2.ts, add to computeStats(). Old skills: still in skill-evolution-modules/ — migration ongoing. Never add new skills to skill-evolution-modules/.


🔍 Impact Analysis (Before Editing Shared Files)

Before modifying any file imported by 3+ other files, check the dependency graph you read at session start. If 5+ files import it, or it's in a circular chain (check docs/circular-deps.txt), use ask_advisor with advisor_type: "review" before touching it.


Definition of Done

A task is complete only when ALL of these are true:

  • Implementation done
  • bun run typecheck — 0 errors
  • bun run lint — 0 errors
  • Relevant tests pass (bun run test)
  • New tests added for any bug fix or new system
  • Changes committed and pushed
  • Gitea issue updated to ai:done (closes the issue)

🚫 Banned — Never Add These

  • Lifesteal / healing mechanics — player cannot take damage
  • Scroll crafting — violates NO INSTANT FINISHING design pillar
  • Ascension skills — deleted
  • LabTab — permanently removed, do not re-add under any name
  • Pause button — does not exist
  • Mana types: life, blood, wood, mental, force — banned

📐 File Size

400 lines maximum per file — enforced by pre-commit hook. If a file approaches this, split it before the hook rejects the commit.


🔮 Mana Types Reference

Base (7): Fire 🔥, Water 💧, Air 🌬️, Earth ⛰️, Light ☀️, Dark 🌑, Death 💀

Utility (1): Transference 🔗

Compound (3): Fire+Earth=Metal, Earth+Water=Sand, Fire+Air=Lightning

Exotic (3): Sand+Sand+Light=Crystal, Fire+Fire+Light=Stellar, Dark+Dark+Death=Void


Quick Reference — Adding Things

New effect:

  1. Define in data/enchantment-effects.ts
  2. Add stat mapping in effects.tscomputeEquipmentEffects()
  3. Apply via getUnifiedEffects(state)

New skill:

  1. Define in constants/skills-v2.ts
  2. Add to computeStats() effect mapping

New spell:

  1. Define in constants/spells.ts
  2. Add enchantment in data/enchantment-effects.ts
  3. Add research skill in constants/skills-v2.ts
  4. Map research to effect in EFFECT_RESEARCH_MAPPING