ralph-code 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ralph/claude_runner.py ADDED
@@ -0,0 +1,22 @@
1
+ """Claude Code CLI integration for ralph-coding application.
2
+
3
+ This module is deprecated. Use harness_runner instead.
4
+ Maintained for backwards compatibility.
5
+ """
6
+
7
+ # Re-export everything from harness_runner for backwards compatibility
8
+ from .harness_runner import (
9
+ HarnessResponse as ClaudeResponse,
10
+ HarnessRunner as ClaudeRunner,
11
+ HarnessResponse,
12
+ HarnessRunner,
13
+ HARNESS_MODEL_MAPPING,
14
+ )
15
+
16
+ __all__ = [
17
+ "ClaudeResponse",
18
+ "ClaudeRunner",
19
+ "HarnessResponse",
20
+ "HarnessRunner",
21
+ "HARNESS_MODEL_MAPPING",
22
+ ]
ralph/colors.py ADDED
@@ -0,0 +1,183 @@
1
+ """Nord Theme color constants for ralph-coding.
2
+
3
+ This module defines the complete Nord Theme color palette (16 colors) for use
4
+ throughout the application. Colors are organized into four semantic groups:
5
+
6
+ POLAR NIGHT (Background colors):
7
+ NORD0-NORD3: Dark background colors, from darkest to lightest.
8
+ Use for backgrounds, panels, and UI chrome.
9
+ - NORD0: Main background
10
+ - NORD1: Elevated surfaces, secondary backgrounds
11
+ - NORD2: Selections, highlights, UI borders
12
+ - NORD3: Comments, inactive elements, subtle text
13
+
14
+ SNOW STORM (Foreground colors):
15
+ NORD4-NORD6: Light text colors, from dimmest to brightest.
16
+ Use for text and foreground elements.
17
+ - NORD4: Main text color
18
+ - NORD5: Brighter text, emphasis
19
+ - NORD6: Brightest text, maximum contrast
20
+
21
+ FROST (Accent colors):
22
+ NORD7-NORD10: Cool accent colors for interactive elements.
23
+ - NORD7 (FROST_TEAL): Success states, secondary accents
24
+ - NORD8 (FROST_CYAN): Primary accent, highlights, links
25
+ - NORD9 (FROST_LIGHT_BLUE): Tertiary accents, decorative
26
+ - NORD10 (FROST_BLUE): Primary buttons, key UI elements
27
+
28
+ AURORA (State colors):
29
+ NORD11-NORD15: Vivid colors for semantic states.
30
+ - NORD11 (AURORA_RED): Errors, destructive actions, critical
31
+ - NORD12 (AURORA_ORANGE): Warnings, caution states
32
+ - NORD13 (AURORA_YELLOW): Attention, pending states, in-progress
33
+ - NORD14 (AURORA_GREEN): Success, completed, positive states
34
+ - NORD15 (AURORA_PURPLE): Special, info, accent states
35
+
36
+ Color Mappings for Application States:
37
+ - Idle/Inactive: NORD3 (dim polar night)
38
+ - Pending: NORD13 (aurora yellow)
39
+ - In Progress: NORD8 (frost cyan) or NORD13 (aurora yellow)
40
+ - Success/Complete: NORD14 (aurora green)
41
+ - Error/Failed: NORD11 (aurora red)
42
+ - Warning: NORD12 (aurora orange)
43
+ - Info/Special: NORD15 (aurora purple)
44
+
45
+ Usage Example:
46
+ from ralph.colors import NORD8, AURORA_GREEN, POLAR_NIGHT_0
47
+
48
+ # Use hex values directly
49
+ print(f"Primary accent: {NORD8}")
50
+
51
+ # Use semantic aliases
52
+ print(f"Success color: {AURORA_GREEN}")
53
+ """
54
+
55
+ from typing import Final
56
+
57
+ # =============================================================================
58
+ # POLAR NIGHT - Background Colors
59
+ # =============================================================================
60
+ # Dark base colors for backgrounds and UI surfaces
61
+
62
+ NORD0: Final[str] = "#2E3440"
63
+ """Darkest background - main application background."""
64
+
65
+ NORD1: Final[str] = "#3B4252"
66
+ """Elevated surfaces - panels, cards, secondary backgrounds."""
67
+
68
+ NORD2: Final[str] = "#434C5E"
69
+ """Selection backgrounds - highlights, UI borders, dividers."""
70
+
71
+ NORD3: Final[str] = "#4C566A"
72
+ """Comments and inactive - subtle text, disabled states."""
73
+
74
+ # Semantic aliases for Polar Night
75
+ POLAR_NIGHT_0: Final[str] = NORD0
76
+ POLAR_NIGHT_1: Final[str] = NORD1
77
+ POLAR_NIGHT_2: Final[str] = NORD2
78
+ POLAR_NIGHT_3: Final[str] = NORD3
79
+
80
+ # Functional aliases for backgrounds
81
+ BG_PRIMARY: Final[str] = NORD0
82
+ BG_SECONDARY: Final[str] = NORD1
83
+ BG_HIGHLIGHT: Final[str] = NORD2
84
+ BG_COMMENT: Final[str] = NORD3
85
+
86
+ # =============================================================================
87
+ # SNOW STORM - Foreground Colors
88
+ # =============================================================================
89
+ # Light colors for text and foreground elements
90
+
91
+ NORD4: Final[str] = "#D8DEE9"
92
+ """Main text color - standard foreground text."""
93
+
94
+ NORD5: Final[str] = "#E5E9F0"
95
+ """Brighter text - emphasis, important content."""
96
+
97
+ NORD6: Final[str] = "#ECEFF4"
98
+ """Brightest text - maximum contrast, headings."""
99
+
100
+ # Semantic aliases for Snow Storm
101
+ SNOW_STORM_0: Final[str] = NORD4
102
+ SNOW_STORM_1: Final[str] = NORD5
103
+ SNOW_STORM_2: Final[str] = NORD6
104
+
105
+ # Functional aliases for foregrounds
106
+ FG_PRIMARY: Final[str] = NORD4
107
+ FG_BRIGHT: Final[str] = NORD5
108
+ FG_BRIGHTEST: Final[str] = NORD6
109
+
110
+ # =============================================================================
111
+ # FROST - Accent Colors
112
+ # =============================================================================
113
+ # Cool accent colors for interactive and decorative elements
114
+
115
+ NORD7: Final[str] = "#8FBCBB"
116
+ """Frost teal - secondary accents, success indicators."""
117
+
118
+ NORD8: Final[str] = "#88C0D0"
119
+ """Frost cyan - primary accent, highlights, links."""
120
+
121
+ NORD9: Final[str] = "#81A1C1"
122
+ """Frost light blue - tertiary accents, decorative elements."""
123
+
124
+ NORD10: Final[str] = "#5E81AC"
125
+ """Frost blue - primary buttons, key interactive elements."""
126
+
127
+ # Semantic aliases for Frost
128
+ FROST_TEAL: Final[str] = NORD7
129
+ FROST_CYAN: Final[str] = NORD8
130
+ FROST_LIGHT_BLUE: Final[str] = NORD9
131
+ FROST_BLUE: Final[str] = NORD10
132
+
133
+ # Functional aliases for accents
134
+ ACCENT_PRIMARY: Final[str] = NORD8
135
+ ACCENT_SECONDARY: Final[str] = NORD7
136
+ ACCENT_TERTIARY: Final[str] = NORD9
137
+ ACCENT_BUTTON: Final[str] = NORD10
138
+
139
+ # =============================================================================
140
+ # AURORA - State Colors
141
+ # =============================================================================
142
+ # Vivid colors for semantic states and feedback
143
+
144
+ NORD11: Final[str] = "#BF616A"
145
+ """Aurora red - errors, destructive actions, critical alerts."""
146
+
147
+ NORD12: Final[str] = "#D08770"
148
+ """Aurora orange - warnings, caution states, attention needed."""
149
+
150
+ NORD13: Final[str] = "#EBCB8B"
151
+ """Aurora yellow - pending states, in-progress, highlights."""
152
+
153
+ NORD14: Final[str] = "#A3BE8C"
154
+ """Aurora green - success, completed, positive confirmation."""
155
+
156
+ NORD15: Final[str] = "#B48EAD"
157
+ """Aurora purple - special states, info, accent highlights."""
158
+
159
+ # Semantic aliases for Aurora
160
+ AURORA_RED: Final[str] = NORD11
161
+ AURORA_ORANGE: Final[str] = NORD12
162
+ AURORA_YELLOW: Final[str] = NORD13
163
+ AURORA_GREEN: Final[str] = NORD14
164
+ AURORA_PURPLE: Final[str] = NORD15
165
+
166
+ # Functional aliases for states
167
+ STATE_ERROR: Final[str] = NORD11
168
+ STATE_WARNING: Final[str] = NORD12
169
+ STATE_PENDING: Final[str] = NORD13
170
+ STATE_SUCCESS: Final[str] = NORD14
171
+ STATE_INFO: Final[str] = NORD15
172
+
173
+ # =============================================================================
174
+ # All colors tuple for iteration
175
+ # =============================================================================
176
+
177
+ ALL_NORD_COLORS: Final[tuple[str, ...]] = (
178
+ NORD0, NORD1, NORD2, NORD3, # Polar Night
179
+ NORD4, NORD5, NORD6, # Snow Storm
180
+ NORD7, NORD8, NORD9, NORD10, # Frost
181
+ NORD11, NORD12, NORD13, NORD14, NORD15, # Aurora
182
+ )
183
+ """All 16 Nord colors in order (NORD0-NORD15)."""
ralph/config.py ADDED
@@ -0,0 +1,227 @@
1
+ """Configuration management for ralph-coding application."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any, Literal
6
+
7
+ from .storage import get_config_path
8
+
9
+
10
+ ErrorMode = Literal["block", "retry", "pause", "skip"]
11
+
12
+
13
+ DEFAULT_CONFIG = {
14
+ "harness": "claude",
15
+ "worker_model": "opus",
16
+ "summary_model": "haiku",
17
+ "max_iterations": 10,
18
+ "max_story_attempts": 3,
19
+ "auto_spec_without_oversight": True,
20
+ "wait_on_rate_limit": True,
21
+ "pause_on_completion": True,
22
+ "always_build_tests": False,
23
+ "branch_prefix": "ralph",
24
+ "on_error": "block",
25
+ }
26
+
27
+
28
+ class Config:
29
+ """Global configuration manager for ralph-coding."""
30
+
31
+ def __init__(self) -> None:
32
+ self._config: dict[str, Any] = {}
33
+ self._load()
34
+
35
+ def _load(self) -> None:
36
+ """Load configuration from disk, using defaults for missing values."""
37
+ config_path = get_config_path()
38
+ needs_save = False
39
+
40
+ if config_path.exists():
41
+ try:
42
+ with open(config_path, "r", encoding="utf-8") as f:
43
+ self._config = json.load(f)
44
+ except (json.JSONDecodeError, IOError):
45
+ self._config = {}
46
+
47
+ # Migration: convert old 'claude_binary' key to 'harness'
48
+ if "claude_binary" in self._config and "harness" not in self._config:
49
+ self._config["harness"] = self._config.pop("claude_binary")
50
+ needs_save = True
51
+
52
+ # Migration: map old 'model' key to worker_model
53
+ if "model" in self._config and "worker_model" not in self._config:
54
+ self._config["worker_model"] = self._config["model"]
55
+ needs_save = True
56
+ if "model" in self._config:
57
+ self._config.pop("model", None)
58
+ needs_save = True
59
+
60
+ # Set worker model if missing (based on harness defaults)
61
+ if "worker_model" not in self._config:
62
+ harness = self._config.get("harness", DEFAULT_CONFIG["harness"])
63
+ if harness == "codex":
64
+ self._config["worker_model"] = "gpt-5.2-codex"
65
+ else:
66
+ self._config["worker_model"] = "opus"
67
+ needs_save = True
68
+
69
+ # Set summary model if missing (based on harness defaults)
70
+ if "summary_model" not in self._config:
71
+ harness = self._config.get("harness", DEFAULT_CONFIG["harness"])
72
+ if harness == "codex":
73
+ self._config["summary_model"] = "gpt-5.2"
74
+ else:
75
+ self._config["summary_model"] = "haiku"
76
+ needs_save = True
77
+
78
+ # Apply defaults for any missing keys
79
+ for key, default in DEFAULT_CONFIG.items():
80
+ if key not in self._config:
81
+ self._config[key] = default
82
+
83
+ # Save if migration was performed
84
+ if needs_save:
85
+ self._save()
86
+
87
+ def _save(self) -> None:
88
+ """Save configuration to disk."""
89
+ config_path = get_config_path()
90
+ config_path.parent.mkdir(parents=True, exist_ok=True)
91
+
92
+ with open(config_path, "w", encoding="utf-8") as f:
93
+ json.dump(self._config, f, indent=2, ensure_ascii=False)
94
+
95
+ @property
96
+ def harness(self) -> str:
97
+ """Path to the harness CLI tool (e.g., claude, aider)."""
98
+ return str(self._config["harness"])
99
+
100
+ @harness.setter
101
+ def harness(self, value: str) -> None:
102
+ self._config["harness"] = value
103
+ self._save()
104
+
105
+ @property
106
+ def worker_model(self) -> str:
107
+ """Model to use for implementation (harness-specific model names)."""
108
+ return str(self._config["worker_model"])
109
+
110
+ @worker_model.setter
111
+ def worker_model(self, value: str) -> None:
112
+ self._config["worker_model"] = value
113
+ self._save()
114
+
115
+ @property
116
+ def summary_model(self) -> str:
117
+ """Model to use for summarization and review tasks."""
118
+ return str(self._config["summary_model"])
119
+
120
+ @summary_model.setter
121
+ def summary_model(self, value: str) -> None:
122
+ self._config["summary_model"] = value
123
+ self._save()
124
+
125
+ @property
126
+ def max_iterations(self) -> int:
127
+ """Maximum iterations for implementation loop."""
128
+ return int(self._config["max_iterations"])
129
+
130
+ @max_iterations.setter
131
+ def max_iterations(self, value: int) -> None:
132
+ self._config["max_iterations"] = max(1, value)
133
+ self._save()
134
+
135
+ @property
136
+ def auto_spec_without_oversight(self) -> bool:
137
+ """Whether to auto-spec tasks without user confirmation."""
138
+ return bool(self._config["auto_spec_without_oversight"])
139
+
140
+ @auto_spec_without_oversight.setter
141
+ def auto_spec_without_oversight(self, value: bool) -> None:
142
+ self._config["auto_spec_without_oversight"] = value
143
+ self._save()
144
+
145
+ @property
146
+ def wait_on_rate_limit(self) -> bool:
147
+ """Whether to wait and retry on rate limit."""
148
+ return bool(self._config["wait_on_rate_limit"])
149
+
150
+ @wait_on_rate_limit.setter
151
+ def wait_on_rate_limit(self, value: bool) -> None:
152
+ self._config["wait_on_rate_limit"] = value
153
+ self._save()
154
+
155
+ @property
156
+ def pause_on_completion(self) -> bool:
157
+ """Whether to pause after completing all tasks."""
158
+ return bool(self._config["pause_on_completion"])
159
+
160
+ @pause_on_completion.setter
161
+ def pause_on_completion(self, value: bool) -> None:
162
+ self._config["pause_on_completion"] = value
163
+ self._save()
164
+
165
+ @property
166
+ def always_build_tests(self) -> bool:
167
+ """Whether to always build tests for implementations."""
168
+ return bool(self._config["always_build_tests"])
169
+
170
+ @always_build_tests.setter
171
+ def always_build_tests(self, value: bool) -> None:
172
+ self._config["always_build_tests"] = value
173
+ self._save()
174
+
175
+ @property
176
+ def max_story_attempts(self) -> int:
177
+ """Maximum attempts per story before marking as blocked."""
178
+ return int(self._config.get("max_story_attempts", 3))
179
+
180
+ @max_story_attempts.setter
181
+ def max_story_attempts(self, value: int) -> None:
182
+ self._config["max_story_attempts"] = max(1, value)
183
+ self._save()
184
+
185
+ @property
186
+ def branch_prefix(self) -> str:
187
+ """Prefix for feature branch names (e.g., 'ralph' creates 'ralph/feature-name')."""
188
+ return str(self._config.get("branch_prefix", "ralph"))
189
+
190
+ @branch_prefix.setter
191
+ def branch_prefix(self, value: str) -> None:
192
+ self._config["branch_prefix"] = value
193
+ self._save()
194
+
195
+ @property
196
+ def on_error(self) -> ErrorMode:
197
+ """Error handling mode (block, retry, pause, skip)."""
198
+ value: ErrorMode = self._config["on_error"]
199
+ return value
200
+
201
+ @on_error.setter
202
+ def on_error(self, value: ErrorMode) -> None:
203
+ if value not in ("block", "retry", "pause", "skip"):
204
+ raise ValueError(f"Invalid error mode: {value}")
205
+ self._config["on_error"] = value
206
+ self._save()
207
+
208
+ def to_dict(self) -> dict[str, Any]:
209
+ """Return a copy of the configuration as a dictionary."""
210
+ return self._config.copy()
211
+
212
+ def reset_to_defaults(self) -> None:
213
+ """Reset all settings to defaults."""
214
+ self._config = DEFAULT_CONFIG.copy()
215
+ self._save()
216
+
217
+
218
+ # Global config instance
219
+ _config: Config | None = None
220
+
221
+
222
+ def get_config() -> Config:
223
+ """Get the global configuration instance."""
224
+ global _config
225
+ if _config is None:
226
+ _config = Config()
227
+ return _config