diff --git a/CLAUDE.md b/CLAUDE.md index f29c034..70e7809 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -135,6 +135,7 @@ Player data lives in `~/.storied/players/{player_id}/`: ├── character.md # Free-text backstory ├── notes.md # Appending journal of player observations ├── session.md # Current situation, location, present, threads +├── input-history # Readline history of what the player typed └── worlds/{world_id}/ # Player's discovered knowledge per world ``` diff --git a/src/storied/advancement.py b/src/storied/advancement.py index 3f2ef05..bade457 100644 --- a/src/storied/advancement.py +++ b/src/storied/advancement.py @@ -79,7 +79,7 @@ def build_advancement_context( def evaluate_advancement( world_id: str = "default", player_id: str = "default", - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", on_progress: Callable[[str], None] | None = None, ) -> AdvancementResult: """Evaluate whether the character has earned a level-up.""" @@ -155,7 +155,7 @@ class BackgroundAdvancement: self, world_id: str, player_id: str, - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", interval: int = 5, ): self._world_id = world_id diff --git a/src/storied/claude.py b/src/storied/claude.py index f8527ef..91d3c7f 100644 --- a/src/storied/claude.py +++ b/src/storied/claude.py @@ -206,7 +206,7 @@ def stream_with_tools( # pragma: no cover user_message: str, mcp_url: str, *, - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", effort: str = "medium", resume_session_id: str | None = None, cwd: Path | None = None, @@ -269,7 +269,7 @@ def run_with_tools( user_message: str, mcp_url: str, *, - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", effort: str = "high", on_tool_start: Callable[[str], None] | None = None, cwd: Path | None = None, @@ -343,7 +343,7 @@ def run_prompt( # pragma: no cover system_prompt: str, user_message: str, *, - model: str = "claude-haiku-4-5-20251001", + model: str = "claude-haiku-4-5", effort: str | None = None, timeout: int = 30, ) -> str | None: diff --git a/src/storied/cli.py b/src/storied/cli.py index 9855b5b..768a9fd 100644 --- a/src/storied/cli.py +++ b/src/storied/cli.py @@ -605,8 +605,6 @@ def _run_engine_loop( def cmd_play(args: argparse.Namespace) -> int: """Start an interactive DM session.""" import atexit - - # Set up readline history import contextlib import readline import shutil @@ -619,24 +617,6 @@ def cmd_play(args: argparse.Namespace) -> int: from storied.character import load_character from storied.engine import DMEngine - history_file = Path.home() / ".storied_history" - with contextlib.suppress(FileNotFoundError): - readline.read_history_file(history_file) - readline.set_history_length(1000) - atexit.register(readline.write_history_file, history_file) - - # Set up slash command autocomplete - def slash_completer(text: str, state: int) -> str | None: - if text.startswith("/"): - matches = [cmd for cmd in SLASH_COMMANDS if cmd.startswith(text)] - return matches[state] if state < len(matches) else None - return None - - readline.set_completer_delims(readline.get_completer_delims().replace("/", "")) - readline.set_completer(slash_completer) - readline.parse_and_bind("tab: complete") - readline.parse_and_bind("set enable-bracketed-paste on") - console = Console() world_id = args.world if args.world else "default" player_id = "default" @@ -667,7 +647,27 @@ def cmd_play(args: argparse.Namespace) -> int: os.environ["STORIED_HOME"] = str(data_home()) - from storied.paths import world_path + from storied.paths import player_path, world_path + + # History belongs to the player, so this runs after configure(). A + # --base-path or sandbox run then writes it alongside its own data. + history_file = player_path(player_id) / "input-history" + history_file.parent.mkdir(parents=True, exist_ok=True) + with contextlib.suppress(FileNotFoundError): + readline.read_history_file(history_file) + readline.set_history_length(1000) + atexit.register(readline.write_history_file, history_file) + + def slash_completer(text: str, state: int) -> str | None: + if text.startswith("/"): + matches = [cmd for cmd in SLASH_COMMANDS if cmd.startswith(text)] + return matches[state] if state < len(matches) else None + return None + + readline.set_completer_delims(readline.get_completer_delims().replace("/", "")) + readline.set_completer(slash_completer) + readline.parse_and_bind("tab: complete") + readline.parse_and_bind("set enable-bracketed-paste on") transcript_path = Path(args.transcript) if args.transcript else None @@ -1185,8 +1185,8 @@ def build_parser() -> argparse.ArgumentParser: seed_parser.add_argument( "--model", "-m", - default="claude-opus-4-7", - help="Model to use for seeding (default: claude-opus-4-7)", + default="claude-opus-5", + help="Model to use for seeding (default: claude-opus-5)", ) seed_parser.add_argument( "--force", diff --git a/src/storied/engine.py b/src/storied/engine.py index 73a08a7..58d2697 100644 --- a/src/storied/engine.py +++ b/src/storied/engine.py @@ -72,7 +72,7 @@ class DMEngine: self, world_id: str = "default", player_id: str = "default", - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", prompt_name: str = "dm-system", transcript_path: Path | None = None, ): diff --git a/src/storied/planner.py b/src/storied/planner.py index 9594f21..6308454 100644 --- a/src/storied/planner.py +++ b/src/storied/planner.py @@ -292,7 +292,7 @@ class PlanResult: def plan_world( world_id: str = "default", player_id: str = "default", - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", threshold: float = 0.7, max_entities: int = 8, dry_run: bool = False, @@ -385,7 +385,7 @@ class SeedResult: def seed_world( world_id: str = "default", player_id: str = "default", - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", on_progress: Callable[[str], None] | None = None, ) -> SeedResult: """Build the initial world from a character sheet.""" @@ -467,7 +467,7 @@ def seed_world( def plot_arc( world_id: str = "default", player_id: str = "default", - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", on_progress: Callable[[str], None] | None = None, ) -> SeedResult: """Two-pass arc planning: cold draft → architect commit. @@ -660,7 +660,7 @@ def build_tick_context( def tick_world( # pragma: no cover world_id: str = "default", player_id: str = "default", - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", on_progress: Callable[[str], None] | None = None, ) -> TickResult: """Advance the world by evaluating Will triggers and adding small changes. @@ -730,7 +730,7 @@ class BackgroundTicker: self, world_id: str, player_id: str, - model: str = "claude-opus-4-7", + model: str = "claude-opus-5", ): self._world_id = world_id self._player_id = player_id diff --git a/tests/test_arc_planner.py b/tests/test_arc_planner.py index 5cb2092..5566a15 100644 --- a/tests/test_arc_planner.py +++ b/tests/test_arc_planner.py @@ -277,7 +277,7 @@ class TestPlotArc: # Pass A: cold draft via run_prompt mock_run_prompt.assert_called_once() assert mock_run_prompt.call_args.kwargs["effort"] == "max" - assert mock_run_prompt.call_args.kwargs["model"] == "claude-opus-4-7" + assert mock_run_prompt.call_args.kwargs["model"] == "claude-opus-5" # Pass B: architect via run_with_tools mock_run_with_tools.assert_called_once() diff --git a/tests/test_planner.py b/tests/test_planner.py index 3ee7ca9..630cfaf 100644 --- a/tests/test_planner.py +++ b/tests/test_planner.py @@ -372,7 +372,7 @@ class TestPlanWorld: result = plan_world( world_id=populated_world.world_id, player_id=populated_world.player_id, - model="claude-opus-4-7", + model="claude-opus-5", ) assert result.dry_run is False @@ -429,7 +429,7 @@ class TestPlanWorld: result = plan_world( world_id=populated_world.world_id, player_id=populated_world.player_id, - model="claude-opus-4-7", + model="claude-opus-5", ) assert result.tool_calls == 1