- Coerce string add/done to list in journal_update tool
- Rewrite _update_journal with section-based parsing (no broken index tracking)
- Add duplicate prevention, blank line collapsing
- Save last DM prompt to session/last_prompt.md so game resumes
from last scene on restart instead of regenerating
- Fix last_narrative -> last_prompt bug in generate() and generate_stream()
- Add **kwargs guard to build_user_message() to catch wrong param names
- Extract _set_llm_env() helper that always sets API key (fallback placeholder)
- Add 30s timeout to all litellm.completion() calls
- Update config model to openai/deepseek-r1 for ramalama
- Replace shifting dots spinner with fixed-character shape morph
- Add tool-use loop in generate_with_tools(): LLM can call read_file,
roll, player_roll, and think tools via tool fenced blocks before
producing the final json response. Tool results fed back for
multi-round reasoning.
- Add player_roll tool: shows a modal overlay asking the player to
physically roll dice and type the result. Blocks the worker thread
via threading.Event until the player responds.
- Add RollModal Screen with dice label, reason, input, and submit.
- Every tool call updates status bar with a specific DM action
message (e.g. "DM is rolling dice", "DM is reading the character
sheet") instead of the generic "weaving the narrative".
The thinking-dot animation uses the last-reported action as its base.
- Replace all user-facing "LLM" references with "DM".
- Remove predefined choices: no more choice buttons or choice
container. Player types freely. System prompt says "never present
predefined choices" and all instruction templates say "let the
player decide".
- Add book story context to system prompt — last 3 turns from
book.md injected as "Recent Story" section.
- Fix build_user_message: detects existing book content; if story
exists, sends "continue from where it left off" instead of
"establish the opening scene".
- Improve parse_response: add JSON-only fallback when no json
fenced block is found.
- LLM progress indicator: animated status bar ('✦ LLM is weaving the
narrative ✦') with rotating dots, shown during processing, hidden on
completion. Disabled input shows 'LLM is thinking...' placeholder.
- Fix input not visible: added 'height: 1fr' to TabbedContent so the PLAY
tab and its input widget fill available vertical space.
- Replace @work(thread=True) with threading.Thread for reliable worker
execution across all environments (headless, test, TUI).
The game is now self-contained: run.sh starts the TUI, which calls the
LLM directly via engine.py. No external agent (OpenCode) needed.
- tools/engine.py: Game engine with prompt builder, litellm client,
response parser (JSON block extraction), and state persistence
- tools/run.py: Refactored TUI with PLAY/CHAR/LOG/BOOK tabs. PLAY tab
has streaming narrative pane, dynamic choice buttons, and text input.
Game loop: scene -> input -> resolve -> archive -> apply -> scene
- session/config.json: LLM provider configuration (model, api_key, etc.)
- AGENTS.md: Updated to document the new architecture
- tools/__init__.py: Package marker for clean imports
- session/turn_description.md, turn_reaction.md: Deprecated - no longer
needed now that the TUI drives the game loop internally
- AGENTS.md: formalized game loop (print turn → wait for reaction →
process → generate next turn), fixed project layout paths
- tools/store_turn.py: new script to append turn to book.md and clear
temp files
- tools/run.py: TUI redesign — TODO always on top, CHARACTER/LOG tabs,
TURN section with rendered markdown, input writes to turn_reaction.md,
scrolling via VerticalScroll, log auto-populates from previous day,
>>--- NOW ---> marker at log end with auto-scroll
- session/book.md: story book (append-only narrative)
- session/log/2026-06-25.md: today's log seeded from previous session
- tools/run.py: pygame.mixer subsystem — polls session/ambience.md,
crossfades tracks, shows ♫ in status bar
- tools/music-fetch.py: search/download from YouTube via yt-dlp,
auto-increment filenames, --replace and --dry-run modes
- tools/ambience.py: companion CLI to set ambience state
- session/ambience.md: current ambience state file (DM writes here)
- session/ambience_options.md: ambience → file mapping table
- session/ambience_sources.md: file → YouTube URL tracking for re-download
- session/audio/ added to .gitignore (audio files not tracked in git)